[go: up one dir, main page]

Module server

Module server 

Source
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-continue header, 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§

RecvRequestResult
The possible states after receiving a request.
SendResponseResult
The possible states after sending a response.

Constants§

MAX_REQUEST_HEADERS
Maximum number of headers to parse from an HTTP request.