diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index bb5a962f5..2f4e44142 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,14 @@ # Changes ## Unreleased - 2020-xx-xx +### Added +* HttpResponse builders for 1xx status codes. [#1768] + +### Fixed +* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767] + +[#1767]: https://github.com/actix/actix-web/pull/1767 +[#1768]: https://github.com/actix/actix-web/pull/1768 ## 2.1.0 - 2020-10-30 diff --git a/actix-http/src/h1/encoder.rs b/actix-http/src/h1/encoder.rs index e16b4c3dc..b7648eff1 100644 --- a/actix-http/src/h1/encoder.rs +++ b/actix-http/src/h1/encoder.rs @@ -64,14 +64,17 @@ pub(crate) trait MessageType: Sized { // Content length if let Some(status) = self.status() { match status { - StatusCode::NO_CONTENT - | StatusCode::CONTINUE - | StatusCode::PROCESSING => length = BodySize::None, - StatusCode::SWITCHING_PROTOCOLS => { + StatusCode::CONTINUE + | StatusCode::SWITCHING_PROTOCOLS + | StatusCode::PROCESSING + | StatusCode::NO_CONTENT => { + // skip content-length and transfer-encoding headers + // See https://tools.ietf.org/html/rfc7230#section-3.3.1 + // and https://tools.ietf.org/html/rfc7230#section-3.3.2 skip_len = true; - length = BodySize::Stream; + length = BodySize::None } - _ => (), + _ => {} } } match length { @@ -676,4 +679,28 @@ mod tests { assert!(data.contains("authorization: another authorization\r\n")); assert!(data.contains("date: date\r\n")); } + + #[test] + fn test_no_content_length() { + let mut bytes = BytesMut::with_capacity(2048); + + let mut res: Response<()> = + Response::new(StatusCode::SWITCHING_PROTOCOLS).into_body::<()>(); + res.headers_mut() + .insert(DATE, HeaderValue::from_static(&"")); + res.headers_mut() + .insert(CONTENT_LENGTH, HeaderValue::from_static(&"0")); + + let _ = res.encode_headers( + &mut bytes, + Version::HTTP_11, + BodySize::Stream, + ConnectionType::Upgrade, + &ServiceConfig::default(), + ); + let data = + String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap(); + assert!(!data.contains("content-length: 0\r\n")); + assert!(!data.contains("transfer-encoding: chunked\r\n")); + } } diff --git a/actix-http/src/httpcodes.rs b/actix-http/src/httpcodes.rs index 0c7f23fc8..f421d8e23 100644 --- a/actix-http/src/httpcodes.rs +++ b/actix-http/src/httpcodes.rs @@ -1,10 +1,12 @@ -//! Basic http responses +//! Status code based HTTP response builders. + #![allow(non_upper_case_globals)] + use http::StatusCode; use crate::response::{Response, ResponseBuilder}; -macro_rules! STATIC_RESP { +macro_rules! static_resp { ($name:ident, $status:expr) => { #[allow(non_snake_case, missing_docs)] pub fn $name() -> ResponseBuilder { @@ -14,63 +16,67 @@ macro_rules! STATIC_RESP { } impl Response { - STATIC_RESP!(Ok, StatusCode::OK); - STATIC_RESP!(Created, StatusCode::CREATED); - STATIC_RESP!(Accepted, StatusCode::ACCEPTED); - STATIC_RESP!( + static_resp!(Continue, StatusCode::CONTINUE); + static_resp!(SwitchingProtocols, StatusCode::SWITCHING_PROTOCOLS); + static_resp!(Processing, StatusCode::PROCESSING); + + static_resp!(Ok, StatusCode::OK); + static_resp!(Created, StatusCode::CREATED); + static_resp!(Accepted, StatusCode::ACCEPTED); + static_resp!( NonAuthoritativeInformation, StatusCode::NON_AUTHORITATIVE_INFORMATION ); - STATIC_RESP!(NoContent, StatusCode::NO_CONTENT); - STATIC_RESP!(ResetContent, StatusCode::RESET_CONTENT); - STATIC_RESP!(PartialContent, StatusCode::PARTIAL_CONTENT); - STATIC_RESP!(MultiStatus, StatusCode::MULTI_STATUS); - STATIC_RESP!(AlreadyReported, StatusCode::ALREADY_REPORTED); + static_resp!(NoContent, StatusCode::NO_CONTENT); + static_resp!(ResetContent, StatusCode::RESET_CONTENT); + static_resp!(PartialContent, StatusCode::PARTIAL_CONTENT); + static_resp!(MultiStatus, StatusCode::MULTI_STATUS); + static_resp!(AlreadyReported, StatusCode::ALREADY_REPORTED); - STATIC_RESP!(MultipleChoices, StatusCode::MULTIPLE_CHOICES); - STATIC_RESP!(MovedPermanently, StatusCode::MOVED_PERMANENTLY); - STATIC_RESP!(Found, StatusCode::FOUND); - STATIC_RESP!(SeeOther, StatusCode::SEE_OTHER); - STATIC_RESP!(NotModified, StatusCode::NOT_MODIFIED); - STATIC_RESP!(UseProxy, StatusCode::USE_PROXY); - STATIC_RESP!(TemporaryRedirect, StatusCode::TEMPORARY_REDIRECT); - STATIC_RESP!(PermanentRedirect, StatusCode::PERMANENT_REDIRECT); + static_resp!(MultipleChoices, StatusCode::MULTIPLE_CHOICES); + static_resp!(MovedPermanently, StatusCode::MOVED_PERMANENTLY); + static_resp!(Found, StatusCode::FOUND); + static_resp!(SeeOther, StatusCode::SEE_OTHER); + static_resp!(NotModified, StatusCode::NOT_MODIFIED); + static_resp!(UseProxy, StatusCode::USE_PROXY); + static_resp!(TemporaryRedirect, StatusCode::TEMPORARY_REDIRECT); + static_resp!(PermanentRedirect, StatusCode::PERMANENT_REDIRECT); - STATIC_RESP!(BadRequest, StatusCode::BAD_REQUEST); - STATIC_RESP!(NotFound, StatusCode::NOT_FOUND); - STATIC_RESP!(Unauthorized, StatusCode::UNAUTHORIZED); - STATIC_RESP!(PaymentRequired, StatusCode::PAYMENT_REQUIRED); - STATIC_RESP!(Forbidden, StatusCode::FORBIDDEN); - STATIC_RESP!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED); - STATIC_RESP!(NotAcceptable, StatusCode::NOT_ACCEPTABLE); - STATIC_RESP!( + static_resp!(BadRequest, StatusCode::BAD_REQUEST); + static_resp!(NotFound, StatusCode::NOT_FOUND); + static_resp!(Unauthorized, StatusCode::UNAUTHORIZED); + static_resp!(PaymentRequired, StatusCode::PAYMENT_REQUIRED); + static_resp!(Forbidden, StatusCode::FORBIDDEN); + static_resp!(MethodNotAllowed, StatusCode::METHOD_NOT_ALLOWED); + static_resp!(NotAcceptable, StatusCode::NOT_ACCEPTABLE); + static_resp!( ProxyAuthenticationRequired, StatusCode::PROXY_AUTHENTICATION_REQUIRED ); - STATIC_RESP!(RequestTimeout, StatusCode::REQUEST_TIMEOUT); - STATIC_RESP!(Conflict, StatusCode::CONFLICT); - STATIC_RESP!(Gone, StatusCode::GONE); - STATIC_RESP!(LengthRequired, StatusCode::LENGTH_REQUIRED); - STATIC_RESP!(PreconditionFailed, StatusCode::PRECONDITION_FAILED); - STATIC_RESP!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED); - STATIC_RESP!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE); - STATIC_RESP!(UriTooLong, StatusCode::URI_TOO_LONG); - STATIC_RESP!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE); - STATIC_RESP!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE); - STATIC_RESP!(ExpectationFailed, StatusCode::EXPECTATION_FAILED); - STATIC_RESP!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY); - STATIC_RESP!(TooManyRequests, StatusCode::TOO_MANY_REQUESTS); + static_resp!(RequestTimeout, StatusCode::REQUEST_TIMEOUT); + static_resp!(Conflict, StatusCode::CONFLICT); + static_resp!(Gone, StatusCode::GONE); + static_resp!(LengthRequired, StatusCode::LENGTH_REQUIRED); + static_resp!(PreconditionFailed, StatusCode::PRECONDITION_FAILED); + static_resp!(PreconditionRequired, StatusCode::PRECONDITION_REQUIRED); + static_resp!(PayloadTooLarge, StatusCode::PAYLOAD_TOO_LARGE); + static_resp!(UriTooLong, StatusCode::URI_TOO_LONG); + static_resp!(UnsupportedMediaType, StatusCode::UNSUPPORTED_MEDIA_TYPE); + static_resp!(RangeNotSatisfiable, StatusCode::RANGE_NOT_SATISFIABLE); + static_resp!(ExpectationFailed, StatusCode::EXPECTATION_FAILED); + static_resp!(UnprocessableEntity, StatusCode::UNPROCESSABLE_ENTITY); + static_resp!(TooManyRequests, StatusCode::TOO_MANY_REQUESTS); - STATIC_RESP!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR); - STATIC_RESP!(NotImplemented, StatusCode::NOT_IMPLEMENTED); - STATIC_RESP!(BadGateway, StatusCode::BAD_GATEWAY); - STATIC_RESP!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE); - STATIC_RESP!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT); - STATIC_RESP!(VersionNotSupported, StatusCode::HTTP_VERSION_NOT_SUPPORTED); - STATIC_RESP!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES); - STATIC_RESP!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE); - STATIC_RESP!(LoopDetected, StatusCode::LOOP_DETECTED); + static_resp!(InternalServerError, StatusCode::INTERNAL_SERVER_ERROR); + static_resp!(NotImplemented, StatusCode::NOT_IMPLEMENTED); + static_resp!(BadGateway, StatusCode::BAD_GATEWAY); + static_resp!(ServiceUnavailable, StatusCode::SERVICE_UNAVAILABLE); + static_resp!(GatewayTimeout, StatusCode::GATEWAY_TIMEOUT); + static_resp!(VersionNotSupported, StatusCode::HTTP_VERSION_NOT_SUPPORTED); + static_resp!(VariantAlsoNegotiates, StatusCode::VARIANT_ALSO_NEGOTIATES); + static_resp!(InsufficientStorage, StatusCode::INSUFFICIENT_STORAGE); + static_resp!(LoopDetected, StatusCode::LOOP_DETECTED); } #[cfg(test)] diff --git a/actix-web-actors/src/ws.rs b/actix-web-actors/src/ws.rs index 3f5972532..8fd03f6a1 100644 --- a/actix-web-actors/src/ws.rs +++ b/actix-web-actors/src/ws.rs @@ -164,7 +164,6 @@ pub fn handshake_with_protocols( let mut response = HttpResponse::build(StatusCode::SWITCHING_PROTOCOLS) .upgrade("websocket") - .header(header::TRANSFER_ENCODING, "chunked") .header(header::SEC_WEBSOCKET_ACCEPT, key.as_str()) .take(); @@ -664,10 +663,10 @@ mod tests { ) .to_http_request(); - assert_eq!( - StatusCode::SWITCHING_PROTOCOLS, - handshake(&req).unwrap().finish().status() - ); + let resp = handshake(&req).unwrap().finish(); + assert_eq!(StatusCode::SWITCHING_PROTOCOLS, resp.status()); + assert_eq!(None, resp.headers().get(&header::CONTENT_LENGTH)); + assert_eq!(None, resp.headers().get(&header::TRANSFER_ENCODING)); let req = TestRequest::default() .header(