Commit 3ab43a56 authored by Honfika's avatar Honfika

TunnelConnectResponse event + minor improvements

parent ea86d5a7
......@@ -120,12 +120,12 @@ namespace Titanium.Web.Proxy.Examples.Basic
//proxyServer.CertificateManager.RemoveTrustedRootCertificates();
}
private async Task OnTunnelConnectRequest(object sender, TunnelConnectEventArgs e)
private async Task OnTunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{
Console.WriteLine("Tunnel to: " + e.ConnectRequest.Host);
Console.WriteLine("Tunnel to: " + e.WebSession.Request.Host);
}
private async Task OnTunnelConnectResponse(object sender, TunnelConnectEventArgs e)
private async Task OnTunnelConnectResponse(object sender, TunnelConnectSessionEventArgs e)
{
}
......
......@@ -7,12 +7,13 @@ using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments
{
public class TunnelConnectEventArgs : EventArgs
public class TunnelConnectSessionEventArgs : SessionEventArgs
{
public bool IsHttps { get; set; }
public Request ConnectRequest { get; set; }
public Request ConnectResponse { get; set; }
public TunnelConnectSessionEventArgs() : base(0, null)
{
}
}
}
......@@ -30,13 +30,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content encoding for this response
/// </summary>
public string ContentEncoding
{
get
{
return ResponseHeaders.GetHeaderValueOrNull("content-encoding")?.Trim();
}
}
public string ContentEncoding => ResponseHeaders.GetHeaderValueOrNull("content-encoding")?.Trim();
/// <summary>
/// Http version
......@@ -67,13 +61,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Content type of this response
/// </summary>
public string ContentType
{
get
{
return ResponseHeaders.GetHeaderValueOrNull("content-type");
}
}
public string ContentType => ResponseHeaders.GetHeaderValueOrNull("content-type");
/// <summary>
/// Length of response body
......
......@@ -4,6 +4,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models;
......@@ -12,31 +13,29 @@ namespace Titanium.Web.Proxy
{
public partial class ProxyServer
{
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, Request request)
private async Task<bool> CheckAuthorization(StreamWriter clientStreamWriter, SessionEventArgs session)
{
if (AuthenticateUserFunc == null)
{
return true;
}
var httpHeaders = request.RequestHeaders.ToArray();
var httpHeaders = session.WebSession.Request.RequestHeaders.ToArray();
try
{
if (httpHeaders.All(t => t.Name != "Proxy-Authorization"))
var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (header == null)
{
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Required");
session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Required");
return false;
}
var header = httpHeaders.FirstOrDefault(t => t.Name == "Proxy-Authorization");
if (header == null)
throw new NullReferenceException();
string headerValue = header.Value.Trim();
if (!headerValue.StartsWith("basic", StringComparison.CurrentCultureIgnoreCase))
{
//Return not authorized
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
return false;
}
......@@ -46,7 +45,7 @@ namespace Titanium.Web.Proxy
if (decoded.Contains(":") == false)
{
//Return not authorized
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
return false;
}
......@@ -59,21 +58,25 @@ namespace Titanium.Web.Proxy
ExceptionFunc(new ProxyAuthorizationException("Error whilst authorizing request", e, httpHeaders));
//Return not authorized
await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
session.WebSession.Response = await SendAuthentication407Response(clientStreamWriter, "Proxy Authentication Invalid");
return false;
}
}
private async Task SendAuthentication407Response(StreamWriter clientStreamWriter, string description)
private async Task<Response> SendAuthentication407Response(StreamWriter clientStreamWriter, string description)
{
await WriteResponseStatus(HttpHeader.Version11, "407", description, clientStreamWriter);
var response = new Response();
var response = new Response
{
HttpVersion = HttpHeader.Version11,
ResponseStatusCode = "407",
ResponseStatusDescription = description
};
response.ResponseHeaders.AddHeader("Proxy-Authenticate", "Basic realm=\"TitaniumProxy\"");
response.ResponseHeaders.AddHeader("Proxy-Connection", "close");
await WriteResponseHeaders(clientStreamWriter, response);
await clientStreamWriter.WriteLineAsync();
await WriteResponse(response, clientStreamWriter);
return response;
}
}
}
......@@ -172,9 +172,15 @@ namespace Titanium.Web.Proxy
/// </summary>
public event Func<object, SessionEventArgs, Task> BeforeResponse;
public event Func<object, TunnelConnectEventArgs, Task> TunnelConnectRequest;
/// <summary>
/// Intercept tunnel connect reques
/// </summary>
public event Func<object, TunnelConnectSessionEventArgs, Task> TunnelConnectRequest;
public event Func<object, TunnelConnectEventArgs, Task> TunnelConnectResponse;
/// <summary>
/// Intercept tunnel connect response
/// </summary>
public event Func<object, TunnelConnectSessionEventArgs, Task> TunnelConnectResponse;
/// <summary>
/// External proxy for Http
......
......@@ -96,28 +96,40 @@ namespace Titanium.Web.Proxy
connectRequest = new ConnectRequest();
await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders);
var connectArgs = new TunnelConnectSessionEventArgs();
connectArgs.WebSession.Request = connectRequest;
if (TunnelConnectRequest != null)
{
await TunnelConnectRequest.InvokeParallelAsync(this, connectArgs);
}
if (httpRemoteUri.Port == 80)
{
// why is this needed? HTTPS not allowed on port 80?
excluded = true;
}
if (!excluded && await CheckAuthorization(clientStreamWriter, connectRequest) == false)
if (!excluded && await CheckAuthorization(clientStreamWriter, connectArgs) == false)
{
if (TunnelConnectResponse != null)
{
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs);
}
return;
}
//write back successfull CONNECT response
await WriteConnectResponse(clientStreamWriter, version);
connectArgs.WebSession.Response = CreateConnectResponse(version);
await WriteResponse(connectArgs.WebSession.Response, clientStreamWriter);
bool isClientHello = await HttpsTools.IsClientHello(clientStream);
if (TunnelConnectRequest != null)
if (TunnelConnectResponse != null)
{
var args = new TunnelConnectEventArgs();
args.IsHttps = isClientHello;
args.ConnectRequest = connectRequest;
await TunnelConnectRequest.InvokeParallelAsync(this, args);
connectArgs.IsHttps = isClientHello;
await TunnelConnectResponse.InvokeParallelAsync(this, connectArgs);
}
if (!excluded && isClientHello)
......@@ -340,7 +352,7 @@ namespace Titanium.Web.Proxy
args.ProxyClient.ClientStreamWriter = clientStreamWriter;
//proxy authorization check
if (httpsConnectHostname == null && await CheckAuthorization(clientStreamWriter, args.WebSession.Request) == false)
if (httpsConnectHostname == null && await CheckAuthorization(clientStreamWriter, args) == false)
{
args.Dispose();
break;
......@@ -586,15 +598,19 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Write successfull CONNECT response to client
/// </summary>
/// <param name="clientStreamWriter"></param>
/// <param name="httpVersion"></param>
/// <returns></returns>
private async Task WriteConnectResponse(StreamWriter clientStreamWriter, Version httpVersion)
private ConnectResponse CreateConnectResponse(Version httpVersion)
{
await clientStreamWriter.WriteLineAsync($"HTTP/{httpVersion.Major}.{httpVersion.Minor} 200 Connection established");
await clientStreamWriter.WriteLineAsync($"Timestamp: {DateTime.Now}");
await clientStreamWriter.WriteLineAsync();
await clientStreamWriter.FlushAsync();
var response = new ConnectResponse
{
HttpVersion = httpVersion,
ResponseStatusCode = "200",
ResponseStatusDescription = "Connection established"
};
response.ResponseHeaders.AddHeader("Timestamp", DateTime.Now.ToString());
return response;
}
/// <summary>
......
......@@ -149,6 +149,19 @@ namespace Titanium.Web.Proxy
return await compressor.Compress(responseBodyStream);
}
/// <summary>
/// Writes the response.
/// </summary>
/// <param name="response"></param>
/// <param name="responseWriter"></param>
/// <param name="flush"></param>
/// <returns></returns>
private async Task WriteResponse(Response response, StreamWriter responseWriter, bool flush = true)
{
await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription, responseWriter);
await WriteResponseHeaders(responseWriter, response, flush);
}
/// <summary>
/// Write response status
/// </summary>
......@@ -167,8 +180,9 @@ namespace Titanium.Web.Proxy
/// </summary>
/// <param name="responseWriter"></param>
/// <param name="response"></param>
/// <param name="flush"></param>
/// <returns></returns>
private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response)
private async Task WriteResponseHeaders(StreamWriter responseWriter, Response response, bool flush = true)
{
FixProxyHeaders(response.ResponseHeaders);
......@@ -178,7 +192,10 @@ namespace Titanium.Web.Proxy
}
await responseWriter.WriteLineAsync();
await responseWriter.FlushAsync();
if (flush)
{
await responseWriter.FlushAsync();
}
}
/// <summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment