Example #1
0
        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();
            }
        }
Example #2
0
        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);
        }
Example #3
0
        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;
        }
Example #4
0
        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;
        }
Example #5
0
        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;
        }
Example #6
0
        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;
                }
            }
        }
Example #7
0
        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;
        }
Example #8
0
        //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]);
                        }
                    }
                }
            }
        }
Example #9
0
        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;
        }
Example #10
0
        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;
            }
        }
Example #11
0
        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;
        }
Example #12
0
 public WebAsyncResult(HttpWebRequest request, AsyncCallback cb, object state)
 {
     this.cb = cb;
     this.state = state;
 }