Expand description
HTTP/1.1 server protocol
Sans-IO protocol impl, which means “writing” and “reading” are made via buffers rather than the Write/Read std traits.
The Reply object attempts to encode correct HTTP/1.1 handling using
state variables, for example Reply<RecvRequest> to represent the
lifecycle stage where we are to receive a request.
The states are:
- RecvRequest - Receive the request, which is the method, path, version and the request headers
- Send100 - If there is an
Expect: 100-continueheader, the server should send a 100 Continue response before receiving the body - RecvBody - Receive the request body
- ProvideResponse - Prepare a response to the request
- SendResponse - Send the response status and headers
- SendBody - Send the response body
- Cleanup - Close the connection or prepare for the next request
┌──────────────────┐
┌──│ RecvRequest │───────────────┐
│ └──────────────────┘ │
│ │ │
│ │ │
│ ▼ ▼
│ ┌──────────────────┐ ┌──────────────────┐
│ │ RecvBody │◀────│ Send100 │
│ └──────────────────┘ └──────────────────┘
│ │ │
│ │ │
│ ▼ │
└─▶┌──────────────────┐ │
│ ProvideResponse │ reject
└──────────────────┘ │
│ │
│ │
▼ │
┌──────────────────┐◀──────────────┘
│ SendResponse │──┐
└──────────────────┘ │
│ │
│ │
▼ │
┌──────────────────┐ │
│ SendBody │ │
└──────────────────┘ │
│ │
│ │
▼ │
┌──────────────────┐ │
│ Cleanup │◀─┘
└──────────────────┘§Example
use ureq_proto::server::*;
use http::{Response, StatusCode, Version};
// ********************************** RecvRequest
// Create a new Reply in the RecvRequest state
let mut reply = Reply::new().unwrap();
// Receive a request from the client
let input = b"POST /my-path HTTP/1.1\r\n\
host: example.test\r\n\
transfer-encoding: chunked\r\n\
expect: 100-continue\r\n\
\r\n";
let (input_used, request) = reply.try_request(input).unwrap();
assert_eq!(input_used, 96);
let request = request.unwrap();
assert_eq!(request.uri().path(), "/my-path");
assert_eq!(request.method(), "POST");
// Check if we can proceed to the next state
// In a real server, you would implement this method
// let can_proceed = reply.can_proceed();
// Proceed to the next state
let reply = reply.proceed().unwrap();
// ********************************** Send100
// In this example, we know the next state is Send100 because
// the request included an "Expect: 100-continue" header.
// A real server needs to match on the variants.
let reply = match reply {
RecvRequestResult::Send100(v) => v,
_ => panic!(),
};
// We can either accept or reject the 100-continue request
// Here we accept it and proceed to receiving the body
let mut output = vec![0_u8; 1024];
let (output_used, reply) = reply.accept(&mut output).unwrap();
assert_eq!(output_used, 25);
assert_eq!(&output[..output_used], b"HTTP/1.1 100 Continue\r\n\r\n");
// ********************************** RecvBody
// Now we can receive the request body
let mut reply = reply;
// Receive the body in chunks (chunked encoding format)
let input = b"5\r\nhello\r\n0\r\n\r\n";
let mut body_buffer = vec![0_u8; 1024];
let (input_used, output_used) = reply.read(input, &mut body_buffer).unwrap();
assert_eq!(input_used, 15);
assert_eq!(output_used, 5);
assert_eq!(&body_buffer[..output_used], b"hello");
// Check if the body is fully received
// In this example, we'll assume it is
assert!(reply.is_ended());
// Proceed to providing a response
let reply = reply.proceed().unwrap();
// ********************************** ProvideResponse
// Create a response
let response = Response::builder()
.status(StatusCode::OK)
.header("content-type", "text/plain")
.body(())
.unwrap();
// Provide the response and proceed to sending it
let mut reply = reply.provide(response).unwrap();
// ********************************** SendResponse
// Send the response headers
let output_used = reply.write(&mut output).unwrap();
assert_eq!(&output[..output_used], b"\
HTTP/1.1 200 OK\r\n\
transfer-encoding: chunked\r\n\
content-type: text/plain\r\n\
\r\n");
// Check if the response headers are fully sent
assert!(reply.is_finished());
// Proceed to sending the response body
let SendResponseResult::SendBody(mut reply) = reply.proceed() else {
panic!("Expected SendBody");
};
// ********************************** SendBody
// Send the response body
let (input_used, output_used) = reply.write(b"hello world", &mut output).unwrap();
assert_eq!(input_used, 11);
assert_eq!(&output[..output_used], b"b\r\nhello world\r\n");
// Indicate the end of the body with an empty input
let (input_used, output_used) = reply.write(&[], &mut output).unwrap();
assert_eq!(input_used, 0);
assert_eq!(&output[..output_used], b"0\r\n\r\n");
// Check if the body is fully sent
assert!(reply.is_finished());
// ********************************** Cleanup
// Proceed to cleanup
let reply = reply.proceed();
// Check if we need to close the connection
if reply.must_close_connection() {
// connection.close();
} else {
// Prepare for the next request
// let new_reply = Reply::new().unwrap();
}Structs§
- Reply
- A state machine for an HTTP request/response cycle.
Enums§
- Recv
Request Result - The possible states after receiving a request.
- Send
Response Result - The possible states after sending a response.
Constants§
- MAX_
REQUEST_ HEADERS - Maximum number of headers to parse from an HTTP request.