diff --git a/Cargo.lock b/Cargo.lock index a464f73..3609007 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,7 +104,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http-request" -version = "4.0.0" +version = "4.1.0" dependencies = [ "color-output", "hex", diff --git a/Cargo.toml b/Cargo.toml index a65815f..38d24d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "http-request" -version = "4.0.0" +version = "4.1.0" edition = "2021" authors = ["ltpp-universe "] license = "MIT" diff --git a/readme.md b/readme.md index 4c764bf..717971c 100644 --- a/readme.md +++ b/readme.md @@ -7,7 +7,7 @@ [Official Documentation](https://docs.ltpp.vip/HTTP-REQUEST/) -> http_request is a lightweight, efficient library for building, sending, and handling HTTP/HTTPS requests in Rust applications. It provides a simple and intuitive API, allowing developers to easily interact with web services, whether they use the "HTTP" or "HTTPS" protocol. The library supports various HTTP methods, custom headers, request bodies, and automatic handling of redirects (including detecting redirect loops), enabling fast and secure communication. Whether working with secure "HTTPS" connections or standard "HTTP" requests, the library is optimized for performance, minimal resource usage, and easy integration into Rust projects. +> http_request is a lightweight, efficient library for building, sending, and handling HTTP/HTTPS requests in Rust applications. It provides a simple and intuitive API, allowing developers to easily interact with web services, whether they use the "HTTP" or "HTTPS" protocol. The library supports various HTTP methods, custom headers, request bodies, timeout, and automatic handling of redirects (including detecting redirect loops), enabling fast and secure communication. Whether working with secure "HTTPS" connections or standard "HTTP" requests, the library is optimized for performance, minimal resource usage, and easy integration into Rust projects. ## Features @@ -20,6 +20,7 @@ - **Response Handling**: Provides a simple wrapper around HTTP responses, making it easy to access and process response data. - **Optimized Memory Management**: Implements efficient memory management to minimize unnecessary memory allocations and improve performance. - **Redirect Handling**: Supports redirect handling, allows setting the maximum number of redirects, and includes redirect loop detection. +- **timeout**: Supports timeout ## Installation diff --git a/src/constant/common.rs b/src/constant/common.rs index 8f04e77..298a784 100644 --- a/src/constant/common.rs +++ b/src/constant/common.rs @@ -1,2 +1,8 @@ /// The name of the application. pub static APP_NAME: &str = "http-request"; + +/// space +pub static SPACE: &str = " "; + +/// Colon space +pub static COLON_SPACE: &str = ": "; diff --git a/src/constant/http.rs b/src/constant/http.rs index f386dfc..cd4b869 100644 --- a/src/constant/http.rs +++ b/src/constant/http.rs @@ -25,6 +25,9 @@ pub static USER_AGENT: &str = "User-Agent"; /// The HTTP header field name `Host`, used to specify the host and port number of the server. pub static HOST: &str = "Host"; +/// Unknown HTTP version +pub static UNKNOWN_HTTP_VERSION: &str = ""; + /// The default HTTP version `HTTP/1.1` used in requests and responses. pub static HTTP_VERSION_1_1: &str = "HTTP/1.1"; diff --git a/src/request/cfg.rs b/src/request/cfg.rs index 00309c2..fa15273 100644 --- a/src/request/cfg.rs +++ b/src/request/cfg.rs @@ -79,14 +79,17 @@ fn test_https_post_request() { header.insert("Connection", "keep-alive"); header.insert("Accept-Encoding", "gzip, deflate"); let mut body: HashMap<&str, &str> = HashMap::new(); - body.insert("code", "hello"); + body.insert( + "code", + "fn main() {\r\nloop {} println!(\"hello world\");\r\n}", + ); body.insert("language", "rust"); body.insert("testin", ""); let mut _request_builder = HttpRequestBuilder::new() .post("https://code.ltpp.vip/") .json(body) .headers(header) - .timeout(6000) + .timeout(1000) .redirect() .max_redirect_times(8) .http1_1_only() diff --git a/src/request/error.rs b/src/request/error.rs index 657213c..2184da2 100644 --- a/src/request/error.rs +++ b/src/request/error.rs @@ -28,6 +28,7 @@ pub enum Error { ReadConnectionError, TlsConnectorBuildError, SetReadTimeoutError, + SetWriteTimeoutError, TlsStreamConnectError, MaxRedirectTimes, RedirectUrlDeadLoop, @@ -56,6 +57,7 @@ impl fmt::Display for Error { Error::ReadConnectionError => write!(f, "Connection Read Error"), Error::TlsConnectorBuildError => write!(f, "TLS Connector Build Error"), Error::SetReadTimeoutError => write!(f, "Failed to Set Read Timeout"), + Error::SetWriteTimeoutError => write!(f, "Failed to Set Write Timeout"), Error::TlsStreamConnectError => write!(f, "TLS Stream Connection Error"), Error::MaxRedirectTimes => write!(f, "Max Redirect Times"), Error::RedirectUrlDeadLoop => write!(f, "Redirect URL Dead Loop"), diff --git a/src/request/http_request/impl.rs b/src/request/http_request/impl.rs index 6d39947..94a45a9 100644 --- a/src/request/http_request/impl.rs +++ b/src/request/http_request/impl.rs @@ -347,6 +347,7 @@ impl HttpRequest { /// - `Error::TlsStreamConnectError`: If the TLS stream could not be established. fn get_connection_stream(&self, host: String, port: u16) -> Result, Error> { let host_port: (String, u16) = (host.clone(), port); + let timeout: Duration = Duration::from_millis(self.config.timeout); let stream: Result, Error> = if self.get_protocol().is_https() { let tls_connector: TlsConnector = TlsConnector::builder() .build() @@ -354,8 +355,11 @@ impl HttpRequest { let tcp_stream: TcpStream = TcpStream::connect(host_port.clone()).map_err(|_| Error::TcpStreamConnectError)?; tcp_stream - .set_read_timeout(Some(Duration::from_secs(self.config.timeout))) + .set_read_timeout(Some(timeout)) .map_err(|_| Error::SetReadTimeoutError)?; + tcp_stream + .set_write_timeout(Some(timeout)) + .map_err(|_| Error::SetWriteTimeoutError)?; let tls_stream: TlsStream = tls_connector .connect(&host.clone(), tcp_stream) .map_err(|_| Error::TlsStreamConnectError)?; @@ -364,8 +368,11 @@ impl HttpRequest { let tcp_stream: TcpStream = TcpStream::connect(host_port.clone()).map_err(|_| Error::TcpStreamConnectError)?; tcp_stream - .set_read_timeout(Some(Duration::from_millis(self.config.timeout))) + .set_read_timeout(Some(timeout)) .map_err(|_| Error::SetReadTimeoutError)?; + tcp_stream + .set_write_timeout(Some(timeout)) + .map_err(|_| Error::SetWriteTimeoutError)?; Ok(Box::new(tcp_stream)) }; stream diff --git a/src/request/http_version/impl.rs b/src/request/http_version/impl.rs index efb6d7e..7d23723 100644 --- a/src/request/http_version/impl.rs +++ b/src/request/http_version/impl.rs @@ -1,5 +1,5 @@ use super::r#type::HttpVersion; -use crate::constant::http::{HTTP_VERSION_1_1, HTTP_VERSION_2}; +use crate::constant::http::{HTTP_VERSION_1_1, HTTP_VERSION_2, UNKNOWN_HTTP_VERSION}; use std::fmt; impl Default for HttpVersion { @@ -32,6 +32,7 @@ impl fmt::Display for HttpVersion { let version_str = match self { HttpVersion::HTTP1_1 => HTTP_VERSION_1_1, HttpVersion::HTTP2 => HTTP_VERSION_2, + HttpVersion::Unknown(_) => UNKNOWN_HTTP_VERSION, }; write!(f, "{}", version_str) } diff --git a/src/request/http_version/type.rs b/src/request/http_version/type.rs index c138f7d..a005f52 100644 --- a/src/request/http_version/type.rs +++ b/src/request/http_version/type.rs @@ -20,4 +20,7 @@ pub enum HttpVersion { /// HTTP version 2.0 HTTP2, + + /// Unknown version + Unknown(String), } diff --git a/src/response/impl.rs b/src/response/impl.rs index d4055d9..12508f5 100644 --- a/src/response/impl.rs +++ b/src/response/impl.rs @@ -1,6 +1,10 @@ use super::r#type::HttpResponse; use crate::{ - constant::http::{CONTENT_LENGTH, HTTP_BR, HTTP_VERSION_1_1}, + constant::{ + common::{COLON_SPACE, SPACE}, + http::{CONTENT_LENGTH, HTTP_BR}, + }, + request::http_version::r#type::HttpVersion, status_code::r#type::StatusCode, }; use std::collections::HashMap; @@ -64,26 +68,32 @@ impl HttpResponse { let mut lines: Lines<'_> = response.lines(); let status_line: &str = lines.next().unwrap_or(""); let status_parts: Vec<&str> = status_line.split_whitespace().collect(); - let http_version: String = status_parts.get(0).unwrap_or(&HTTP_VERSION_1_1).to_string(); + let http_version: String = status_parts + .get(0) + .unwrap_or(&HttpVersion::Unknown(String::new()).to_string().as_str()) + .to_string(); let status_code: u16 = status_parts .get(1) .unwrap_or(&StatusCode::Ok.to_string().as_str()) .parse() .unwrap_or(StatusCode::Unknown.code()); - let status_text: String = status_parts.get(2..).unwrap_or(&[]).join(" "); + let status_text: String = status_parts + .get(2..) + .unwrap_or(&[&StatusCode::Unknown.to_string()]) + .join(SPACE); let mut headers: HashMap = HashMap::new(); while let Some(line) = lines.next() { if line.is_empty() { break; } - let header_parts: Vec<&str> = line.splitn(2, ": ").collect(); + let header_parts: Vec<&str> = line.splitn(2, COLON_SPACE).collect(); if header_parts.len() == 2 { let key: String = header_parts[0].trim().to_string(); let value: String = header_parts[1].trim().to_string(); headers.insert(key, value); } } - let body: String = lines.collect::>().join("\n"); + let body: String = lines.collect::>().join(HTTP_BR); HttpResponse { http_version, status_code, @@ -102,7 +112,7 @@ impl HttpResponse { impl Default for HttpResponse { fn default() -> Self { HttpResponse { - http_version: HTTP_VERSION_1_1.to_string(), + http_version: HttpVersion::Unknown(String::new()).to_string(), status_code: StatusCode::Unknown.code(), status_text: StatusCode::Unknown.to_string(), headers: HashMap::new(),