static async Task<int> GetResponseAsync(StreamSocketStream nstream, WebConnectionData data, ServicePoint sPoint) { var isContinue = false; var emptyFirstLine = false; using (var lineReader = new HttpLineReader(nstream)) { do { if (data.ReadState == ReadState.Aborted) return -1; if (data.ReadState == ReadState.None) { var line = await lineReader.ReadLineAsync(CancellationToken.None).ConfigureAwait(false); if (null == line) { data.ReadState = ReadState.Aborted; return 0; } if (string.IsNullOrEmpty(line)) { emptyFirstLine = true; continue; } emptyFirstLine = false; data.ReadState = ReadState.Status; var parts = line.Split(' '); if (parts.Length < 2) return -1; if (string.Compare(parts[0], "HTTP/1.1", StringComparison.OrdinalIgnoreCase) == 0) { data.Version = HttpVersion.Version11; sPoint.SetVersion(HttpVersion.Version11); } else { data.Version = HttpVersion.Version10; sPoint.SetVersion(HttpVersion.Version10); } data.StatusCode = (int)UInt32.Parse(parts[1]); if (parts.Length >= 3) data.StatusDescription = string.Join(" ", parts, 2, parts.Length - 2); else data.StatusDescription = ""; } emptyFirstLine = false; if (data.ReadState == ReadState.Status) { data.ReadState = ReadState.Headers; data.Headers = new WebHeaderCollection(); var headers = new List<string>(); var finished = false; while (!finished) { var line = await lineReader.ReadLineAsync(CancellationToken.None).ConfigureAwait(false); if (null == line) break; if (string.IsNullOrEmpty(line)) { // Empty line: end of headers finished = true; continue; } if (line.Length > 0 && (line[0] == ' ' || line[0] == '\t')) { var count = headers.Count - 1; if (count < 0) break; var prev = headers[count] + line; headers[count] = prev; } else headers.Add(line); } if (!finished) return 0; lineReader.SyncStream(); foreach (var s in headers) data.Headers.SetInternal(s); if (data.StatusCode == (int)HttpStatusCode.Continue) { sPoint.SendContinue = true; if (data.Request.ExpectContinue) { data.Request.DoContinueDelegate(data.StatusCode, data.Headers); // Prevent double calls when getting the // headers in several packets. data.Request.ExpectContinue = false; } data.ReadState = ReadState.None; isContinue = true; } else { data.ReadState = ReadState.Content; return 1; } } } while (emptyFirstLine || isContinue); } return -1; }
async Task<bool> CreateStreamAsync(HttpWebRequest request) { try { var serverStream = new StreamSocketStream(_socket, false); _ssl = request.Address.Scheme == Uri.UriSchemeHttps; _nstream = serverStream; } catch (Exception) { if (!request.Aborted) _status = WebExceptionStatus.ConnectFailure; return false; } return true; }
async Task<bool> CreateTunnelAsync(HttpWebRequest request, Uri connectUri, StreamSocketStream stream /*, out byte[] buffer */) { bool haveAuth; var connectBytes = CreateConnectBytes(request, connectUri, out haveAuth); await stream.WriteAsync(connectBytes, 0, connectBytes.Length).ConfigureAwait(false); var readHeaders = await ReadHeadersAsync(stream).ConfigureAwait(false); var status = readHeaders.Item1; var result = readHeaders.Item2; if ((!haveAuth || _connectNtlmAuthState == NtlmAuthState.Challenge) && result != null && status == 407) { // Needs proxy auth var connectionHeader = result["Connection"]; if (_socket != null && !string.IsNullOrEmpty(connectionHeader) && connectionHeader.ToLower() == "close") { // The server is requesting that this connection be closed _socket.Dispose(); _socket = null; } Data.StatusCode = status; Data.Challenge = result.GetValues_internal("Proxy-Authenticate", false); return false; } if (status != 200) { var msg = String.Format("The remote server returned a {0} status code.", status); HandleError(WebExceptionStatus.SecureChannelFailure, null, msg); return false; } return result != null; }
async Task<Tuple<int, WebHeaderCollection>> ReadHeadersAsync(StreamSocketStream stream) { var status = 200; //var ms = new MemoryStream(); var gotStatus = false; using (var lineReader = new HttpLineReader(stream)) { while (true) { //var n = await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); //if (n == 0) //{ // HandleError(WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders"); // return null; //} //ms.Write(buffer, 0, n); //var start = 0; var headers = new WebHeaderCollection(); for (; ; ) { var str = await lineReader.ReadLineAsync(CancellationToken.None).ConfigureAwait(false); if (str == null) { var contentLen = 0L; try { if (!long.TryParse(headers["Content-Length"], out contentLen)) contentLen = 0; } catch { contentLen = 0; } lineReader.SyncStream(); //if (false) //ms.Length - start - contentLen > 0) //{ // // we've read more data than the response header and contents, // // give back extra data to the caller // //retBuffer = new byte[ms.Length - start - contentLen]; // //Buffer.BlockCopy(ms.GetBuffer(), (int) (start + contentLen), retBuffer, 0, retBuffer.Length); //} if (contentLen > 0) { // haven't read in some or all of the contents for the response, do so now await FlushContentsAsync(stream, contentLen).ConfigureAwait(false); } return Tuple.Create(status, headers); } if (gotStatus) { headers.Add(str); continue; } var spaceidx = str.IndexOf(' '); if (spaceidx == -1) { HandleError(WebExceptionStatus.ServerProtocolViolation, null, "ReadHeaders2"); return null; } status = (int)UInt32.Parse(str.Substring(spaceidx + 1, 3)); gotStatus = true; } } } }
//internal IAsyncResult BeginWrite(HttpWebRequest request, byte[] buffer, int offset, int size, AsyncCallback cb, object state) //{ // Stream s = null; // lock (this) // { // if (Data.Request != request) // throw new ObjectDisposedException(typeof (StreamSocketStream).FullName); // if (_nstream == null) // return null; // s = _nstream; // } // IAsyncResult result = null; // try // { // result = s.BeginWrite(buffer, offset, size, cb, state); // var x = s.WriteAsync(buffer, offset, size).ConfigureAwait(false) // } // catch (Exception) // { // _status = WebExceptionStatus.SendFailure; // throw; // } // return result; //} //internal bool EndWrite(HttpWebRequest request, bool throwOnError, IAsyncResult result) //{ // if (request.FinishedReading) // return true; // Stream s = null; // lock (this) // { // if (Data.Request != request) // throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); // if (_nstream == null) // throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); // s = _nstream; // } // try // { // s.EndWrite(result); // return true; // } // catch (Exception exc) // { // _status = WebExceptionStatus.SendFailure; // if (throwOnError && exc.InnerException != null) // throw exc.InnerException; // return false; // } //} //internal int Read(HttpWebRequest request, byte[] buffer, int offset, int size) //{ // Stream s = null; // lock (this) // { // if (Data.Request != request) // throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); // if (_nstream == null) // return 0; // s = _nstream; // } // var result = 0; // try // { // var done = false; // if (!_chunkedRead) // { // result = s.Read(buffer, offset, size); // done = (result == 0); // } // if (_chunkedRead) // { // try // { // _chunkStream.WriteAndReadBack(buffer, offset, size, ref result); // if (!done && result == 0 && _chunkStream.WantMore) // result = EnsureReadAsync(buffer, offset, size); // } // catch (Exception e) // { // HandleError(WebExceptionStatus.ReceiveFailure, e, "chunked Read1"); // throw; // } // if ((done || result == 0) && _chunkStream.WantMore) // { // HandleError(WebExceptionStatus.ReceiveFailure, null, "chunked Read2"); // throw new WebException("Read error", null, WebExceptionStatus.ReceiveFailure, null); // } // } // } // catch (Exception e) // { // HandleError(WebExceptionStatus.ReceiveFailure, e, "Read"); // } // return result; //} internal bool Write(HttpWebRequest request, byte[] buffer, int offset, int size, ref string err_msg) { err_msg = null; Stream s = null; lock (this) { if (Data.Request != request) throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); s = _nstream; if (s == null) return false; } try { s.Write(buffer, offset, size); // here SSL handshake should have been done if (_ssl && !_certsAvailable) GetCertificates(s); } catch (Exception e) { err_msg = e.Message; var wes = WebExceptionStatus.SendFailure; var msg = "Write: " + err_msg; if (e is WebException) { HandleError(wes, e, msg); return false; } // if SSL is in use then check for TrustFailure if (_ssl) { #if SECURITY_DEP && MONOTOUCH HttpsClientStream https = (s as HttpsClientStream); if (https.TrustFailure) { #else if ((bool)_piTrustFailure.GetValue(s, null)) { #endif wes = WebExceptionStatus.TrustFailure; msg = "Trust failure"; } } HandleError(wes, e, msg); return false; } return true; } internal void Close(bool sendNext) { lock (this) { if (Data != null && Data.Request != null && Data.Request.ReuseConnection) { Data.Request.ReuseConnection = false; return; } if (_nstream != null) { try { _nstream.Close(); } catch { } _nstream = null; } if (_socket != null) { try { _socket.Dispose(); } catch { } _socket = null; } if (_ntlmAuthenticated) ResetNtlm(); if (Data != null) { lock (Data) { Data.ReadState = ReadState.Aborted; } } _state.SetIdle(); Data = new WebConnectionData(); if (sendNext) SendNext(); _connectRequest = null; _connectNtlmAuthState = NtlmAuthState.None; } } void Abort(object sender, EventArgs args) { lock (this) { lock (_queue) { var req = (HttpWebRequest)sender; if (Data.Request == req || Data.Request == null) { if (!req.FinishedReading) { _status = WebExceptionStatus.RequestCanceled; Close(false); if (_queue.Count > 0) { Data.Request = _queue.Dequeue(); SendRequest(Data.Request); } } return; } req.FinishedReading = true; req.SetResponseError(WebExceptionStatus.RequestCanceled, null, "User aborted"); if (_queue.Count > 0 && _queue.Peek() == sender) _queue.Dequeue(); else if (_queue.Count > 0) { var old = _queue.ToArray(); _queue.Clear(); for (var i = old.Length - 1; i >= 0; i--) { if (old[i] != sender) _queue.Enqueue(old[i]); } } } } }