internal void NextRead() { lock (this) { if (Data.Request != null) Data.Request.FinishedReading = true; var header = _sPoint.UsesProxy ? "Proxy-Connection" : "Connection"; var cncHeader = null != Data.Headers ? Data.Headers[header] : null; var keepAlive = Data.Version == HttpVersion.Version11 && _keepAlive; if (cncHeader != null) { cncHeader = cncHeader.ToLower(); keepAlive = (_keepAlive && cncHeader.IndexOf("keep-alive", StringComparison.Ordinal) != -1); } if ((!keepAlive || (cncHeader != null && cncHeader.IndexOf("close", StringComparison.Ordinal) != -1))) Close(false); _state.SetIdle(); if (_priorityRequest != null) { SendRequest(_priorityRequest); _priorityRequest = null; } else SendNext(); } }
async Task InitConnectionAsync(HttpWebRequest request) { request.WebConnection = this; if (request.ReuseConnection) request.StoredConnection = this; if (request.Aborted) return; _keepAlive = request.KeepAlive; Data = new WebConnectionData(request); retry: await ConnectAsync(request).ConfigureAwait(false); if (request.Aborted) return; if (_status != WebExceptionStatus.Success) { if (!request.Aborted) { request.SetWriteStreamError(_status, _connectException); Close(true); } return; } if (!await CreateStreamAsync(request).ConfigureAwait(false)) { if (request.Aborted) return; var st = _status; if (Data.Challenge != null) goto retry; var cncExc = _connectException; _connectException = null; request.SetWriteStreamError(st, cncExc); Close(true); return; } await request.SetWriteStreamAsync(new WebConnectionStream(this, request)).ConfigureAwait(false); }
internal EventHandler SendRequest(HttpWebRequest request) { if (request.Aborted) return null; lock (this) { if (_state.TrySetBusy()) { _status = WebExceptionStatus.Success; var task = InitConnectionAsync(request); task.ContinueWith(t => { var ex = t.Exception; if (null != ex) Debug.WriteLine("InitConnectionAsync failed: " + ex.Message); }); } else { lock (_queue) { #if MONOTOUCH if (!warned_about_queue) { warned_about_queue = true; Console.WriteLine ("WARNING: An HttpWebRequest was added to the ConnectionGroup queue because the connection limit was reached."); } #endif _queue.Enqueue(request); } } } return _abortHandler; }
byte[] CreateConnectBytes(HttpWebRequest request, Uri connectUri, out bool haveAuth) { var sb = new StringBuilder(); sb.Append("CONNECT "); sb.Append(request.Address.Host); sb.Append(':'); sb.Append(request.Address.Port); sb.Append(" HTTP/"); if (request.ServicePoint.ProtocolVersion == HttpVersion.Version11) sb.Append("1.1"); else sb.Append("1.0"); sb.Append("\r\nHost: "); sb.Append(request.Address.Authority); var ntlm = false; var challenge = Data.Challenge; Data.Challenge = null; var authHeader = request.Headers["Proxy-Authorization"]; haveAuth = authHeader != null; if (haveAuth) { sb.Append("\r\nProxy-Authorization: "); sb.Append(authHeader); ntlm = authHeader.ToUpper().Contains("NTLM"); } else if (challenge != null && Data.StatusCode == 407) { var creds = request.Proxy.Credentials; haveAuth = true; if (_connectRequest == null) { // create a CONNECT request to use with Authenticate _connectRequest = (HttpWebRequest)WebRequest.Create( connectUri.Scheme + "://" + connectUri.Host + ":" + connectUri.Port.ToString(CultureInfo.InvariantCulture) + "/"); _connectRequest.Method = "CONNECT"; _connectRequest.Credentials = creds; } foreach (var c in challenge) { var auth = AuthenticationManager.Authenticate(c, _connectRequest, creds); if (auth == null) continue; ntlm = (auth.Module.AuthenticationType == "NTLM"); sb.Append("\r\nProxy-Authorization: "); sb.Append(auth.Message); break; } } if (ntlm) { sb.Append("\r\nProxy-Connection: keep-alive"); _connectNtlmAuthState++; } sb.Append("\r\n\r\n"); Data.StatusCode = 0; var connectBytes = Encoding.UTF8.GetBytes(sb.ToString()); return connectBytes; }
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 ConnectAsync(HttpWebRequest request) { var isLocked = false; try { Monitor.TryEnter(_socketLock, ref isLocked); if (_socket != null && _status == WebExceptionStatus.Success) { // Take the chunked stream to the expected state (State.None) if (CanReuse() && await CompleteChunkedReadAsync(CancellationToken.None).ConfigureAwait(false)) { _reused = true; return; } } _reused = false; if (_socket != null) { _socket.Dispose(); _socket = null; } _chunkStream = null; } finally { if (isLocked) Monitor.Exit(_socketLock); } var hostEntry = await _sPoint.GetHostEntryAsync().ConfigureAwait(false); if (hostEntry == null) { #if MONOTOUCH monotouch_start_wwan (sPoint.Address.ToString ()); hostEntry = sPoint.HostEntry; if (hostEntry == null) { #endif _status = _sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure : WebExceptionStatus.NameResolutionFailure; return; #if MONOTOUCH } #endif } foreach (var address in hostEntry.AddressList) { lock (_socketLock) { if (null != _socket) { _socket.Dispose(); _socket = null; } try { _socket = new StreamSocket(); } catch (Exception se) { // The Socket ctor can throw if we run out of FD's if (!request.Aborted) _status = WebExceptionStatus.ConnectFailure; _connectException = se; return; } _socket.Control.NoDelay = !_sPoint.UseNagleAlgorithm; try { _sPoint.KeepAliveSetup(_socket); } catch { // Ignore. Not supported in all platforms. } } var port = _sPoint.Address.Port; var remote = new IPEndPoint(address, port); IPEndPoint local; if (!_sPoint.GetLocalEndPointFromDelegate(remote, out local)) { StreamSocket s; lock (_socketLock) { s = _socket; _socket = null; _status = WebExceptionStatus.ConnectFailure; } if (s != null) s.Dispose(); return; } try { if (request.Aborted) return; var remoteHostName = new HostName(request.RequestUri.Host); var remoteServiceName = port.ToString(CultureInfo.InvariantCulture); await _socket.ConnectAsync(remoteHostName, remoteServiceName, _ssl ? SocketProtectionLevel.Ssl : SocketProtectionLevel.PlainSocket).AsTask().ConfigureAwait(false); _status = WebExceptionStatus.Success; break; } catch (ThreadAbortException) { // program exiting... StreamSocket s; lock (_socketLock) { s = _socket; _socket = null; } if (s != null) s.Dispose(); return; } catch (ObjectDisposedException) { // socket closed from another thread return; } catch (Exception exc) { StreamSocket s; lock (_socketLock) { s = _socket; _socket = null; if (!request.Aborted) _status = WebExceptionStatus.ConnectFailure; } if (s != null) s.Dispose(); _connectException = exc; } } }
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; }
//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]); } } } } }
internal async Task<bool> WriteAsync(HttpWebRequest request, byte[] buffer, int offset, int size, CancellationToken cancellationToken) { Stream s; lock (this) { if (Data.Request != request) throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); if (_nstream == null) throw new InvalidOperationException("No stream available"); s = _nstream; } try { await s.WriteAsync(buffer, offset, size, cancellationToken).ConfigureAwait(false); return true; } catch (Exception exc) { _status = WebExceptionStatus.SendFailure; if (exc.InnerException != null) throw exc.InnerException; } return request.FinishedReading; }
internal async Task FlushAsync(HttpWebRequest request, CancellationToken cancellationToken) { Stream s; lock (this) { if (Data.Request != request) throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); if (_nstream == null) throw new InvalidOperationException("No stream available"); s = _nstream; } try { await s.FlushAsync(cancellationToken).ConfigureAwait(false); } catch (Exception exc) { _status = WebExceptionStatus.SendFailure; if (exc.InnerException != null) throw exc.InnerException; } }
internal async Task<int> ReadAsync(HttpWebRequest request, byte[] buffer, int offset, int size, CancellationToken cancellationToken) { //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken)", offset, size); StreamSocketStream s; lock (this) { if (Data.Request != request) throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); if (_nstream == null) throw new InvalidOperationException("No stream available"); s = _nstream; } var nbytes = 0; var done = false; if (!_chunkedRead || (!_chunkStream.DataAvailable && _chunkStream.WantMore)) { try { //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken) calling socket read, chunkedRead {2} dataAvail {3} wantMore {4}", // offset, size, _chunkedRead, // null == _chunkStream ? "<null>" : _chunkStream.DataAvailable.ToString(), // null == _chunkStream ? "<null>" : _chunkStream.WantMore.ToString()); nbytes = await s.ReadAsync(buffer, offset, size, cancellationToken).ConfigureAwait(false); done = nbytes == 0; //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken) socket read returned, chunkedRead {2} dataAvail {3} wantMore {4} nbytes {5}", // offset, size, _chunkedRead, // null == _chunkStream ? "<null>" : _chunkStream.DataAvailable.ToString(), // null == _chunkStream ? "<null>" : _chunkStream.WantMore.ToString(), // nbytes); //DumpBase64(buffer, offset, nbytes); } catch (Exception) { HandleError(WebExceptionStatus.ReceiveFailure, null, "chunked ReadAsync"); throw; } } //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken) read {2} done {3}", offset, size, nbytes, done); lock (this) { if (Data.Request != request) throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); if (_nstream == null) throw new ObjectDisposedException(typeof(StreamSocketStream).FullName); s = _nstream; } if (_chunkedRead) { try { //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken) calling WriteAndReadBack {2}", offset, size, nbytes); _chunkStream.WriteAndReadBack(buffer, offset, size, ref nbytes); if (!done && nbytes == 0 && _chunkStream.WantMore) { nbytes = await EnsureReadAsync(buffer, offset, size, cancellationToken).ConfigureAwait(false); //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken) EnsureReadAsync returned {2}", offset, size, nbytes); } } catch (Exception e) { if (e is WebException) throw; throw new WebException("Invalid chunked data.", e, WebExceptionStatus.ServerProtocolViolation, null); } if ((done || nbytes == 0) && _chunkStream.ChunkLeft != 0) { HandleError(WebExceptionStatus.ReceiveFailure, null, "chunked EndRead"); throw new WebException("Read error", null, WebExceptionStatus.ReceiveFailure, null); } } var ret = (nbytes != 0) ? nbytes : -1; //Debug.WriteLine("WebConnection.ReadAsync(buffer, {0}, {1}, cancellationToken) returning {2} (nbytes {3})", offset, size, ret, nbytes); return ret; }
public WebAsyncResult(HttpWebRequest request, AsyncCallback cb, object state) { this.cb = cb; this.state = state; }