Skip to content

Commit

Permalink
Merge pull request #66 from jayjamesjay/v0.11
Browse files Browse the repository at this point in the history
V0.11
  • Loading branch information
jayjamesjay authored Jun 27, 2024
2 parents 7f50f7f + 3cbb03e commit 0aaf2e2
Show file tree
Hide file tree
Showing 18 changed files with 1,799 additions and 1,289 deletions.
335 changes: 312 additions & 23 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 10 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "http_req"
version = "0.10.3"
version = "0.11.0"
license = "MIT"
description = "simple and lightweight HTTP client with built-in HTTPS support"
repository = "https://github.com/jayjamesjay/http_req"
Expand All @@ -15,24 +15,29 @@ unicase = "^2.7"

[features]
default = ["native-tls"]
rust-tls = ["rustls", "webpki", "webpki-roots", "rustls-pemfile"]
rust-tls = ["rustls", "rustls-pki-types", "webpki", "webpki-roots", "rustls-pemfile"]

[dependencies.native-tls]
version = "^0.2"
optional = true

[dependencies.rustls]
version = "^0.21"
version = "^0.23"
optional = true

[dependencies.rustls-pemfile]
version = "^1.0"
version = "^2.1"
optional = true

[dependencies.webpki]
version = "^0.22"
optional = true

[dependencies.webpki-roots]
version = "^0.25"
version = "^0.26"
optional = true

[dependencies.rustls-pki-types]
version = "^1.7"
features = ["alloc"]
optional = true
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2018-2023 jayjamesjay
Copyright (c) 2018-2024 jayjamesjay

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# http_req
> [!CAUTION]
> v0.11.0 introduces major changes to design of `RequestBuilder` and `Request`. Please review [documentation](https://docs.rs/http_req/0.11.0/http_req/) before migrating from previous versions.
[![Rust](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml/badge.svg)](https://github.com/jayjamesjay/http_req/actions/workflows/rust.yml)
[![Crates.io](https://img.shields.io/badge/crates.io-v0.10.3-orange.svg?longCache=true)](https://crates.io/crates/http_req)
[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.10.3/http_req/)
[![Crates.io](https://img.shields.io/badge/crates.io-v0.11.0-orange.svg?longCache=true)](https://crates.io/crates/http_req)
[![Docs.rs](https://docs.rs/http_req/badge.svg)](https://docs.rs/http_req/0.11.0/http_req/)

Simple and lightweight HTTP client with built-in HTTPS support.

Expand Down Expand Up @@ -29,7 +32,7 @@ Take a look at [more examples](https://github.com/jayjamesjay/http_req/tree/mast
In order to use `http_req` with `rustls` in your project, add the following lines to `Cargo.toml`:
```toml
[dependencies]
http_req = {version="^0.10", default-features = false, features = ["rust-tls"]}
http_req = {version="^0.11", default-features = false, features = ["rust-tls"]}
```

## License
Expand Down
8 changes: 2 additions & 6 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate http_req;
extern crate test;

use http_req::{request::Request, response::Response, uri::Uri};
use std::{convert::TryFrom, fs::File, io::Read, time::Duration};
use std::{convert::TryFrom, fs::File, io::Read};
use test::Bencher;

#[bench]
Expand All @@ -24,13 +24,9 @@ const URI: &str = "https://www.rust-lang.org/";
fn request_send(b: &mut Bencher) {
b.iter(|| {
let uri = Uri::try_from(URI).unwrap();
let timeout = Some(Duration::from_secs(6));
let mut writer = Vec::new();

let res = Request::new(&uri)
.timeout(timeout)
.send(&mut writer)
.unwrap();
let res = Request::new(&uri).send(&mut writer).unwrap();

res
});
Expand Down
12 changes: 12 additions & 0 deletions examples/chunked.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use http_req::request;

fn main() {
// Sends a HTTP GET request and processes the response.
let mut body = Vec::new();
let res = request::get("https://jigsaw.w3.org/HTTP/ChunkedScript", &mut body).unwrap();

// Prints details about the response.
println!("Status: {} {}", res.status_code(), res.reason());
println!("Headers: {}", res.headers());
//println!("{}", String::from_utf8_lossy(&body));
}
4 changes: 2 additions & 2 deletions examples/get.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use http_req::request;

fn main() {
//Container for body of a response.
// Container for body of a response.
let mut body = Vec::new();

//Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable.
// Sends a HTTP GET request and processes the response. Saves body of the response to `body` variable.
let res = request::get("https://www.rust-lang.org/learn", &mut body).unwrap();

//Prints details about the response.
Expand Down
4 changes: 2 additions & 2 deletions examples/head.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use http_req::request;

fn main() {
//Sends a HTTP HEAD request and processes the response.
// Sends a HTTP HEAD request and processes the response.
let res = request::head("https://www.rust-lang.org/learn").unwrap();

//Prints details about the response.
// Prints the details about the response.
println!("Status: {} {}", res.status_code(), res.reason());
println!("Headers: {}", res.headers());
}
10 changes: 5 additions & 5 deletions examples/post.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use http_req::request;

fn main() {
//Container for body of a response.
// Container for body of a response.
let mut res_body = Vec::new();

//Body of a request.
// Body of a request.
const REQ_BODY: &[u8; 27] = b"field1=value1&field2=value2";

//Sends a HTTP POST request and processes the response.
// Sends a HTTP POST request and processes the response.
let res = request::post("https://httpbin.org/post", REQ_BODY, &mut res_body).unwrap();

//Prints details about the response.
// Prints details about the response.
println!("Status: {} {}", res.status_code(), res.reason());
println!("Headers: {}", res.headers());
println!("{}", String::from_utf8_lossy(&res_body));
//println!("{}", String::from_utf8_lossy(&res_body));
}
52 changes: 35 additions & 17 deletions examples/request_builder_get.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
use http_req::{request::RequestBuilder, tls, uri::Uri};
use std::{convert::TryFrom, net::TcpStream};
use http_req::{
request::RequestBuilder,
response::Response,
stream::{self, Stream},
uri::Uri,
};
use std::{
convert::TryFrom,
io::{BufReader, Read, Write},
time::Duration,
};

fn main() {
//Parses a URI and assigns it to a variable `addr`.
// Parses a URI and assigns it to a variable `addr`.
let addr: Uri = Uri::try_from("https://www.rust-lang.org/learn").unwrap();

//Connects to a remote host. Uses information from `addr`.
let stream = TcpStream::connect((addr.host().unwrap(), addr.corr_port())).unwrap();
// Containers for a server's response.
let raw_head;
let mut body = Vec::new();

//Opens a secure connection over TlsStream. This is required due to use of `https` protocol.
let mut stream = tls::Config::default()
.connect(addr.host().unwrap_or(""), stream)
.unwrap();
// Prepares a request message.
let request_msg = RequestBuilder::new(&addr)
.header("Connection", "Close")
.parse();

//Container for a response's body.
let mut writer = Vec::new();
// Connects to a server. Uses information from `addr`.
let mut stream = Stream::new(&addr, Some(Duration::from_secs(60))).unwrap();
stream = Stream::try_to_https(stream, &addr, None).unwrap();

//Adds a header `Connection: Close`.
let response = RequestBuilder::new(&addr)
.header("Connection", "Close")
.send(&mut stream, &mut writer)
.unwrap();
// Makes a request to server. Sends the prepared message.
stream.write_all(&request_msg).unwrap();

// Wraps the stream in BufReader to make it easier to read from it.
// Reads a response from the server and saves the head to `raw_head`, and the body to `body`.
let mut stream = BufReader::new(stream);
raw_head = stream::read_head(&mut stream);
stream.read_to_end(&mut body).unwrap();

// Parses and processes the response.
let response = Response::from_head(&raw_head).unwrap();

// Prints infromation about the response.
println!("Status: {} {}", response.status_code(), response.reason());
println!("Headers: {}", response.headers());
//println!("{}", String::from_utf8_lossy(&writer));
//println!("{}", String::from_utf8_lossy(&body));
}
59 changes: 43 additions & 16 deletions src/chunked.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
//! module chunked implements the wire protocol for HTTP's "chunked" Transfer-Encoding.
//! And it's a rust version of the reference implementation in [Go][1].
//!
//! [1]: https://golang.google.cn/src/net/http/internal/chunked.go
//!

//! support for Transfer-Encoding: chunked
use crate::CR_LF;
use std::io::{self, BufRead, BufReader, Error, ErrorKind, Read};

const MAX_LINE_LENGTH: usize = 4096;
const CR_LF: [u8; 2] = [b'\r', b'\n'];

pub struct Reader<R> {
/// Implements the wire protocol for HTTP's Transfer-Encoding: chunked.
///
/// It's a Rust version of the [reference implementation in Go](https://golang.google.cn/src/net/http/internal/chunked.go)
pub struct ChunkReader<R> {
check_end: bool,
eof: bool,
err: Option<Error>,
n: usize,
reader: BufReader<R>,
}

impl<R> Read for Reader<R>
impl<R> Read for ChunkReader<R>
where
R: Read,
{
Expand All @@ -36,7 +34,7 @@ where
}

if let Ok(_) = self.reader.read_exact(&mut footer) {
if footer != CR_LF {
if &footer != CR_LF {
self.err = Some(error_malformed_chunked_encoding());
break;
}
Expand Down Expand Up @@ -93,10 +91,39 @@ where
}
}

impl<R> Reader<R>
impl<R> BufRead for ChunkReader<R>
where
R: Read,
{
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.reader.fill_buf()
}

fn consume(&mut self, amt: usize) {
self.reader.consume(amt)
}
}

impl<R> From<BufReader<R>> for ChunkReader<R>
where
R: Read,
{
fn from(value: BufReader<R>) -> Self {
ChunkReader {
check_end: false,
eof: false,
err: None,
n: 0,
reader: value,
}
}
}

impl<R> ChunkReader<R>
where
R: Read,
{
/// Creates a new `ChunkReader` from `reader`
pub fn new(reader: R) -> Self
where
R: Read,
Expand Down Expand Up @@ -216,7 +243,7 @@ mod tests {
#[test]
fn read() {
let data: &[u8] = b"7\r\nhello, \r\n17\r\nworld! 0123456789abcdef\r\n0\r\n";
let mut reader = Reader::new(data);
let mut reader = ChunkReader::new(data);
let mut writer = vec![];
io::copy(&mut reader, &mut writer).expect("failed to dechunk");

Expand All @@ -226,7 +253,7 @@ mod tests {
fn read_multiple() {
{
let data: &[u8] = b"3\r\nfoo\r\n3\r\nbar\r\n0\r\n";
let mut reader = Reader::new(data);
let mut reader = ChunkReader::new(data);
let mut writer = vec![0u8; 10];
let n = reader.read(&mut writer).expect("unexpect error");

Expand All @@ -235,7 +262,7 @@ mod tests {
}
{
let data: &[u8] = b"3\r\nfoo\r\n0\r\n";
let mut reader = Reader::new(data);
let mut reader = ChunkReader::new(data);
let mut writer = vec![0u8; 3];
let n = reader.read(&mut writer).expect("unexpect error");

Expand All @@ -246,7 +273,7 @@ mod tests {
#[test]
fn read_partial() {
let data: &[u8] = b"7\r\n1234567";
let mut reader = Reader::new(data);
let mut reader = ChunkReader::new(data);
let mut writer = vec![];
io::copy(&mut reader, &mut writer).expect("failed to dechunk");

Expand All @@ -260,7 +287,7 @@ mod tests {
+ "world! 0123456789abcdef\r\n"
+ "0;someextension=sometoken\r\n";
let data = data_str.as_bytes();
let mut reader = Reader::new(data);
let mut reader = ChunkReader::new(data);
let mut writer = vec![];

reader.read_to_end(&mut writer).expect("failed to dechunk");
Expand Down
Loading

0 comments on commit 0aaf2e2

Please sign in to comment.