public static MessageBody For( HttpVersion httpVersion, HttpRequestHeaders headers, Http1Connection context) { // see also http://tools.ietf.org/html/rfc2616#section-4.4 var keepAlive = httpVersion != HttpVersion.Http10; var upgrade = false; if (headers.HasConnection) { var connectionOptions = HttpHeaders.ParseConnection(headers.HeaderConnection); upgrade = (connectionOptions & ConnectionOptions.Upgrade) != 0; keepAlive = keepAlive || (connectionOptions & ConnectionOptions.KeepAlive) != 0; keepAlive = keepAlive && (connectionOptions & ConnectionOptions.Close) == 0; } // Ignore upgrades if the request has a body. Technically it's possible to support, but we'd have to add a lot // more logic to allow reading/draining the normal body before the connection could be fully upgraded. // See https://tools.ietf.org/html/rfc7230#section-6.7, https://tools.ietf.org/html/rfc7540#section-3.2 if (upgrade && headers.ContentLength.GetValueOrDefault() == 0 && headers.HeaderTransferEncoding.Count == 0) { context.OnTrailersComplete(); // No trailers for these. return(new Http1UpgradeMessageBody(context, keepAlive)); } if (headers.HasTransferEncoding) { var transferEncoding = headers.HeaderTransferEncoding; var transferCoding = HttpHeaders.GetFinalTransferCoding(transferEncoding); // https://tools.ietf.org/html/rfc7230#section-3.3.3 // If a Transfer-Encoding header field // is present in a request and the chunked transfer coding is not // the final encoding, the message body length cannot be determined // reliably; the server MUST respond with the 400 (Bad Request) // status code and then close the connection. if (transferCoding != TransferCoding.Chunked) { KestrelBadHttpRequestException.Throw(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding); } // TODO may push more into the wrapper rather than just calling into the message body // NBD for now. return(new Http1ChunkedEncodingMessageBody(context, keepAlive)); } if (headers.ContentLength.HasValue) { var contentLength = headers.ContentLength.Value; if (contentLength == 0) { return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose); } return(new Http1ContentLengthMessageBody(context, contentLength, keepAlive)); } // If we got here, request contains no Content-Length or Transfer-Encoding header. // Reject with 411 Length Required. if (context.Method == HttpMethod.Post || context.Method == HttpMethod.Put) { var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10; KestrelBadHttpRequestException.Throw(requestRejectionReason, context.Method); } context.OnTrailersComplete(); // No trailers for these. return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose); }
public static MessageBody For( HttpVersion httpVersion, HttpRequestHeaders headers, Http1Connection context) { // see also http://tools.ietf.org/html/rfc2616#section-4.4 var keepAlive = httpVersion != HttpVersion.Http10; var connection = headers.HeaderConnection; var upgrade = false; if (connection.Count > 0) { var connectionOptions = HttpHeaders.ParseConnection(connection); upgrade = (connectionOptions & ConnectionOptions.Upgrade) == ConnectionOptions.Upgrade; keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive; } if (upgrade) { if (headers.HeaderTransferEncoding.Count > 0 || (headers.ContentLength.HasValue && headers.ContentLength.Value != 0)) { context.ThrowRequestRejected(RequestRejectionReason.UpgradeRequestCannotHavePayload); } return(new ForUpgrade(context)); } var transferEncoding = headers.HeaderTransferEncoding; if (transferEncoding.Count > 0) { var transferCoding = HttpHeaders.GetFinalTransferCoding(headers.HeaderTransferEncoding); // https://tools.ietf.org/html/rfc7230#section-3.3.3 // If a Transfer-Encoding header field // is present in a request and the chunked transfer coding is not // the final encoding, the message body length cannot be determined // reliably; the server MUST respond with the 400 (Bad Request) // status code and then close the connection. if (transferCoding != TransferCoding.Chunked) { context.ThrowRequestRejected(RequestRejectionReason.FinalTransferCodingNotChunked, transferEncoding.ToString()); } return(new ForChunkedEncoding(keepAlive, context)); } if (headers.ContentLength.HasValue) { var contentLength = headers.ContentLength.Value; if (contentLength == 0) { return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose); } return(new ForContentLength(keepAlive, contentLength, context)); } // Avoid slowing down most common case if (!object.ReferenceEquals(context.Method, HttpMethods.Get)) { // If we got here, request contains no Content-Length or Transfer-Encoding header. // Reject with 411 Length Required. if (HttpMethods.IsPost(context.Method) || HttpMethods.IsPut(context.Method)) { var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10; context.ThrowRequestRejected(requestRejectionReason, context.Method); } } return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose); }
public static MessageBody For( HttpVersion httpVersion, HttpRequestHeaders headers, Http1Connection context) { // see also http://tools.ietf.org/html/rfc2616#section-4.4 var keepAlive = httpVersion != HttpVersion.Http10; var upgrade = false; if (headers.HasConnection) { var connectionOptions = HttpHeaders.ParseConnection(headers.HeaderConnection); upgrade = (connectionOptions & ConnectionOptions.Upgrade) == ConnectionOptions.Upgrade; keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) == ConnectionOptions.KeepAlive; } if (upgrade) { if (headers.HeaderTransferEncoding.Count > 0 || (headers.ContentLength.HasValue && headers.ContentLength.Value != 0)) { BadHttpRequestException.Throw(RequestRejectionReason.UpgradeRequestCannotHavePayload); } return(new Http1UpgradeMessageBody(context)); } if (headers.HasTransferEncoding) { var transferEncoding = headers.HeaderTransferEncoding; var transferCoding = HttpHeaders.GetFinalTransferCoding(transferEncoding); // https://tools.ietf.org/html/rfc7230#section-3.3.3 // If a Transfer-Encoding header field // is present in a request and the chunked transfer coding is not // the final encoding, the message body length cannot be determined // reliably; the server MUST respond with the 400 (Bad Request) // status code and then close the connection. if (transferCoding != TransferCoding.Chunked) { BadHttpRequestException.Throw(RequestRejectionReason.FinalTransferCodingNotChunked, in transferEncoding); } // TODO may push more into the wrapper rather than just calling into the message body // NBD for now. return(new Http1ChunkedEncodingMessageBody(keepAlive, context)); } if (headers.ContentLength.HasValue) { var contentLength = headers.ContentLength.Value; if (contentLength == 0) { return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose); } return(new Http1ContentLengthMessageBody(keepAlive, contentLength, context)); } // If we got here, request contains no Content-Length or Transfer-Encoding header. // Reject with 411 Length Required. if (context.Method == HttpMethod.Post || context.Method == HttpMethod.Put) { var requestRejectionReason = httpVersion == HttpVersion.Http11 ? RequestRejectionReason.LengthRequired : RequestRejectionReason.LengthRequiredHttp10; BadHttpRequestException.Throw(requestRejectionReason, context.Method); } return(keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose); }