Example #1
0
        public async Task <bool> HandleRequest(Uri serverAddress)
        {
            bool gotRequest;

            NotHttp.NHError error = new NotHttp.NHError();
            try
            {
                gotRequest = await request.Parse(serverAddress, inputStream, error);

                if (gotRequest == false)
                {
                    // the connection was closed cleanly
                    return(false);
                }
            }
            catch (Exception e)
            {
                // stream error parsing request; close connection without trying to send a response
                logger.Log("Server got exception parsing request: " + e.ToString());
                return(false);
            }

            if (error.status == HttpStatusCode.OK)
            {
                return(await Accept());
            }
            else
            {
                response.StatusCode        = (int)error.status;
                response.StatusDescription = error.message;

                // make sure we consume all the client's input as well as sending the bad response
                await Task.WhenAll(response.CloseAsync(), request.CloseAsync());

                // keep the connection open if we managed to send a civilized error response
                return(true);
            }
        }
Example #2
0
        protected override async Task <bool> MaybeContinue(Stream request, NotHttp.NHError error)
        {
            if (HeadersInternal[HttpRequestHeader.Expect] != null && HeadersInternal[HttpRequestHeader.Expect].StartsWith("100"))
            {
                if (error.status == HttpStatusCode.OK)
                {
                    Logger.Log("Writing continue");
                    await request.WriteAsync(continueBytes, 0, continueBytes.Length);

                    await request.FlushAsync();

                    return(true);
                }
                else
                {
                    // skip the payload
                    return(false);
                }
            }
            else
            {
                return(true);
            }
        }
Example #3
0
        private async Task ReadStream()
        {
            int responseSequenceNumber = -1;

            while (true)
            {
                Task <bool> idleBlocker = null;
                lock (this)
                {
                    if (outstandingRequests == 0)
                    {
                        // add ContinueWith so the continuation is asynchronous, since await generates
                        // a synchronous continuation
                        idleBlocker = idleTask.Task.ContinueWith((t) => t.Result);
                    }
                }

                if (idleBlocker != null)
                {
                    bool mustExit = await idleBlocker;
                    if (mustExit)
                    {
                        endpoint.Logger.Log("Reader exiting due to idle connection");
                        return;
                    }
                }

                if (!endpoint.IsOverlapped)
                {
                    // sequence number is 0 for the first response
                    ++responseSequenceNumber;
                }

                // for an overlapped connection we pass in responseSequenceNumber=-1 and the actual sequence
                // number is read from the protocol. For a sequential connection we pass in the actual sequence
                // number
                NHClientResponse response = new NHClientResponse(this, responseSequenceNumber, endpoint.Logger);
                try
                {
                    NotHttp.NHError error      = new NotHttp.NHError();
                    bool            gotRequest = await response.Parse(null, stream, error);

                    if (!gotRequest)
                    {
                        throw new ApplicationException("Server closed connection");
                    }

                    if (response.StatusCode != HttpStatusCode.OK || endpoint.IsOverlapped)
                    {
                        // fetch the whole response now, so we're ready to wait for the next message even if
                        // the caller isn't ready to read this one
                        await response.BufferInput();
                    }

                    lock (this)
                    {
                        TaskCompletionSource <IHttpResponse> tcs;
                        if (pendingClients.TryGetValue(response.SequenceNumber, out tcs))
                        {
                            // run this asynchronously outside the lock
                            Task abandon = Task.Run(() => ReturnResult(tcs, response));
                            pendingClients.Remove(response.SequenceNumber);
                        }
                        else
                        {
                            if (receivedResponses.ContainsKey(response.SequenceNumber))
                            {
                                StreamFaulted(new ApplicationException("Received duplicate sequence numbers " + response.SequenceNumber));
                                return;
                            }

                            receivedResponses.Add(response.SequenceNumber, response);
                        }
                    }

                    // wait until the caller has consumed all the payload data (if we buffered it above this
                    // falls through immediately)
                    await response.Completed;

                    string closeHeader = response.HeadersInternal[HttpRequestHeader.Connection];
                    if (closeHeader != null && closeHeader.ToLower() == "close")
                    {
                        endpoint.Logger.Log("Passing stream connection closed header");
                        StreamFaulted(new ApplicationException("Connection closed by server request"));
                        return;
                    }

                    lock (this)
                    {
                        if (!endpoint.IsOverlapped)
                        {
                            currentRequest = null;
                        }

                        --outstandingRequests;
                        if (outstandingRequests == 0)
                        {
                            idleTask = new TaskCompletionSource <bool>();
                            // capture member variable in local scope to give to closure
                            TaskCompletionSource <bool> currentIdleTask = idleTask;
                            Task abandon = Task.Delay(endpoint.IdleTimeout).ContinueWith((t) => ExitOnIdle(currentIdleTask));
                        }
                    }

                    if (!endpoint.IsOverlapped)
                    {
                        endpoint.ReportConnectionReady(this);
                    }
                }
                catch (Exception e)
                {
                    endpoint.Logger.Log("Passing exception status " + response.StatusCode);
                    StreamFaulted(e);
                    return;
                }
            }
        }
Example #4
0
 protected override Task <bool> MaybeContinue(Stream request, NotHttp.NHError error)
 {
     return(Task.FromResult <bool>(true));
 }
Example #5
0
        protected override bool ParsePreamble(Uri serverAddress, StreamReader reader, NotHttp.NHError error)
        {
            string header = reader.ReadLine();

            string[] parts = header.Split();

            if (parts.Length < 3)
            {
                throw new ApplicationException("Bad header string " + header);
            }

            int statusPart = 1;

            if (parts[0] == "NOTHTTP/1.1")
            {
                if (sequenceNumber != -1)
                {
                    throw new ApplicationException("Got overlapped response on sequential connection");
                }
                sequenceNumber = int.Parse(parts[1]);
                if (sequenceNumber < 0 || parts.Length < 4)
                {
                    throw new ApplicationException("Bad header string " + header);
                }
                statusPart = 2;
            }
            else if (parts[0] == "HTTP/1.1")
            {
                if (sequenceNumber == -1)
                {
                    throw new ApplicationException("Got sequential response on overlapped connection");
                }
            }
            else
            {
                throw new ApplicationException("Bad header string " + header);
            }

            status = (HttpStatusCode)int.Parse(parts[statusPart]);
            StringBuilder sb = new StringBuilder(parts[statusPart + 1]);

            for (int i = statusPart + 2; i < parts.Length; ++i)
            {
                sb.Append(" ");
                sb.Append(parts[i]);
            }
            statusDescription = sb.ToString();

            return(true);
        }
Example #6
0
        protected override bool ParsePreamble(Uri serverAddress, StreamReader reader, NotHttp.NHError error)
        {
            string request = reader.ReadLine();

            string[] parts = request.Split();

            if (parts[2] == "NOTHTTP/1.1")
            {
                context.SequenceNumber = int.Parse(parts[3]);
            }
            else if (parts[2] != "HTTP/1.1")
            {
                throw new ApplicationException("Unknown request string " + request);
            }

            string prefix = serverAddress.AbsolutePath;

            if (parts[1].StartsWith(prefix))
            {
                UriBuilder builder  = new UriBuilder(serverAddress);
                int        queryPos = parts[1].IndexOf('?');
                if (queryPos == -1)
                {
                    builder.Path = parts[1];
                    queryString  = new NameValueCollection();
                }
                else
                {
                    builder.Path = parts[1].Substring(0, queryPos);
                    queryString  = System.Web.HttpUtility.ParseQueryString(parts[1].Substring(queryPos + 1));
                }

                url = builder.Uri;
            }
            else
            {
                error.status  = HttpStatusCode.NotFound;
                error.message = "Bad request URI " + parts[1];
            }

            method = parts[0];

            return(method == "POST" || method == "PUT");
        }