public HttpResponseMessage SendRequest(HttpRequestMessage request, bool ignoreSslCertification = false) { lock (Lock) { try { EnsureConnectedToTor(ignoreSslCertification); var isConnectRequest = Equals(request.Method, new HttpMethod("CONNECT")); string location; if (!isConnectRequest) { location = request.RequestUri.PathAndQuery; } else { location = $"{request.RequestUri.DnsSafeHost}:{request.RequestUri.Port}"; } string requestHead = $"{request.Method.Method} {location} HTTP/{request.Version}\r\n"; if (!isConnectRequest) { string host = request.Headers.Contains("Host") ? request.Headers.Host : request.RequestUri.Host; requestHead += $"Host: {host}\r\n"; } string content = ""; if (request.Content != null && !isConnectRequest && request.Method != HttpMethod.Head) { content = request.Content.ReadAsStringAsync().Result; // determine whether to use chunked transfer encoding long?contentLength = null; if (!request.Headers.TransferEncodingChunked.GetValueOrDefault(false)) { contentLength = content.Length; } // set the appropriate content transfer headers if (contentLength.HasValue) { contentLength = request.Content.Headers.ContentLength ?? contentLength; requestHead += $"Content-Length: {contentLength}\r\n"; } else { requestHead += "Transfer-Encoding: chunked\r\n"; } //write all content headers string result = ""; foreach (var header in request.Content.Headers) { if (!string.Equals(header.Key, "Transfer-Encoding", StringComparison.Ordinal)) { if (!string.Equals(header.Key, "Content-Length", StringComparison.Ordinal)) { if (!string.Equals(header.Key, "Host", StringComparison.Ordinal)) { result = ParseHeaderToString(header); } } } } requestHead += result; } // write the rest of the request headers foreach (var header in request.Headers) { if (!string.Equals(header.Key, "Transfer-Encoding", StringComparison.Ordinal)) { if (!string.Equals(header.Key, "Content-Length", StringComparison.Ordinal)) { if (!string.Equals(header.Key, "Host", StringComparison.Ordinal)) { requestHead += ParseHeaderToString(header); } } } } requestHead += "\r\n"; var headAndContent = requestHead + content; Stream.Write(Encoding.UTF8.GetBytes(headAndContent), 0, headAndContent.Length); Stream.Flush(); var reader = new ByteStreamReader(Stream, Socket.ReceiveBufferSize, preserveLineEndings: false); // initialize the response var response = new HttpResponseMessage { RequestMessage = request }; // read the first line of the response string line = reader.ReadLine(); var pieces = line.Split(new[] { ' ' }, 3); if (!string.Equals(pieces[0], "HTTP/1.1", StringComparison.Ordinal)) { throw new HttpRequestException($"Only HTTP/1.1 is supported, actual: {pieces[0]}"); } response.StatusCode = (HttpStatusCode)int.Parse(pieces[1]); response.ReasonPhrase = pieces[2]; // read the headers response.Content = new ByteArrayContent(new byte[0]); while ((line = reader.ReadLine()) != null && line != string.Empty) { pieces = line.Split(new[] { ":" }, 2, StringSplitOptions.None); if (pieces[1].StartsWith(" ", StringComparison.Ordinal)) { pieces[1] = pieces[1].Substring(1); } if (!response.Headers.TryAddWithoutValidation(pieces[0], pieces[1]) && !response.Content.Headers.TryAddWithoutValidation(pieces[0], pieces[1])) { throw new InvalidOperationException( $"The header '{pieces[0]}' could not be added to the response message or to the response content."); } } if (!(request.Method == new HttpMethod("CONNECT") || request.Method == HttpMethod.Head)) { HttpContent httpContent = null; if (response.Headers.TransferEncodingChunked.GetValueOrDefault(false)) { // read the body with chunked transfer encoding var chunkedStream = new ReadsFromChunksStream(reader.RemainingStream); httpContent = new StreamContent(chunkedStream); } else if (response.Content.Headers.ContentLength.HasValue) { // read the body with a content-length var limitedStream = new LimitedStream( reader.RemainingStream, response.Content.Headers.ContentLength.Value); httpContent = new StreamContent(limitedStream); } else { return(response); } if (content != null) { // copy over the content headers foreach (var header in response.Content.Headers) { httpContent.Headers.TryAddWithoutValidation(header.Key, header.Value); } response.Content = httpContent; } } return(response); } catch (SocketException) { DestroySocket(); throw; } } }