2

I'm trying in a single method to return the result of a HttpWebRequest POST method via the Reactive interface IObservable. I've managed to do this for a GET method using the code below:

var request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(url);
  request.Method = method;
  request.Accept = GetHttpType();
  request.CookieContainer = new CookieContainer();

   return Observable.FromAsyncPattern(request.BeginGetResponse, ar => ProcessResponse(method, ar, request))()
                    .Select(r => r);

But I am unsure how to chain together the async observer of writing to the request stream with the reading of the response stream which is required for a HTTP POST operation. How to do connect the following variables obs1 & obs2 together so that I can return obs2?

var request = (HttpWebRequest)WebRequestCreator.ClientHttp.Create(url);
var type = GetHttpType();
request.Method = method;
request.Accept = type;
request.ContentType = type;
request.CookieContainer = new CookieContainer();

var data = Serialize(requestResource);

var obs1 = Observable.FromAsyncPattern(request.BeginGetRequestStream, ar1 => ProcessRequest(method, data, ar1, request))

var obs2 = Observable.FromAsyncPattern(request.BeginGetResponse, ar2 => ProcessResponse(method, ar2, request))();

// How do I connect obs1 And obs2 together...

return obs2;

I would have thought the following should work but the 'ProcessResponse' method is never called, does anyone know why?

 var obs1 = Observable.FromAsyncPattern(request.BeginGetRequestStream, ar1 => ProcessRequest(method, data, ar1, request))();
 var obs2 = Observable.FromAsyncPattern(request.BeginGetResponse, ar2 => ProcessResponse(method, ar2, request));

 return obs1.SelectMany(a => obs2(), (a, b) => b);
AwkwardCoder
  • 24,893
  • 27
  • 82
  • 152

3 Answers3

2

FromAsyncPattern is expecting both the BeginAction and EndAction methods as arguments. I think what you are looking for is this:

HttpWebRequest httpRequest = 
    (HttpWebRequest)HttpWebRequest.Create("http://www.stackoverflow.com");

httpRequest.Method = "POST";

var requestAsync = Observable.FromAsyncPattern<Stream>(
    httpRequest.BeginGetRequestStream, httpRequest.EndGetRequestStream);

var responseAsync = Observable.FromAsyncPattern<WebResponse>(
    httpRequest.BeginGetResponse, httpRequest.EndGetResponse);

requestAsync()
    .SelectMany(requestStream =>
    {
        // requestStream.Write

        return responseAsync();
    })
    .Subscribe(webResponse =>
    {
        // complete
    });

Let me know if your writing to the request stream is asynchronous and I can update the sample accordingly.

Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • I had something similar but if you have more than one subscriber to the observable then the 'SelectMany' will fire mulitple times and therefore attempt to write to stream multiple times which is not the required behaviour - ideally writing to the request stream happens only once... – AwkwardCoder Dec 01 '10 at 22:56
  • @AWC - Sounds like you want to use `Prune` or `Replay` to remember the last value. – Richard Szalay Dec 02 '10 at 07:41
1

OK, I've got it now. First the code, then explanation:

byte[] buffer1 = Encoding.UTF8.GetBytes(data); // assume post data is utf-8 encoded

var obs1 = Observable.FromAsyncPattern(request.BeginGetRequestStream, ar1 => request.EndGetRequestStream(ar1));

return from stream in obs1()
       let obs2 = Observable.FromAsyncPattern((ac, state) => stream.BeginWrite(buffer1, 0, buffer1.Length, ac, state), ar2 => stream.EndWrite(ar2))
       from _ in obs2()
       let obs3 = Observable.FromAsyncPattern(request.BeginGetResponse, ar2 => request.EndGetResponse(ar2))
       from response in obs3()
       select response;

You were on the right track with the SelectMany, but I find regular LINQ syntax a bit more readable. I've also improved your example by posting asynchronously, that is writing to the upload stream in an async way. Basically the first from gives us a stream which we use for the async post in the first let, the third from will ignore the result of the upload (thus strangely named variable '_') and finally the last from simply subscribes to the result of the get response.

I must admit that I find this example stretching the concept of the Observable. I think you'd get better results and more readable code by using Tasks, and probably even nicer results using new async support in C# 5 (but that's not available except as CPT).

rawpower
  • 1,850
  • 16
  • 18
  • What I should have also said was, this is being used in Silverlight - no blocking methods and I was trying to right a class to encapsulate the use of HttpWebRequest class and 'push' out all results via an interface clients can easily subscribe to - thanks for the update, I'll give it ago in the morning... – AwkwardCoder Dec 01 '10 at 23:12
  • However, this code won't pass your test - I've just seen the response below and your comment - if you subscribe to this twice, the second subcriber will fail with an exception complaining that we're calling BeginGetRequestStream again while previous call is still in progress. Like I said, this could be much better resolved with the Task based approach, at least in my head it makes more sense. Thanks for the interesting challenge, though. – rawpower Dec 01 '10 at 23:14
  • One more thing - I used plain .NET 4.0 console application, not Silverlight, but there shouldn't be any big differences that you couldn't resolve yourself. – rawpower Dec 01 '10 at 23:15
  • Finally I now see what you meant - Tasks are not supported in Silverlight, sigh. – rawpower Dec 01 '10 at 23:16
-1

obs1.Concat(obs2) perhaps?

rawpower
  • 1,850
  • 16
  • 18