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 body_encoder::BodyEncoder;
pub use client::connect;
use futures_lite::io::Cursor;
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,
}
};
}