Async POST fails on WP7 and F#

RestAsynchronousF#Windows Phone

Rest Problem Overview


When I do let! read = from.AsyncRead buf in F#, it blocks and doesn't return until the TCP socket is dead. Why? And how do I fix it?

Its code:

module StreamUtil

open System.IO

/// copy from 'from' stream to 'toStream'
let (|>>) (from : Stream) (toStream : Stream) =
  let buf = Array.zeroCreate<byte> 1024
  let rec doBlock () =
    async {
      let! read = from.AsyncRead buf
      if read <= 0 then
        toStream.Flush()
        return ()
      else
        do! toStream.AsyncWrite(buf, 0, read)
        return! doBlock () }
  doBlock ()

It's being called from this code:

use fs = new FileStream(targPath, FileMode.CreateNew, FileAccess.ReadWrite)
do! req.InputStream |>> fs

and requested over HTTP with this code from Windows Phone 7.1 emulator:

public void Send()
{
	var b = new UriBuilder(_imageService.BaseUrl) {Path = "/images"};

	var req = WebRequest.CreateHttp(b.Uri);
	req.ContentType = "image/jpeg";
	req.Method = "POST";
	var imgLen = SelectedImage.ImageStream.Length;
	req.Headers[HttpRequestHeader.ContentLength] = imgLen.ToString(CultureInfo.InvariantCulture);
	req.Accept = "application/json";
	req.BeginGetRequestStream(RequestReady, new ReqState(req, imgLen));
}

void RequestReady(IAsyncResult ar)
{
	var state = (ReqState)ar.AsyncState;
	var req = state.Request;

	var reqStream = req.EndGetRequestStream(ar);

	SmartDispatcher.BeginInvoke(() =>
		{
			using (var sw = new StreamWriter(reqStream))
			using (var br = new BinaryReader(SelectedVoucher.ImageStream))
			{
				var readBytes = br.ReadBytes(state.ImgLen);

				// tried both 2
				sw.Write(readBytes);
				//sw.Write(Convert.ToBase64String(readBytes));
				sw.Flush();
				sw.Close();
			}
			req.BeginGetResponse(ResponseReady, req);
		});
}

// WHY IS IT YOU ARE NOT CALLED???
void ResponseReady(IAsyncResult ar)
{
	try
	{
		var request = (HttpWebRequest)ar.AsyncState;
		var response = request.EndGetResponse(ar);

		SmartDispatcher.BeginInvoke(() =>
			{
				var rdr = new StreamReader(response.GetResponseStream());
				var msg = rdr.ReadToEnd();

				var imageLocation = response.Headers["Location"];

				Debug.WriteLine(msg);
				Debug.WriteLine(imageLocation);
			});
	}
	catch (WebException ex)
	{
		Debug.WriteLine(ex.ToString());
	}
	catch (Exception ex)
	{
		Debug.WriteLine(ex.ToString());
	}
}

Unsuccessfully. The ResponseReady callback is never reached.

Meanwhile, this code works excellent:

open System
open System.Net.Http // WebAPI nuget

let sync aw = Async.RunSynchronously aw
   
let postC<'a> (c : HttpClient) (r : Uri) (cont : HttpContent) =
  let response = sync <| Async.AwaitTask( c.PostAsync(r, cont) )
  let struc:'a = sync <| deserialize<'a> response
  response, struc

let withContent<'a> (fVerb : (HttpClient -> Uri -> HttpContent -> _ * 'a))=
  let c = new HttpClient()
  fVerb c

[<Test>]
let ``POST /images 201 + Location header`` () =
  let post = withContent<MyImage> postC
  let bytes = IO.File.ReadAllBytes("sample.jpg")
  let hash = SHA1.Create().ComputeHash(bytes) |> Convert.ToBase64String
  let pic = new ByteArrayContent(bytes)
  pic.Headers.Add("Content-Type", "image/jpeg")
  pic.Headers.Add("X-SHA1-Hash", hash)
  let resp, ri = (resource "/images", pic) ||> post
  
  resp.StatusCode =? Code.Created
  ri.sha1 =? hash
  mustHaveHeaders resp

I couldn't get Fiddler2 working with WP7.

EDIT: Welcome to a yak. I've moved onto greener pastures myself ;)

Rest Solutions


Solution 1 - Rest

YOu should put the bytes into the before sending and using BufferStream INput output

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHenrikView Question on Stackoverflow
Solution 1 - RestRaju yourPepeView Answer on Stackoverflow