async_http_proxy/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2//! `async-http-proxy` is a lightweight asynchronous HTTP proxy client library, which can be used
3//! to connect a to a TCP port via HTTP Connect proxy. It can use [Tokio](https://tokio.rs/) and
4//! [async-std](https://async.rs/) as asynchronous runtime.  
5//! # Example
6//! The following example shows how to connect to `example.org` via Connect proxy (`tokio`):
7//! ```ignore
8//! use async_http_proxy::http_connect_tokio;
9//! use std::error::Error;
10//! use tokio::net::TcpStream;
11//! // Features "runtime-tokio" have to be activated
12//! #[tokio::main]
13//! async fn main() -> Result<(), Box<dyn Error>> {
14//!     let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
15//!     http_connect_tokio(&mut stream, "example.org", 443).await?;
16//!     // stream is now connect to github.com
17//!     Ok(())
18//! }
19//! ```
20//!
21//! The following example shows how to connect to `example.org` with Basic Authentication via
22//! Connect proxy (`async-std`):
23//! ```ignore
24//! use async_http_proxy::http_connect_async_std_with_basic_auth;
25//! use async_std::net::TcpStream;
26//! use async_std::task;
27//! use std::error::Error;
28//! // Features "async-std-tokio" and "basic-auth" have to be activated
29//! fn main() -> Result<(), Box<dyn Error>> {
30//!     task::block_on(async {
31//!         let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
32//!         http_connect_async_std_with_basic_auth(
33//!             &mut stream,
34//!             "example.org",
35//!             443,
36//!             "username",
37//!             "password",
38//!         )
39//!         .await?;
40//!         // stream is now connect to github.com
41//!         Ok(())
42//!     })
43//! }
44//! ```
45
46#[cfg(all(
47    not(feature = "runtime-tokio"),
48    not(feature = "runtime-async-std"),
49    not(doc)
50))]
51compile_error!(
52    "An async runtime have to be specified by feature: \"runtime-tokio\" \"runtime-async-std\""
53);
54
55mod request;
56mod response;
57
58#[cfg(feature = "runtime-async-std")]
59use async_std::io::{Read, Write};
60use httparse::Error as HttpParseError;
61#[cfg(feature = "runtime-async-std")]
62use response::recv_and_check_response_async_std;
63#[cfg(feature = "runtime-tokio")]
64use response::recv_and_check_response_tokio;
65use std::io::Error as IoError;
66use thiserror::Error as ThisError;
67#[cfg(feature = "runtime-tokio")]
68use tokio::io::{AsyncRead, AsyncWrite, BufStream};
69
70/// The maximum length of the response header.
71pub const MAXIMUM_RESPONSE_HEADER_LENGTH: usize = 4096;
72/// The maximum HTTP Headers, which can be parsed.
73pub const MAXIMUM_RESPONSE_HEADERS: usize = 16;
74
75/// This enum contains all errors, which can occur during the HTTP `CONNECT`.
76#[derive(Debug, ThisError)]
77pub enum HttpError {
78    #[error("IO Error: {0}")]
79    IoError(#[from] IoError),
80    #[error("HTTP parse error: {0}")]
81    HttpParseError(#[from] HttpParseError),
82    #[error("The maximum response header length is exceeded: {0}")]
83    MaximumResponseHeaderLengthExceeded(String),
84    #[error("The end of file is reached")]
85    EndOfFile,
86    #[error("No HTTP code was found in the response")]
87    NoHttpCode,
88    #[error("The HTTP code is not equal 200: {0}")]
89    HttpCode200(u16),
90    #[error("No HTTP reason was found in the response")]
91    NoHttpReason,
92    #[error("The HTTP reason is not equal 'ConnectionEstablished': {0}")]
93    HttpReasonConnectionEstablished(String),
94}
95
96/// Connect to the server defined by the host and port and check if the connection was established.
97///
98/// The functions will use HTTP CONNECT request and the tokio runtime.
99///
100/// # Example
101/// ```no_run
102/// use async_http_proxy::http_connect_tokio;
103/// use std::error::Error;
104/// use tokio::net::TcpStream;
105/// // Features "runtime-tokio" have to be activated
106/// #[tokio::main]
107/// async fn main() -> Result<(), Box<dyn Error>> {
108///     let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
109///     http_connect_tokio(&mut stream, "example.org", 443).await?;
110///     // stream is now connect to github.com
111///     Ok(())
112/// }
113/// ```
114#[cfg(feature = "runtime-tokio")]
115#[cfg_attr(docsrs, doc(cfg(feature = "runtime-tokio")))]
116pub async fn http_connect_tokio<IO>(io: &mut IO, host: &str, port: u16) -> Result<(), HttpError>
117where
118    IO: AsyncRead + AsyncWrite + Unpin,
119{
120    let mut stream = BufStream::new(io);
121
122    request::send_request_tokio(&mut stream, host, port).await?;
123
124    recv_and_check_response_tokio(&mut stream).await?;
125
126    Ok(())
127}
128
129/// Connect to the server defined by the host and port with basic auth and check if the connection \
130/// was established.
131///
132/// The functions will use HTTP CONNECT request and the tokio runtime.
133///
134/// # Example
135/// use async_http_proxy::http_connect_tokio_with_basic_auth;
136/// use std::error::Error;
137/// use tokio::net::TcpStream;
138/// // Features "runtime-tokio" and "basic-auth" have to be activated
139/// #[tokio::main]
140/// async fn main() -> Result<(), Box<dyn Error>> {
141///     let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
142///     http_connect_tokio_with_basic_auth(&mut stream, "example.org", 443, "username", "password")
143///         .await?;
144///     // stream is now connect to github.com
145///     Ok(())
146/// }
147/// ```no_run
148/// ```
149#[cfg(all(feature = "runtime-tokio", feature = "basic-auth"))]
150#[cfg_attr(
151    docsrs,
152    doc(cfg(all(feature = "runtime-tokio", feature = "basic-auth")))
153)]
154pub async fn http_connect_tokio_with_basic_auth<IO>(
155    io: &mut IO,
156    host: &str,
157    port: u16,
158    username: &str,
159    password: &str,
160) -> Result<(), HttpError>
161where
162    IO: AsyncRead + AsyncWrite + Unpin,
163{
164    let mut stream = BufStream::new(io);
165
166    request::send_request_tokio_with_basic_auth(&mut stream, host, port, username, password)
167        .await?;
168
169    recv_and_check_response_tokio(&mut stream).await?;
170
171    Ok(())
172}
173
174/// Connect to the server defined by the host and port and check if the connection was established.
175///
176/// The functions will use HTTP CONNECT request and the tokio framework.
177///
178/// # Example
179/// ```no_run
180/// use async_http_proxy::http_connect_async_std;
181/// use async_std::net::TcpStream;
182/// use async_std::task;
183/// use std::error::Error;
184/// // Features "runtime-async-std" have to be activated
185/// fn main() -> Result<(), Box<dyn Error>> {
186///     task::block_on(async {
187///         let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
188///         http_connect_async_std(&mut stream, "example.org", 443).await?;
189///         // stream is now connect to github.com
190///         Ok(())
191///     })
192/// }
193/// ```
194#[cfg(feature = "runtime-async-std")]
195#[cfg_attr(docsrs, doc(cfg(feature = "runtime-async-std")))]
196pub async fn http_connect_async_std<IO>(io: &mut IO, host: &str, port: u16) -> Result<(), HttpError>
197where
198    IO: Read + Write + Unpin,
199{
200    request::send_request_async_std(io, host, port).await?;
201
202    recv_and_check_response_async_std(io).await?;
203
204    Ok(())
205}
206
207/// Connect to the server defined by the host and port with basic auth and check if the connection \
208/// was established.
209///
210/// The functions will use HTTP CONNECT request and the async std framework.
211///
212/// # Example
213/// ```no_run
214/// use async_http_proxy::http_connect_async_std_with_basic_auth;
215/// use async_std::net::TcpStream;
216/// use async_std::task;
217/// use std::error::Error;
218/// // Features "async-std-tokio" and "basic-auth" have to be activated
219/// fn main() -> Result<(), Box<dyn Error>> {
220///     task::block_on(async {
221///         let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
222///         http_connect_async_std_with_basic_auth(
223///             &mut stream,
224///             "example.org",
225///             443,
226///             "username",
227///             "password",
228///         )
229///         .await?;
230///         // stream is now connect to github.com
231///         Ok(())
232///     })
233/// }
234/// ```
235#[cfg(all(feature = "runtime-async-std", feature = "basic-auth"))]
236#[cfg_attr(
237    docsrs,
238    doc(cfg(all(feature = "runtime-async-std", feature = "basic-auth")))
239)]
240pub async fn http_connect_async_std_with_basic_auth<IO>(
241    io: &mut IO,
242    host: &str,
243    port: u16,
244    username: &str,
245    password: &str,
246) -> Result<(), HttpError>
247where
248    IO: Read + Write + Unpin,
249{
250    request::send_request_async_std_with_basic_auth(io, host, port, username, password).await?;
251
252    recv_and_check_response_async_std(io).await?;
253
254    Ok(())
255}