--- /dev/null
+//
+// WebAsyncData.cs
+//
+// Author:
+// Martin Baulig <martin.baulig@xamarin.com>
+//
+// Copyright (c) 2015 Xamarin, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System.IO;
+using System.Threading;
+
+namespace System.Net
+{
+ class WebAsyncData
+ {
+ readonly HttpWebRequest request;
+ readonly Stream stream;
+ readonly byte[] buffer;
+ readonly int offset, size;
+
+ public WebAsyncData (HttpWebRequest request, Stream stream, byte[] buffer, int offset, int size)
+ {
+ this.request = request;
+ this.stream = stream;
+ this.buffer = buffer;
+ this.offset = offset;
+ this.size = size;
+ }
+
+ public HttpWebRequest Request {
+ get { return request; }
+ }
+
+ public Stream Stream {
+ get { return stream; }
+ }
+
+ public byte[] Buffer {
+ get { return buffer; }
+ }
+
+ public int Offset {
+ get { return offset; }
+ }
+
+ public int Size {
+ get { return size; }
+ }
+ }
+}
+
return true;
}
-
- internal IAsyncResult BeginRead (HttpWebRequest request, byte [] buffer, int offset, int size, AsyncCallback cb, object state)
+ internal void ReadAsync (HttpWebRequest request, byte [] buffer, int offset, int size, WebAsyncResult result)
{
+ bool error = false;
Stream s = null;
lock (this) {
if (Data.request != request)
- throw new ObjectDisposedException (typeof (NetworkStream).FullName);
- if (nstream == null)
- return null;
+ error = true;
s = nstream;
}
- IAsyncResult result = null;
- if (!chunkedRead || (!chunkStream.DataAvailable && chunkStream.WantMore)) {
- try {
- result = s.BeginRead (buffer, offset, size, cb, state);
- cb = null;
- } catch (Exception) {
- HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked BeginRead");
- throw;
- }
+ if (error) {
+ result.SetCompleted (true, new ObjectDisposedException (typeof(NetworkStream).FullName));
+ result.DoCallback ();
+ return;
+ } else if (s == null) {
+ result.SetCompleted (true);
+ result.DoCallback ();
+ return;
}
- if (chunkedRead) {
- WebAsyncResult wr = new WebAsyncResult (cb, state, buffer, offset, size);
- wr.InnerAsyncResult = result;
- if (result == null) {
- // Will be completed from the data in ChunkStream
- wr.SetCompleted (true, (Exception) null);
- wr.DoCallback ();
- }
- return wr;
+ var data = new WebAsyncData (request, s, buffer, offset, size);
+ ReadAsync (data, result);
+ }
+
+ void ReadAsync (WebAsyncData data, WebAsyncResult result)
+ {
+ bool running;
+ try {
+ running = ReadAsyncInner (data, result);
+ } catch (Exception ex) {
+ result.SetCompleted (true, ex);
+ running = false;
}
+ if (!running)
+ result.DoCallback ();
+ }
- return result;
+ bool ReadAsyncInner (WebAsyncData data, WebAsyncResult result)
+ {
+ if (chunkedRead && (chunkStream.DataAvailable || !chunkStream.WantMore)) {
+ ReadAsyncInnerCB (data, result, null);
+ return false;
+ }
+
+ result.InnerAsyncResult = data.Stream.BeginRead (data.Buffer, data.Offset, data.Size, inner => ReadAsyncInnerCB (data, result, inner), null);
+ return result.InnerAsyncResult != null && !result.InnerAsyncResult.CompletedSynchronously;
}
-
- internal int EndRead (HttpWebRequest request, IAsyncResult result)
+
+ bool ReadAsyncInnerCB (WebAsyncData data, WebAsyncResult result, IAsyncResult innerResult)
{
+ var synch = innerResult != null ? innerResult.CompletedSynchronously : false;
+
+ bool error = false;
Stream s = null;
lock (this) {
- if (Data.request != request)
- throw new ObjectDisposedException (typeof (NetworkStream).FullName);
- if (nstream == null)
- throw new ObjectDisposedException (typeof (NetworkStream).FullName);
+ if (Data.request != data.Request || nstream == null)
+ error = true;
s = nstream;
}
+ if (error) {
+ result.SetCompleted (synch, new ObjectDisposedException (typeof(NetworkStream).FullName));
+ result.DoCallback ();
+ return false;
+ }
+
int nbytes = 0;
- bool done = false;
- WebAsyncResult wr = null;
- IAsyncResult nsAsync = ((WebAsyncResult) result).InnerAsyncResult;
- if (chunkedRead && (nsAsync is WebAsyncResult)) {
- wr = (WebAsyncResult) nsAsync;
- IAsyncResult inner = wr.InnerAsyncResult;
- if (inner != null && !(inner is WebAsyncResult)) {
- nbytes = s.EndRead (inner);
- done = nbytes == 0;
- }
- } else if (!(nsAsync is WebAsyncResult)) {
- nbytes = s.EndRead (nsAsync);
- wr = (WebAsyncResult) result;
- done = nbytes == 0;
+ try {
+ if (innerResult != null)
+ nbytes = s.EndRead (innerResult);
+ } catch (Exception ex) {
+ result.SetCompleted (synch, ex);
+ result.DoCallback ();
+ return false;
}
- if (chunkedRead) {
- try {
- chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes);
- if (!done && nbytes == 0 && chunkStream.WantMore)
- nbytes = EnsureRead (wr.Buffer, wr.Offset, wr.Size);
- } catch (Exception e) {
- if (e is WebException)
- throw e;
+ var done = nbytes == 0;
- throw new WebException ("Invalid chunked data.", e,
- WebExceptionStatus.ServerProtocolViolation, null);
- }
+ if (!chunkedRead) {
+ result.SetCompleted (synch, nbytes);
+ result.DoCallback ();
+ return false;
+ }
- if ((done || nbytes == 0) && chunkStream.ChunkLeft != 0) {
- HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead");
- throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
+ try {
+ chunkStream.WriteAndReadBack (data.Buffer, data.Offset, data.Size, ref nbytes);
+ } catch (Exception e) {
+ if (!(e is WebException))
+ e = new WebException ("Invalid chunked data.", e, WebExceptionStatus.ServerProtocolViolation, null);
+ result.SetCompleted (synch, e);
+ result.DoCallback ();
+ return false;
+ }
+
+ try {
+ if (!done && nbytes == 0 && chunkStream.WantMore)
+ return ReadAsyncInner (data, result);
+ } catch (Exception e) {
+ if (!(e is WebException))
+ e = new WebException ("Invalid chunked data.", e, WebExceptionStatus.ServerProtocolViolation, null);
+ result.SetCompleted (synch, e);
+ result.DoCallback ();
+ return false;
+ }
+
+ if (done || nbytes == 0) {
+ if (chunkStream.ChunkLeft != 0) {
+ result.SetCompleted (synch, new WebException ("Read error", null, WebExceptionStatus.ConnectionClosed, null));
+ result.DoCallback ();
+ return false;
+ } else if (chunkStream.WantMore) {
+ result.SetCompleted (synch, new IOException ("Connection closed"));
+ result.DoCallback ();
+ return false;
}
}
- return (nbytes != 0) ? nbytes : -1;
+ result.SetCompleted (synch, nbytes);
+ result.DoCallback ();
+ return false;
}
// To be called on chunkedRead when we can read no data from the ChunkStream yet
}
WebAsyncResult result = new WebAsyncResult (cb, state, buffer, offset, size);
+ result.AsyncObject = request;
if (totalRead >= contentLength) {
result.SetCompleted (true, -1);
result.DoCallback ();
size = (int)(contentLength - totalRead);
if (!read_eof) {
- result.InnerAsyncResult = cnc.BeginRead (request, buffer, offset, size, cb, result);
+ cnc.ReadAsync (request, buffer, offset, size, result);
} else {
result.SetCompleted (true, result.NBytes);
result.DoCallback ();
public override int EndRead (IAsyncResult r)
{
WebAsyncResult result = (WebAsyncResult) r;
- if (result.EndCalled) {
- int xx = result.NBytes;
- return (xx >= 0) ? xx : 0;
- }
+ int nb = result.NBytes;
+ if (result.EndCalled)
+ return (nb >= 0) ? nb : 0;
result.EndCalled = true;
- if (!result.IsCompleted) {
- int nbytes = -1;
- try {
- nbytes = cnc.EndRead (request, result);
- } catch (Exception exc) {
- lock (locker) {
- pendingReads--;
- if (pendingReads == 0)
- pending.Set ();
- }
-
- nextReadCalled = true;
- cnc.Close (true);
- result.SetCompleted (false, exc);
- result.DoCallback ();
- throw;
- }
-
- if (nbytes < 0) {
- nbytes = 0;
- read_eof = true;
- }
-
- totalRead += nbytes;
- result.SetCompleted (false, nbytes + result.NBytes);
- result.DoCallback ();
- if (nbytes == 0)
- contentLength = totalRead;
- }
-
lock (locker) {
pendingReads--;
if (pendingReads == 0)
pending.Set ();
}
+ if (result.GotException) {
+ nextReadCalled = true;
+ cnc.Close (true);
+ throw result.Exception;
+ }
+
+ if (nb < 0) {
+ read_eof = true;
+ } else {
+ totalRead += result.NBytes;
+ if (nb == 0)
+ contentLength = totalRead;
+ }
+
if (totalRead >= contentLength && !nextReadCalled)
ReadAll ();
- int nb = result.NBytes;
return (nb >= 0) ? nb : 0;
}