Last active
March 28, 2025 20:19
-
-
Save KunYi/08b4741e76fccbc2d2ebc1142e8f86c0 to your computer and use it in GitHub Desktop.
a example for libuv + llhttp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/* include libuv & llhttp */ | |
#include <llhttp.h> | |
#include <uv.h> | |
#define DEFAULT_PORT 8080 | |
uv_loop_t *loop; | |
// HTTP parser settings | |
llhttp_settings_t settings; | |
// HTTP request structure | |
typedef struct { | |
uv_tcp_t handle; | |
llhttp_t parser; | |
} client_t; | |
// Buffer to store received data | |
char buffer[4096]; | |
static void on_alloc(uv_handle_t *handle, size_t suggested_size, | |
uv_buf_t *buf) { | |
*buf = uv_buf_init((char *)malloc(suggested_size), suggested_size); | |
//buf->base = (char *)malloc(suggested_size); | |
//buf->len = suggested_size; | |
} | |
static void on_close(uv_handle_t* handle) { | |
free(handle); | |
} | |
static void on_write(struct uv_write_s *req, int status) { | |
} | |
// Callback to handle HTTP request data | |
void on_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { | |
client_t *client = (client_t *)stream; | |
if (nread > 0) { | |
// Parse the received data | |
enum llhttp_errno err = llhttp_execute(&client->parser, buf->base, nread); | |
if (err == HPE_OK) { | |
llhttp_t *parser = &client->parser; | |
// parsed successfully | |
fprintf(stdout, "Parse pass, type:%d, method:%d, status_code=%d\n", | |
parser->type, parser->method, parser->status_code); | |
if (parser->type == HTTP_REQUEST && parser->method == HTTP_GET && | |
parser->status_code == 0) { | |
// 構建 HTTP 響應 | |
const char *response = "HTTP/1.1 200 OK\r\n" | |
"Content-Type: text/plain\r\n" | |
"Content-Length: 12\r\n" | |
"\r\n" | |
"Hello World!"; | |
uv_buf_t resbuf = uv_buf_init((char *)response, strlen(response)); | |
// 發送 HTTP 響應 | |
uv_write_t write_req; | |
uv_write(&write_req, stream, &resbuf, 1, on_write); | |
} | |
} else { | |
fprintf(stderr, "Parse error: %s %s\n", llhttp_errno_name(err), | |
client->parser.reason); | |
uv_close((uv_handle_t *)&client->handle, on_close); | |
} | |
} else if (nread < 0) { | |
// Error or EOF | |
if (nread != UV_EOF) { | |
fprintf(stderr, "Read error %s\n", uv_strerror(nread)); | |
} | |
fprintf(stdout, "UV_EOF, close the connection\n\n"); | |
uv_close((uv_handle_t *)&client->handle, on_close); | |
} | |
free(buf->base); | |
} | |
// Callback to handle HTTP method | |
int on_message_begin(llhttp_t *parser) { | |
printf("on_message_begin HTTP method: %ul\n", parser->method); | |
return 0; | |
} | |
// Callback to handle URL | |
int on_url(llhttp_t *parser, const char *at, size_t length) { | |
printf("URL: %.*s\n", (int)length, at); | |
return 0; | |
} | |
// Callback to handle HTTP version | |
int on_status(llhttp_t *parser, const char *at, size_t length) { | |
printf("HTTP version: %.*s\n", (int)length, at); | |
return 0; | |
} | |
// Callback to handle header field | |
int on_header_field(llhttp_t *parser, const char *at, size_t length) { | |
printf("Header field: %.*s\n", (int)length, at); | |
return 0; | |
} | |
// Callback to handle header value | |
int on_header_value(llhttp_t *parser, const char *at, size_t length) { | |
printf("Header value: %.*s\n", (int)length, at); | |
return 0; | |
} | |
// Main callback to handle request complete | |
int on_message_complete(llhttp_t *parser) { | |
printf("Request complete\n"); | |
return 0; | |
} | |
// Callback for new client connections | |
void on_new_connection(uv_stream_t *server, int status) { | |
if (status < 0) { | |
fprintf(stderr, "New connection error %s\n", uv_strerror(status)); | |
return; | |
} | |
client_t *client = (client_t *)malloc(sizeof(client_t)); | |
uv_tcp_init(loop, &client->handle); | |
if (uv_accept(server, (uv_stream_t *)client) == 0) { | |
llhttp_init(&client->parser, HTTP_REQUEST, &settings); | |
client->handle.data = client; | |
uv_read_start((uv_stream_t *)client, on_alloc, on_read); | |
} else { | |
uv_close((uv_handle_t *)client, on_close); | |
fprintf(stderr, "New connection error %s\n", uv_strerror(status)); | |
} | |
} | |
int main() { | |
loop = uv_default_loop(); | |
// Initialize HTTP parser settings | |
llhttp_settings_init(&settings); | |
settings.on_message_begin = on_message_begin; | |
settings.on_url = on_url; | |
settings.on_status = on_status; | |
settings.on_header_field = on_header_field; | |
settings.on_header_value = on_header_value; | |
settings.on_message_complete = on_message_complete; | |
// Initialize TCP server | |
uv_tcp_t server; | |
uv_tcp_init(loop, &server); | |
struct sockaddr_in bind_addr; | |
uv_ip4_addr("127.0.0.1", DEFAULT_PORT, &bind_addr); | |
uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0); | |
int r = uv_listen((uv_stream_t *)&server, SOMAXCONN, on_new_connection); | |
if (r) { | |
fprintf(stderr, "Listen error %s\n", uv_strerror(r)); | |
return 1; | |
} | |
printf("Server listening on port %d...\n", DEFAULT_PORT); | |
return uv_run(loop, UV_RUN_DEFAULT); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment