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}