Commit fb9ce94c authored by Honfika's avatar Honfika

Header improvements (part 2)

parent 1a2a9546
...@@ -135,11 +135,11 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -135,11 +135,11 @@ namespace Titanium.Web.Proxy.EventArguments
HttpClient.Response = new Response(); HttpClient.Response = new Response();
} }
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers) internal void OnMultipartRequestPartSent(ReadOnlySpan<char> boundary, HeaderCollection headers)
{ {
try try
{ {
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers)); MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary.ToString(), headers));
} }
catch (Exception ex) catch (Exception ex)
{ {
...@@ -252,7 +252,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -252,7 +252,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData) if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{ {
var reader = getStreamReader(true); var reader = getStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType); var boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, BufferPool)) using (var copyStream = new CopyStream(reader, writer, BufferPool))
{ {
...@@ -268,7 +268,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -268,7 +268,7 @@ namespace Titanium.Web.Proxy.EventArguments
{ {
var headers = new HeaderCollection(); var headers = new HeaderCollection();
await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken); await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken);
OnMultipartRequestPartSent(boundary, headers); OnMultipartRequestPartSent(boundary.Span, headers);
} }
} }
...@@ -333,7 +333,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -333,7 +333,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Read a line from the byte stream /// Read a line from the byte stream
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, string boundary, CancellationToken cancellationToken) private async Task<long> readUntilBoundaryAsync(ICustomStreamReader reader, long totalBytesToRead, ReadOnlyMemory<char> boundary, CancellationToken cancellationToken)
{ {
int bufferDataLength = 0; int bufferDataLength = 0;
...@@ -360,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments ...@@ -360,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments
bool ok = true; bool ok = true;
for (int i = 0; i < boundary.Length; i++) for (int i = 0; i < boundary.Length; i++)
{ {
if (buffer[startIdx + i] != boundary[i]) if (buffer[startIdx + i] != boundary.Span[i])
{ {
ok = false; ok = false;
break; break;
......
...@@ -18,6 +18,50 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -18,6 +18,50 @@ namespace Titanium.Web.Proxy.Helpers
public static Encoding HeaderEncoding => defaultEncoding; public static Encoding HeaderEncoding => defaultEncoding;
struct SemicolonSplitEnumerator
{
private readonly ReadOnlyMemory<char> data;
private ReadOnlyMemory<char> current;
private int idx;
public SemicolonSplitEnumerator(string str) : this(str.AsMemory())
{
}
public SemicolonSplitEnumerator(ReadOnlyMemory<char> data)
{
this.data = data;
current = null;
idx = 0;
}
public SemicolonSplitEnumerator GetEnumerator() { return this; }
public bool MoveNext()
{
if (this.idx > data.Length) return false;
int idx = data.Span.Slice(this.idx).IndexOf(';');
if (idx == -1)
{
idx = data.Length;
}
else
{
idx += this.idx;
}
current = data.Slice(this.idx, idx - this.idx);
this.idx = idx + 1;
return true;
}
public ReadOnlyMemory<char> Current => current;
}
/// <summary> /// <summary>
/// Gets the character encoding of request/response from content-type header /// Gets the character encoding of request/response from content-type header
/// </summary> /// </summary>
...@@ -34,24 +78,24 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -34,24 +78,24 @@ namespace Titanium.Web.Proxy.Helpers
} }
// extract the encoding by finding the charset // extract the encoding by finding the charset
var parameters = contentType.Split(ProxyConstants.SemiColonSplit); foreach (var p in new SemicolonSplitEnumerator(contentType))
foreach (string parameter in parameters)
{ {
var split = parameter.Split(ProxyConstants.EqualSplit, 2); var parameter = p.Span;
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset)) int equalsIndex = parameter.IndexOf('=');
if (equalsIndex != -1 && parameter.Slice(0, equalsIndex).TrimStart().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset.AsSpan()))
{ {
string value = split[1]; var value = parameter.Slice(equalsIndex + 1);
if (value.EqualsIgnoreCase("x-user-defined")) if (value.EqualsIgnoreCase("x-user-defined".AsSpan()))
{ {
continue; continue;
} }
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"') if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"')
{ {
value = value.Substring(1, value.Length - 2); value = value.Slice(1, value.Length - 2);
} }
return Encoding.GetEncoding(value); return Encoding.GetEncoding(value.ToString());
} }
} }
} }
...@@ -65,21 +109,20 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -65,21 +109,20 @@ namespace Titanium.Web.Proxy.Helpers
return defaultEncoding; return defaultEncoding;
} }
internal static string GetBoundaryFromContentType(string contentType) internal static ReadOnlyMemory<char> GetBoundaryFromContentType(string contentType)
{ {
if (contentType != null) if (contentType != null)
{ {
// extract the boundary // extract the boundary
var parameters = contentType.Split(ProxyConstants.SemiColonSplit); foreach (var parameter in new SemicolonSplitEnumerator(contentType))
foreach (string parameter in parameters)
{ {
var split = parameter.Split(ProxyConstants.EqualSplit, 2); int equalsIndex = parameter.Span.IndexOf('=');
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeBoundary)) if (equalsIndex != -1 && parameter.Span.Slice(0, equalsIndex).TrimStart().EqualsIgnoreCase(KnownHeaders.ContentTypeBoundary.AsSpan()))
{ {
string value = split[1]; var value = parameter.Slice(equalsIndex + 1);
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"') if (value.Length > 2 && value.Span[0] == '"' && value.Span[value.Length - 1] == '"')
{ {
value = value.Substring(1, value.Length - 2); value = value.Slice(1, value.Length - 2);
} }
return value; return value;
......
...@@ -17,15 +17,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -17,15 +17,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request. /// Writes the request.
/// </summary> /// </summary>
/// <param name="request">The request object.</param> /// <param name="request">The request object.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns></returns> /// <returns></returns>
internal async Task WriteRequestAsync(Request request, bool flush = true, internal async Task WriteRequestAsync(Request request, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
await WriteLineAsync(Request.CreateRequestLine(request.Method, request.RequestUriString, request.HttpVersion), var headerBuilder = new HeaderBuilder();
cancellationToken); headerBuilder.WriteRequestLine(request.Method, request.RequestUriString, request.HttpVersion);
await WriteAsync(request, flush, cancellationToken); await WriteAsync(request, headerBuilder, cancellationToken);
} }
} }
} }
...@@ -18,29 +18,13 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -18,29 +18,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the response. /// Writes the response.
/// </summary> /// </summary>
/// <param name="response">The response object.</param> /// <param name="response">The response object.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param> /// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns> /// <returns>The Task.</returns>
internal async Task WriteResponseAsync(Response response, bool flush = true, internal async Task WriteResponseAsync(Response response, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
await WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription, var headerBuilder = new HeaderBuilder();
cancellationToken); headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
await WriteAsync(response, flush, cancellationToken); await WriteAsync(response, headerBuilder, cancellationToken);
}
/// <summary>
/// Write response status
/// </summary>
/// <param name="version">The Http version.</param>
/// <param name="code">The HTTP status code.</param>
/// <param name="description">The HTTP status description.</param>
/// <param name="cancellationToken">Optional cancellation token for this async task.</param>
/// <returns>The Task.</returns>
internal Task WriteResponseStatusAsync(Version version, int code, string description,
CancellationToken cancellationToken)
{
return WriteLineAsync(Response.CreateResponseLine(version, code, description), cancellationToken);
} }
} }
} }
using System; using System;
using System.Buffers;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text; using System.Text;
...@@ -86,28 +87,20 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -86,28 +87,20 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary> /// <summary>
/// Write the headers to client /// Write the headers to client
/// </summary> /// </summary>
/// <param name="headers"></param> /// <param name="header"></param>
/// <param name="flush"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true, internal async Task WriteHeadersAsync(HeaderBuilder header, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{
var headerBuilder = new StringBuilder();
foreach (var header in headers)
{ {
headerBuilder.Append($"{header.ToString()}{ProxyConstants.NewLine}"); await WriteAsync(header.GetBytes(), true, cancellationToken);
}
headerBuilder.Append(ProxyConstants.NewLine);
await WriteAsync(headerBuilder.ToString(), cancellationToken);
if (flush)
{
await stream.FlushAsync(cancellationToken);
}
} }
/// <summary>
/// Writes the data to the stream.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="flush">Should we flush after write?</param>
/// <param name="cancellationToken">The cancellation token.</param>
internal async Task WriteAsync(byte[] data, bool flush = false, CancellationToken cancellationToken = default) internal async Task WriteAsync(byte[] data, bool flush = false, CancellationToken cancellationToken = default)
{ {
await stream.WriteAsync(data, 0, data.Length, cancellationToken); await stream.WriteAsync(data, 0, data.Length, cancellationToken);
...@@ -280,14 +273,14 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -280,14 +273,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request/response headers and body. /// Writes the request/response headers and body.
/// </summary> /// </summary>
/// <param name="requestResponse"></param> /// <param name="requestResponse"></param>
/// <param name="flush"></param> /// <param name="headerBuilder"></param>
/// <param name="cancellationToken"></param> /// <param name="cancellationToken"></param>
/// <returns></returns> /// <returns></returns>
protected async Task WriteAsync(RequestResponseBase requestResponse, bool flush = true, protected async Task WriteAsync(RequestResponseBase requestResponse, HeaderBuilder headerBuilder, CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default)
{ {
var body = requestResponse.CompressBodyAndUpdateContentLength(); var body = requestResponse.CompressBodyAndUpdateContentLength();
await WriteHeadersAsync(requestResponse.Headers, flush, cancellationToken); headerBuilder.WriteHeaders(requestResponse.Headers);
await WriteAsync(headerBuilder.GetBytes(), true, cancellationToken);
if (body != null) if (body != null)
{ {
...@@ -322,5 +315,31 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -322,5 +315,31 @@ namespace Titanium.Web.Proxy.Helpers
{ {
return stream.WriteAsync(buffer, offset, count, cancellationToken); return stream.WriteAsync(buffer, offset, count, cancellationToken);
} }
#if NETSTANDARD2_1
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
return stream.WriteAsync(buffer, cancellationToken);
}
#else
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="P:System.Threading.CancellationToken.None" />.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
public Task WriteAsync2(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
{
var buf = ArrayPool<byte>.Shared.Rent(buffer.Length);
buffer.CopyTo(buf);
return stream.WriteAsync(buf, 0, buf.Length, cancellationToken);
}
#endif
} }
} }
using System;
using System.Buffers;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Http
{
internal class HeaderBuilder
{
private MemoryStream stream = new MemoryStream();
public void WriteRequestLine(string httpMethod, string httpUrl, Version version)
{
// "{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
Write(httpMethod);
Write(" ");
Write(httpUrl);
Write(" HTTP/");
Write(version.Major.ToString());
Write(".");
Write(version.Minor.ToString());
WriteLine();
}
public void WriteResponseLine(Version version, int statusCode, string statusDescription)
{
// "HTTP/{version.Major}.{version.Minor} {statusCode} {statusDescription}";
Write("HTTP/");
Write(version.Major.ToString());
Write(".");
Write(version.Minor.ToString());
Write(" ");
Write(statusCode.ToString());
Write(" ");
Write(statusDescription);
WriteLine();
}
public void WriteHeaders(HeaderCollection headers)
{
foreach (var header in headers)
{
WriteHeader(header);
}
WriteLine();
}
public void WriteHeader(HttpHeader header)
{
Write(header.Name);
Write(": ");
Write(header.Value);
WriteLine();
}
public void WriteLine()
{
var data = ProxyConstants.NewLineBytes;
stream.Write(data, 0, data.Length);
}
public void Write(string str)
{
var encoding = HttpHelper.HeaderEncoding;
#if NETSTANDARD2_1
var buf = ArrayPool<byte>.Shared.Rent(str.Length * 4);
var span = new Span<byte>(buf);
encoding.GetBytes(str.AsSpan(), span);
stream.Write(span);
ArrayPool<byte>.Shared.Return(buf);
#else
var data = encoding.GetBytes(str);
stream.Write(data, 0, data.Length);
#endif
}
public byte[] GetBytes()
{
return stream.ToArray();
}
}
}
...@@ -14,8 +14,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -14,8 +14,15 @@ namespace Titanium.Web.Proxy.Http
string tmpLine; string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken))) while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken)))
{ {
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2); int colonIndex = tmpLine.IndexOf(':');
headerCollection.AddHeader(header[0], header[1]); if (colonIndex == -1)
{
throw new Exception("Header line should contain a colon character.");
}
string headerName = tmpLine.AsSpan(0, colonIndex).ToString();
string headerValue = tmpLine.AsSpan(colonIndex + 1).ToString();
headerCollection.AddHeader(headerName, headerValue);
} }
} }
......
...@@ -107,11 +107,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -107,11 +107,10 @@ namespace Titanium.Web.Proxy.Http
url = Request.RequestUri.GetOriginalPathAndQuery(); url = Request.RequestUri.GetOriginalPathAndQuery();
} }
var headerBuilder = new StringBuilder(); var headerBuilder = new HeaderBuilder();
// prepare the request & headers // prepare the request & headers
headerBuilder.Append(Request.CreateRequestLine(Request.Method, url, Request.HttpVersion)); headerBuilder.WriteRequestLine(Request.Method, url, Request.HttpVersion);
headerBuilder.Append(ProxyConstants.NewLine);
// Send Authentication to Upstream proxy if needed // Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null if (!isTransparent && upstreamProxy != null
...@@ -119,8 +118,8 @@ namespace Titanium.Web.Proxy.Http ...@@ -119,8 +118,8 @@ namespace Titanium.Web.Proxy.Http
&& !string.IsNullOrEmpty(upstreamProxy.UserName) && !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null) && upstreamProxy.Password != null)
{ {
headerBuilder.Append($"{HttpHeader.ProxyConnectionKeepAlive}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(HttpHeader.ProxyConnectionKeepAlive);
headerBuilder.Append($"{HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password));
} }
// write request headers // write request headers
...@@ -128,13 +127,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -128,13 +127,15 @@ namespace Titanium.Web.Proxy.Http
{ {
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization) if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
{ {
headerBuilder.Append($"{header}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(header);
} }
} }
headerBuilder.Append(ProxyConstants.NewLine); headerBuilder.WriteLine();
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken); var data = headerBuilder.GetBytes();
await writer.WriteAsync(data, 0, data.Length, cancellationToken);
if (enable100ContinueBehaviour && Request.ExpectContinue) if (enable100ContinueBehaviour && Request.ExpectContinue)
{ {
......
...@@ -3,6 +3,7 @@ using System.ComponentModel; ...@@ -3,6 +3,7 @@ using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Exceptions; using Titanium.Web.Proxy.Exceptions;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -154,15 +155,15 @@ namespace Titanium.Web.Proxy.Http ...@@ -154,15 +155,15 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var sb = new StringBuilder(); var headerBuilder = new HeaderBuilder();
sb.Append($"{CreateRequestLine(Method, RequestUriString, HttpVersion)}{ProxyConstants.NewLine}"); headerBuilder.WriteRequestLine(Method, RequestUriString, HttpVersion);
foreach (var header in Headers) foreach (var header in Headers)
{ {
sb.Append($"{header}{ProxyConstants.NewLine}"); headerBuilder.WriteHeader(header);
} }
sb.Append(ProxyConstants.NewLine); headerBuilder.WriteLine();
return sb.ToString(); return HttpHelper.HeaderEncoding.GetString(headerBuilder.GetBytes());
} }
} }
...@@ -197,11 +198,6 @@ namespace Titanium.Web.Proxy.Http ...@@ -197,11 +198,6 @@ namespace Titanium.Web.Proxy.Http
} }
} }
internal static string CreateRequestLine(string httpMethod, string httpUrl, Version version)
{
return $"{httpMethod} {httpUrl} HTTP/{version.Major}.{version.Minor}";
}
internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl, internal static void ParseRequestLine(string httpCmd, out string httpMethod, out string httpUrl,
out Version version) out Version version)
{ {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
using System.ComponentModel; using System.ComponentModel;
using System.Text; using System.Text;
using Titanium.Web.Proxy.Extensions; using Titanium.Web.Proxy.Extensions;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
...@@ -99,15 +100,10 @@ namespace Titanium.Web.Proxy.Http ...@@ -99,15 +100,10 @@ namespace Titanium.Web.Proxy.Http
{ {
get get
{ {
var sb = new StringBuilder(); var headerBuilder = new HeaderBuilder();
sb.Append($"{CreateResponseLine(HttpVersion, StatusCode, StatusDescription)}{ProxyConstants.NewLine}"); headerBuilder.WriteResponseLine(HttpVersion, StatusCode, StatusDescription);
foreach (var header in Headers) headerBuilder.WriteHeaders(Headers);
{ return HttpHelper.HeaderEncoding.GetString(headerBuilder.GetBytes());
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
} }
} }
......
...@@ -217,7 +217,7 @@ namespace Titanium.Web.Proxy ...@@ -217,7 +217,7 @@ namespace Titanium.Web.Proxy
connection = null; connection = null;
} }
var result = await handleHttpSessionRequest(httpCmd, args, connection, var result = await handleHttpSessionRequest(httpMethod, httpUrl, version, args, connection,
clientConnection.NegotiatedApplicationProtocol, clientConnection.NegotiatedApplicationProtocol,
cancellationToken, cancellationTokenSource); cancellationToken, cancellationTokenSource);
...@@ -295,7 +295,7 @@ namespace Titanium.Web.Proxy ...@@ -295,7 +295,7 @@ namespace Titanium.Web.Proxy
} }
} }
private async Task<RetryResult> handleHttpSessionRequest(string httpCmd, SessionEventArgs args, private async Task<RetryResult> handleHttpSessionRequest(string requestHttpMethod, string requestHttpUrl, Version requestVersion, SessionEventArgs args,
TcpServerConnection serverConnection, SslApplicationProtocol sslApplicationProtocol, TcpServerConnection serverConnection, SslApplicationProtocol sslApplicationProtocol,
CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource) CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource)
{ {
...@@ -315,7 +315,7 @@ namespace Titanium.Web.Proxy ...@@ -315,7 +315,7 @@ namespace Titanium.Web.Proxy
args.HttpClient.ConnectRequest.TunnelType = TunnelType.Websocket; args.HttpClient.ConnectRequest.TunnelType = TunnelType.Websocket;
// if upgrading to websocket then relay the request without reading the contents // if upgrading to websocket then relay the request without reading the contents
await handleWebSocketUpgrade(httpCmd, args, args.HttpClient.Request, await handleWebSocketUpgrade(requestHttpMethod, requestHttpUrl, requestVersion, args, args.HttpClient.Request,
args.HttpClient.Response, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamWriter, args.HttpClient.Response, args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamWriter,
connection, cancellationTokenSource, cancellationToken); connection, cancellationTokenSource, cancellationToken);
return false; return false;
...@@ -346,9 +346,13 @@ namespace Titanium.Web.Proxy ...@@ -346,9 +346,13 @@ namespace Titanium.Web.Proxy
{ {
var clientStreamWriter = args.ProxyClient.ClientStreamWriter; var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
var response = args.HttpClient.Response; var response = args.HttpClient.Response;
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
response.StatusDescription, cancellationToken);
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken); var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
headerBuilder.WriteHeaders(response.Headers);
await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
await args.ClearResponse(cancellationToken); await args.ClearResponse(cancellationToken);
} }
......
...@@ -91,9 +91,8 @@ namespace Titanium.Web.Proxy ...@@ -91,9 +91,8 @@ namespace Titanium.Web.Proxy
// clear current response // clear current response
await args.ClearResponse(cancellationToken); await args.ClearResponse(cancellationToken);
var httpCmd = Request.CreateRequestLine(args.HttpClient.Request.Method, await handleHttpSessionRequest(args.HttpClient.Request.Method, args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion,
args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion); args, null, args.ClientConnection.NegotiatedApplicationProtocol,
await handleHttpSessionRequest(httpCmd, args, null, args.ClientConnection.NegotiatedApplicationProtocol,
cancellationToken, args.CancellationTokenSource); cancellationToken, args.CancellationTokenSource);
return; return;
} }
...@@ -112,9 +111,10 @@ namespace Titanium.Web.Proxy ...@@ -112,9 +111,10 @@ namespace Titanium.Web.Proxy
else else
{ {
// Write back response status to client // Write back response status to client
await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, var headerBuilder = new HeaderBuilder();
response.StatusDescription, cancellationToken); headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken: cancellationToken); headerBuilder.WriteHeaders(response.Headers);
await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
// Write body if exists // Write body if exists
if (response.HasBody) if (response.HasBody)
......
...@@ -12,10 +12,6 @@ namespace Titanium.Web.Proxy.Shared ...@@ -12,10 +12,6 @@ namespace Titanium.Web.Proxy.Shared
{ {
internal static readonly char DotSplit = '.'; internal static readonly char DotSplit = '.';
internal static readonly char[] ColonSplit = { ':' };
internal static readonly char[] SemiColonSplit = { ';' };
internal static readonly char[] EqualSplit = { '=' };
internal static readonly string NewLine = "\r\n"; internal static readonly string NewLine = "\r\n";
internal static readonly byte[] NewLineBytes = { (byte)'\r', (byte)'\n' }; internal static readonly byte[] NewLineBytes = { (byte)'\r', (byte)'\n' };
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
<PackageReference Include="BrotliSharpLib" Version="0.3.3" /> <PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" /> <PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Buffers" Version="4.5.0" /> <PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
...@@ -16,16 +16,17 @@ namespace Titanium.Web.Proxy ...@@ -16,16 +16,17 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Handle upgrade to websocket /// Handle upgrade to websocket
/// </summary> /// </summary>
private async Task handleWebSocketUpgrade(string httpCmd, private async Task handleWebSocketUpgrade(string requestHttpMethod, string requestHttpUrl, Version requestVersion,
SessionEventArgs args, Request request, Response response, SessionEventArgs args, Request request, Response response,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter, CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
TcpServerConnection serverConnection, TcpServerConnection serverConnection,
CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken) CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
{ {
// prepare the prefix content // prepare the prefix content
await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken); var headerBuilder = new HeaderBuilder();
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers, headerBuilder.WriteRequestLine(requestHttpMethod, requestHttpUrl, requestVersion);
cancellationToken: cancellationToken); headerBuilder.WriteHeaders(request.Headers);
await serverConnection.StreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
string httpStatus; string httpStatus;
try try
......
...@@ -7,6 +7,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -7,6 +7,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
{ {
internal static class HttpMessageParsing internal static class HttpMessageParsing
{ {
private static readonly char[] colonSplit = { ':' };
/// <summary> /// <summary>
/// This is a terribly inefficient way of reading & parsing an /// This is a terribly inefficient way of reading & parsing an
/// http request, but it's good enough for testing purposes. /// http request, but it's good enough for testing purposes.
...@@ -30,7 +32,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -30,7 +32,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
}; };
while (!string.IsNullOrEmpty(line = reader.ReadLine())) while (!string.IsNullOrEmpty(line = reader.ReadLine()))
{ {
var header = line.Split(ProxyConstants.ColonSplit, 2); var header = line.Split(colonSplit, 2);
request.Headers.AddHeader(header[0], header[1]); request.Headers.AddHeader(header[0], header[1]);
} }
...@@ -76,7 +78,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers ...@@ -76,7 +78,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
while (!string.IsNullOrEmpty(line = reader.ReadLine())) while (!string.IsNullOrEmpty(line = reader.ReadLine()))
{ {
var header = line.Split(ProxyConstants.ColonSplit, 2); var header = line.Split(colonSplit, 2);
response.Headers.AddHeader(header[0], header[1]); response.Headers.AddHeader(header[0], header[1]);
} }
......
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