7

I have a restful (webHttpBinding) self-hosted WCF service. Most methods are returning xml or json version of objects to the client.

I have a couple of GET methods that trigger long running methods and I'd like to stream the log reponse to the browser (or application) so that the user knows what's going on. This would be simple to accomplish with HttpContext.Current.Response.OutputStream.Write. Unfortunately, HttpContext.Current is always null in a self-hosted WCF service, even if I include the aspNetCompatibilityEnabled config (IIS is not an option unfortunately).

I have tried AnonymousPipeServerStream: WCF and streaming requests and responses

along with first setting:

OutgoingWebResponseContext context = WebOperationContext.Current.OutgoingResponse;
context.ContentType = "text/plain";

so that the response comes into the browser it doesn't download the stream into a file to save.

In Chrome it doesn't work at all - it buffers until the end. In IE or wget it appears to buffer for around 4k (or something) at a time. This is no good for logging as unless I spit out loads of unnecessary log messages to force output, the user doesn't really know what's going on. I can only assume that this is because the response is actually a chunked response and the chunks are 4k (rather than just writing to the outputstream).

The fix to get chrome to output is apparently to write some garbage to the content before sending the chunked response: Chunked transfer encoding - browser behavior, however, I don't think this is possible with WCF.

So, possible solutions I'm looking for:

  • A way to write to the outputstream in WCF in a self hosted service (without IIS). or
  • A way to control the chunk sizes in a stream response (& a way to write some content first so that Chrome will render the chunks).

Another option, I suppose, is to ditch WCF in favour of something more REST friendly (I'm beginning to think WCF wasn't the right choice). However, having written so much in WCF now, this seems like a tedious task. Unless there is something I can switch to that would be an easy migration (e.g. if I could reuse the same service classes, perhaps with just different attributes). Nancy maybe?

Community
  • 1
  • 1
David
  • 682
  • 2
  • 9
  • 15

3 Answers3

2

I've done something like what you're asking about here - self hosted, too. I wrote a WCF (BasicHttpBinding) service that did both streaming and buffering of data to client devices consuming my service for data synchronization. Streaming is hard, as you've probably figured out, and I don't think there's any way to "write into the stream".

In a basic sense, Streaming over a WCF service works the same way that File.IO works, as seen in the code below

 FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
 BinaryReader br = new BinaryReader(fs);

If the file in question is 1 GB, your filestream will begin returning bytes before its read to the end of the file. Streaming over WCF works the same way (in fact, it implements FileStream, in my experience), which is why it's good for huge chunks of data. It reads ... it sends; it reads ... it sends. So I'm not sure how you'd inject some information into that stream for output to your screen.

Having said that, our Synch UI displays the count of bytes coming down, plus the percentage complete, to keep the users from turning off the machine or canceling. We do this by having a separate thread reads the size of the downloading file every 10 seconds and calculating the percentage of the whole (the complete size is send back as a parameter in the response), then writing the results out to the UI results window. So the solution is actually pretty simple, in our case.

Brian
  • 3,653
  • 1
  • 22
  • 33
  • So, in your opinion, what I'm trying to do just isn't possible? – David May 13 '14 at 15:29
  • I guess it depends on what you're trying to stream. Is it raw data you're moving from one place to another? Streaming is usually used for huge data sets (i.e., we stream music and movies from Amazon) and once the streaming has started, the client and host are in lockstep, sending and receiving until the process is finished. I'm not sure how or why you'd want to inject something into that kind of flow. If you're worried about your user doing something weird because he/she doesn't see progress, there are ways (as I describe) around that problem. – Brian May 14 '14 at 07:06
  • I'm trying to stream logging information (see OP). So the amount of data could vary from a few lines (if things are going well) to 1000s of lines if things are going badly. This is something i've done in past with no problem by writing to the output stream (in .NET and other languages), but the problem seems to be that WCF doesn't give me the flexibility I'm looking for. – David May 14 '14 at 10:26
  • I'm also currently of the impression that as WCF was not built with the Restful world in mind (Rest generally doesn't map to an RPC use case) that it would be better to ditch it for this purpose and use something like Nancy instead. I've already had to switch to using JSON.NET for JSON serialisation, so the WCF staack is really only giving me a lightweight web server and authentication. – David May 14 '14 at 10:26
  • Have you considered using WebSockets for "streaming" log data to the client. You can still use a WCF for the REST calls but let the server transmit to a websockets server that you also listen to in the client.. Or something like that. – Jon Lindeheim May 16 '14 at 13:57
  • I think that might be a good idea. It would be easier than ditching WCF entirely to get this one feature, for sure. I did look into Nancy, but it's easy to forget how much stuff you get for free with WCF (e.g. Windows Auth) and personally I prefer the WCF Service Contract interface based Rest definition to the constructor based definitions in Nancy. I do feel like I must be missing something though - I would have thought that there must be a way to get to all the stuff that WCF is abstracting away for us. It is just HTTP! – David May 28 '14 at 14:43
0

I do file streaming like this:

Group the methods that returns data which needs to be streamed into an endpoint and then add the streamed transferMode on that endpoint.

Here is the configuration I use (for basicHttpBinding).

<services>
  <service name="CustomersService">
    <endpoint address="FilesService.svc" binding="basicHttpBinding" bindingConfiguration="StreamedBinding" contract="Soap.Interfaces.IFilesService" />
  </service>
</services>

and the define the binding configuration:

<bindings>
    <basicHttpBinding>
        <binding name="IntersolveWebServicesStreamedBinding" allowCookies="true" transferMode="Streamed" maxReceivedMessageSize="67108864" />
    </basicHttpBinding>
</bindings>

Basically, you have to set the transferMode in the binding configuration.

I haven't tried it with webHttpBinding, so please let me know if it works for you.

Cosmin Vană
  • 1,562
  • 12
  • 28
0

Just trick the browser into thinking there is an HTML response with the Multipart Content-Type

http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html

I have used this for quite a few things including MJPEG but you can also use it for COMET / WebSocket like responses.

Jay
  • 3,276
  • 1
  • 28
  • 38
  • I've tried this, but WCF appears to take control of how much data is buffered in the chunked response. The "text/plain" content type I mention in the OP has the same impact. – David May 28 '14 at 14:46