Commit 3ab43a56 authored by Honfika's avatar Honfika

TunnelConnectResponse event + minor improvements

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