Unverified Commit a007c1c0 authored by honfika's avatar honfika Committed by GitHub

Merge pull request #395 from justcoding121/develop

merge to beta
parents 7eb8f297 ef38da77
...@@ -107,15 +107,18 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -107,15 +107,18 @@ namespace Titanium.Web.Proxy.EventArguments
/// <summary> /// <summary>
/// Constructor to initialize the proxy /// Constructor to initialize the proxy
/// </summary> /// </summary>
internal SessionEventArgs(int bufferSize, internal SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ExceptionHandler exceptionFunc)
ProxyEndPoint endPoint, : this(bufferSize, endPoint, exceptionFunc, null)
ExceptionHandler exceptionFunc) {
}
protected SessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ExceptionHandler exceptionFunc, Request request)
{ {
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
this.exceptionFunc = exceptionFunc; this.exceptionFunc = exceptionFunc;
ProxyClient = new ProxyClient(); ProxyClient = new ProxyClient();
WebSession = new HttpWebClient(bufferSize); WebSession = new HttpWebClient(bufferSize, request);
LocalEndPoint = endPoint; LocalEndPoint = endPoint;
WebSession.ProcessId = new Lazy<int>(() => WebSession.ProcessId = new Lazy<int>(() =>
...@@ -138,6 +141,21 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -138,6 +141,21 @@ namespace Titanium.Web.Proxy.EventArguments
}); });
} }
private CustomBufferedStream GetStream(bool isRequest)
{
return isRequest ? ProxyClient.ClientStream : WebSession.ServerConnection.Stream;
}
private CustomBinaryReader GetStreamReader(bool isRequest)
{
return isRequest ? ProxyClient.ClientStreamReader : WebSession.ServerConnection.StreamReader;
}
private HttpWriter GetStreamWriter(bool isRequest)
{
return isRequest ? (HttpWriter)ProxyClient.ClientStreamWriter : WebSession.ServerConnection.StreamWriter;
}
/// <summary> /// <summary>
/// Read request body content as bytes[] for current session /// Read request body content as bytes[] for current session
/// </summary> /// </summary>
...@@ -150,7 +168,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -150,7 +168,7 @@ namespace Titanium.Web.Proxy.EventArguments
//If not already read (not cached yet) //If not already read (not cached yet)
if (!request.IsBodyRead) if (!request.IsBodyRead)
{ {
var body = await ReadBodyAsync(ProxyClient.ClientStreamReader, true); var body = await ReadBodyAsync(true);
request.Body = body; request.Body = body;
//Now set the flag to true //Now set the flag to true
...@@ -165,8 +183,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -165,8 +183,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary> /// </summary>
internal async Task ClearResponse() internal async Task ClearResponse()
{ {
//siphon out the body //syphon out the response body from server
await ReadResponseBodyAsync(); await SyphonOutBodyAsync(false);
WebSession.Response = new Response(); WebSession.Response = new Response();
} }
...@@ -225,7 +243,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -225,7 +243,7 @@ namespace Titanium.Web.Proxy.EventArguments
//If not already read (not cached yet) //If not already read (not cached yet)
if (!response.IsBodyRead) if (!response.IsBodyRead)
{ {
var body = await ReadBodyAsync(WebSession.ServerConnection.StreamReader, false); var body = await ReadBodyAsync(false);
response.Body = body; response.Body = body;
//Now set the flag to true //Now set the flag to true
...@@ -235,11 +253,12 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -235,11 +253,12 @@ namespace Titanium.Web.Proxy.EventArguments
} }
} }
private async Task<byte[]> ReadBodyAsync(CustomBinaryReader reader, bool isRequest) private async Task<byte[]> ReadBodyAsync(bool isRequest)
{ {
using (var bodyStream = new MemoryStream()) using (var bodyStream = new MemoryStream())
{ {
var writer = new HttpWriter(bodyStream, bufferSize); var writer = new HttpWriter(bodyStream, bufferSize);
if (isRequest) if (isRequest)
{ {
await CopyRequestBodyAsync(writer, TransformationMode.Uncompress); await CopyRequestBodyAsync(writer, TransformationMode.Uncompress);
...@@ -253,21 +272,35 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -253,21 +272,35 @@ namespace Titanium.Web.Proxy.EventArguments
} }
} }
internal async Task SyphonOutBodyAsync(bool isRequest)
{
var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response;
if (requestResponse.IsBodyRead || !requestResponse.OriginalHasBody)
{
return;
}
using (var bodyStream = new MemoryStream())
{
var writer = new HttpWriter(bodyStream, bufferSize);
await CopyBodyAsync(isRequest, writer, TransformationMode.None, null);
}
}
/// <summary> /// <summary>
/// This is called when the request is PUT/POST/PATCH to read the body /// This is called when the request is PUT/POST/PATCH to read the body
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
internal async Task CopyRequestBodyAsync(HttpWriter writer, TransformationMode transformation) internal async Task CopyRequestBodyAsync(HttpWriter writer, TransformationMode transformation)
{ {
// End the operation
var request = WebSession.Request; var request = WebSession.Request;
var reader = ProxyClient.ClientStreamReader;
long contentLength = request.ContentLength; long contentLength = request.ContentLength;
//send the request body bytes to server //send the request body bytes to server
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData) if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{ {
var reader = GetStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType); string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, bufferSize)) using (var copyStream = new CopyStream(reader, writer, bufferSize))
...@@ -294,21 +327,22 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -294,21 +327,22 @@ namespace Titanium.Web.Proxy.EventArguments
} }
else else
{ {
await CopyBodyAsync(ProxyClient.ClientStream, reader, writer, request, transformation, OnDataSent); await CopyBodyAsync(true, writer, transformation, OnDataSent);
} }
} }
internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation) internal async Task CopyResponseBodyAsync(HttpWriter writer, TransformationMode transformation)
{ {
var response = WebSession.Response; await CopyBodyAsync(false, writer, transformation, OnDataReceived);
var reader = WebSession.ServerConnection.StreamReader;
await CopyBodyAsync(WebSession.ServerConnection.Stream, reader, writer, response, transformation, OnDataReceived);
} }
private async Task CopyBodyAsync(CustomBufferedStream stream, CustomBinaryReader reader, HttpWriter writer, private async Task CopyBodyAsync(bool isRequest, HttpWriter writer, TransformationMode transformation, Action<byte[], int, int> onCopy)
RequestResponseBase requestResponse, TransformationMode transformation, Action<byte[], int, int> onCopy)
{ {
var stream = GetStream(isRequest);
var reader = GetStreamReader(isRequest);
var requestResponse = isRequest ? (RequestResponseBase)WebSession.Request : WebSession.Response;
bool isChunked = requestResponse.IsChunked; bool isChunked = requestResponse.IsChunked;
long contentLength = requestResponse.ContentLength; long contentLength = requestResponse.ContentLength;
if (transformation == TransformationMode.None) if (transformation == TransformationMode.None)
...@@ -440,7 +474,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -440,7 +474,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Sets the request body /// Sets the request body
/// </summary> /// </summary>
/// <param name="body"></param> /// <param name="body"></param>
public async Task SetRequestBody(byte[] body) public void SetRequestBody(byte[] body)
{ {
var request = WebSession.Request; var request = WebSession.Request;
if (request.Locked) if (request.Locked)
...@@ -448,34 +482,21 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -448,34 +482,21 @@ namespace Titanium.Web.Proxy.EventArguments
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
} }
//syphon out the request body from client before setting the new body
if (!request.IsBodyRead)
{
await ReadRequestBodyAsync();
}
request.Body = body; request.Body = body;
request.UpdateContentLength();
} }
/// <summary> /// <summary>
/// Sets the body with the specified string /// Sets the body with the specified string
/// </summary> /// </summary>
/// <param name="body"></param> /// <param name="body"></param>
public async Task SetRequestBodyString(string body) public void SetRequestBodyString(string body)
{ {
if (WebSession.Request.Locked) if (WebSession.Request.Locked)
{ {
throw new Exception("You cannot call this function after request is made to server."); throw new Exception("You cannot call this function after request is made to server.");
} }
//syphon out the request body from client before setting the new body SetRequestBody(WebSession.Request.Encoding.GetBytes(body));
if (!WebSession.Request.IsBodyRead)
{
await ReadRequestBodyAsync();
}
await SetRequestBody(WebSession.Request.Encoding.GetBytes(body));
} }
/// <summary> /// <summary>
...@@ -510,7 +531,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -510,7 +531,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Set the response body bytes /// Set the response body bytes
/// </summary> /// </summary>
/// <param name="body"></param> /// <param name="body"></param>
public async Task SetResponseBody(byte[] body) public void SetResponseBody(byte[] body)
{ {
if (!WebSession.Request.Locked) if (!WebSession.Request.Locked)
{ {
...@@ -518,39 +539,23 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -518,39 +539,23 @@ namespace Titanium.Web.Proxy.EventArguments
} }
var response = WebSession.Response; var response = WebSession.Response;
//syphon out the response body from server before setting the new body
if (response.Body == null)
{
await GetResponseBody();
}
response.Body = body; response.Body = body;
//If there is a content length header update it
response.UpdateContentLength();
} }
/// <summary> /// <summary>
/// Replace the response body with the specified string /// Replace the response body with the specified string
/// </summary> /// </summary>
/// <param name="body"></param> /// <param name="body"></param>
public async Task SetResponseBodyString(string body) public void SetResponseBodyString(string body)
{ {
if (!WebSession.Request.Locked) if (!WebSession.Request.Locked)
{ {
throw new Exception("You cannot call this function before request is made to server."); throw new Exception("You cannot call this function before request is made to server.");
} }
//syphon out the response body from server before setting the new body
if (!WebSession.Response.IsBodyRead)
{
await GetResponseBody();
}
var bodyBytes = WebSession.Response.Encoding.GetBytes(body); var bodyBytes = WebSession.Response.Encoding.GetBytes(body);
await SetResponseBody(bodyBytes); SetResponseBody(bodyBytes);
} }
/// <summary> /// <summary>
...@@ -651,18 +656,30 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -651,18 +656,30 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
if (WebSession.Request.Locked) if (WebSession.Request.Locked)
{ {
throw new Exception("You cannot call this function after request is made to server."); if (WebSession.Response.Locked)
{
throw new Exception("You cannot call this function after response is sent to the client.");
} }
response.Locked = true;
response.TerminateResponse = WebSession.Response.TerminateResponse;
WebSession.Response = response;
}
else
{
WebSession.Request.Locked = true; WebSession.Request.Locked = true;
response.Locked = true; response.Locked = true;
response.IsBodyRead = true;
WebSession.Response = response; WebSession.Response = response;
WebSession.Request.CancelRequest = true; WebSession.Request.CancelRequest = true;
} }
}
public void TerminateServerConnection()
{
WebSession.Response.TerminateResponse = true;
}
/// <summary> /// <summary>
/// implement any cleanup here /// implement any cleanup here
......
...@@ -11,9 +11,8 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -11,9 +11,8 @@ namespace Titanium.Web.Proxy.EventArguments
public bool IsHttpsConnect { get; internal set; } public bool IsHttpsConnect { get; internal set; }
internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest, ExceptionHandler exceptionFunc) internal TunnelConnectSessionEventArgs(int bufferSize, ProxyEndPoint endPoint, ConnectRequest connectRequest, ExceptionHandler exceptionFunc)
: base(bufferSize, endPoint, exceptionFunc) : base(bufferSize, endPoint, exceptionFunc, connectRequest)
{ {
WebSession.Request = connectRequest;
} }
} }
} }
...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -20,7 +20,7 @@ namespace Titanium.Web.Proxy.Helpers
public async Task WriteResponseAsync(Response response, bool flush = true) public async Task WriteResponseAsync(Response response, bool flush = true)
{ {
await WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription); await WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription);
await WriteHeadersAsync(response.Headers, flush); await WriteAsync(response, flush);
} }
/// <summary> /// <summary>
......
...@@ -5,7 +5,7 @@ using System.Text; ...@@ -5,7 +5,7 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using StreamExtended.Helpers; using StreamExtended.Helpers;
using StreamExtended.Network; using StreamExtended.Network;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -93,14 +93,11 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -93,14 +93,11 @@ namespace Titanium.Web.Proxy.Helpers
/// <param name="flush"></param> /// <param name="flush"></param>
/// <returns></returns> /// <returns></returns>
public async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true) public async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true)
{
if (headers != null)
{ {
foreach (var header in headers) foreach (var header in headers)
{ {
await header.WriteToStreamAsync(this); await header.WriteToStreamAsync(this);
} }
}
await WriteLineAsync(); await WriteLineAsync();
if (flush) if (flush)
...@@ -265,5 +262,22 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -265,5 +262,22 @@ namespace Titanium.Web.Proxy.Helpers
onCopy?.Invoke(buffer, 0, bytesRead); onCopy?.Invoke(buffer, 0, bytesRead);
} }
} }
/// <summary>
/// Writes the request/response headers and body.
/// </summary>
/// <param name="requestResponse"></param>
/// <param name="flush"></param>
/// <returns></returns>
protected async Task WriteAsync(RequestResponseBase requestResponse, bool flush = true)
{
var body = requestResponse.CompressBodyAndUpdateContentLength();
await WriteHeadersAsync(requestResponse.Headers, flush);
if (body != null)
{
await WriteBodyAsync(body, requestResponse.IsChunked);
}
}
} }
} }
...@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -38,7 +38,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Web Request. /// Web Request.
/// </summary> /// </summary>
public Request Request { get; internal set; } public Request Request { get; }
/// <summary> /// <summary>
/// Web Response. /// Web Response.
...@@ -56,13 +56,13 @@ namespace Titanium.Web.Proxy.Http ...@@ -56,13 +56,13 @@ namespace Titanium.Web.Proxy.Http
/// </summary> /// </summary>
public bool IsHttps => Request.IsHttps; public bool IsHttps => Request.IsHttps;
internal HttpWebClient(int bufferSize) internal HttpWebClient(int bufferSize, Request request = null, Response response = null)
{ {
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
RequestId = Guid.NewGuid(); RequestId = Guid.NewGuid();
Request = new Request(); Request = request ?? new Request();
Response = new Response(); Response = response ?? new Response();
} }
/// <summary> /// <summary>
......
...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -37,7 +37,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Has request body? /// Has request body?
/// </summary> /// </summary>
public bool HasBody public override bool HasBody
{ {
get get
{ {
...@@ -102,6 +102,11 @@ namespace Titanium.Web.Proxy.Http ...@@ -102,6 +102,11 @@ namespace Titanium.Web.Proxy.Http
internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true) internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
{ {
if (BodyInternal != null)
{
return;
}
//GET request don't have a request body to read //GET request don't have a request body to read
if (!HasBody) if (!HasBody)
{ {
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using Titanium.Web.Proxy.Compression;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -15,7 +16,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -15,7 +16,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Cached body content as byte array /// Cached body content as byte array
/// </summary> /// </summary>
private byte[] body; protected byte[] BodyInternal;
/// <summary> /// <summary>
/// Cached body as string /// Cached body as string
...@@ -126,16 +127,84 @@ namespace Titanium.Web.Proxy.Http ...@@ -126,16 +127,84 @@ namespace Titanium.Web.Proxy.Http
get get
{ {
EnsureBodyAvailable(); EnsureBodyAvailable();
return body; return BodyInternal;
} }
internal set internal set
{ {
body = value; BodyInternal = value;
bodyString = null; bodyString = null;
//If there is a content length header update it
UpdateContentLength();
} }
} }
/// <summary>
/// Has the request/response body?
/// </summary>
public abstract bool HasBody { get; }
/// <summary>
/// Store weather the original request/response has body or not, since the user may change the parameters
/// </summary>
internal bool OriginalHasBody;
internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true); internal abstract void EnsureBodyAvailable(bool throwWhenNotReadYet = true);
/// <summary>
/// get the compressed body from given bytes
/// </summary>
/// <param name="encodingType"></param>
/// <param name="body"></param>
/// <returns></returns>
internal byte[] GetCompressedBody(string encodingType, byte[] body)
{
var compressor = CompressionFactory.GetCompression(encodingType);
using (var ms = new MemoryStream())
{
using (var zip = compressor.GetStream(ms))
{
zip.Write(body, 0, body.Length);
}
return ms.ToArray();
}
}
internal byte[] CompressBodyAndUpdateContentLength()
{
if (!IsBodyRead && BodyInternal == null)
{
return null;
}
bool isChunked = IsChunked;
string contentEncoding = ContentEncoding;
if (HasBody)
{
var body = Body;
if (contentEncoding != null && body != null)
{
body = GetCompressedBody(contentEncoding, body);
if (isChunked == false)
{
ContentLength = body.Length;
}
else
{
ContentLength = -1;
}
}
return body;
}
ContentLength = 0;
return null;
}
/// <summary> /// <summary>
/// Body as string /// Body as string
/// Use the encoding specified to decode the byte[] data to string /// Use the encoding specified to decode the byte[] data to string
...@@ -155,7 +224,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -155,7 +224,7 @@ namespace Titanium.Web.Proxy.Http
internal void UpdateContentLength() internal void UpdateContentLength()
{ {
ContentLength = IsChunked ? -1 : body.Length; ContentLength = IsChunked ? -1 : BodyInternal?.Length ?? 0;
} }
/// <summary> /// <summary>
...@@ -165,7 +234,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -165,7 +234,7 @@ namespace Titanium.Web.Proxy.Http
{ {
if (!KeepBody) if (!KeepBody)
{ {
body = null; BodyInternal = null;
bodyString = null; bodyString = null;
} }
} }
......
...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Http ...@@ -26,7 +26,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary> /// <summary>
/// Has response body? /// Has response body?
/// </summary> /// </summary>
public bool HasBody public override bool HasBody
{ {
get get
{ {
...@@ -77,8 +77,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -77,8 +77,15 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal bool TerminateResponse { get; set; }
internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true) internal override void EnsureBodyAvailable(bool throwWhenNotReadYet = true)
{ {
if (BodyInternal != null)
{
return;
}
if (!IsBodyRead && throwWhenNotReadYet) if (!IsBodyRead && throwWhenNotReadYet)
{ {
throw new Exception("Response body is not read yet. " + throw new Exception("Response body is not read yet. " +
...@@ -121,6 +128,21 @@ namespace Titanium.Web.Proxy.Http ...@@ -121,6 +128,21 @@ namespace Titanium.Web.Proxy.Http
} }
} }
/// <summary>
/// Constructor.
/// </summary>
public Response()
{
}
/// <summary>
/// Constructor.
/// </summary>
public Response(byte[] body)
{
Body = body;
}
internal static string CreateResponseLine(Version version, int statusCode, string statusDescription) internal static string CreateResponseLine(Version version, int statusCode, string statusDescription)
{ {
return $"HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}"; return $"HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
......
...@@ -15,5 +15,13 @@ namespace Titanium.Web.Proxy.Http.Responses ...@@ -15,5 +15,13 @@ namespace Titanium.Web.Proxy.Http.Responses
StatusCode = (int)HttpStatusCode.OK; StatusCode = (int)HttpStatusCode.OK;
StatusDescription = "OK"; StatusDescription = "OK";
} }
/// <summary>
/// Constructor.
/// </summary>
public OkResponse(byte[] body) : this()
{
Body = body;
}
} }
} }
...@@ -46,7 +46,6 @@ namespace Titanium.Web.Proxy ...@@ -46,7 +46,6 @@ namespace Titanium.Web.Proxy
try try
{ {
string connectHostname = null; string connectHostname = null;
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)
...@@ -297,7 +296,7 @@ namespace Titanium.Web.Proxy ...@@ -297,7 +296,7 @@ namespace Titanium.Web.Proxy
string httpCmd = await clientStreamReader.ReadLineAsync(); string httpCmd = await clientStreamReader.ReadLineAsync();
if (string.IsNullOrEmpty(httpCmd)) if (string.IsNullOrEmpty(httpCmd))
{ {
break; return;
} }
var args = new SessionEventArgs(BufferSize, endPoint, ExceptionFunc) var args = new SessionEventArgs(BufferSize, endPoint, ExceptionFunc)
...@@ -347,11 +346,12 @@ namespace Titanium.Web.Proxy ...@@ -347,11 +346,12 @@ namespace Titanium.Web.Proxy
} }
} }
args.WebSession.Request.RequestUri = httpRemoteUri; var request = args.WebSession.Request;
args.WebSession.Request.OriginalUrl = httpUrl; request.RequestUri = httpRemoteUri;
request.OriginalUrl = httpUrl;
args.WebSession.Request.Method = httpMethod; request.Method = httpMethod;
args.WebSession.Request.HttpVersion = version; 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;
...@@ -363,35 +363,40 @@ namespace Titanium.Web.Proxy ...@@ -363,35 +363,40 @@ namespace Titanium.Web.Proxy
//send the response //send the response
await clientStreamWriter.WriteResponseAsync(args.WebSession.Response); await clientStreamWriter.WriteResponseAsync(args.WebSession.Response);
break; return;
} }
if (!isTransparentEndPoint) if (!isTransparentEndPoint)
{ {
PrepareRequestHeaders(args.WebSession.Request.Headers); PrepareRequestHeaders(request.Headers);
args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; request.Host = request.RequestUri.Authority;
} }
//if win auth is enabled //if win auth is enabled
//we need a cache of request body //we need a cache of request body
//so that we can send it after authentication in WinAuthHandler.cs //so that we can send it after authentication in WinAuthHandler.cs
if (isWindowsAuthenticationEnabledAndSupported && args.WebSession.Request.HasBody) if (isWindowsAuthenticationEnabledAndSupported && request.HasBody)
{ {
await args.GetRequestBody(); await args.GetRequestBody();
} }
request.OriginalHasBody = request.HasBody;
//If user requested interception do it //If user requested interception do it
await InvokeBeforeRequest(args); await InvokeBeforeRequest(args);
var response = args.WebSession.Response; var response = args.WebSession.Response;
if (args.WebSession.Request.CancelRequest) if (request.CancelRequest)
{ {
//syphon out the request body from client before setting the new body
await args.SyphonOutBodyAsync(true);
await HandleHttpSessionResponse(args); await HandleHttpSessionResponse(args);
if (!response.KeepAlive) if (!response.KeepAlive)
{ {
break; return;
} }
continue; continue;
...@@ -399,7 +404,7 @@ namespace Titanium.Web.Proxy ...@@ -399,7 +404,7 @@ namespace Titanium.Web.Proxy
//create a new connection if hostname/upstream end point changes //create a new connection if hostname/upstream end point changes
if (connection != null if (connection != null
&& (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase) && (!connection.HostName.Equals(request.RequestUri.Host, StringComparison.OrdinalIgnoreCase)
|| (args.WebSession.UpStreamEndPoint != null || (args.WebSession.UpStreamEndPoint != null
&& !args.WebSession.UpStreamEndPoint.Equals(connection.UpStreamEndPoint)))) && !args.WebSession.UpStreamEndPoint.Equals(connection.UpStreamEndPoint))))
{ {
...@@ -413,12 +418,11 @@ namespace Titanium.Web.Proxy ...@@ -413,12 +418,11 @@ namespace Titanium.Web.Proxy
} }
//if upgrading to websocket then relay the requet without reading the contents //if upgrading to websocket then relay the requet without reading the contents
if (args.WebSession.Request.UpgradeToWebSocket) if (request.UpgradeToWebSocket)
{ {
//prepare the prefix content //prepare the prefix content
var requestHeaders = args.WebSession.Request.Headers;
await connection.StreamWriter.WriteLineAsync(httpCmd); await connection.StreamWriter.WriteLineAsync(httpCmd);
await connection.StreamWriter.WriteHeadersAsync(requestHeaders); await connection.StreamWriter.WriteHeadersAsync(request.Headers);
string httpStatus = await connection.StreamReader.ReadLineAsync(); string httpStatus = await connection.StreamReader.ReadLineAsync();
Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode, Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode,
...@@ -445,16 +449,22 @@ namespace Titanium.Web.Proxy ...@@ -445,16 +449,22 @@ namespace Titanium.Web.Proxy
(buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); },
ExceptionFunc); ExceptionFunc);
break; return;
} }
//construct the web request that we are going to issue on behalf of the client. //construct the web request that we are going to issue on behalf of the client.
await HandleHttpSessionRequestInternal(connection, args); await HandleHttpSessionRequestInternal(connection, args);
if (args.WebSession.ServerConnection == null)
{
//server connection was closed
return;
}
//if connection is closing exit //if connection is closing exit
if (!response.KeepAlive) if (!response.KeepAlive)
{ {
break; return;
} }
} }
catch (Exception e) when (!(e is ProxyHttpException)) catch (Exception e) when (!(e is ProxyHttpException))
...@@ -493,6 +503,8 @@ namespace Titanium.Web.Proxy ...@@ -493,6 +503,8 @@ namespace Titanium.Web.Proxy
var request = args.WebSession.Request; var request = args.WebSession.Request;
request.Locked = true; request.Locked = true;
var body = request.CompressBodyAndUpdateContentLength();
//if expect continue is enabled then send the headers first //if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue //and see if server would return 100 conitinue
if (request.ExpectContinue) if (request.ExpectContinue)
...@@ -531,25 +543,8 @@ namespace Titanium.Web.Proxy ...@@ -531,25 +543,8 @@ namespace Titanium.Web.Proxy
//If request was modified by user //If request was modified by user
if (request.IsBodyRead) if (request.IsBodyRead)
{ {
bool isChunked = request.IsChunked; var writer = args.WebSession.ServerConnection.StreamWriter;
string contentEncoding = request.ContentEncoding; await writer.WriteBodyAsync(body, request.IsChunked);
var body = request.Body;
if (contentEncoding != null && body != null)
{
body = GetCompressedBody(contentEncoding, body);
if (isChunked == false)
{
request.ContentLength = body.Length;
}
else
{
request.ContentLength = -1;
}
}
await args.WebSession.ServerConnection.StreamWriter.WriteBodyAsync(body, isChunked);
} }
else else
{ {
......
...@@ -43,6 +43,29 @@ namespace Titanium.Web.Proxy ...@@ -43,6 +43,29 @@ namespace Titanium.Web.Proxy
await InvokeBeforeResponse(args); await InvokeBeforeResponse(args);
} }
// it may changed in the user event
response = args.WebSession.Response;
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
if (response.TerminateResponse || response.Locked)
{
await clientStreamWriter.WriteResponseAsync(response);
if (!response.TerminateResponse)
{
//syphon out the response body from server before setting the new body
await args.SyphonOutBodyAsync(false);
}
else
{
args.WebSession.ServerConnection.Dispose();
args.WebSession.ServerConnection = null;
}
return;
}
//if user requested to send request again //if user requested to send request again
//likely after making modifications from User Response Handler //likely after making modifications from User Response Handler
if (args.ReRequest) if (args.ReRequest)
...@@ -55,8 +78,6 @@ namespace Titanium.Web.Proxy ...@@ -55,8 +78,6 @@ namespace Titanium.Web.Proxy
response.Locked = true; response.Locked = true;
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
//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)
{ {
...@@ -69,9 +90,6 @@ namespace Titanium.Web.Proxy ...@@ -69,9 +90,6 @@ namespace Titanium.Web.Proxy
await clientStreamWriter.WriteLineAsync(); await clientStreamWriter.WriteLineAsync();
} }
//Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription);
if (!args.IsTransparent) if (!args.IsTransparent)
{ {
response.Headers.FixProxyHeaders(); response.Headers.FixProxyHeaders();
...@@ -79,29 +97,12 @@ namespace Titanium.Web.Proxy ...@@ -79,29 +97,12 @@ namespace Titanium.Web.Proxy
if (response.IsBodyRead) if (response.IsBodyRead)
{ {
bool isChunked = response.IsChunked; await clientStreamWriter.WriteResponseAsync(response);
string contentEncoding = response.ContentEncoding;
var body = response.Body;
if (contentEncoding != null && body != null)
{
body = GetCompressedBody(contentEncoding, body);
if (isChunked == false)
{
response.ContentLength = body.Length;
}
else
{
response.ContentLength = -1;
}
}
await clientStreamWriter.WriteHeadersAsync(response.Headers);
await clientStreamWriter.WriteBodyAsync(body, isChunked);
} }
else else
{ {
//Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription);
await clientStreamWriter.WriteHeadersAsync(response.Headers); await clientStreamWriter.WriteHeadersAsync(response.Headers);
//Write body if exists //Write body if exists
...@@ -117,27 +118,6 @@ namespace Titanium.Web.Proxy ...@@ -117,27 +118,6 @@ namespace Titanium.Web.Proxy
} }
} }
/// <summary>
/// get the compressed body from given bytes
/// </summary>
/// <param name="encodingType"></param>
/// <param name="body"></param>
/// <returns></returns>
private byte[] GetCompressedBody(string encodingType, byte[] body)
{
var compressor = CompressionFactory.GetCompression(encodingType);
using (var ms = new MemoryStream())
{
using (var zip = compressor.GetStream(ms))
{
zip.Write(body, 0, body.Length);
}
return ms.ToArray();
}
}
private async Task InvokeBeforeResponse(SessionEventArgs args) private async Task InvokeBeforeResponse(SessionEventArgs args)
{ {
if (BeforeResponse != null) if (BeforeResponse != null)
......
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