There is a self-hosted WCF service with the following contract:

[ServiceContract] interface IMyService { [OperationContract] [WebInvoke(Method = "HEAD", UriTemplate = "Files/{fileName}")] void GetFileInfo(string fileName); [OperationContract] [WebGet(UriTemplate = "Files/{fileName}")] Stream StreamFile(string fileName); } 

Its simplified implementation is as follows:

 class MyService : IMyService { public void GetFileInfo(string fileName) { string filePath = Path.Combine("Files", fileName); FileInfo fi = new FileInfo(filePath); var response = WebOperationContext.Current.OutgoingResponse; response.ContentType = "application/octet-stream"; response.ContentLength = fi.Length; response.StatusCode = HttpStatusCode.OK; response.SuppressEntityBody = true; } public Stream StreamFile(string fileName) { ... } } 

The problem is in the GetFileInfo method. When a HEAD request to a service occurs (for example, using WebRequest ):

 var uri = new Uri("http://localhost:8733/MyService/Files/test.dat"); var req = WebRequest.Create(uri); req.Method = "HEAD"; using (var resp = req.GetResponse() as HttpWebResponse) { Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription); foreach (string header in resp.Headers) Console.WriteLine("{0}: {1}", header, resp.Headers[header]); } 

then the answer comes:

 HTTP/1.1 200 OK Content-Length: 1234 Content-Type: application/octet-stream Date: Wed, 22 Jun 2016 10:17:38 GMT Server: Microsoft-HTTPAPI/2.0 

but this is in case the length of the requested file does not exceed int.MaxValue . If the file is longer than int.MaxValue then the Content-Length in the response is 0:

 Content-Length: 0 

How to win it? How to make return the normal length for large files?

WCF service configuration (if important):

 <system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="be_webHttpStreamed"> <webHttp /> </behavior> </endpointBehaviors> </behaviors> <services> <service name="WCFTestService.MyService"> <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpStreamed" behaviorConfiguration="be_webHttpStreamed" contract="WCFTestService.IMyService" /> <host> <baseAddresses> <add baseAddress="http://localhost:8733/MyService/" /> </baseAddresses> </host> </service> </services> <bindings> <webHttpBinding> <binding name="webHttpStreamed" transferMode="StreamedResponse" /> </webHttpBinding> </bindings> </system.serviceModel> 

    1 answer 1

    It seems that this is an error in the System.ServiceModel code, and specifically in this section . Interestingly, the problem persists when hosting a service in IIS.

    As a correction option, create custom binding, which inherits from the WebHttpBinding , with the custom transport channel , and in it organize the correct layout of the headers with HttpListener (this class is used for self-hosting services with http transport).

    IMHO, this is unreasonably difficult. It is easier to violate the principles of the RESTful architecture and request a file size via POST with the result in the response body. Or send the length in a different header.


    Problem areas of the code :

     // HttpChannelHelpers.cs, line 2988 if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0) { int contentLength = -1; if (httpMethodIsHead && int.TryParse(value, out contentLength)) { this.SetContentLength(contentLength); } // else //this will be taken care of by System.Net when we write to the content } 

    and

     // HttpChannelHelpers.cs, line 3061 protected override void SetContentLength(int contentLength) { listenerResponse.ContentLength64 = contentLength; } 
    • I did not quite understand, but the problem is in the server response? The server is in principle no longer able to give away, or is the client no longer able to read it? - Monk
    • @Monk yes, the server can not give more. See the first link for an implementation of SetContentLength. - kmv
    • Maybe this is not a mistake, and the size is organic consciously. Only it is not clear to me why. - kmv
    • The size is limited straight at the base level - SetContentLengh accepts an int. Then there was enough for all 2GB. - Monk
    • one
      Yes, you should write to Connect. Let me write. Maybe include in the answer pieces of code so that you can give a link to the answer. In Russian, of course, they will not understand, but I think the code will be understood. I suggest two pieces to insert. Here is this if (string.Compare(name, "content-length", StringComparison.OrdinalIgnoreCase) == 0) { ... } with everything inside the curly braces, and the SetContentLength method with its contents. - i-one