Commit fb9ce94c authored by Honfika's avatar Honfika

Header improvements (part 2)

parent 1a2a9546
......@@ -135,11 +135,11 @@ namespace Titanium.Web.Proxy.EventArguments
HttpClient.Response = new Response();
}
internal void OnMultipartRequestPartSent(string boundary, HeaderCollection headers)
internal void OnMultipartRequestPartSent(ReadOnlySpan<char> boundary, HeaderCollection headers)
{
try
{
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary, headers));
MultipartRequestPartSent?.Invoke(this, new MultipartRequestPartSentEventArgs(boundary.ToString(), headers));
}
catch (Exception ex)
{
......@@ -252,7 +252,7 @@ namespace Titanium.Web.Proxy.EventArguments
if (contentLength > 0 && hasMulipartEventSubscribers && request.IsMultipartFormData)
{
var reader = getStreamReader(true);
string boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
var boundary = HttpHelper.GetBoundaryFromContentType(request.ContentType);
using (var copyStream = new CopyStream(reader, writer, BufferPool))
{
......@@ -268,7 +268,7 @@ namespace Titanium.Web.Proxy.EventArguments
{
var headers = new HeaderCollection();
await HeaderParser.ReadHeaders(copyStream, headers, cancellationToken);
OnMultipartRequestPartSent(boundary, headers);
OnMultipartRequestPartSent(boundary.Span, headers);
}
}
......@@ -333,7 +333,7 @@ namespace Titanium.Web.Proxy.EventArguments
/// Read a line from the byte stream
/// </summary>
/// <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;
......@@ -360,7 +360,7 @@ namespace Titanium.Web.Proxy.EventArguments
bool ok = true;
for (int i = 0; i < boundary.Length; i++)
{
if (buffer[startIdx + i] != boundary[i])
if (buffer[startIdx + i] != boundary.Span[i])
{
ok = false;
break;
......
......@@ -18,10 +18,54 @@ namespace Titanium.Web.Proxy.Helpers
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>
/// Gets the character encoding of request/response from content-type header
/// </summary>
/// <param name="contentType"></param>
/// <param name="contentType"></param>
/// <returns></returns>
internal static Encoding GetEncodingFromContentType(string contentType)
{
......@@ -34,24 +78,24 @@ namespace Titanium.Web.Proxy.Helpers
}
// extract the encoding by finding the charset
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
foreach (var p in new SemicolonSplitEnumerator(contentType))
{
var split = parameter.Split(ProxyConstants.EqualSplit, 2);
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset))
var parameter = p.Span;
int equalsIndex = parameter.IndexOf('=');
if (equalsIndex != -1 && parameter.Slice(0, equalsIndex).TrimStart().EqualsIgnoreCase(KnownHeaders.ContentTypeCharset.AsSpan()))
{
string value = split[1];
if (value.EqualsIgnoreCase("x-user-defined"))
var value = parameter.Slice(equalsIndex + 1);
if (value.EqualsIgnoreCase("x-user-defined".AsSpan()))
{
continue;
}
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
return defaultEncoding;
}
internal static string GetBoundaryFromContentType(string contentType)
internal static ReadOnlyMemory<char> GetBoundaryFromContentType(string contentType)
{
if (contentType != null)
{
// extract the boundary
var parameters = contentType.Split(ProxyConstants.SemiColonSplit);
foreach (string parameter in parameters)
foreach (var parameter in new SemicolonSplitEnumerator(contentType))
{
var split = parameter.Split(ProxyConstants.EqualSplit, 2);
if (split.Length == 2 && split[0].Trim().EqualsIgnoreCase(KnownHeaders.ContentTypeBoundary))
int equalsIndex = parameter.Span.IndexOf('=');
if (equalsIndex != -1 && parameter.Span.Slice(0, equalsIndex).TrimStart().EqualsIgnoreCase(KnownHeaders.ContentTypeBoundary.AsSpan()))
{
string value = split[1];
if (value.Length > 2 && value[0] == '"' && value[value.Length - 1] == '"')
var value = parameter.Slice(equalsIndex + 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;
......
......@@ -17,15 +17,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request.
/// </summary>
/// <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>
/// <returns></returns>
internal async Task WriteRequestAsync(Request request, bool flush = true,
CancellationToken cancellationToken = default)
internal async Task WriteRequestAsync(Request request, CancellationToken cancellationToken = default)
{
await WriteLineAsync(Request.CreateRequestLine(request.Method, request.RequestUriString, request.HttpVersion),
cancellationToken);
await WriteAsync(request, flush, cancellationToken);
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(request.Method, request.RequestUriString, request.HttpVersion);
await WriteAsync(request, headerBuilder, cancellationToken);
}
}
}
......@@ -18,29 +18,13 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the response.
/// </summary>
/// <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>
/// <returns>The Task.</returns>
internal async Task WriteResponseAsync(Response response, bool flush = true,
CancellationToken cancellationToken = default)
internal async Task WriteResponseAsync(Response response, CancellationToken cancellationToken = default)
{
await WriteResponseStatusAsync(response.HttpVersion, response.StatusCode, response.StatusDescription,
cancellationToken);
await WriteAsync(response, flush, 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);
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
await WriteAsync(response, headerBuilder, cancellationToken);
}
}
}
using System;
using System.Buffers;
using System.Globalization;
using System.IO;
using System.Text;
......@@ -86,28 +87,20 @@ namespace Titanium.Web.Proxy.Helpers
/// <summary>
/// Write the headers to client
/// </summary>
/// <param name="headers"></param>
/// <param name="flush"></param>
/// <param name="header"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
internal async Task WriteHeadersAsync(HeaderCollection headers, bool flush = true,
CancellationToken cancellationToken = default)
internal async Task WriteHeadersAsync(HeaderBuilder header, CancellationToken cancellationToken = default)
{
var headerBuilder = new StringBuilder();
foreach (var header in headers)
{
headerBuilder.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
headerBuilder.Append(ProxyConstants.NewLine);
await WriteAsync(headerBuilder.ToString(), cancellationToken);
if (flush)
{
await stream.FlushAsync(cancellationToken);
}
await WriteAsync(header.GetBytes(), true, 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)
{
await stream.WriteAsync(data, 0, data.Length, cancellationToken);
......@@ -280,14 +273,14 @@ namespace Titanium.Web.Proxy.Helpers
/// Writes the request/response headers and body.
/// </summary>
/// <param name="requestResponse"></param>
/// <param name="flush"></param>
/// <param name="headerBuilder"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected async Task WriteAsync(RequestResponseBase requestResponse, bool flush = true,
CancellationToken cancellationToken = default)
protected async Task WriteAsync(RequestResponseBase requestResponse, HeaderBuilder headerBuilder, CancellationToken cancellationToken = default)
{
var body = requestResponse.CompressBodyAndUpdateContentLength();
await WriteHeadersAsync(requestResponse.Headers, flush, cancellationToken);
headerBuilder.WriteHeaders(requestResponse.Headers);
await WriteAsync(headerBuilder.GetBytes(), true, cancellationToken);
if (body != null)
{
......@@ -322,5 +315,31 @@ namespace Titanium.Web.Proxy.Helpers
{
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
string tmpLine;
while (!string.IsNullOrEmpty(tmpLine = await reader.ReadLineAsync(cancellationToken)))
{
var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
headerCollection.AddHeader(header[0], header[1]);
int colonIndex = tmpLine.IndexOf(':');
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
url = Request.RequestUri.GetOriginalPathAndQuery();
}
var headerBuilder = new StringBuilder();
var headerBuilder = new HeaderBuilder();
// prepare the request & headers
headerBuilder.Append(Request.CreateRequestLine(Request.Method, url, Request.HttpVersion));
headerBuilder.Append(ProxyConstants.NewLine);
headerBuilder.WriteRequestLine(Request.Method, url, Request.HttpVersion);
// Send Authentication to Upstream proxy if needed
if (!isTransparent && upstreamProxy != null
......@@ -119,8 +118,8 @@ namespace Titanium.Web.Proxy.Http
&& !string.IsNullOrEmpty(upstreamProxy.UserName)
&& upstreamProxy.Password != null)
{
headerBuilder.Append($"{HttpHeader.ProxyConnectionKeepAlive}{ProxyConstants.NewLine}");
headerBuilder.Append($"{HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password)}{ProxyConstants.NewLine}");
headerBuilder.WriteHeader(HttpHeader.ProxyConnectionKeepAlive);
headerBuilder.WriteHeader(HttpHeader.GetProxyAuthorizationHeader(upstreamProxy.UserName, upstreamProxy.Password));
}
// write request headers
......@@ -128,13 +127,15 @@ namespace Titanium.Web.Proxy.Http
{
if (isTransparent || header.Name != KnownHeaders.ProxyAuthorization)
{
headerBuilder.Append($"{header}{ProxyConstants.NewLine}");
headerBuilder.WriteHeader(header);
}
}
headerBuilder.Append(ProxyConstants.NewLine);
await writer.WriteAsync(headerBuilder.ToString(), cancellationToken);
headerBuilder.WriteLine();
var data = headerBuilder.GetBytes();
await writer.WriteAsync(data, 0, data.Length, cancellationToken);
if (enable100ContinueBehaviour && Request.ExpectContinue)
{
......
......@@ -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;
......@@ -154,15 +155,15 @@ namespace Titanium.Web.Proxy.Http
{
get
{
var sb = new StringBuilder();
sb.Append($"{CreateRequestLine(Method, RequestUriString, HttpVersion)}{ProxyConstants.NewLine}");
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(Method, RequestUriString, HttpVersion);
foreach (var header in Headers)
{
sb.Append($"{header}{ProxyConstants.NewLine}");
headerBuilder.WriteHeader(header);
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
headerBuilder.WriteLine();
return HttpHelper.HeaderEncoding.GetString(headerBuilder.GetBytes());
}
}
......@@ -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,
out Version version)
{
......
......@@ -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;
......@@ -99,15 +100,10 @@ namespace Titanium.Web.Proxy.Http
{
get
{
var sb = new StringBuilder();
sb.Append($"{CreateResponseLine(HttpVersion, StatusCode, StatusDescription)}{ProxyConstants.NewLine}");
foreach (var header in Headers)
{
sb.Append($"{header.ToString()}{ProxyConstants.NewLine}");
}
sb.Append(ProxyConstants.NewLine);
return sb.ToString();
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteResponseLine(HttpVersion, StatusCode, StatusDescription);
headerBuilder.WriteHeaders(Headers);
return HttpHelper.HeaderEncoding.GetString(headerBuilder.GetBytes());
}
}
......
......@@ -217,7 +217,7 @@ namespace Titanium.Web.Proxy
connection = null;
}
var result = await handleHttpSessionRequest(httpCmd, args, connection,
var result = await handleHttpSessionRequest(httpMethod, httpUrl, version, args, connection,
clientConnection.NegotiatedApplicationProtocol,
cancellationToken, cancellationTokenSource);
......@@ -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,
CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource)
{
......@@ -315,7 +315,7 @@ namespace Titanium.Web.Proxy
args.HttpClient.ConnectRequest.TunnelType = TunnelType.Websocket;
// 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,
connection, cancellationTokenSource, cancellationToken);
return false;
......@@ -346,9 +346,13 @@ namespace Titanium.Web.Proxy
{
var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
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);
}
......
......@@ -91,9 +91,8 @@ namespace Titanium.Web.Proxy
// clear current response
await args.ClearResponse(cancellationToken);
var httpCmd = Request.CreateRequestLine(args.HttpClient.Request.Method,
args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion);
await handleHttpSessionRequest(httpCmd, args, null, args.ClientConnection.NegotiatedApplicationProtocol,
await handleHttpSessionRequest(args.HttpClient.Request.Method, args.HttpClient.Request.RequestUriString, args.HttpClient.Request.HttpVersion,
args, null, args.ClientConnection.NegotiatedApplicationProtocol,
cancellationToken, args.CancellationTokenSource);
return;
}
......@@ -112,9 +111,10 @@ namespace Titanium.Web.Proxy
else
{
// Write back response status to client
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);
// Write body if exists
if (response.HasBody)
......
......@@ -12,10 +12,6 @@ namespace Titanium.Web.Proxy.Shared
{
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 byte[] NewLineBytes = { (byte)'\r', (byte)'\n' };
......
......@@ -15,6 +15,7 @@
<PackageReference Include="BrotliSharpLib" Version="0.3.3" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.5" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
......
......@@ -16,16 +16,17 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Handle upgrade to websocket
/// </summary>
private async Task handleWebSocketUpgrade(string httpCmd,
private async Task handleWebSocketUpgrade(string requestHttpMethod, string requestHttpUrl, Version requestVersion,
SessionEventArgs args, Request request, Response response,
CustomBufferedStream clientStream, HttpResponseWriter clientStreamWriter,
TcpServerConnection serverConnection,
CancellationTokenSource cancellationTokenSource, CancellationToken cancellationToken)
{
// prepare the prefix content
await serverConnection.StreamWriter.WriteLineAsync(httpCmd, cancellationToken);
await serverConnection.StreamWriter.WriteHeadersAsync(request.Headers,
cancellationToken: cancellationToken);
var headerBuilder = new HeaderBuilder();
headerBuilder.WriteRequestLine(requestHttpMethod, requestHttpUrl, requestVersion);
headerBuilder.WriteHeaders(request.Headers);
await serverConnection.StreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);
string httpStatus;
try
......
......@@ -7,6 +7,8 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
{
internal static class HttpMessageParsing
{
private static readonly char[] colonSplit = { ':' };
/// <summary>
/// This is a terribly inefficient way of reading & parsing an
/// http request, but it's good enough for testing purposes.
......@@ -30,7 +32,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
};
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]);
}
......@@ -76,7 +78,7 @@ namespace Titanium.Web.Proxy.IntegrationTests.Helpers
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]);
}
......
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