You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The response stream from AdvancedResponseWriter is disposed too early #2072
Describe the bug
I want to stream response data to my API for advanced processing. For this purpose I use AdvancedResponseWriter because I also want to handle errors from remote resource (=> I cannot use RestSharp.RestClient.DownloadStreamAsync), but the latest changes close response stream too early. It is cause by using in:
using var internalResponse = await ExecuteRequestAsync(request, cancellationToken).ConfigureAwait(false);
I think you are using the advanced response writer for the wrong purpose. It's meant to rewrite the response, and it should finish within the scope of one request, before the response is returned. The stream you get as an input should not be used outside of that closure.
You might want to use DownloadStreamAsync or StreamJsonAsync instead.
You can create a function based on DownloadStreamAsync code. As you can see, it sets the request completion option to ResponseHeadersRead, so you get the headers in the response, and then you can inspect the headers and decide what to do:
publicasyncTask<Stream?>DownloadStreamAsync(RestRequestrequest,CancellationTokencancellationToken=default){// Make sure we only read the headers so we can stream the content body efficientlyrequest.CompletionOption=HttpCompletionOption.ResponseHeadersRead;varresponse=awaitExecuteRequestAsync(request,cancellationToken).ConfigureAwait(false);varexception=response.Exception??response.ResponseMessage?.MaybeException();if(exception!=null){returnOptions.ThrowOnAnyError?throwexception:null;}if(response.ResponseMessage==null)returnnull;returnawaitresponse.ResponseMessage.ReadResponseStream(request.ResponseWriter,cancellationToken).ConfigureAwait(false);}
You still can use OnAfterRequest, you still need to set the completion request option to HttpCompletionOption.ResponseHeadersRead:
/// <summary>/// When supplied, the function will be called after the request is complete/// </summary>publicFunc<HttpResponseMessage,ValueTask>?OnAfterRequest{get;set;}
My current thought is to change the interface signature and make Download work differently. Right now, it returns Task<Stream?>, which prevents the user to inspect the response for errors. It crashes if the call wasn't successful.
So, the idea is to make ExecuteStreamAsync, which will return Task<RestStreamResponse>. The response will be like RestResponse but instead of Content it will have Stream?. Naturally, it requires to be IDisposable.
The first step there was to create a source generator to create instance of RestResponseBase child class, as copying those properties manually is error prone (adding a new prop requires many changes). That part is done, so now this thing should be relatively easy.
Activity
alexeyzimarev commentedon Aug 19, 2023
I think you are using the advanced response writer for the wrong purpose. It's meant to rewrite the response, and it should finish within the scope of one request, before the response is returned. The stream you get as an input should not be used outside of that closure.
You might want to use
DownloadStreamAsyncorStreamJsonAsyncinstead.sensslen commentedon Aug 19, 2023
My use case is to inspect the headers before executing the actual download. How would this be possible with DownloadStreamAsync?
alexeyzimarev commentedon Aug 19, 2023
You can create a function based on
DownloadStreamAsynccode. As you can see, it sets the request completion option toResponseHeadersRead, so you get the headers in the response, and then you can inspect the headers and decide what to do:alexeyzimarev commentedon Aug 19, 2023
Ah sorry,
ExecuteRequestAsyncis private.You still can use
OnAfterRequest, you still need to set the completion request option toHttpCompletionOption.ResponseHeadersRead:alexeyzimarev commentedon Jun 17, 2024
My current thought is to change the interface signature and make Download work differently. Right now, it returns
Task<Stream?>, which prevents the user to inspect the response for errors. It crashes if the call wasn't successful.So, the idea is to make
ExecuteStreamAsync, which will returnTask<RestStreamResponse>. The response will be likeRestResponsebut instead ofContentit will haveStream?. Naturally, it requires to beIDisposable.The first step there was to create a source generator to create instance of
RestResponseBasechild class, as copying those properties manually is error prone (adding a new prop requires many changes). That part is done, so now this thing should be relatively easy.alexeyzimarev commentedon Jun 17, 2024
Continues in #2226