1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Streaming async HTTP 1.1 parser.
//!
//! At its core HTTP is a stateful RPC protocol, where a client and server
//! communicate with one another by encoding and decoding messages between them.
//!
//! - `client` encodes HTTP requests, and decodes HTTP responses.
//! - `server` decodes HTTP requests, and encodes HTTP responses.
//!
//! A client always starts the HTTP connection. The lifetime of an HTTP
//! connection looks like this:
//!
//! ```txt
//! 1. encode            2. decode
//!         \            /
//!         -> request  ->
//!  client                server
//!         <- response <-
//!         /            \
//! 4. decode            3. encode
//! ```
//!
//! See also [`async-tls`](https://docs.rs/async-tls),
//! [`async-std`](https://docs.rs/async-std).
//!
//! # Example
//!
//! __HTTP client__
//!
//! ```no_run
//! use async_std::net::TcpStream;
//! use http_types::{Method, Request, Url};
//!
//! #[async_std::main]
//! async fn main() -> http_types::Result<()> {
//!     let stream = TcpStream::connect("127.0.0.1:8080").await?;
//!
//!     let peer_addr = stream.peer_addr()?;
//!     println!("connecting to {}", peer_addr);
//!     let url = Url::parse(&format!("http://{}/foo", peer_addr))?;
//!
//!     let req = Request::new(Method::Get, url);
//!     let res = async_h1::connect(stream.clone(), req).await?;
//!     println!("{:?}", res);
//!
//!     Ok(())
//! }
//! ```
//!
//! __HTTP Server__
//!
//! ```no_run
//! use async_std::net::{TcpStream, TcpListener};
//! use async_std::prelude::*;
//! use async_std::task;
//! use http_types::{Response, StatusCode};
//!
//! #[async_std::main]
//! async fn main() -> http_types::Result<()> {
//!     // Open up a TCP connection and create a URL.
//!     let listener = TcpListener::bind(("127.0.0.1", 8080)).await?;
//!     let addr = format!("http://{}", listener.local_addr()?);
//!     println!("listening on {}", addr);
//!
//!     // For each incoming TCP connection, spawn a task and call `accept`.
//!     let mut incoming = listener.incoming();
//!     while let Some(stream) = incoming.next().await {
//!         let stream = stream?;
//!         task::spawn(async {
//!             if let Err(err) = accept(stream).await {
//!                 eprintln!("{}", err);
//!             }
//!         });
//!     }
//!     Ok(())
//! }
//!
//! // Take a TCP stream, and convert it into sequential HTTP request / response pairs.
//! async fn accept(stream: TcpStream) -> http_types::Result<()> {
//!     println!("starting new connection from {}", stream.peer_addr()?);
//!     async_h1::accept(stream.clone(), |_req| async move {
//!         let mut res = Response::new(StatusCode::Ok);
//!         res.insert_header("Content-Type", "text/plain");
//!         res.set_body("Hello");
//!         Ok(res)
//!     })
//!     .await?;
//!     Ok(())
//! }
//! ```

#![forbid(unsafe_code)]
#![deny(missing_debug_implementations, nonstandard_style, rust_2018_idioms)]
#![warn(missing_docs, missing_doc_code_examples, unreachable_pub)]
#![cfg_attr(test, deny(warnings))]
#![allow(clippy::if_same_then_else)]
#![allow(clippy::len_zero)]
#![allow(clippy::match_bool)]
#![allow(clippy::unreadable_literal)]

/// The maximum amount of headers parsed on the server.
const MAX_HEADERS: usize = 128;

/// The maximum length of the head section we'll try to parse.
/// See: https://nodejs.org/en/blog/vulnerability/november-2018-security-releases/#denial-of-service-with-large-http-headers-cve-2018-12121
const MAX_HEAD_LENGTH: usize = 8 * 1024;

mod body_encoder;
mod chunked;
mod date;
mod read_notifier;

pub mod client;
pub mod server;

use async_std::io::Cursor;
use body_encoder::BodyEncoder;
pub use client::connect;
pub use server::{accept, accept_with_opts, ServerOptions};

#[derive(Debug)]
pub(crate) enum EncoderState {
    Start,
    Head(Cursor<Vec<u8>>),
    Body(BodyEncoder),
    End,
}

/// like ready! but early-returns the Poll<Result<usize>> early in all situations other than Ready(Ok(0))
#[macro_export]
macro_rules! read_to_end {
    ($expr:expr) => {
        match $expr {
            Poll::Ready(Ok(0)) => (),
            other => return other,
        }
    };
}