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();
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();
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>();
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>();
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");
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");
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();
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();
Getting a Single Cookie
let cookie = ctx.get_request().try_get_cookie("session_id");
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)]
This automatically extracts the raw request body into the body variable.
JSON Request Body
#[request_body_json(body: TestData)]
This automatically deserializes the JSON body into a TestData struct.
Request Header
#[request_header(HOST => host_value)]
This extracts the HOST header into the host_value variable.
Try Get Request Header
#[try_get_request_header(HOST => host_value)]
This is the safe version that returns an Option instead of panicking if the header is missing.
Request Path
#[request_path(path)]
This extracts the request path into the path variable.
Query Parameter
#[request_query("key" => query_value)]
This extracts the key query parameter into query_value.
Method Checking
#[is_get_method]
This attribute checks if the request method is GET.
Multiple Methods
#[methods("GET", "POST")]
This checks if the request method matches any of the specified methods.
HTTP Version Checking
#[is_http1_1_version]
This checks if the request uses HTTP/1.1.
WebSocket Upgrade Checking
#[is_ws_upgrade_type]
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
}
}
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;
}
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);
Checking Keep-Alive Status
let keep_alive: bool = stream.is_keep_alive(ctx.get_request().is_enable_keep_alive());
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;
}
}
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");
Getting an Attribute
let value: Option<String> = ctx.try_get_attribute("key");
Removing an Attribute
ctx.remove_attribute("key");
Clearing All Attributes
ctx.clear_attribute();
Attribute Macro Support
#[try_get_attribute("key" => value: String)]
#[attribute("key" => value: String)]
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
}
}
Best Practices
Use attribute macros for cleaner code: They reduce boilerplate and make your handlers more readable.
Handle missing data gracefully: Use
try_get_*methods instead ofget_*when the data might not be present.Validate request data early: Check for required parameters and headers at the beginning of your handler.
Use JSON deserialization for structured data: The
get_body_json::<T>()method is more robust than manual string parsing.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)