Commit 20df2e6e authored by Honfika's avatar Honfika

Multipart form data event, some streamextended classes added

parent 88f91981
using System;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.EventArguments
{
public class MultipartRequestPartSentEventArgs : EventArgs
{
public string Boundary { get; }
public HeaderCollection Headers { get; }
public MultipartRequestPartSentEventArgs(string boundary, HeaderCollection headers)
{
Boundary = boundary;
Headers = headers;
}
}
}
\ No newline at end of file
......@@ -42,6 +42,8 @@ namespace Titanium.Web.Proxy.EventArguments
/// </summary>
internal ProxyClient ProxyClient { get; set; }
internal bool HasMulipartEventSubscribers => MultipartRequestPartSent != null;
/// <summary>
/// Returns a unique Id for this request/response session
/// same as RequestId of WebSession
......@@ -90,6 +92,11 @@ namespace Titanium.Web.Proxy.EventArguments
public event EventHandler<DataEventArgs> DataReceived;
/// <summary>
/// Occurs when multipart request part sent.
/// </summary>
public event EventHandler<MultipartRequestPartSentEventArgs> MultipartRequestPartSent;
public ProxyEndPoint LocalEndPoint;
/// <summary>
......@@ -189,6 +196,11 @@ namespace Titanium.Web.Proxy.EventArguments
DataReceived?.Invoke(this, new DataEventArgs(buffer, offset, count));
}
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers)
{
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers));
}
/// <summary>
/// Read response body as byte[] for current response
/// </summary>
......
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Extensions
{
/// <summary>
/// Extensions on HttpWebSession object
/// </summary>
internal static class HttpWebRequestExtensions
{
/// <summary>
/// parse the character encoding of request from request headers
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
internal static Encoding GetEncoding(this Request request)
{
return HttpHelper.GetEncodingFromContentType(request.ContentType);
}
}
}
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http;
namespace Titanium.Web.Proxy.Extensions
{
internal static class HttpWebResponseExtensions
{
/// <summary>
/// Gets the character encoding of response from response headers
/// </summary>
/// <param name="response"></param>
/// <returns></returns>
internal static Encoding GetResponseCharacterEncoding(this Response response)
{
return HttpHelper.GetEncodingFromContentType(response.ContentType);
}
}
}
......@@ -17,6 +17,7 @@ namespace Titanium.Web.Proxy.Extensions
/// <param name="input"></param>
/// <param name="output"></param>
/// <param name="onCopy"></param>
/// <param name="bufferSize"></param>
internal static async Task CopyToAsync(this Stream input, Stream output, Action<byte[], int, int> onCopy, int bufferSize)
{
byte[] buffer = new byte[bufferSize];
......
......@@ -8,7 +8,12 @@ namespace Titanium.Web.Proxy.Helpers
{
private static readonly Encoding defaultEncoding = Encoding.GetEncoding("ISO-8859-1");
public static Encoding GetEncodingFromContentType(string contentType)
/// <summary>
/// Gets the character encoding of request/response from content-type header
/// </summary>
/// <param name="contentType"></param>
/// <returns></returns>
internal static Encoding GetEncodingFromContentType(string contentType)
{
try
{
......@@ -22,10 +27,10 @@ namespace Titanium.Web.Proxy.Helpers
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
{
var encodingSplit = parameter.Split(ProxyConstants.EqualSplit, 2);
if (encodingSplit.Length == 2 && encodingSplit[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
var split = parameter.Split(ProxyConstants.EqualSplit, 2);
if (split.Length == 2 && split[0].Trim().Equals("charset", StringComparison.CurrentCultureIgnoreCase))
{
string value = encodingSplit[1];
string value = split[1];
if (value.Equals("x-user-defined", StringComparison.OrdinalIgnoreCase))
{
//todo: what is this?
......@@ -51,6 +56,32 @@ namespace Titanium.Web.Proxy.Helpers
return defaultEncoding;
}
internal static string GetBoundaryFromContentType(string contentType)
{
if (contentType != null)
{
//extract the boundary
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
{
var split = parameter.Split(ProxyConstants.EqualSplit, 2);
if (split.Length == 2 && split[0].Trim().Equals("boundary", StringComparison.CurrentCultureIgnoreCase))
{
string value = split[1];
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"')
{
value = value.Substring(1, value.Length - 2);
}
return value;
}
}
}
//return null if not specified
return null;
}
/// <summary>
/// Tries to get root domain from a given hostname
/// Adapted from below answer
......
using StreamExtended.Network;
using System;
using StreamExtended.Network;
using System.Collections.Generic;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Models;
......@@ -25,7 +26,7 @@ namespace Titanium.Web.Proxy.Http
{
nonUniqueResponseHeaders[newHeader.Name].Add(newHeader);
}
//if header is alread in unique header collection then move both to non-unique collection
//if header is already in unique header collection then move both to non-unique collection
else if (headers.ContainsKey(newHeader.Name))
{
var existing = headers[newHeader.Name];
......@@ -46,5 +47,17 @@ namespace Titanium.Web.Proxy.Http
}
}
}
/// <summary>
/// Increase size of buffer and copy existing content to new buffer
/// </summary>
/// <param name="buffer"></param>
/// <param name="size"></param>
private static void ResizeBuffer(ref byte[] buffer, long size)
{
var newBuffer = new byte[size];
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
buffer = newBuffer;
}
}
}
......@@ -3,6 +3,7 @@ using System.ComponentModel;
using System.Text;
using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
......@@ -156,6 +157,8 @@ namespace Titanium.Web.Proxy.Http
}
}
public bool IsMultipartFormData => ContentType?.StartsWith("multipart/form-data") == true;
/// <summary>
/// Request Url
/// </summary>
......@@ -164,7 +167,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Encoding for this request
/// </summary>
public Encoding Encoding => this.GetEncoding();
public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary>
/// Terminates the underlying Tcp Connection to client after current request
......
......@@ -2,6 +2,7 @@
using System.ComponentModel;
using System.Text;
using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
......@@ -36,7 +37,7 @@ namespace Titanium.Web.Proxy.Http
/// <summary>
/// Encoding used in response
/// </summary>
public Encoding Encoding => this.GetResponseCharacterEncoding();
public Encoding Encoding => HttpHelper.GetEncodingFromContentType(ContentType);
/// <summary>
/// Content encoding for this response
......
......@@ -457,11 +457,12 @@ namespace Titanium.Web.Proxy
try
{
args.WebSession.Request.RequestLocked = true;
var request = args.WebSession.Request;
request.RequestLocked = true;
//if expect continue is enabled then send the headers first
//and see if server would return 100 conitinue
if (args.WebSession.Request.ExpectContinue)
if (request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour);
......@@ -470,12 +471,12 @@ namespace Titanium.Web.Proxy
//If 100 continue was the response inform that to the client
if (Enable100ContinueBehaviour)
{
if (args.WebSession.Request.Is100Continue)
if (request.Is100Continue)
{
await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.Continue, "Continue");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
}
else if (args.WebSession.Request.ExpectationFailed)
else if (request.ExpectationFailed)
{
await args.ProxyClient.ClientStreamWriter.WriteResponseStatusAsync(args.WebSession.Response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed");
await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
......@@ -483,38 +484,37 @@ namespace Titanium.Web.Proxy
}
//If expect continue is not enabled then set the connectio and send request headers
if (!args.WebSession.Request.ExpectContinue)
if (!request.ExpectContinue)
{
args.WebSession.SetConnection(connection);
await args.WebSession.SendRequest(Enable100ContinueBehaviour);
}
//check if content-length is > 0
if (args.WebSession.Request.ContentLength > 0)
if (request.ContentLength > 0)
{
//If request was modified by user
if (args.WebSession.Request.IsBodyRead)
if (request.IsBodyRead)
{
if (args.WebSession.Request.ContentEncoding != null)
if (request.ContentEncoding != null)
{
args.WebSession.Request.Body =
await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.Body);
request.Body = await GetCompressedResponseBody(request.ContentEncoding, request.Body);
}
var body = args.WebSession.Request.Body;
var body = request.Body;
//chunked send is not supported as of now
args.WebSession.Request.ContentLength = body.Length;
request.ContentLength = body.Length;
var newStream = args.WebSession.ServerConnection.Stream;
await newStream.WriteAsync(body, 0, body.Length);
}
else
{
if (!args.WebSession.Request.ExpectationFailed)
if (!request.ExpectationFailed)
{
//If its a post/put/patch request, then read the client html body and send it to server
if (args.WebSession.Request.HasBody)
if (request.HasBody)
{
await SendClientRequestBody(args);
}
......@@ -523,7 +523,7 @@ namespace Titanium.Web.Proxy
}
//If not expectation failed response was returned by server then parse response
if (!args.WebSession.Request.ExpectationFailed)
if (!request.ExpectationFailed)
{
disposed = await HandleHttpSessionResponse(args);
......@@ -624,7 +624,18 @@ namespace Titanium.Web.Proxy
//send the request body bytes to server
if (args.WebSession.Request.ContentLength > 0)
{
await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength);
var request = args.WebSession.Request;
if (args.HasMulipartEventSubscribers && request.IsMultipartFormData)
{
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
args.OnMultipartRequestPartSent(boundary);
// todo
await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength);
}
else
{
await args.ProxyClient.ClientStreamReader.CopyBytesToStream(postStream, args.WebSession.Request.ContentLength);
}
}
//Need to revist, find any potential bugs
//send the request body bytes to server in chunks
......
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