Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit d3a3da6

Browse files
committed
HttpClient xplat: Add chunked mode support
1 parent 9719572 commit d3a3da6

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

src/System.Net.Http/src/System/Net/Http/Unix/CurlCallbacks.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,6 @@ private unsafe static size_t CurlReceiveBodyCallback(
9393
// TODO: Fail if we find that task is already Canceled or Faulted
9494
state.TrySetResult(state.ResponseMessage);
9595

96-
// TODO: Handle compression and chunked-mode
97-
9896
// Wait for a reader
9997
// TODO: The below call blocks till all the data has been read since
10098
// response body is not suppored to be buffered in memory.

src/System.Net.Http/src/System/Net/Http/Unix/CurlHandler.cs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ internal partial class CurlHandler : HttpMessageHandler
3535
private readonly static string[] AuthenticationSchemes = { "Negotiate", "Digest", "Basic" }; // the order in which libcurl goes over authentication schemes
3636
private static readonly string[] s_headerDelimiters = new string[] { "\r\n" };
3737
private const int s_requestBufferSize = 16384; // Default used by libcurl
38+
private const string NoTransferEncoding = HttpKnownHeaderNames.TransferEncoding + ":";
3839

3940
#endregion
4041

@@ -202,10 +203,17 @@ protected internal override Task<HttpResponseMessage> SendAsync(
202203
{
203204
throw new ArgumentNullException("request", SR.net_http_handler_norequest);
204205
}
205-
else if ((request.RequestUri.Scheme != UriSchemeHttp) && (request.RequestUri.Scheme != UriSchemeHttps))
206+
207+
if ((request.RequestUri.Scheme != UriSchemeHttp) && (request.RequestUri.Scheme != UriSchemeHttps))
206208
{
207209
throw NotImplemented.ByDesignWithMessage(SR.net_http_client_http_baseaddress_required);
208210
}
211+
212+
if (request.Headers.TransferEncodingChunked.GetValueOrDefault() && (request.Content == null))
213+
{
214+
throw new InvalidOperationException(SR.net_http_chunked_not_allowed_with_empty_content);
215+
}
216+
209217
// TODO: Check that SendAsync is not being called again for same request object.
210218
// Probably fix is needed in WinHttpHandler as well
211219

@@ -612,6 +620,8 @@ private SafeCurlSlistHandle SetRequestHeaders(SafeCurlHandle handle, HttpRequest
612620
HttpHeaders contentHeaders = null;
613621
if (request.Content != null)
614622
{
623+
SetChunkedModeForSend(request);
624+
615625
// TODO: Content-Length header isn't getting correctly placed using ToString()
616626
// This is a bug in HttpContentHeaders that needs to be fixed.
617627
if (request.Content.Headers.ContentLength.HasValue)
@@ -632,14 +642,23 @@ private SafeCurlSlistHandle SetRequestHeaders(SafeCurlHandle handle, HttpRequest
632642
IntPtr rawHandle = IntPtr.Zero;
633643
for (int i = 0; i < allHeaders.Length; i++)
634644
{
635-
string header = allHeaders[i];
645+
string header = allHeaders[i].Trim();
636646
if (header.Equals("{") || header.Equals("}"))
637647
{
638648
continue;
639649
}
640650
rawHandle = Interop.libcurl.curl_slist_append(rawHandle, header);
641651
retVal.SetHandle(rawHandle);
642652
}
653+
654+
// Since libcurl always adds a Transfer-Encoding header, we need to explicitly block
655+
// it if caller specifically does not want to set the header
656+
if (request.Headers.TransferEncodingChunked.HasValue && !request.Headers.TransferEncodingChunked.Value)
657+
{
658+
rawHandle = Interop.libcurl.curl_slist_append(rawHandle, NoTransferEncoding);
659+
retVal.SetHandle(rawHandle);
660+
}
661+
643662
if (!retVal.IsInvalid)
644663
{
645664
SetCurlOption(handle, CURLoption.CURLOPT_HTTPHEADER, rawHandle);
@@ -656,6 +675,29 @@ private SafeCurlSlistHandle SetRequestHeaders(SafeCurlHandle handle, HttpRequest
656675
return retVal;
657676
}
658677

678+
private static void SetChunkedModeForSend(HttpRequestMessage request)
679+
{
680+
bool chunkedMode = request.Headers.TransferEncodingChunked.GetValueOrDefault();
681+
HttpContent requestContent = request.Content;
682+
Debug.Assert(requestContent != null);
683+
684+
// Deal with conflict between 'Content-Length' vs. 'Transfer-Encoding: chunked' semantics.
685+
// libcurl adds a Tranfer-Encoding header by default and the request fails if both are set.
686+
if (requestContent.Headers.ContentLength.HasValue)
687+
{
688+
if (chunkedMode)
689+
{
690+
// Same behaviour as WinHttpHandler
691+
requestContent.Headers.ContentLength = null;
692+
}
693+
else
694+
{
695+
// Prevent libcurl from adding Transfer-Encoding header
696+
request.Headers.TransferEncodingChunked = false;
697+
}
698+
}
699+
}
700+
659701
private void AddEasyHandle(RequestCompletionSource state)
660702
{
661703
bool gotReference = false;

0 commit comments

Comments
 (0)