I have a need to mass export content via WebAPI using the Odata protocol. We are trying to transfer the results directly from the database using PushStreamContent. When I start the service in my local IIS instance, it works fine, but when I push this to the server, it will transmit data, pause and hang on the last 2 kilobytes.
I checked this by keeping track of the file size. On local start, I get a file of 9094 KB in size, and when I deploy the same code to the server, I get 9092 KB, and then the connection remains open and the transfer stops. If I kill the client and look at the file, I will see that json, which was a stream, was disconnected in the middle of the recording. In addition, I can look at open connections in IIS and see that the connection is still active.
In any case, why would PushStreamContent just stop sending data and not close the stream? If an error occurs, the stream and connection will be closed.
public HttpResponseMessage GetBulkExport(ODataQueryOptions<vwBulkExport> options)
{
var reportData = options.ApplyTo(dbContext.vwBulkExport, new ODataQuerySettings() { EnsureStableOrdering = false });
return new ResponseStreamer(Request).StreamAsync(reportData);
}
public class ResponseStreamer
{
private HttpRequestMessage request;
public ResponseStreamer(HttpRequestMessage request)
{
this.request = request;
}
public HttpResponseMessage StreamAsync(IQueryable data)
{
HttpResponseMessage response = request.CreateResponse();
response.Content = new PushStreamContent(
async (outputStream, httpContent, transportContext) =>
{
try
{
int counter = 0;
foreach (var item in data)
{
counter++;
string json = JsonConvert.SerializeObject(item);
var buffer = Encoding.UTF8.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
if (counter == 10)
{
counter = 0;
await outputStream.FlushAsync();
}
}
}
finally
{
await outputStream.FlushAsync();
outputStream.Close();
outputStream.Dispose();
}
});
return response;
}
}
Here is my client code
using (var writer = File.OpenWrite("C:\\temp\\" + Guid.NewGuid().ToString()))
{
var client = new RestClient("http://localhost");
var url = "/odata/BulkExport";
var request = new RestRequest(url);
request.AddHeader("authorization", string.Format("Bearer {0}", authToken));
request.ResponseWriter = (responseStream) => responseStream.CopyTo(writer);
var response = client.DownloadData(request);
}
Update
I am doing extensive testing, and in my opinion, what happens is that the thread never closes (and therefore the last piece never gets sent). I came to this conclusion by changing the data iteration above:
for (int count = 0; count < 1000; count++)
{
string json = JsonConvert.SerializeObject(count.ToString()) + Environment.NewLine;
var buffer = Encoding.Default.GetBytes(json);
await outputStream.WriteAsync(buffer, 0, buffer.Length);
}
, , 600 . 2 , , . count <601
, , . , , - 4K ( , 0-600), , . ?
, ? , .
Update
. HTTP 1.1 , . , , - . Connection: Keep-Alive
Connection: Close
. , ( ), . , .
, . ? , , HttpContext.Current.ApplicationInstance.CompleteRequest();
. , finally , . .
, dev, IIS, -.
Windows 10, asp.net 5 IIS7 .
- - Windows Server ( ) IIS8, asp.net 4.5. , - asp.net, , ASP.NET 4.5. , 4.5, , .