DEV Community

tengxgfyrz67s
tengxgfyrz67s

Posted on

Request Handling

Project Code:https://github.com/hyperlane-dev/hyperlane

Introduction

Handling incoming HTTP requests is at the core of any web server. Hyperlane provides a rich set of tools for extracting data from requests, including headers, query parameters, request body, and route parameters. This article covers all the ways you can access request data in Hyperlane.

Accessing Basic Request Information

Hyperlane's Context object provides methods to access all the fundamental parts of an HTTP request:

let method = ctx.get_request().get_method();
let path = ctx.get_request().get_path();
let host = ctx.get_request().get_host();
let headers = ctx.get_request().get_headers();
Enter fullscreen mode Exit fullscreen mode

These methods return:

  • get_method() — The HTTP method (GET, POST, PUT, DELETE, etc.)
  • get_path() — The URL path (e.g., /users/123)
  • get_host() — The Host header value
  • get_headers() — All request headers as a collection

Reading the Request Body

Hyperlane supports multiple ways to read the request body, depending on the content type:

As a String

let body = ctx.get_request().get_body_string();
Enter fullscreen mode Exit fullscreen mode

This returns the raw body as a UTF-8 string. It's useful for text-based content types like plain text, HTML, or JSON.

As JSON

let json_body: T = ctx.get_request().get_body_json::<T>();
Enter fullscreen mode Exit fullscreen mode

This deserializes the body as JSON into a type T that implements serde::Deserialize. Replace T with your target type:

#[derive(serde::Deserialize)]
struct TestData {
    name: String,
    age: u32,
}

let json_body: TestData = ctx.get_request().get_body_json::<TestData>();
Enter fullscreen mode Exit fullscreen mode

As Raw Bytes

The body can also be accessed as raw bytes through the request object, which is useful for binary data.

Query Parameters

Query parameters are the key-value pairs that appear after ? in a URL. For example, in /search?q=rust&page=1, the query parameters are q=rust and page=1.

Getting a Single Query Parameter

let query = ctx.get_request().try_get_query("key");
Enter fullscreen mode Exit fullscreen mode

This returns an Option<String>. If the parameter doesn't exist, it returns None.

Getting All Query Parameters

You can also retrieve all query parameters at once using the request object's query methods.

Request Headers

Headers provide additional information about the request, such as content type, authentication tokens, and client capabilities.

Getting a Single Header

let host_value = ctx.get_request().try_get_header_back("HOST");
Enter fullscreen mode Exit fullscreen mode

The try_get_header_back method returns an Option<String> for the specified header name.

Getting All Headers

let headers = ctx.get_request().get_headers();
Enter fullscreen mode Exit fullscreen mode

This returns all headers as a collection that you can iterate over.

Cookie Handling

Cookies are a fundamental part of web applications, used for session management, tracking, and storing user preferences.

Getting All Cookies

let cookies = ctx.get_request().try_get_cookies();
Enter fullscreen mode Exit fullscreen mode

Getting a Single Cookie

let cookie = ctx.get_request().try_get_cookie("session_id");
Enter fullscreen mode Exit fullscreen mode

This returns the value of the cookie named session_id, or None if it doesn't exist.

Attribute Macro Request Handling

Hyperlane's attribute macros provide a declarative way to extract request data. Instead of manually calling getter methods, you can annotate variables with attributes:

Request Body

#[request_body(body)]
Enter fullscreen mode Exit fullscreen mode

This automatically extracts the raw request body into the body variable.

JSON Request Body

#[request_body_json(body: TestData)]
Enter fullscreen mode Exit fullscreen mode

This automatically deserializes the JSON body into a TestData struct.

Request Header

#[request_header(HOST => host_value)]
Enter fullscreen mode Exit fullscreen mode

This extracts the HOST header into the host_value variable.

Try Get Request Header

#[try_get_request_header(HOST => host_value)]
Enter fullscreen mode Exit fullscreen mode

This is the safe version that returns an Option instead of panicking if the header is missing.

Request Path

#[request_path(path)]
Enter fullscreen mode Exit fullscreen mode

This extracts the request path into the path variable.

Query Parameter

#[request_query("key" => query_value)]
Enter fullscreen mode Exit fullscreen mode

This extracts the key query parameter into query_value.

Method Checking

#[is_get_method]
Enter fullscreen mode Exit fullscreen mode

This attribute checks if the request method is GET.

Multiple Methods

#[methods("GET", "POST")]
Enter fullscreen mode Exit fullscreen mode

This checks if the request method matches any of the specified methods.

HTTP Version Checking

#[is_http1_1_version]
Enter fullscreen mode Exit fullscreen mode

This checks if the request uses HTTP/1.1.

WebSocket Upgrade Checking

#[is_ws_upgrade_type]
Enter fullscreen mode Exit fullscreen mode

This checks if the request is a WebSocket upgrade request.

WebSocket Requests

For WebSocket connections, Hyperlane provides special handling:

#[route("/ws_upgrade_type")]
struct Websocket;

impl ServerHook for Websocket {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    #[is_ws_upgrade_type]
    #[try_get_websocket_request(body)]
    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        let body_list: Vec<ResponseBody> = WebSocketFrame::create_frame_list(&body);
        stream.send_list(body_list).await;
        Status::Continue
    }
}
Enter fullscreen mode Exit fullscreen mode

The #[try_get_websocket_request(body)] attribute extracts the WebSocket request body, and WebSocketFrame::create_frame_list creates the response frames.

Server-Sent Events (SSE)

Hyperlane supports Server-Sent Events for streaming data to clients:

let data: Vec<u8> = ctx
    .get_mut_response()
    .set_header(CONTENT_TYPE, TEXT_EVENT_STREAM)
    .set_body(Vec::new())
    .build();
stream.try_send(data).await;

for i in 0..10 {
    let body: String = format!("data:{i}{HTTP_DOUBLE_BR}");
    stream.try_send(&body).await;
}
Enter fullscreen mode Exit fullscreen mode

This sets the Content-Type to text/event-stream and sends a series of events to the client.

Connection Management

Hyperlane provides fine-grained control over connection lifecycle:

Closing a Connection

stream.set_closed(true);
Enter fullscreen mode Exit fullscreen mode

Checking Keep-Alive Status

let keep_alive: bool = stream.is_keep_alive(ctx.get_request().is_enable_keep_alive());
Enter fullscreen mode Exit fullscreen mode

Handling Multiple Requests on One Connection (Keep-Alive)

while stream.try_get_http_request().await.is_ok() {
    if !ctx.get_request().is_enable_keep_alive() {
        stream.set_closed(true);
        break;
    }
}
Enter fullscreen mode Exit fullscreen mode

This loop reads multiple requests from the same connection when Keep-Alive is enabled, and closes the connection when it's not.

Context Attributes

Hyperlane's Context supports storing and retrieving custom data during request processing:

Setting an Attribute

ctx.set_attribute("key", "key", "value");
Enter fullscreen mode Exit fullscreen mode

Getting an Attribute

let value: Option<String> = ctx.try_get_attribute("key");
Enter fullscreen mode Exit fullscreen mode

Removing an Attribute

ctx.remove_attribute("key");
Enter fullscreen mode Exit fullscreen mode

Clearing All Attributes

ctx.clear_attribute();
Enter fullscreen mode Exit fullscreen mode

Attribute Macro Support

#[try_get_attribute("key" => value: String)]
#[attribute("key" => value: String)]
Enter fullscreen mode Exit fullscreen mode

These attributes provide a declarative way to work with context attributes.

Complete Request Handling Example

Here's a comprehensive example that demonstrates various request handling techniques:

use hyperlane::*;
use hyperlane_macros::*;

#[route("/api/users/{id}")]
struct UserRoute;

impl ServerHook for UserRoute {
    async fn new(_: &mut Stream, _: &mut Context) -> Self {
        Self
    }

    #[request_path(path)]
    #[request_query("name" => query_value)]
    async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
        // Get route parameter
        let id: String = ctx.get_route_param("id");

        // Get request method
        let method = ctx.get_request().get_method();

        // Get request body as string
        let body = ctx.get_request().get_body_string();

        // Get a header
        let host = ctx.get_request().try_get_header_back("HOST").unwrap_or_default();

        // Get a cookie
        let session = ctx.get_request().try_get_cookie("session_id");

        // Process the request...
        let response_body = format!("User ID: {}, Method: {:?}, Host: {}", id, method, host);

        let data: Vec<u8> = ctx
            .get_mut_response()
            .set_status_code(200)
            .set_body(response_body)
            .build();
        stream.try_send(data).await;

        Status::Continue
    }
}
Enter fullscreen mode Exit fullscreen mode

Best Practices

  1. Use attribute macros for cleaner code: They reduce boilerplate and make your handlers more readable.

  2. Handle missing data gracefully: Use try_get_* methods instead of get_* when the data might not be present.

  3. Validate request data early: Check for required parameters and headers at the beginning of your handler.

  4. Use JSON deserialization for structured data: The get_body_json::<T>() method is more robust than manual string parsing.

  5. Manage connection lifecycle properly: Always check Keep-Alive status and close connections when appropriate.

Conclusion

Hyperlane provides a comprehensive set of tools for handling HTTP requests. From basic request information like method and path to advanced features like WebSocket upgrades and SSE, the library covers all the common request handling scenarios. The attribute macro system makes your code more declarative and maintainable, while the programmatic API gives you full control when needed.

In the next article, we'll explore how to build and send HTTP responses, including status codes, headers, and body content.


Project Code:https://github.com/hyperlane-dev/hyperlane

Top comments (0)