Commit 24bc2f2b authored by justcoding121's avatar justcoding121 Committed by justcoding121

Merge pull request #286 from honfika/develop

Use int type for response code. Parse requesr/response first lines in…
parents 81975c47 cdd8aa9c
...@@ -191,7 +191,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -191,7 +191,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
//if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return; //if (!e.ProxySession.Request.Host.Equals("medeczane.sgk.gov.tr")) return;
if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST") if (e.WebSession.Request.Method == "GET" || e.WebSession.Request.Method == "POST")
{ {
if (e.WebSession.Response.ResponseStatusCode == "200") if (e.WebSession.Response.ResponseStatusCode == (int)HttpStatusCode.OK)
{ {
if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html")) if (e.WebSession.Response.ContentType != null && e.WebSession.Response.ContentType.Trim().ToLower().Contains("text/html"))
{ {
......
...@@ -13,8 +13,12 @@ ...@@ -13,8 +13,12 @@
<ColumnDefinition Width="3" /> <ColumnDefinition Width="3" />
<ColumnDefinition /> <ColumnDefinition />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" /> <Grid.RowDefinitions>
<ListView Grid.Column="0" HorizontalAlignment="Stretch" ItemsSource="{Binding Sessions}" SelectedItem="{Binding SelectedSession}" <RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GridSplitter Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch" />
<ListView Grid.Column="0" Grid.Row="0" HorizontalAlignment="Stretch" ItemsSource="{Binding Sessions}" SelectedItem="{Binding SelectedSession}"
KeyDown="ListViewSessions_OnKeyDown"> KeyDown="ListViewSessions_OnKeyDown">
<ListView.View> <ListView.View>
<GridView> <GridView>
...@@ -29,7 +33,7 @@ ...@@ -29,7 +33,7 @@
</GridView> </GridView>
</ListView.View> </ListView.View>
</ListView> </ListView>
<TabControl Grid.Column="2"> <TabControl Grid.Column="2" Grid.Row="0">
<TabItem Header="Session"> <TabItem Header="Session">
<Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Background="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
...@@ -41,5 +45,11 @@ ...@@ -41,5 +45,11 @@
</Grid> </Grid>
</TabItem> </TabItem>
</TabControl> </TabControl>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<TextBlock Text="ClientConnectionCount:" />
<TextBlock Text="{Binding ClientConnectionCount}" Margin="10,0,20,0" />
<TextBlock Text="ServerConnectionCount:" />
<TextBlock Text="{Binding ServerConnectionCount}" Margin="10,0,20,0" />
</StackPanel>
</Grid> </Grid>
</Window> </Window>
...@@ -44,6 +44,24 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -44,6 +44,24 @@ namespace Titanium.Web.Proxy.Examples.Wpf
} }
} }
public static readonly DependencyProperty ClientConnectionCountProperty = DependencyProperty.Register(
nameof(ClientConnectionCount), typeof(int), typeof(MainWindow), new PropertyMetadata(default(int)));
public int ClientConnectionCount
{
get { return (int)GetValue(ClientConnectionCountProperty); }
set { SetValue(ClientConnectionCountProperty, value); }
}
public static readonly DependencyProperty ServerConnectionCountProperty = DependencyProperty.Register(
nameof(ServerConnectionCount), typeof(int), typeof(MainWindow), new PropertyMetadata(default(int)));
public int ServerConnectionCount
{
get { return (int)GetValue(ServerConnectionCountProperty); }
set { SetValue(ServerConnectionCountProperty, value); }
}
private readonly Dictionary<SessionEventArgs, SessionListItem> sessionDictionary = new Dictionary<SessionEventArgs, SessionListItem>(); private readonly Dictionary<SessionEventArgs, SessionListItem> sessionDictionary = new Dictionary<SessionEventArgs, SessionListItem>();
private SessionListItem selectedSession; private SessionListItem selectedSession;
...@@ -59,6 +77,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -59,6 +77,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
proxyServer.BeforeResponse += ProxyServer_BeforeResponse; proxyServer.BeforeResponse += ProxyServer_BeforeResponse;
proxyServer.TunnelConnectRequest += ProxyServer_TunnelConnectRequest; proxyServer.TunnelConnectRequest += ProxyServer_TunnelConnectRequest;
proxyServer.TunnelConnectResponse += ProxyServer_TunnelConnectResponse; proxyServer.TunnelConnectResponse += ProxyServer_TunnelConnectResponse;
proxyServer.ClientConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ClientConnectionCount = proxyServer.ClientConnectionCount; }); };
proxyServer.ServerConnectionCountChanged += delegate { Dispatcher.Invoke(() => { ServerConnectionCount = proxyServer.ServerConnectionCount; }); };
proxyServer.Start(); proxyServer.Start();
proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp); proxyServer.SetAsSystemProxy(explicitEndPoint, ProxyProtocolType.AllHttp);
...@@ -68,7 +88,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -68,7 +88,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_TunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e) private async Task ProxyServer_TunnelConnectRequest(object sender, TunnelConnectSessionEventArgs e)
{ {
Dispatcher.Invoke(() => await Dispatcher.InvokeAsync(() =>
{ {
AddSession(e); AddSession(e);
}); });
...@@ -76,7 +96,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -76,7 +96,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_TunnelConnectResponse(object sender, SessionEventArgs e) private async Task ProxyServer_TunnelConnectResponse(object sender, SessionEventArgs e)
{ {
Dispatcher.Invoke(() => await Dispatcher.InvokeAsync(() =>
{ {
SessionListItem item; SessionListItem item;
if (sessionDictionary.TryGetValue(e, out item)) if (sessionDictionary.TryGetValue(e, out item))
...@@ -93,7 +113,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -93,7 +113,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e) private async Task ProxyServer_BeforeRequest(object sender, SessionEventArgs e)
{ {
SessionListItem item = null; SessionListItem item = null;
Dispatcher.Invoke(() => await Dispatcher.InvokeAsync(() =>
{ {
item = AddSession(e); item = AddSession(e);
}); });
...@@ -107,7 +127,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -107,7 +127,7 @@ namespace Titanium.Web.Proxy.Examples.Wpf
private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e) private async Task ProxyServer_BeforeResponse(object sender, SessionEventArgs e)
{ {
SessionListItem item = null; SessionListItem item = null;
Dispatcher.Invoke(() => await Dispatcher.InvokeAsync(() =>
{ {
SessionListItem item2; SessionListItem item2;
if (sessionDictionary.TryGetValue(e, out item2)) if (sessionDictionary.TryGetValue(e, out item2))
......
...@@ -100,7 +100,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -100,7 +100,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
{ {
var request = SessionArgs.WebSession.Request; var request = SessionArgs.WebSession.Request;
var response = SessionArgs.WebSession.Response; var response = SessionArgs.WebSession.Response;
StatusCode = response?.ResponseStatusCode ?? "-"; int statusCode = response?.ResponseStatusCode ?? 0;
StatusCode = statusCode == 0 ? "-" : statusCode.ToString();
Protocol = request.RequestUri.Scheme; Protocol = request.RequestUri.Scheme;
if (SessionArgs is TunnelConnectSessionEventArgs) if (SessionArgs is TunnelConnectSessionEventArgs)
......
...@@ -57,9 +57,9 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -57,9 +57,9 @@ namespace Titanium.Web.Proxy.EventArguments
get { return reRequest; } get { return reRequest; }
set set
{ {
if (WebSession.Response.ResponseStatusCode == null) if (WebSession.Response.ResponseStatusCode == 0)
{ {
throw new Exception("Response status code is null. Cannot request again a request " + "which was never send to server."); throw new Exception("Response status code is empty. Cannot request again a request " + "which was never send to server.");
} }
reRequest = value; reRequest = value;
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -114,18 +115,21 @@ namespace Titanium.Web.Proxy.Http ...@@ -114,18 +115,21 @@ namespace Titanium.Web.Proxy.Http
{ {
if (Request.ExpectContinue) if (Request.ExpectContinue)
{ {
var httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
string responseStatusCode = httpResult[1].Trim();
string responseStatusDescription = httpResult[2].Trim(); Version version;
int responseStatusCode;
string responseStatusDescription;
Response.ParseResponseLine(httpStatus, out version, out responseStatusCode, out responseStatusDescription);
//find if server is willing for expect continue //find if server is willing for expect continue
if (responseStatusCode.Equals("100") if (responseStatusCode == (int)HttpStatusCode.Continue
&& responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase)) && responseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{ {
Request.Is100Continue = true; Request.Is100Continue = true;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
} }
else if (responseStatusCode.Equals("417") else if (responseStatusCode == (int)HttpStatusCode.ExpectationFailed
&& responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase)) && responseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{ {
Request.ExpectationFailed = true; Request.ExpectationFailed = true;
...@@ -142,54 +146,49 @@ namespace Titanium.Web.Proxy.Http ...@@ -142,54 +146,49 @@ namespace Titanium.Web.Proxy.Http
internal async Task ReceiveResponse() internal async Task ReceiveResponse()
{ {
//return if this is already read //return if this is already read
if (Response.ResponseStatusCode != null) if (Response.ResponseStatusCode != 0)
return; return;
string line = await ServerConnection.StreamReader.ReadLineAsync(); string httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
if (line == null) if (httpStatus == null)
{ {
throw new IOException(); throw new IOException();
} }
var httpResult = line.Split(ProxyConstants.SpaceSplit, 3); if (httpStatus == string.Empty)
if (string.IsNullOrEmpty(httpResult[0]))
{ {
//Empty content in first-line, try again //Empty content in first-line, try again
httpResult = (await ServerConnection.StreamReader.ReadLineAsync()).Split(ProxyConstants.SpaceSplit, 3); httpStatus = await ServerConnection.StreamReader.ReadLineAsync();
} }
string httpVersion = httpResult[0]; Version version;
int statusCode;
var version = HttpHeader.Version11; string statusDescription;
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase)) Response.ParseResponseLine(httpStatus, out version, out statusCode, out statusDescription);
{
version = HttpHeader.Version10;
}
Response.HttpVersion = version; Response.HttpVersion = version;
Response.ResponseStatusCode = httpResult[1].Trim(); Response.ResponseStatusCode = statusCode;
Response.ResponseStatusDescription = httpResult[2].Trim(); Response.ResponseStatusDescription = statusDescription;
//For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request //For HTTP 1.1 comptibility server may send expect-continue even if not asked for it in request
if (Response.ResponseStatusCode.Equals("100") if (Response.ResponseStatusCode == (int)HttpStatusCode.Continue
&& Response.ResponseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase)) && Response.ResponseStatusDescription.Equals("continue", StringComparison.CurrentCultureIgnoreCase))
{ {
//Read the next line after 100-continue //Read the next line after 100-continue
Response.Is100Continue = true; Response.Is100Continue = true;
Response.ResponseStatusCode = null; Response.ResponseStatusCode = 0;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
//now receive response //now receive response
await ReceiveResponse(); await ReceiveResponse();
return; return;
} }
if (Response.ResponseStatusCode.Equals("417") if (Response.ResponseStatusCode == (int)HttpStatusCode.ExpectationFailed
&& Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase)) && Response.ResponseStatusDescription.Equals("expectation failed", StringComparison.CurrentCultureIgnoreCase))
{ {
//read next line after expectation failed response //read next line after expectation failed response
Response.ExpectationFailed = true; Response.ExpectationFailed = true;
Response.ResponseStatusCode = null; Response.ResponseStatusCode = 0;
await ServerConnection.StreamReader.ReadLineAsync(); await ServerConnection.StreamReader.ReadLineAsync();
//now receive response //now receive response
await ReceiveResponse(); await ReceiveResponse();
......
...@@ -3,6 +3,8 @@ using System.Collections.Generic; ...@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -228,6 +230,57 @@ namespace Titanium.Web.Proxy.Http ...@@ -228,6 +230,57 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl, out Version version)
{
//break up the line into three components (method, remote URL & Http Version)
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);
if (httpCmdSplit.Length < 2)
{
throw new Exception("Invalid HTTP request line: " + httpCmd);
}
//Find the request Verb
httpMethod = httpCmdSplit[0];
if (!IsAllUpper(httpMethod))
{
//method should be upper cased: https://tools.ietf.org/html/rfc7231#section-4
//todo: create protocol violation message
//fix it
httpMethod = httpMethod.ToUpper();
}
httpUrl = httpCmdSplit[1];
//parse the HTTP version
version = HttpHeader.Version11;
if (httpCmdSplit.Length == 3)
{
string httpVersion = httpCmdSplit[2].Trim();
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
{
version = HttpHeader.Version10;
}
}
}
private static bool IsAllUpper(string input)
{
for (int i = 0; i < input.Length; i++)
{
char ch = input[i];
if (ch < 'A' || ch > 'Z')
{
return false;
}
}
return true;
}
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
......
...@@ -4,6 +4,7 @@ using System.Linq; ...@@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http namespace Titanium.Web.Proxy.Http
{ {
...@@ -15,7 +16,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,7 +16,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Response Status Code. /// Response Status Code.
/// </summary> /// </summary>
public string ResponseStatusCode { get; set; } public int ResponseStatusCode { get; set; }
/// <summary> /// <summary>
/// Response Status description. /// Response Status description.
...@@ -208,6 +209,26 @@ namespace Titanium.Web.Proxy.Http ...@@ -208,6 +209,26 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal static void ParseResponseLine(string httpStatus, out Version version, out int statusCode, out string statusDescription)
{
var httpResult = httpStatus.Split(ProxyConstants.SpaceSplit, 3);
if (httpResult.Length != 3)
{
throw new Exception("Invalid HTTP status line: " + httpStatus);
}
string httpVersion = httpResult[0];
version = HttpHeader.Version11;
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
{
version = HttpHeader.Version10;
}
statusCode = int.Parse(httpResult[1]);
statusDescription = httpResult[2];
}
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
......
...@@ -13,7 +13,10 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -13,7 +13,10 @@ namespace Titanium.Web.Proxy.Http.Responses
/// <param name="status"></param> /// <param name="status"></param>
public GenericResponse(HttpStatusCode status) public GenericResponse(HttpStatusCode status)
{ {
ResponseStatusCode = ((int)status).ToString(); ResponseStatusCode = (int)status;
//todo: this is not really correct, status description should contain spaces, too
//see: https://tools.ietf.org/html/rfc7231#section-6.1
ResponseStatusDescription = status.ToString(); ResponseStatusDescription = status.ToString();
} }
...@@ -22,7 +25,7 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -22,7 +25,7 @@ namespace Titanium.Web.Proxy.Http.Responses
/// </summary> /// </summary>
/// <param name="statusCode"></param> /// <param name="statusCode"></param>
/// <param name="statusDescription"></param> /// <param name="statusDescription"></param>
public GenericResponse(string statusCode, string statusDescription) public GenericResponse(int statusCode, string statusDescription)
{ {
ResponseStatusCode = statusCode; ResponseStatusCode = statusCode;
ResponseStatusDescription = statusDescription; ResponseStatusDescription = statusDescription;
......
namespace Titanium.Web.Proxy.Http.Responses using System.Net;
namespace Titanium.Web.Proxy.Http.Responses
{ {
/// <summary> /// <summary>
/// 200 Ok response /// 200 Ok response
...@@ -10,8 +12,8 @@ ...@@ -10,8 +12,8 @@
/// </summary> /// </summary>
public OkResponse() public OkResponse()
{ {
ResponseStatusCode = "200"; ResponseStatusCode = (int)HttpStatusCode.OK;
ResponseStatusDescription = "Ok"; ResponseStatusDescription = "OK";
} }
} }
} }
namespace Titanium.Web.Proxy.Http.Responses using System.Net;
namespace Titanium.Web.Proxy.Http.Responses
{ {
/// <summary> /// <summary>
/// Redirect response /// Redirect response
...@@ -10,7 +12,7 @@ ...@@ -10,7 +12,7 @@
/// </summary> /// </summary>
public RedirectResponse() public RedirectResponse()
{ {
ResponseStatusCode = "302"; ResponseStatusCode = (int)HttpStatusCode.Found;
ResponseStatusDescription = "Found"; ResponseStatusDescription = "Found";
} }
} }
......
...@@ -124,7 +124,7 @@ namespace Titanium.Web.Proxy.Network.Tcp ...@@ -124,7 +124,7 @@ namespace Titanium.Web.Proxy.Network.Tcp
throw; throw;
} }
Interlocked.Increment(ref server.serverConnectionCount); server.UpdateServerConnectionCount(true);
return new TcpConnection return new TcpConnection
{ {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
...@@ -68,7 +69,7 @@ namespace Titanium.Web.Proxy ...@@ -68,7 +69,7 @@ namespace Titanium.Web.Proxy
var response = new Response var response = new Response
{ {
HttpVersion = HttpHeader.Version11, HttpVersion = HttpHeader.Version11,
ResponseStatusCode = "407", ResponseStatusCode = (int)HttpStatusCode.ProxyAuthenticationRequired,
ResponseStatusDescription = description ResponseStatusDescription = description
}; };
......
...@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy ...@@ -67,7 +67,7 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Backing field for corresponding public property /// Backing field for corresponding public property
/// </summary> /// </summary>
internal int serverConnectionCount; private int serverConnectionCount;
/// <summary> /// <summary>
/// A object that creates tcp connection to server /// A object that creates tcp connection to server
...@@ -199,6 +199,16 @@ namespace Titanium.Web.Proxy ...@@ -199,6 +199,16 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public event Func<object, TunnelConnectSessionEventArgs, Task> TunnelConnectResponse; public event Func<object, TunnelConnectSessionEventArgs, Task> TunnelConnectResponse;
/// <summary>
/// Occurs when client connection count changed.
/// </summary>
public event EventHandler ClientConnectionCountChanged;
/// <summary>
/// Occurs when server connection count changed.
/// </summary>
public event EventHandler ServerConnectionCountChanged;
/// <summary> /// <summary>
/// External proxy for Http /// External proxy for Http
/// </summary> /// </summary>
...@@ -752,7 +762,7 @@ namespace Titanium.Web.Proxy ...@@ -752,7 +762,7 @@ namespace Titanium.Web.Proxy
private async Task HandleClient(TcpClient tcpClient, ProxyEndPoint endPoint) private async Task HandleClient(TcpClient tcpClient, ProxyEndPoint endPoint)
{ {
Interlocked.Increment(ref clientConnectionCount); UpdateClientConnectionCount(true);
tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000; tcpClient.ReceiveTimeout = ConnectionTimeOutSeconds * 1000;
tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000; tcpClient.SendTimeout = ConnectionTimeOutSeconds * 1000;
...@@ -770,7 +780,7 @@ namespace Titanium.Web.Proxy ...@@ -770,7 +780,7 @@ namespace Titanium.Web.Proxy
} }
finally finally
{ {
Interlocked.Decrement(ref clientConnectionCount); UpdateClientConnectionCount(false);
try try
{ {
...@@ -799,5 +809,33 @@ namespace Titanium.Web.Proxy ...@@ -799,5 +809,33 @@ namespace Titanium.Web.Proxy
endPoint.Listener.Stop(); endPoint.Listener.Stop();
endPoint.Listener.Server.Dispose(); endPoint.Listener.Server.Dispose();
} }
internal void UpdateClientConnectionCount(bool increment)
{
if (increment)
{
Interlocked.Increment(ref clientConnectionCount);
}
else
{
Interlocked.Decrement(ref clientConnectionCount);
}
ClientConnectionCountChanged?.Invoke(this, EventArgs.Empty);
}
internal void UpdateServerConnectionCount(bool increment)
{
if (increment)
{
Interlocked.Increment(ref serverConnectionCount);
}
else
{
Interlocked.Decrement(ref serverConnectionCount);
}
ServerConnectionCountChanged?.Invoke(this, EventArgs.Empty);
}
} }
} }
...@@ -56,25 +56,12 @@ namespace Titanium.Web.Proxy ...@@ -56,25 +56,12 @@ namespace Titanium.Web.Proxy
return; return;
} }
//break up the line into three components (method, remote URL & Http Version) string httpMethod;
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); string httpUrl;
Version version;
Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version);
//Find the request Verb httpRemoteUri = httpMethod == "CONNECT" ? new Uri("http://" + httpUrl) : new Uri(httpUrl);
string httpVerb = httpCmdSplit[0].ToUpper();
httpRemoteUri = httpVerb == "CONNECT" ? new Uri("http://" + httpCmdSplit[1]) : new Uri(httpCmdSplit[1]);
//parse the HTTP version
var version = HttpHeader.Version11;
if (httpCmdSplit.Length == 3)
{
string httpVersion = httpCmdSplit[2].Trim();
if (string.Equals(httpVersion, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
{
version = HttpHeader.Version10;
}
}
//filter out excluded host names //filter out excluded host names
bool excluded = false; bool excluded = false;
...@@ -92,13 +79,13 @@ namespace Titanium.Web.Proxy ...@@ -92,13 +79,13 @@ namespace Titanium.Web.Proxy
ConnectRequest connectRequest = null; ConnectRequest connectRequest = null;
//Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request) //Client wants to create a secure tcp tunnel (probably its a HTTPS or Websocket request)
if (httpVerb == "CONNECT") if (httpMethod == "CONNECT")
{ {
connectRequest = new ConnectRequest connectRequest = new ConnectRequest
{ {
RequestUri = httpRemoteUri, RequestUri = httpRemoteUri,
HttpVersion = version, HttpVersion = version,
Method = httpVerb, Method = httpMethod,
}; };
await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders); await HeaderParser.ReadHeaders(clientStreamReader, connectRequest.RequestHeaders);
...@@ -139,7 +126,7 @@ namespace Titanium.Web.Proxy ...@@ -139,7 +126,7 @@ namespace Titanium.Web.Proxy
if (!excluded && isClientHello) if (!excluded && isClientHello)
{ {
httpRemoteUri = new Uri("https://" + httpCmdSplit[1]); httpRemoteUri = new Uri("https://" + httpUrl);
SslStream sslStream = null; SslStream sslStream = null;
...@@ -181,7 +168,7 @@ namespace Titanium.Web.Proxy ...@@ -181,7 +168,7 @@ namespace Titanium.Web.Proxy
{ {
await TcpHelper.SendRaw(clientStream, connection, await TcpHelper.SendRaw(clientStream, connection,
(buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); }); (buffer, offset, count) => { connectArgs.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { connectArgs.OnDataReceived(buffer, offset, count); });
Interlocked.Decrement(ref serverConnectionCount); UpdateServerConnectionCount(false);
} }
return; return;
...@@ -297,34 +284,22 @@ namespace Titanium.Web.Proxy ...@@ -297,34 +284,22 @@ namespace Titanium.Web.Proxy
try try
{ {
//break up the line into three components (method, remote URL & Http Version) string httpMethod;
var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3); string httpUrl;
Version version;
string httpMethod = httpCmdSplit[0].ToUpper(); Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version);
//find the request HTTP version
var httpVersion = HttpHeader.Version11;
if (httpCmdSplit.Length == 3)
{
string httpVersionString = httpCmdSplit[2].Trim();
if (string.Equals(httpVersionString, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
{
httpVersion = HttpHeader.Version10;
}
}
//Read the request headers in to unique and non-unique header collections //Read the request headers in to unique and non-unique header collections
await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.RequestHeaders); await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.RequestHeaders);
var httpRemoteUri = new Uri(httpsConnectHostname == null var httpRemoteUri = new Uri(httpsConnectHostname == null
? httpCmdSplit[1] ? httpUrl
: string.Concat("https://", args.WebSession.Request.Host ?? httpsConnectHostname, httpCmdSplit[1])); : string.Concat("https://", args.WebSession.Request.Host ?? httpsConnectHostname, httpUrl));
args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.RequestUri = httpRemoteUri;
args.WebSession.Request.Method = httpMethod; args.WebSession.Request.Method = httpMethod;
args.WebSession.Request.HttpVersion = httpVersion; args.WebSession.Request.HttpVersion = version;
args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStream = clientStream;
args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamReader = clientStreamReader;
args.ProxyClient.ClientStreamWriter = clientStreamWriter; args.ProxyClient.ClientStreamWriter = clientStreamWriter;
...@@ -369,7 +344,7 @@ namespace Titanium.Web.Proxy ...@@ -369,7 +344,7 @@ namespace Titanium.Web.Proxy
else if (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase)) else if (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase))
{ {
connection.Dispose(); connection.Dispose();
Interlocked.Decrement(ref serverConnectionCount); UpdateServerConnectionCount(false);
connection = await GetServerConnection(args); connection = await GetServerConnection(args);
} }
...@@ -399,12 +374,25 @@ namespace Titanium.Web.Proxy ...@@ -399,12 +374,25 @@ namespace Titanium.Web.Proxy
} }
string httpStatus = await connection.StreamReader.ReadLineAsync(); string httpStatus = await connection.StreamReader.ReadLineAsync();
//todo: parse status
Version responseVersion;
int responseStatusCode;
string responseStatusDescription;
Response.ParseResponseLine(httpStatus, out responseVersion, out responseStatusCode, out responseStatusDescription);
args.WebSession.Response.HttpVersion = responseVersion;
args.WebSession.Response.ResponseStatusCode = responseStatusCode;
args.WebSession.Response.ResponseStatusDescription = responseStatusDescription;
await HeaderParser.ReadHeaders(connection.StreamReader, args.WebSession.Response.ResponseHeaders); await HeaderParser.ReadHeaders(connection.StreamReader, args.WebSession.Response.ResponseHeaders);
await clientStreamWriter.WriteLineAsync(httpStatus); await WriteResponse(args.WebSession.Response, clientStreamWriter);
await WriteResponseHeaders(clientStreamWriter, args.WebSession.Response);
//If user requested call back then do it
if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked)
{
await BeforeResponse.InvokeParallelAsync(this, args, ExceptionFunc);
}
await TcpHelper.SendRaw(clientStream, connection, await TcpHelper.SendRaw(clientStream, connection,
(buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }); (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); });
...@@ -478,12 +466,12 @@ namespace Titanium.Web.Proxy ...@@ -478,12 +466,12 @@ namespace Titanium.Web.Proxy
{ {
if (args.WebSession.Request.Is100Continue) if (args.WebSession.Request.Is100Continue)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter); await WriteResponseStatus(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
else if (args.WebSession.Request.ExpectationFailed) else if (args.WebSession.Request.ExpectationFailed)
{ {
await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter); await WriteResponseStatus(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
} }
...@@ -616,7 +604,7 @@ namespace Titanium.Web.Proxy ...@@ -616,7 +604,7 @@ namespace Titanium.Web.Proxy
var response = new ConnectResponse var response = new ConnectResponse
{ {
HttpVersion = httpVersion, HttpVersion = httpVersion,
ResponseStatusCode = "200", ResponseStatusCode = (int)HttpStatusCode.OK,
ResponseStatusDescription = "Connection established" ResponseStatusDescription = "Connection established"
}; };
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Compression; using Titanium.Web.Proxy.Compression;
...@@ -37,7 +38,7 @@ namespace Titanium.Web.Proxy ...@@ -37,7 +38,7 @@ namespace Titanium.Web.Proxy
//check for windows authentication //check for windows authentication
if (EnableWinAuth if (EnableWinAuth
&& !RunTime.IsRunningOnMono && !RunTime.IsRunningOnMono
&& response.ResponseStatusCode == "401") && response.ResponseStatusCode == (int)HttpStatusCode.Unauthorized)
{ {
bool disposed = await Handle401UnAuthorized(args); bool disposed = await Handle401UnAuthorized(args);
...@@ -71,18 +72,17 @@ namespace Titanium.Web.Proxy ...@@ -71,18 +72,17 @@ namespace Titanium.Web.Proxy
//Write back to client 100-conitinue response if that's what server returned //Write back to client 100-conitinue response if that's what server returned
if (response.Is100Continue) if (response.Is100Continue)
{ {
await WriteResponseStatus(response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter); await WriteResponseStatus(response.HttpVersion, (int)HttpStatusCode.Continue, "Continue", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
else if (response.ExpectationFailed) else if (response.ExpectationFailed)
{ {
await WriteResponseStatus(response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter); await WriteResponseStatus(response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", args.ProxyClient.ClientStreamWriter);
await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
} }
//Write back response status to client //Write back response status to client
await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode, await WriteResponseStatus(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter);
if (response.ResponseBodyRead) if (response.ResponseBodyRead)
{ {
...@@ -166,7 +166,7 @@ namespace Titanium.Web.Proxy ...@@ -166,7 +166,7 @@ namespace Titanium.Web.Proxy
/// <param name="description"></param> /// <param name="description"></param>
/// <param name="responseWriter"></param> /// <param name="responseWriter"></param>
/// <returns></returns> /// <returns></returns>
private async Task WriteResponseStatus(Version version, string code, string description, StreamWriter responseWriter) private async Task WriteResponseStatus(Version version, int code, string description, StreamWriter responseWriter)
{ {
await responseWriter.WriteLineAsync($"HTTP/{version.Major}.{version.Minor} {code} {description}"); await responseWriter.WriteLineAsync($"HTTP/{version.Major}.{version.Minor} {code} {description}");
} }
...@@ -227,7 +227,7 @@ namespace Titanium.Web.Proxy ...@@ -227,7 +227,7 @@ namespace Titanium.Web.Proxy
if (serverConnection != null) if (serverConnection != null)
{ {
serverConnection.Dispose(); serverConnection.Dispose();
Interlocked.Decrement(ref serverConnectionCount); UpdateServerConnectionCount(false);
} }
} }
} }
......
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