1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-02 21:39:26 +00:00
actix-web/actix-http/src/h1/encoder.rs

672 lines
21 KiB
Rust
Raw Normal View History

use std::{
cmp,
io::{self, Write as _},
marker::PhantomData,
ptr::copy_nonoverlapping,
slice::from_raw_parts_mut,
};
2017-11-09 00:44:23 +00:00
use bytes::{BufMut, BytesMut};
2017-11-07 00:23:58 +00:00
2021-06-17 16:57:58 +00:00
use crate::{
body::BodySize,
header::{
map::Value, HeaderMap, HeaderName, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
},
helpers, ConnectionType, RequestHeadType, Response, ServiceConfig, StatusCode, Version,
2021-06-17 16:57:58 +00:00
};
2018-10-04 23:22:00 +00:00
2018-11-19 22:57:12 +00:00
const AVERAGE_HEADER_SIZE: usize = 30;
2018-06-25 04:58:04 +00:00
#[derive(Debug)]
2018-11-19 22:57:12 +00:00
pub(crate) struct MessageEncoder<T: MessageType> {
2021-10-19 16:30:32 +00:00
#[allow(dead_code)]
2019-03-27 16:24:55 +00:00
pub length: BodySize,
2018-10-05 06:39:11 +00:00
pub te: TransferEncoding,
2021-01-04 00:49:02 +00:00
_phantom: PhantomData<T>,
2018-06-25 04:58:04 +00:00
}
2018-11-19 22:57:12 +00:00
impl<T: MessageType> Default for MessageEncoder<T> {
2018-10-05 06:39:11 +00:00
fn default() -> Self {
2018-11-19 22:57:12 +00:00
MessageEncoder {
2019-03-27 16:24:55 +00:00
length: BodySize::None,
2018-10-05 06:39:11 +00:00
te: TransferEncoding::empty(),
2021-01-04 00:49:02 +00:00
_phantom: PhantomData,
2018-06-25 04:58:04 +00:00
}
}
}
2018-11-19 22:57:12 +00:00
pub(crate) trait MessageType: Sized {
fn status(&self) -> Option<StatusCode>;
2018-10-08 22:24:51 +00:00
2018-11-19 22:57:12 +00:00
fn headers(&self) -> &HeaderMap;
fn extra_headers(&self) -> Option<&HeaderMap>;
fn camel_case(&self) -> bool {
false
}
2019-02-19 04:24:50 +00:00
fn chunked(&self) -> bool;
2018-11-19 22:57:12 +00:00
fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()>;
2018-10-08 22:24:51 +00:00
2018-11-19 22:57:12 +00:00
fn encode_headers(
2018-11-18 04:21:28 +00:00
&mut self,
2018-11-19 22:57:12 +00:00
dst: &mut BytesMut,
2018-11-18 04:21:28 +00:00
version: Version,
2019-03-27 16:24:55 +00:00
mut length: BodySize,
conn_type: ConnectionType,
2018-11-19 22:57:12 +00:00
config: &ServiceConfig,
) -> io::Result<()> {
let chunked = self.chunked();
2019-03-27 16:24:55 +00:00
let mut skip_len = length != BodySize::Stream;
let camel_case = self.camel_case();
2018-11-19 22:57:12 +00:00
// Content length
if let Some(status) = self.status() {
match status {
StatusCode::CONTINUE
| StatusCode::SWITCHING_PROTOCOLS
| StatusCode::PROCESSING
| StatusCode::NO_CONTENT => {
// skip content-length and transfer-encoding headers
// see https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
// and https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
2018-11-19 22:57:12 +00:00
skip_len = true;
length = BodySize::None
2018-11-19 22:57:12 +00:00
}
StatusCode::NOT_MODIFIED => {
// 304 responses should never have a body but should retain a manually set
// content-length header
// see https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
skip_len = false;
length = BodySize::None;
}
_ => {}
2018-11-19 22:57:12 +00:00
}
2018-01-21 00:12:38 +00:00
}
2018-11-19 22:57:12 +00:00
match length {
2019-03-27 16:24:55 +00:00
BodySize::Stream => {
if chunked {
2021-08-12 19:18:09 +00:00
skip_len = true;
if camel_case {
dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n")
} else {
dst.put_slice(b"\r\ntransfer-encoding: chunked\r\n")
}
} else {
skip_len = false;
2019-04-07 17:03:38 +00:00
dst.put_slice(b"\r\n");
2019-02-19 04:24:50 +00:00
}
2018-11-19 22:57:12 +00:00
}
2021-12-08 06:01:11 +00:00
BodySize::Sized(0) if camel_case => dst.put_slice(b"\r\nContent-Length: 0\r\n"),
BodySize::Sized(0) => dst.put_slice(b"\r\ncontent-length: 0\r\n"),
BodySize::Sized(len) => helpers::write_content_length(len, dst, camel_case),
2019-04-07 17:03:38 +00:00
BodySize::None => dst.put_slice(b"\r\n"),
2018-11-19 22:57:12 +00:00
}
// Connection
match conn_type {
2019-04-07 17:03:38 +00:00
ConnectionType::Upgrade => dst.put_slice(b"connection: upgrade\r\n"),
2018-11-19 22:57:12 +00:00
ConnectionType::KeepAlive if version < Version::HTTP_11 => {
if camel_case {
dst.put_slice(b"Connection: keep-alive\r\n")
} else {
dst.put_slice(b"connection: keep-alive\r\n")
}
2018-11-19 22:57:12 +00:00
}
ConnectionType::Close if version >= Version::HTTP_11 => {
if camel_case {
dst.put_slice(b"Connection: close\r\n")
} else {
dst.put_slice(b"connection: close\r\n")
}
2018-11-19 22:57:12 +00:00
}
2021-01-04 01:01:35 +00:00
_ => {}
2018-11-19 22:57:12 +00:00
}
// write headers
2018-11-19 22:57:12 +00:00
let mut has_date = false;
2021-01-12 14:38:53 +00:00
let mut buf = dst.chunk_mut().as_mut_ptr();
let mut remaining = dst.capacity() - dst.len();
// tracks bytes written since last buffer resize
// since buf is a raw pointer to a bytes container storage but is written to without the
// container's knowledge, this is used to sync the containers cursor after data is written
let mut pos = 0;
2021-01-12 14:38:53 +00:00
self.write_headers(|key, value| {
2019-01-29 18:14:00 +00:00
match *key {
2021-01-12 14:38:53 +00:00
CONNECTION => return,
TRANSFER_ENCODING | CONTENT_LENGTH if skip_len => return,
DATE => has_date = true,
2021-01-04 01:01:35 +00:00
_ => {}
2018-11-19 22:57:12 +00:00
}
2018-11-19 22:57:12 +00:00
let k = key.as_str().as_bytes();
let k_len = k.len();
2021-02-09 22:59:17 +00:00
for val in value.iter() {
let v = val.as_ref();
let v_len = v.len();
2021-02-09 22:59:17 +00:00
// key length + value length + colon + space + \r\n
let len = k_len + v_len + 4;
2021-02-09 22:59:17 +00:00
if len > remaining {
// SAFETY: all the bytes written up to position "pos" are initialized
// the written byte count and pointer advancement are kept in sync
unsafe {
dst.advance_mut(pos);
}
2021-02-09 22:59:17 +00:00
pos = 0;
dst.reserve(len * 2);
remaining = dst.capacity() - dst.len();
2021-02-09 22:59:17 +00:00
// re-assign buf raw pointer since it's possible that the buffer was
// reallocated and/or resized
buf = dst.chunk_mut().as_mut_ptr();
}
2021-02-09 22:59:17 +00:00
// SAFETY: on each write, it is enough to ensure that the advancement of
// the cursor matches the number of bytes written
unsafe {
if camel_case {
// use Camel-Case headers
write_camel_case(k, buf, k_len);
2021-02-09 22:59:17 +00:00
} else {
write_data(k, buf, k_len);
}
2021-02-09 22:59:17 +00:00
buf = buf.add(k_len);
2021-02-09 22:59:17 +00:00
write_data(b": ", buf, 2);
buf = buf.add(2);
write_data(v, buf, v_len);
buf = buf.add(v_len);
write_data(b"\r\n", buf, 2);
buf = buf.add(2);
};
pos += len;
remaining -= len;
2018-11-19 22:57:12 +00:00
}
2021-01-12 14:38:53 +00:00
});
// final cursor synchronization with the bytes container
//
// SAFETY: all the bytes written up to position "pos" are initialized
// the written byte count and pointer advancement are kept in sync
2018-11-19 22:57:12 +00:00
unsafe {
dst.advance_mut(pos);
}
if !has_date {
2022-02-04 20:37:33 +00:00
// optimized date header, write_date_header writes its own \r\n
2022-01-31 17:30:34 +00:00
config.write_date_header(dst, camel_case);
2018-11-19 22:57:12 +00:00
}
2022-02-04 20:37:33 +00:00
// end-of-headers marker
dst.extend_from_slice(b"\r\n");
2018-11-19 22:57:12 +00:00
Ok(())
2017-11-09 00:44:23 +00:00
}
2021-01-12 14:38:53 +00:00
fn write_headers<F>(&mut self, mut f: F)
where
F: FnMut(&HeaderName, &Value),
{
match self.extra_headers() {
Some(headers) => {
// merging headers from head and extra headers.
self.headers()
.inner
.iter()
.filter(|(name, _)| !headers.contains_key(*name))
.chain(headers.inner.iter())
.for_each(|(k, v)| f(k, v))
}
None => self.headers().inner.iter().for_each(|(k, v)| f(k, v)),
}
}
2017-11-09 00:44:23 +00:00
}
2018-11-19 22:57:12 +00:00
impl MessageType for Response<()> {
fn status(&self) -> Option<StatusCode> {
Some(self.head().status)
}
2019-02-19 04:24:50 +00:00
fn chunked(&self) -> bool {
2019-03-27 17:38:01 +00:00
self.head().chunked()
2019-02-19 04:24:50 +00:00
}
2018-11-19 22:57:12 +00:00
fn headers(&self) -> &HeaderMap {
&self.head().headers
}
fn extra_headers(&self) -> Option<&HeaderMap> {
None
}
fn camel_case(&self) -> bool {
self.head()
.flags
.contains(crate::message::Flags::CAMEL_CASE)
}
2018-11-19 22:57:12 +00:00
fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()> {
let head = self.head();
let reason = head.reason().as_bytes();
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE + reason.len());
// status line
helpers::write_status_line(head.version, head.status.as_u16(), dst);
2019-04-07 17:03:38 +00:00
dst.put_slice(reason);
2018-11-19 22:57:12 +00:00
Ok(())
}
}
impl MessageType for RequestHeadType {
2018-11-19 22:57:12 +00:00
fn status(&self) -> Option<StatusCode> {
None
}
2019-02-19 04:24:50 +00:00
fn chunked(&self) -> bool {
self.as_ref().chunked()
2019-02-19 04:24:50 +00:00
}
fn camel_case(&self) -> bool {
self.as_ref().camel_case_headers()
}
2018-11-19 22:57:12 +00:00
fn headers(&self) -> &HeaderMap {
self.as_ref().headers()
}
fn extra_headers(&self) -> Option<&HeaderMap> {
self.extra_headers()
2018-11-19 22:57:12 +00:00
}
fn encode_status(&mut self, dst: &mut BytesMut) -> io::Result<()> {
let head = self.as_ref();
dst.reserve(256 + head.headers.len() * AVERAGE_HEADER_SIZE);
2018-11-19 22:57:12 +00:00
write!(
2021-06-17 16:57:58 +00:00
helpers::MutWriter(dst),
2018-11-19 22:57:12 +00:00
"{} {} {}",
head.method,
head.uri.path_and_query().map(|u| u.as_str()).unwrap_or("/"),
match head.version {
2018-11-19 22:57:12 +00:00
Version::HTTP_09 => "HTTP/0.9",
Version::HTTP_10 => "HTTP/1.0",
Version::HTTP_11 => "HTTP/1.1",
Version::HTTP_2 => "HTTP/2.0",
2019-12-05 17:35:43 +00:00
Version::HTTP_3 => "HTTP/3.0",
2021-12-08 06:01:11 +00:00
_ => return Err(io::Error::new(io::ErrorKind::Other, "unsupported version")),
2018-11-19 22:57:12 +00:00
}
2018-12-06 22:32:52 +00:00
)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}
}
2018-11-19 22:57:12 +00:00
impl<T: MessageType> MessageEncoder<T> {
2022-01-31 17:30:34 +00:00
/// Encode chunk.
2018-11-19 22:57:12 +00:00
pub fn encode_chunk(&mut self, msg: &[u8], buf: &mut BytesMut) -> io::Result<bool> {
self.te.encode(msg, buf)
}
2022-01-31 17:30:34 +00:00
/// Encode EOF.
pub fn encode_eof(&mut self, buf: &mut BytesMut) -> io::Result<()> {
self.te.encode_eof(buf)
}
2022-01-31 17:30:34 +00:00
/// Encode message.
2018-11-19 22:57:12 +00:00
pub fn encode(
&mut self,
dst: &mut BytesMut,
message: &mut T,
head: bool,
2019-03-18 04:57:53 +00:00
stream: bool,
2018-11-19 22:57:12 +00:00
version: Version,
2019-03-27 16:24:55 +00:00
length: BodySize,
conn_type: ConnectionType,
2018-11-19 22:57:12 +00:00
config: &ServiceConfig,
) -> io::Result<()> {
// transfer encoding
if !head {
self.te = match length {
BodySize::Sized(0) => TransferEncoding::empty(),
BodySize::Sized(len) => TransferEncoding::length(len),
2019-03-27 16:24:55 +00:00
BodySize::Stream => {
2019-03-18 04:57:53 +00:00
if message.chunked() && !stream {
2019-02-19 04:24:50 +00:00
TransferEncoding::chunked()
} else {
TransferEncoding::eof()
}
}
2019-03-27 16:24:55 +00:00
BodySize::None => TransferEncoding::empty(),
2018-11-19 22:57:12 +00:00
};
} else {
self.te = TransferEncoding::empty();
}
message.encode_status(dst)?;
message.encode_headers(dst, version, length, conn_type, config)
}
}
2017-11-09 00:44:23 +00:00
/// Encoders to handle different Transfer-Encodings.
2018-06-24 02:54:01 +00:00
#[derive(Debug)]
2017-11-09 00:44:23 +00:00
pub(crate) struct TransferEncoding {
kind: TransferEncodingKind,
}
#[derive(Debug, PartialEq, Clone)]
enum TransferEncodingKind {
/// An Encoder for when Transfer-Encoding includes `chunked`.
Chunked(bool),
2017-11-09 00:44:23 +00:00
/// An Encoder for when Content-Length is set.
///
/// Enforces that the body is not longer than the Content-Length header.
Length(u64),
2017-11-09 00:44:23 +00:00
/// An Encoder for when Content-Length is not known.
///
2018-01-15 21:47:25 +00:00
/// Application decides when to stop writing.
2017-11-09 00:44:23 +00:00
Eof,
}
impl TransferEncoding {
2017-12-13 05:32:58 +00:00
#[inline]
2018-06-24 02:54:01 +00:00
pub fn empty() -> TransferEncoding {
2017-11-09 00:44:23 +00:00
TransferEncoding {
2018-11-19 22:57:12 +00:00
kind: TransferEncodingKind::Length(0),
2017-11-09 00:44:23 +00:00
}
}
2017-12-13 05:32:58 +00:00
#[inline]
2018-10-05 06:39:11 +00:00
pub fn eof() -> TransferEncoding {
2017-11-09 00:44:23 +00:00
TransferEncoding {
2018-06-24 02:54:01 +00:00
kind: TransferEncodingKind::Eof,
2017-11-09 00:44:23 +00:00
}
}
2017-12-13 05:32:58 +00:00
#[inline]
2018-10-05 06:39:11 +00:00
pub fn chunked() -> TransferEncoding {
2017-11-09 00:44:23 +00:00
TransferEncoding {
2018-06-24 02:54:01 +00:00
kind: TransferEncodingKind::Chunked(false),
2017-11-09 00:44:23 +00:00
}
}
2017-12-13 05:32:58 +00:00
#[inline]
2018-10-05 06:39:11 +00:00
pub fn length(len: u64) -> TransferEncoding {
2018-06-24 02:54:01 +00:00
TransferEncoding {
kind: TransferEncodingKind::Length(len),
2017-11-09 00:44:23 +00:00
}
}
/// Encode message. Return `EOF` state of encoder
2017-12-14 00:44:35 +00:00
#[inline]
2018-10-05 06:39:11 +00:00
pub fn encode(&mut self, msg: &[u8], buf: &mut BytesMut) -> io::Result<bool> {
2017-11-09 00:44:23 +00:00
match self.kind {
TransferEncodingKind::Eof => {
2018-01-15 01:00:28 +00:00
let eof = msg.is_empty();
2018-10-05 06:39:11 +00:00
buf.extend_from_slice(msg);
2018-01-15 01:00:28 +00:00
Ok(eof)
2018-04-13 23:02:01 +00:00
}
2017-11-09 00:44:23 +00:00
TransferEncodingKind::Chunked(ref mut eof) => {
if *eof {
2018-01-04 17:32:15 +00:00
return Ok(true);
2017-11-09 00:44:23 +00:00
}
if msg.is_empty() {
*eof = true;
2018-10-05 06:39:11 +00:00
buf.extend_from_slice(b"0\r\n\r\n");
2017-11-09 00:44:23 +00:00
} else {
2021-06-17 16:57:58 +00:00
writeln!(helpers::MutWriter(buf), "{:X}\r", msg.len())
2018-01-04 17:32:15 +00:00
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
2018-10-05 06:39:11 +00:00
buf.reserve(msg.len() + 2);
buf.extend_from_slice(msg);
buf.extend_from_slice(b"\r\n");
2017-11-09 00:44:23 +00:00
}
2018-01-04 17:32:15 +00:00
Ok(*eof)
2018-04-13 23:02:01 +00:00
}
2017-11-09 00:44:23 +00:00
TransferEncodingKind::Length(ref mut remaining) => {
2018-01-21 00:12:38 +00:00
if *remaining > 0 {
if msg.is_empty() {
2018-04-13 23:02:01 +00:00
return Ok(*remaining == 0);
2018-01-21 00:12:38 +00:00
}
let len = cmp::min(*remaining, msg.len() as u64);
2018-10-05 06:39:11 +00:00
buf.extend_from_slice(&msg[..len as usize]);
2017-11-09 00:44:23 +00:00
2018-01-21 00:12:38 +00:00
*remaining -= len as u64;
Ok(*remaining == 0)
} else {
Ok(true)
}
2018-04-13 23:02:01 +00:00
}
2017-11-09 00:44:23 +00:00
}
}
/// Encode eof. Return `EOF` state of encoder
2017-12-14 00:44:35 +00:00
#[inline]
2018-10-08 22:24:51 +00:00
pub fn encode_eof(&mut self, buf: &mut BytesMut) -> io::Result<()> {
2017-11-09 00:44:23 +00:00
match self.kind {
2018-10-08 22:24:51 +00:00
TransferEncodingKind::Eof => Ok(()),
TransferEncodingKind::Length(rem) => {
if rem != 0 {
Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""))
} else {
Ok(())
}
}
2017-11-09 00:44:23 +00:00
TransferEncodingKind::Chunked(ref mut eof) => {
if !*eof {
*eof = true;
2018-10-05 06:39:11 +00:00
buf.extend_from_slice(b"0\r\n\r\n");
2017-11-09 00:44:23 +00:00
}
2018-10-08 22:24:51 +00:00
Ok(())
2018-04-13 23:02:01 +00:00
}
2017-11-09 00:44:23 +00:00
}
}
}
/// # Safety
/// Callers must ensure that the given `len` matches the given `value` length and that `buf` is
/// valid for writes of at least `len` bytes.
2019-12-05 17:35:43 +00:00
unsafe fn write_data(value: &[u8], buf: *mut u8, len: usize) {
debug_assert_eq!(value.len(), len);
2019-12-05 17:35:43 +00:00
copy_nonoverlapping(value.as_ptr(), buf, len);
}
/// # Safety
/// Callers must ensure that the given `len` matches the given `value` length and that `buf` is
/// valid for writes of at least `len` bytes.
unsafe fn write_camel_case(value: &[u8], buf: *mut u8, len: usize) {
// first copy entire (potentially wrong) slice to output
write_data(value, buf, len);
// SAFETY: We just initialized the buffer with `value`
let buffer = from_raw_parts_mut(buf, len);
let mut iter = value.iter();
// first character should be uppercase
if let Some(c @ b'a'..=b'z') = iter.next() {
buffer[0] = c & 0b1101_1111;
}
// track 1 ahead of the current position since that's the location being assigned to
let mut index = 2;
// remaining characters after hyphens should also be uppercase
while let Some(&c) = iter.next() {
if c == b'-' {
// advance iter by one and uppercase if needed
if let Some(c @ b'a'..=b'z') = iter.next() {
buffer[index] = c & 0b1101_1111;
}
index += 1;
}
index += 1;
}
}
2018-01-04 17:32:15 +00:00
#[cfg(test)]
mod tests {
2019-12-13 05:24:57 +00:00
use std::rc::Rc;
2018-06-24 04:42:20 +00:00
use bytes::Bytes;
use http::header::{AUTHORIZATION, UPGRADE_INSECURE_REQUESTS};
2018-01-04 17:32:15 +00:00
use super::*;
use crate::{
header::{HeaderValue, CONTENT_TYPE},
RequestHead,
};
2018-01-04 17:32:15 +00:00
#[test]
fn test_chunked_te() {
2018-10-05 06:46:43 +00:00
let mut bytes = BytesMut::new();
let mut enc = TransferEncoding::chunked();
{
2018-10-05 06:46:43 +00:00
assert!(!enc.encode(b"test", &mut bytes).ok().unwrap());
assert!(enc.encode(b"", &mut bytes).ok().unwrap());
}
2018-04-13 23:02:01 +00:00
assert_eq!(
2019-12-05 17:35:43 +00:00
bytes.split().freeze(),
2018-04-13 23:02:01 +00:00
Bytes::from_static(b"4\r\ntest\r\n0\r\n\r\n")
);
2018-01-04 17:32:15 +00:00
}
2021-02-12 21:52:58 +00:00
#[actix_rt::test]
async fn test_camel_case() {
let mut bytes = BytesMut::with_capacity(2048);
let mut head = RequestHead::default();
head.set_camel_case_headers(true);
head.headers.insert(DATE, HeaderValue::from_static("date"));
head.headers
.insert(CONTENT_TYPE, HeaderValue::from_static("plain/text"));
head.headers
.insert(UPGRADE_INSECURE_REQUESTS, HeaderValue::from_static("1"));
let mut head = RequestHeadType::Owned(head);
let _ = head.encode_headers(
&mut bytes,
Version::HTTP_11,
BodySize::Sized(0),
ConnectionType::Close,
&ServiceConfig::default(),
);
2021-12-08 06:01:11 +00:00
let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
2019-11-06 19:20:47 +00:00
assert!(data.contains("Content-Length: 0\r\n"));
assert!(data.contains("Connection: close\r\n"));
assert!(data.contains("Content-Type: plain/text\r\n"));
assert!(data.contains("Date: date\r\n"));
assert!(data.contains("Upgrade-Insecure-Requests: 1\r\n"));
let _ = head.encode_headers(
&mut bytes,
Version::HTTP_11,
BodySize::Stream,
ConnectionType::KeepAlive,
&ServiceConfig::default(),
);
2021-12-08 06:01:11 +00:00
let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
2019-11-06 19:20:47 +00:00
assert!(data.contains("Transfer-Encoding: chunked\r\n"));
assert!(data.contains("Content-Type: plain/text\r\n"));
assert!(data.contains("Date: date\r\n"));
let mut head = RequestHead::default();
head.set_camel_case_headers(false);
head.headers.insert(DATE, HeaderValue::from_static("date"));
head.headers
.insert(CONTENT_TYPE, HeaderValue::from_static("plain/text"));
head.headers
.append(CONTENT_TYPE, HeaderValue::from_static("xml"));
let mut head = RequestHeadType::Owned(head);
let _ = head.encode_headers(
&mut bytes,
Version::HTTP_11,
BodySize::Stream,
ConnectionType::KeepAlive,
&ServiceConfig::default(),
);
2021-12-08 06:01:11 +00:00
let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
2019-11-06 19:20:47 +00:00
assert!(data.contains("transfer-encoding: chunked\r\n"));
assert!(data.contains("content-type: xml\r\n"));
assert!(data.contains("content-type: plain/text\r\n"));
assert!(data.contains("date: date\r\n"));
}
2021-02-12 21:52:58 +00:00
#[actix_rt::test]
async fn test_extra_headers() {
let mut bytes = BytesMut::with_capacity(2048);
let mut head = RequestHead::default();
2019-09-12 15:52:46 +00:00
head.headers.insert(
AUTHORIZATION,
HeaderValue::from_static("some authorization"),
);
let mut extra_headers = HeaderMap::new();
2019-09-12 15:52:46 +00:00
extra_headers.insert(
AUTHORIZATION,
HeaderValue::from_static("another authorization"),
);
extra_headers.insert(DATE, HeaderValue::from_static("date"));
let mut head = RequestHeadType::Rc(Rc::new(head), Some(extra_headers));
let _ = head.encode_headers(
&mut bytes,
Version::HTTP_11,
BodySize::Sized(0),
ConnectionType::Close,
&ServiceConfig::default(),
);
2021-12-08 06:01:11 +00:00
let data = String::from_utf8(Vec::from(bytes.split().freeze().as_ref())).unwrap();
2019-11-06 19:20:47 +00:00
assert!(data.contains("content-length: 0\r\n"));
assert!(data.contains("connection: close\r\n"));
assert!(data.contains("authorization: another authorization\r\n"));
assert!(data.contains("date: date\r\n"));
}
2021-02-12 21:52:58 +00:00
#[actix_rt::test]
async fn test_no_content_length() {
let mut bytes = BytesMut::with_capacity(2048);
let mut res = Response::with_body(StatusCode::SWITCHING_PROTOCOLS, ());
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(),
);
2021-12-08 06:01:11 +00:00
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"));
}
2018-01-04 17:32:15 +00:00
}