Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apply async best practices to Tor library #2137

Merged
merged 6 commits into from Aug 21, 2019
Merged
@@ -30,18 +30,18 @@ public static async Task<HttpRequestMessage> CreateNewAsync(Stream requestStream
// CRLF
// [message - body]

string startLine = await HttpMessageHelper.ReadStartLineAsync(requestStream, ctsToken);
string startLine = await HttpMessageHelper.ReadStartLineAsync(requestStream, ctsToken).ConfigureAwait(false);

var requestLine = RequestLine.CreateNew(startLine);
var requestLine = RequestLine.Parse(startLine);
var request = new HttpRequestMessage(requestLine.Method, requestLine.URI);

string headers = await HttpMessageHelper.ReadHeadersAsync(requestStream, ctsToken);
string headers = await HttpMessageHelper.ReadHeadersAsync(requestStream, ctsToken).ConfigureAwait(false);

var headerSection = HeaderSection.CreateNew(headers);
var headerSection = await HeaderSection.CreateNewAsync(headers).ConfigureAwait(false);
var headerStruct = headerSection.ToHttpRequestHeaders();

HttpMessageHelper.AssertValidHeaders(headerStruct.RequestHeaders, headerStruct.ContentHeaders);
byte[] contentBytes = await HttpMessageHelper.GetContentBytesAsync(requestStream, headerStruct, ctsToken);
byte[] contentBytes = await HttpMessageHelper.GetContentBytesAsync(requestStream, headerStruct, ctsToken).ConfigureAwait(false);
contentBytes = HttpMessageHelper.HandleGzipCompression(headerStruct.ContentHeaders, contentBytes);
request.Content = contentBytes is null ? null : new ByteArrayContent(contentBytes);

@@ -95,7 +95,7 @@ public static async Task<string> ToHttpStringAsync(this HttpRequestMessage me)
headers += headerSection.ToString(endWithTwoCRLF: false);
}

messageBody = await me.Content.ReadAsStringAsync();
messageBody = await me.Content.ReadAsStringAsync().ConfigureAwait(false);
}

return startLine + headers + CRLF + messageBody;
@@ -119,7 +119,7 @@ public static async Task<HttpRequestMessage> CloneAsync(this HttpRequestMessage
}

var ms = new MemoryStream();
await me.Content.CopyToAsync(ms);
await me.Content.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
var newContent = new StreamContent(ms);

@@ -30,18 +30,18 @@ public static async Task<HttpResponseMessage> CreateNewAsync(Stream responseStre
// CRLF
// [message - body]

string startLine = await HttpMessageHelper.ReadStartLineAsync(responseStream);
string startLine = await HttpMessageHelper.ReadStartLineAsync(responseStream).ConfigureAwait(false);

var statusLine = StatusLine.CreateNew(startLine);
var statusLine = StatusLine.Parse(startLine);
var response = new HttpResponseMessage(statusLine.StatusCode);

string headers = await HttpMessageHelper.ReadHeadersAsync(responseStream);
string headers = await HttpMessageHelper.ReadHeadersAsync(responseStream).ConfigureAwait(false);

var headerSection = HeaderSection.CreateNew(headers);
var headerSection = await HeaderSection.CreateNewAsync(headers);
var headerStruct = headerSection.ToHttpResponseHeaders();

HttpMessageHelper.AssertValidHeaders(headerStruct.ResponseHeaders, headerStruct.ContentHeaders);
byte[] contentBytes = await HttpMessageHelper.GetContentBytesAsync(responseStream, headerStruct, requestMethod, statusLine);
byte[] contentBytes = await HttpMessageHelper.GetContentBytesAsync(responseStream, headerStruct, requestMethod, statusLine).ConfigureAwait(false);
contentBytes = HttpMessageHelper.HandleGzipCompression(headerStruct.ContentHeaders, contentBytes);
response.Content = contentBytes is null ? null : new ByteArrayContent(contentBytes);

@@ -55,7 +55,7 @@ public static async Task<HttpResponseMessage> CreateNewAsync(Stream responseStre

public static async Task<Stream> ToStreamAsync(this HttpResponseMessage me)
{
return new MemoryStream(Encoding.UTF8.GetBytes(await me.ToHttpStringAsync()));
return new MemoryStream(Encoding.UTF8.GetBytes(await me.ToHttpStringAsync().ConfigureAwait(false)));
}

public static async Task<string> ToHttpStringAsync(this HttpResponseMessage me)
@@ -78,15 +78,15 @@ public static async Task<string> ToHttpStringAsync(this HttpResponseMessage me)
headers += headerSection.ToString(endWithTwoCRLF: false);
}

messageBody = await me.Content.ReadAsStringAsync();
messageBody = await me.Content.ReadAsStringAsync().ConfigureAwait(false);
}

return startLine + headers + CRLF + messageBody;
}

public static async Task ThrowRequestExceptionFromContentAsync(this HttpResponseMessage me)
{
var error = await me.Content.ReadAsJsonAsync<string>();
var error = await me.Content.ReadAsJsonAsync<string>().ConfigureAwait(false);
string errorMessage = error is null ? string.Empty : $"\n{error}";
throw new HttpRequestException($"{me.StatusCode.ToReasonString()}{errorMessage}");
}
@@ -359,7 +359,7 @@ private static async Task<byte[]> GetDecodedChunkedContentBytesAsync(Stream stre
// of a chunked message in order to supply metadata that might be
// dynamically generated while the message body is sent
string trailerHeaders = await ReadHeadersAsync(stream, ctsToken);
var trailerHeaderSection = HeaderSection.CreateNew(trailerHeaders);
var trailerHeaderSection = await HeaderSection.CreateNewAsync(trailerHeaders);
RemoveInvalidTrailers(trailerHeaderSection);
if (responseHeaders != null)
{
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
using static WalletWasabi.Http.Constants;

@@ -49,7 +50,7 @@ public override string ToString()
return ToString(true);
}

public static HeaderField CreateNew(string fieldString)
public static async Task<HeaderField> CreateNewAsync(string fieldString)
{
fieldString = fieldString.TrimEnd(CRLF, StringComparison.Ordinal);

@@ -71,7 +72,7 @@ public static HeaderField CreateNew(string fieldString)
throw new FormatException($"Wrong {nameof(HeaderField)}: {fieldString}.");
}

var value = reader.ReadToEnd();
var value = await reader.ReadToEndAsync().ConfigureAwait(false);
value = Guard.Correct(value);

return new HeaderField(name, value);
@@ -5,6 +5,7 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using static WalletWasabi.Http.Constants;

namespace WalletWasabi.Http.Models
@@ -32,7 +33,7 @@ public override string ToString()
return ToString(false);
}

public static HeaderSection CreateNew(string headersString)
public static async Task<HeaderSection> CreateNewAsync(string headersString)
{
headersString = HeaderField.CorrectObsFolding(headersString);

@@ -51,7 +52,7 @@ public static HeaderSection CreateNew(string headersString)
{
break;
}
hs.Fields.Add(HeaderField.CreateNew(field));
hs.Fields.Add(await HeaderField.CreateNewAsync(field).ConfigureAwait(false));
}

ValidateAndCorrectHeaders(hs);
@@ -1,5 +1,7 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using static WalletWasabi.Http.Constants;

namespace WalletWasabi.Http.Models
@@ -8,10 +10,10 @@ namespace WalletWasabi.Http.Models
// request-line = method SP request-target SP HTTP-version CRLF
public class RequestLine : StartLine
{
public HttpMethod Method { get; private set; }
public Uri URI { get; private set; }
public HttpMethod Method { get; }
public Uri URI { get; }

public RequestLine(HttpMethod method, Uri uri, HttpProtocol protocol)
public RequestLine(HttpMethod method, Uri uri, HttpProtocol protocol) : base(protocol)
{
Method = method;
// https://tools.ietf.org/html/rfc7230#section-2.7.1
@@ -22,12 +24,14 @@ public RequestLine(HttpMethod method, Uri uri, HttpProtocol protocol)
}

URI = uri;
Protocol = protocol;
}

StartLineString = Method.Method + SP + URI.AbsolutePath + URI.Query + SP + Protocol + CRLF;
public override string ToString()
{
return $"{Method.Method}{SP}{URI.AbsolutePath}{URI.Query}{SP}{Protocol}{CRLF}";
}

public static RequestLine CreateNew(string requestLineString)
public static RequestLine Parse(string requestLineString)
{
try
{
@@ -1,62 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using WalletWasabi.Helpers;
using static WalletWasabi.Http.Constants;

namespace WalletWasabi.Http.Models
{
public abstract class StartLine
{
public HttpProtocol Protocol { get; protected set; }
public string StartLineString { get; protected set; }
public List<string> Parts => GetParts(StartLineString);
public HttpProtocol Protocol { get; }

public override string ToString()
protected StartLine(HttpProtocol protocol)
{
return StartLineString;
Protocol = protocol;
}

public static List<string> GetParts(string startLineString)
public static string[] GetParts(string startLineString)
{
var trimmed = "";
// check if an unexpected crlf in the startlinestring
using (var reader = new StringReader(startLineString))
{
// read to CRLF, if it does not end with that it reads to end, if it does, it removes it
trimmed = reader.ReadLine(strictCRLF: true);
// startLineString must end here
if (reader.Read() != -1)
{
throw new Exception($"Wrong {startLineString} provided.");
}
}

var parts = new List<string>();
using (var reader = new StringReader(trimmed))
{
while (true)
{
var part = reader.ReadPart(SP.ToCharArray()[0]);

if (part is null || part == "")
{
break;
}

if (parts.Count == 2)
{
var rest = reader.ReadToEnd();

// startLineString must end here, the ReadToEnd returns "" if nothing to read instead of null
if (rest != "")
{
part += SP + rest;
}
}
parts.Add(part);
}
}
return parts;
var trimmed = Guard.NotNullOrEmptyOrWhitespace(nameof(startLineString), startLineString, trim: true);
return trimmed.Split(SP, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).ToArray();
}
}
}
@@ -1,23 +1,27 @@
using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using WalletWasabi.Logging;
using static WalletWasabi.Http.Constants;

namespace WalletWasabi.Http.Models
{
public class StatusLine : StartLine
{
public HttpStatusCode StatusCode { get; private set; }
public HttpStatusCode StatusCode { get; }

public StatusLine(HttpProtocol protocol, HttpStatusCode status)
public StatusLine(HttpProtocol protocol, HttpStatusCode status) : base(protocol)
{
Protocol = protocol;
StatusCode = status;
}

StartLineString = Protocol + SP + (int)StatusCode + SP + StatusCode.ToReasonString() + CRLF;
public override string ToString()
{
return $"{Protocol}{SP}{(int)StatusCode}{SP}{StatusCode.ToReasonString()}{CRLF}";
}

public static StatusLine CreateNew(string statusLineString)
public static StatusLine Parse(string statusLineString)
{
try
{
@@ -88,11 +88,11 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat

try
{
using (await AsyncLock.LockAsync(cancel))
using (await AsyncLock.LockAsync(cancel).ConfigureAwait(false))
{
try
{
HttpResponseMessage ret = await SendAsync(request);
HttpResponseMessage ret = await SendAsync(request).ConfigureAwait(false);
TorDoesntWorkSince = null;
return ret;
}
@@ -106,7 +106,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat
cancel.ThrowIfCancellationRequested();
try
{
HttpResponseMessage ret2 = await SendAsync(request);
HttpResponseMessage ret2 = await SendAsync(request).ConfigureAwait(false);
TorDoesntWorkSince = null;
return ret2;
}
@@ -120,7 +120,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat

try
{
await Task.Delay(1000, cancel);
await Task.Delay(1000, cancel).ConfigureAwait(false);
}
catch (TaskCanceledException tce)
{
@@ -134,7 +134,7 @@ public async Task<HttpResponseMessage> SendAsync(HttpMethod method, string relat

cancel.ThrowIfCancellationRequested();

HttpResponseMessage ret3 = await SendAsync(request);
HttpResponseMessage ret3 = await SendAsync(request).ConfigureAwait(false);
TorDoesntWorkSince = null;
return ret3;
}
@@ -187,9 +187,9 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, Can
if (TorSocks5Client is null || !TorSocks5Client.IsConnected)
{
TorSocks5Client = new TorSocks5Client(TorSocks5EndPoint);
await TorSocks5Client.ConnectAsync();
await TorSocks5Client.HandshakeAsync(IsolateStream);
await TorSocks5Client.ConnectToDestinationAsync(host, request.RequestUri.Port);
await TorSocks5Client.ConnectAsync().ConfigureAwait(false);
await TorSocks5Client.HandshakeAsync(IsolateStream).ConfigureAwait(false);
await TorSocks5Client.ConnectToDestinationAsync(host, request.RequestUri.Port).ConfigureAwait(false);

Stream stream = TorSocks5Client.TcpClient.GetStream();
if (request.RequestUri.Scheme == "https")
@@ -219,7 +219,7 @@ await sslStream
host,
new X509CertificateCollection(),
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12,
checkCertificateRevocation: true);
checkCertificateRevocation: true).ConfigureAwait(false);
stream = sslStream;
}

@@ -249,21 +249,21 @@ await sslStream
{
if (request.Content.Headers.ContentLength is null)
{
request.Content.Headers.ContentLength = (await request.Content.ReadAsStringAsync()).Length;
request.Content.Headers.ContentLength = (await request.Content.ReadAsStringAsync().ConfigureAwait(false)).Length;
}
}
}
}

var requestString = await request.ToHttpStringAsync();
var requestString = await request.ToHttpStringAsync().ConfigureAwait(false);

var bytes = Encoding.UTF8.GetBytes(requestString);

await TorSocks5Client.Stream.WriteAsync(bytes, 0, bytes.Length);
await TorSocks5Client.Stream.FlushAsync();
await TorSocks5Client.Stream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
await TorSocks5Client.Stream.FlushAsync().ConfigureAwait(false);
using (var httpResponseMessage = new HttpResponseMessage())
{
return await HttpResponseMessageExtensions.CreateNewAsync(TorSocks5Client.Stream, request.Method);
return await HttpResponseMessageExtensions.CreateNewAsync(TorSocks5Client.Stream, request.Method).ConfigureAwait(false);
}
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.