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); } }
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); } }
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; } } }
protected override Task <bool> MaybeContinue(Stream request, NotHttp.NHError error) { return(Task.FromResult <bool>(true)); }
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); }
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"); }