// The requirements are: // * We must read at least one byte from the stream every time // we get a HasBytesAvailable event. // * SerializeToStreamAsync is executed on a separate thread, // so reads must somehow be synchronized with that thread. // // Current implementation: // * We read data in ReadStreamData (on the same thread // we got the HasBytesAvailable event, i.e. inside the // HasBytesAvailable event handler). // * Data is stored in a class-level buffer. // * SerializeToStreamAsync blocks while waiting for // data from ReadStreamData. // * ReadStreamData will only read more data once SerializeToStreamAsync // has consumed any existing data. This means we'll be // blocking in the HasBytesAvailable event handler until // any previously read data has been processed (this prevents // any unbound memory growth). public CFContentStream (CFHTTPStream stream) { this.http_stream = stream; data = new BufferData () { Buffer = new byte [4096], }; data_event = new AutoResetEvent (false); data_read_event = new AutoResetEvent (true); data_mutex = new Mutex (); }
void CloseStream(CFHTTPStream stream) { lock (streamBuckets) { if (streamBuckets.TryGetValue(stream.Handle, out var bucket)) { bucket.Close(); streamBuckets.Remove(stream.Handle); } } stream.Close(); }
// The requirements are: // * We must read at least one byte from the stream every time // we get a HasBytesAvailable event. // * SerializeToStreamAsync is executed on a separate thread, // so reads must somehow be synchronized with that thread. // // Current implementation: // * We read data in ReadStreamData (on the same thread // we got the HasBytesAvailable event, i.e. inside the // HasBytesAvailable event handler). // * Data is stored in a class-level buffer. // * SerializeToStreamAsync blocks while waiting for // data from ReadStreamData. // * ReadStreamData will only read more data once SerializeToStreamAsync // has consumed any existing data. This means we'll be // blocking in the HasBytesAvailable event handler until // any previously read data has been processed (this prevents // any unbound memory growth). public CFContentStream(CFHTTPStream stream) { this.http_stream = stream; data = new BufferData() { Buffer = new byte [4096], }; data_event = new AutoResetEvent(false); data_read_event = new AutoResetEvent(true); data_mutex = new Mutex(); }
void CloseStream(CFHTTPStream stream) { StreamBucket bucket; if (streamBuckets.TryGetValue(stream.Handle, out bucket)) { bucket.Close(); streamBuckets.Remove(stream.Handle); } stream.Close(); }
protected override void Dispose(bool disposing) { if (disposing) { OnCanceled(); if (stream != null) { stream.Dispose(); stream = null; } if (openCts != null) { openCts.Dispose(); openCts = null; } } base.Dispose(disposing); }
internal async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken, bool isFirstRequest) { sentRequest = true; CFHTTPStream stream; using (var message = CreateWebRequestAsync(request)) { if (request.Content != null) { var data = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false); message.SetBody(data); } stream = CFHTTPStream.CreateForHTTPRequest(message); } if (useSystemProxy) { var proxies = CF.CFNetwork.GetSystemProxySettings(); if (proxies.HTTPEnable) { stream.SetProxy(proxies); } } if (!isFirstRequest && allowAutoRedirect) { stream.ShouldAutoredirect = allowAutoRedirect; } stream.HasBytesAvailableEvent += HandleHasBytesAvailableEvent; stream.ErrorEvent += HandleErrorEvent; stream.ClosedEvent += HandleClosedEvent; var response = new TaskCompletionSource <HttpResponseMessage> (); if (cancellationToken.IsCancellationRequested) { response.SetCanceled(); return(await response.Task); } var bucket = new StreamBucket() { Request = request, Response = response, }; streamBuckets.Add(stream.Handle, bucket); // // Always schedule stream events handling on main-loop. Due to ConfigureAwait (false) we may end up // on any thread-pool thread which may not have run-loop running // stream.EnableEvents(CF.CFRunLoop.Main, CF.CFRunLoop.ModeCommon); stream.Open(); bucket.CancellationTokenRegistration = cancellationToken.Register(() => { StreamBucket bucket2; if (!streamBuckets.TryGetValue(stream.Handle, out bucket2)) { return; } bucket2.Response.TrySetCanceled(); CloseStream(stream); }); if (isFirstRequest) { var initialRequest = await response.Task; var status = initialRequest.StatusCode; if (IsRedirect(status) && allowAutoRedirect) { bucket.StreamCanBeDisposed = true; // we do not care about the first stream cbs stream.HasBytesAvailableEvent -= HandleHasBytesAvailableEvent; stream.ErrorEvent -= HandleErrorEvent; stream.ClosedEvent -= HandleClosedEvent; // remove headers in a redirect for Authentication. request.Headers.Authorization = null; return(await SendAsync(request, cancellationToken, false).ConfigureAwait(false)); } return(initialRequest); } return(await response.Task); }
WebResponseStream(CFHTTPStream stream, WebRequestStream body) { this.stream = stream; this.body = body; syncRoot = new object(); }
void CloseStream (CFHTTPStream stream) { StreamBucket bucket; if (streamBuckets.TryGetValue (stream.Handle, out bucket)) { bucket.Close (); streamBuckets.Remove (stream.Handle); } stream.Close (); }
protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { sentRequest = true; CFHTTPStream stream; using (var message = CreateWebRequestAsync(request)) { if (request.Content != null) { var data = await request.Content.ReadAsByteArrayAsync().ConfigureAwait(false); message.SetBody(data); } stream = CFHTTPStream.CreateForHTTPRequest(message); } if (useSystemProxy) { var proxies = CF.CFNetwork.GetSystemProxySettings(); if (proxies.HTTPEnable) { stream.SetProxy(proxies); } } stream.ShouldAutoredirect = allowAutoRedirect; stream.HasBytesAvailableEvent += HandleHasBytesAvailableEvent; stream.ErrorEvent += HandleErrorEvent; stream.ClosedEvent += HandleClosedEvent; var response = new TaskCompletionSource <HttpResponseMessage> (); if (cancellationToken.IsCancellationRequested) { response.SetCanceled(); return(await response.Task); } var bucket = new StreamBucket() { Request = request, Response = response, }; streamBuckets.Add(stream.Handle, bucket); // // Always schedule stream events handling on main-loop. Due to ConfigureAwait (false) we may end up // on any thread-pool thread which may not have run-loop running // #if XAMCORE_2_0 stream.EnableEvents(CF.CFRunLoop.Main, CF.CFRunLoop.ModeCommon); #else stream.EnableEvents(CF.CFRunLoop.Main, CF.CFRunLoop.CFRunLoopCommonModes); #endif stream.Open(); bucket.CancellationTokenRegistration = cancellationToken.Register(() => { StreamBucket bucket2; if (!streamBuckets.TryGetValue(stream.Handle, out bucket2)) { return; } bucket2.Response.TrySetCanceled(); CloseStream(stream); }); return(await response.Task); }