Skip to content

Commit

Permalink
Copy stream for upload requests and remove expect header (#739)
Browse files Browse the repository at this point in the history
Co-authored-by: Marcin Hagmajer <[email protected]>
  • Loading branch information
mwwoda and mhagmajer authored Aug 20, 2021
1 parent 8845593 commit 69164b3
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 36 deletions.
1 change: 1 addition & 0 deletions Box.V2/Box.V2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@
<Compile Include="Plugin\IResourcePlugin.cs" />
<Compile Include="JWTAuth\RSAUtilities.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Request\ReusableContent.cs" />
<Compile Include="Utility\AssemblyInfo.cs" />
<Compile Include="Utility\BoxProgess.cs" />
<Compile Include="Utility\CrossPlatform.cs" />
Expand Down
75 changes: 39 additions & 36 deletions Box.V2/Request/HttpRequestHandler.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Box.V2.Config;
using Box.V2.Utility;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
Expand All @@ -22,6 +21,9 @@ public class HttpRequestHandler : IRequestHandler
public HttpRequestHandler(IWebProxy webProxy = null)
{
ClientFactory.WebProxy = webProxy;
#if NET45
System.Net.ServicePointManager.Expect100Continue = false;
#endif
}

public async Task<IBoxResponse<T>> ExecuteAsyncWithoutRetry<T>(IBoxRequest request)
Expand Down Expand Up @@ -66,41 +68,42 @@ public async Task<IBoxResponse<T>> ExecuteAsync<T>(IBoxRequest request)

while (true)
{
HttpRequestMessage httpRequest = getHttpRequest(request, isMultiPartRequest, isBinaryRequest);
Debug.WriteLine(string.Format("RequestUri: {0}", httpRequest.RequestUri));
HttpResponseMessage response = await getResponse(request, isStream, httpRequest).ConfigureAwait(false);

//need to wait for Retry-After seconds and then retry request
var retryAfterHeader = response.Headers.RetryAfter;

// If we get a retryable/transient error code and this is not a multi part request (meaning a file upload, which cannot be retried
// because the stream cannot be reset) and we haven't exceeded the number of allowed retries, then retry the request.
// If we get a 202 code and has a retry-after header, we will retry after
if (!isMultiPartRequest &&
(response.StatusCode == TooManyRequests
||
response.StatusCode == HttpStatusCode.InternalServerError
||
response.StatusCode == HttpStatusCode.BadGateway
||
response.StatusCode == HttpStatusCode.ServiceUnavailable
||
response.StatusCode == HttpStatusCode.GatewayTimeout
||
(response.StatusCode == HttpStatusCode.Accepted && retryAfterHeader != null))
&& retryCounter++ < RetryLimit)
{
TimeSpan delay = expBackoff.GetRetryTimeout(retryCounter);

Debug.WriteLine("HttpCode : {0}. Waiting for {1} seconds to retry request. RequestUri: {2}", response.StatusCode, delay.Seconds, httpRequest.RequestUri);

await Task.Delay(delay);
}
else
using (HttpRequestMessage httpRequest = getHttpRequest(request, isMultiPartRequest, isBinaryRequest))
using (HttpResponseMessage response = await getResponse(request, isStream, httpRequest).ConfigureAwait(false))
{
BoxResponse<T> boxResponse = await getBoxResponse<T>(isStream, response).ConfigureAwait(false);

return boxResponse;
Debug.WriteLine(string.Format("RequestUri: {0}", httpRequest.RequestUri));
//need to wait for Retry-After seconds and then retry request
var retryAfterHeader = response.Headers.RetryAfter;

// If we get a retryable/transient error code and this is not a multi part request (meaning a file upload, which cannot be retried
// because the stream cannot be reset) and we haven't exceeded the number of allowed retries, then retry the request.
// If we get a 202 code and has a retry-after header, we will retry after
if (!isMultiPartRequest &&
(response.StatusCode == TooManyRequests
||
response.StatusCode == HttpStatusCode.InternalServerError
||
response.StatusCode == HttpStatusCode.BadGateway
||
response.StatusCode == HttpStatusCode.ServiceUnavailable
||
response.StatusCode == HttpStatusCode.GatewayTimeout
||
(response.StatusCode == HttpStatusCode.Accepted && retryAfterHeader != null))
&& retryCounter++ < RetryLimit)
{
TimeSpan delay = expBackoff.GetRetryTimeout(retryCounter);

Debug.WriteLine("HttpCode : {0}. Waiting for {1} seconds to retry request. RequestUri: {2}", response.StatusCode, delay.Seconds, httpRequest.RequestUri);

await Task.Delay(delay);
}
else
{
BoxResponse<T> boxResponse = await getBoxResponse<T>(isStream, response).ConfigureAwait(false);

return boxResponse;
}
}
}
}
Expand Down Expand Up @@ -346,7 +349,7 @@ private HttpRequestMessage BuildBinaryRequest(BoxBinaryRequest request)
var filePart = request.Part as BoxFilePart;
if (filePart != null)
{
content = new StreamContent(filePart.Value);
content = new ReusableContent(filePart.Value);
}

httpRequest.Content = content;
Expand Down
38 changes: 38 additions & 0 deletions Box.V2/Request/ReusableContent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace Box.V2.Request
{
class ReusableContent : HttpContent
{
private Stream _innerContent;
private long _contentLength;

public ReusableContent(Stream stream)
{
_innerContent = stream;
_contentLength = stream.Length;
}

protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
_innerContent.Position = 0;
await _innerContent.CopyToAsync(stream);
}

protected override bool TryComputeLength(out long length)
{
length = _contentLength;
return true;
}

protected override void Dispose(bool disposing)
{
// Don't call dispose on stream content as it will close the base stream.
_innerContent = null;
base.Dispose(disposing);
}
}
}
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
- Replace insensitive language ([#738](https://github.com/box/box-windows-sdk-v2/pull/738))
- Add new, easier to use method for create terms of service user status ([#740](https://github.com/box/box-windows-sdk-v2/pull/740))

**Bug Fixes:**
- Fix `Cannot access a closed Stream.Request` exception during part upload ([#739](https://github.com/box/box-windows-sdk-v2/pull/739))

## 3.26.0 [2021-04-01]

**New Features and Enhancements:**
Expand Down

0 comments on commit 69164b3

Please sign in to comment.