Task <AFHTTPRequestOperation> enqueueOperation(AFHTTPClient handler, AFHTTPRequestOperation operation, CancellationToken cancelToken, Action onCompleted, Action <Exception> onError) { var tcs = new TaskCompletionSource <AFHTTPRequestOperation>(); if (cancelToken.IsCancellationRequested) { tcs.SetCanceled(); return(tcs.Task); } bool completed = false; operation.SetDownloadProgressBlock((a, b, c) => { // NB: We're totally cheating here, we just happen to know // that we're guaranteed to have response headers after the // first time we get progress. if (completed) { return; } completed = true; tcs.SetResult(operation); }); operation.SetCompletionBlockWithSuccess( (op, _) => onCompleted(), (op, err) => { var ex = new ApplicationException(); ex.Data.Add("op", op); ex.Data.Add("err", err); onCompleted(); if (completed) { onError(ex); return; } // NB: Secret Handshake is Secret completed = true; tcs.SetException(ex); }); handler.EnqueueHTTPRequestOperation(operation); cancelToken.Register(() => { if (completed) { return; } completed = true; operation.Cancel(); tcs.SetCanceled(); }); return(tcs.Task); }
Task<AFHTTPRequestOperation> enqueueOperation(AFHTTPClient handler, AFHTTPRequestOperation operation, CancellationToken cancelToken) { var tcs = new TaskCompletionSource<AFHTTPRequestOperation>(); if (cancelToken.IsCancellationRequested) { tcs.SetCanceled(); return tcs.Task; } bool completed = false; operation.SetCompletionBlockWithSuccess( (op, _) => { if (completed) return; completed = true; tcs.SetResult(op); }, (op, err) => { if (completed) return; // NB: Secret Handshake is Secret completed = true; var ex = new ApplicationException(); ex.Data.Add("op", op); ex.Data.Add("err", err); tcs.SetException(ex); }); handler.EnqueueHTTPRequestOperation(operation); cancelToken.Register(() => { if (completed) return; completed = true; operation.Cancel(); tcs.SetCanceled(); }); return tcs.Task; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var headers = request.Headers as IEnumerable<KeyValuePair<string, IEnumerable<string>>>; var ms = new MemoryStream(); if (request.Content != null) { await request.Content.CopyToAsync(ms).ConfigureAwait(false); headers = headers.Union(request.Content.Headers); } var rq = new NSMutableUrlRequest() { AllowsCellularAccess = true, Body = NSData.FromArray(ms.ToArray()), CachePolicy = NSUrlRequestCachePolicy.UseProtocolCachePolicy, Headers = headers.Aggregate(new NSMutableDictionary(), (acc, x) => { acc.Add(new NSString(x.Key), new NSString(x.Value.LastOrDefault())); return acc; }), HttpMethod = request.Method.ToString().ToUpperInvariant(), Url = NSUrl.FromString(request.RequestUri.AbsoluteUri), }; var host = request.RequestUri.GetLeftPart(UriPartial.Authority); var op = default(AFHTTPRequestOperation); var err = default(NSError); var handler = new AFHTTPClient(new NSUrl(host)); // NB: I have no idea how async methods affects object lifetime and // GC'ing of local variables, soooooooo.... lock (pins) { pins[rq] = new object[] { op, handler, }; } var blockingTcs = new TaskCompletionSource<bool>(); var ret= default(HttpResponseMessage); try { op = await enqueueOperation(handler, new AFHTTPRequestOperation(rq), cancellationToken, () => blockingTcs.SetResult(true), ex => { if (ex is ApplicationException) { err = (NSError)ex.Data["err"]; } if (ret == null) { return; } ret.ReasonPhrase = (err != null ? err.LocalizedDescription : null); }); } catch (ApplicationException ex) { op = (AFHTTPRequestOperation)ex.Data["op"]; err = (NSError)ex.Data["err"]; } var resp = (NSHttpUrlResponse)op.Response; if (err != null && resp == null && err.Domain == NSError.NSUrlErrorDomain) { throw new WebException (err.LocalizedDescription, WebExceptionStatus.NameResolutionFailure); } if (op.IsCancelled) { lock (pins) { pins.Remove(rq); } throw new TaskCanceledException(); } var httpContent = new StreamContent ( new ConcatenatingStream(new Func<Stream>[] { () => op.ResponseData == null || op.ResponseData.Length == 0 ? Stream.Null : op.ResponseData.AsStream(), }, true, blockingTcs.Task, () => { if (!op.IsCancelled && !op.IsFinished) op.Cancel(); })); cancellationToken.Register (httpContent.Dispose); ret = new HttpResponseMessage((HttpStatusCode)resp.StatusCode) { Content = httpContent, RequestMessage = request, ReasonPhrase = (err != null ? err.LocalizedDescription : null), }; foreach(var v in resp.AllHeaderFields) { ret.Headers.TryAddWithoutValidation(v.Key.ToString(), v.Value.ToString()); ret.Content.Headers.TryAddWithoutValidation(v.Key.ToString(), v.Value.ToString()); } lock (pins) { pins.Remove(rq); } return ret; }
Task<AFHTTPRequestOperation> enqueueOperation(AFHTTPClient handler, AFHTTPRequestOperation operation, CancellationToken cancelToken, Action onCompleted, Action<Exception> onError) { var tcs = new TaskCompletionSource<AFHTTPRequestOperation>(); if (cancelToken.IsCancellationRequested) { tcs.SetCanceled(); return tcs.Task; } bool completed = false; operation.SetDownloadProgressBlock((a, b, c) => { // NB: We're totally cheating here, we just happen to know // that we're guaranteed to have response headers after the // first time we get progress. if (completed) return; completed = true; tcs.SetResult(operation); }); operation.SetCompletionBlockWithSuccess( (op, _) => { if (!completed) { completed = true; tcs.SetResult(operation); } onCompleted(); }, (op, err) => { var ex = new ApplicationException(); ex.Data.Add("op", op); ex.Data.Add("err", err); onCompleted(); if (completed) { onError(ex); return; } // NB: Secret Handshake is Secret completed = true; tcs.SetException(ex); }); handler.EnqueueHTTPRequestOperation(operation); cancelToken.Register(() => { if (completed) return; completed = true; operation.Cancel(); tcs.SetCanceled(); }); return tcs.Task; }
protected override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var headers = request.Headers as IEnumerable <KeyValuePair <string, IEnumerable <string> > >; var ms = new MemoryStream(); if (request.Content != null) { await request.Content.CopyToAsync(ms).ConfigureAwait(false); headers = headers.Union(request.Content.Headers); } var rq = new NSMutableUrlRequest() { AllowsCellularAccess = true, Body = NSData.FromArray(ms.ToArray()), CachePolicy = NSUrlRequestCachePolicy.UseProtocolCachePolicy, Headers = headers.Aggregate(new NSMutableDictionary(), (acc, x) => { acc.Add(new NSString(x.Key), new NSString(x.Value.LastOrDefault())); return(acc); }), HttpMethod = request.Method.ToString().ToUpperInvariant(), Url = NSUrl.FromString(request.RequestUri.AbsoluteUri), }; var host = request.RequestUri.GetLeftPart(UriPartial.Authority); var op = default(AFHTTPRequestOperation); var err = default(NSError); var handler = new AFHTTPClient(new NSUrl(host)); // NB: I have no idea how async methods affects object lifetime and // GC'ing of local variables, soooooooo.... lock (pins) { pins[rq] = new object[] { op, handler, }; } var blockingTcs = new TaskCompletionSource <bool>(); var ret = default(HttpResponseMessage); try { op = await enqueueOperation(handler, new AFHTTPRequestOperation(rq), cancellationToken, () => blockingTcs.SetResult(true), ex => { if (ex is ApplicationException) { err = (NSError)ex.Data["err"]; } if (ret == null) { return; } ret.ReasonPhrase = (err != null ? err.LocalizedDescription : null); }); } catch (ApplicationException ex) { op = (AFHTTPRequestOperation)ex.Data["op"]; err = (NSError)ex.Data["err"]; } var resp = (NSHttpUrlResponse)op.Response; if (err != null && resp == null && err.Domain == NSError.NSUrlErrorDomain) { throw new WebException(err.LocalizedDescription, WebExceptionStatus.NameResolutionFailure); } if (op.IsCancelled) { lock (pins) { pins.Remove(rq); } throw new TaskCanceledException(); } var httpContent = new StreamContent( new ConcatenatingStream(new Func <Stream>[] { () => op.ResponseData == null || op.ResponseData.Length == 0 ? Stream.Null : op.ResponseData.AsStream(), }, true, blockingTcs.Task, () => { if (!op.IsCancelled && !op.IsFinished) { op.Cancel(); } })); cancellationToken.Register(httpContent.Dispose); ret = new HttpResponseMessage((HttpStatusCode)resp.StatusCode) { Content = httpContent, RequestMessage = request, ReasonPhrase = (err != null ? err.LocalizedDescription : null), }; foreach (var v in resp.AllHeaderFields) { ret.Headers.TryAddWithoutValidation(v.Key.ToString(), v.Value.ToString()); } lock (pins) { pins.Remove(rq); } return(ret); }