Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions actix-http/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Update `HttpMessage::content_type()` to return `Option<&str>`. [#3797]

[#3797]: https://github.com/actix/actix-web/issues/3797

## 3.13.5

- Reject invalid WebSocket close-frame status codes, malformed payloads, and invalid UTF-8 close reasons.
Expand Down
31 changes: 19 additions & 12 deletions actix-http/src/http_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ pub trait HttpMessage: Sized {
}
}

/// Read the request content type. If request did not contain a *Content-Type* header, an empty
/// string is returned.
fn content_type(&self) -> &str {
if let Some(content_type) = self.headers().get(header::CONTENT_TYPE) {
if let Ok(content_type) = content_type.to_str() {
return content_type.split(';').next().unwrap().trim();
}
}
""
/// Read the request content type.
///
/// If request does not contain a *Content-Type* header, or if the header value is not valid
/// UTF-8, `None` is returned.
fn content_type(&self) -> Option<&str> {
self.headers()
.get(header::CONTENT_TYPE)
.and_then(|ct| ct.to_str().ok())
.map(|ct| ct.split_once(';').map_or(ct, |(ct, _)| ct).trim())
}

/// Get content type encoding.
Expand Down Expand Up @@ -142,13 +142,20 @@ mod tests {
let req = TestRequest::default()
.insert_header(("content-type", "text/plain"))
.finish();
assert_eq!(req.content_type(), "text/plain");
assert_eq!(req.content_type(), Some("text/plain"));
let req = TestRequest::default()
.insert_header(("content-type", "application/json; charset=utf-8"))
.finish();
assert_eq!(req.content_type(), "application/json");
assert_eq!(req.content_type(), Some("application/json"));
let req = TestRequest::default().finish();
assert_eq!(req.content_type(), "");
assert_eq!(req.content_type(), None);
let req = TestRequest::default()
.insert_header((
header::CONTENT_TYPE,
header::HeaderValue::from_bytes(b"\xff").unwrap(),
))
.finish();
assert_eq!(req.content_type(), None);
}

#[test]
Expand Down
4 changes: 4 additions & 0 deletions actix-web/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- Update `HttpMessage::content_type()` (and `HttpRequest` / `ServiceRequest` implementations) to return `Option<&str>`. [#3797]

[#3797]: https://github.com/actix/actix-web/issues/3797

## 4.15.0

- Add `Error::add_response_mapper()` to allow middleware to modify error-generated responses before they are sent.
Expand Down
7 changes: 5 additions & 2 deletions actix-web/src/test/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,10 @@ mod tests {
.set_form(&payload)
.to_request();

assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
assert_eq!(
req.content_type(),
Some("application/x-www-form-urlencoded")
);

let result: Person = call_and_read_body_json(&app, req).await;
assert_eq!(&result.id, "12345");
Expand Down Expand Up @@ -549,7 +552,7 @@ mod tests {
.set_json(&payload)
.to_request();

assert_eq!(req.content_type(), "application/json");
assert_eq!(req.content_type(), Some("application/json"));

let result: Person = call_and_read_body_json(&app, req).await;
assert_eq!(&result.id, "12345");
Expand Down
26 changes: 25 additions & 1 deletion actix-web/src/types/form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,10 @@ impl<T> UrlEncoded<T> {
/// Create a new future to decode a URL encoded request payload.
pub fn new(req: &HttpRequest, payload: &mut Payload) -> Self {
// check content type
if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" {
if !req
.content_type()
.is_some_and(|ct| ct.eq_ignore_ascii_case("application/x-www-form-urlencoded"))
{
return Self::err(UrlencodedError::ContentType);
}
let encoding = match req.encoding() {
Expand Down Expand Up @@ -484,6 +487,12 @@ mod tests {
.to_http_parts();
let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq(info.err().unwrap(), UrlencodedError::ContentType));

let (req, mut pl) = TestRequest::default()
.insert_header((CONTENT_LENGTH, 10))
.to_http_parts();
let info = UrlEncoded::<Info>::new(&req, &mut pl).await;
assert!(eq(info.err().unwrap(), UrlencodedError::ContentType));
}

#[actix_rt::test]
Expand Down Expand Up @@ -520,6 +529,21 @@ mod tests {
counter: 123
}
);

let (req, mut pl) = TestRequest::default()
.insert_header((CONTENT_TYPE, "APPLICATION/X-WWW-FORM-URLENCODED"))
.insert_header((CONTENT_LENGTH, 11))
.set_payload(Bytes::from_static(b"hello=world&counter=123"))
.to_http_parts();

let info = UrlEncoded::<Info>::new(&req, &mut pl).await.unwrap();
assert_eq!(
info,
Info {
hello: "world".to_owned(),
counter: 123
}
);
}

#[actix_rt::test]
Expand Down
Loading