Serene Runtime 1.0.0-dev
C runtime for the Serene programming language
Loading...
Searching...
No Matches
12_http_server.c
Go to the documentation of this file.
1/* -*- C -*-
2 * Serene programming language
3 * Copyright (C) 2019-2026 Sameer Rahmani <[email protected]>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19// Example 12 -- A http server
20//
21// In this example we create a very dumb and basic http server that responses
22// to each reaquest with a random delay. it spawns a new fiber for each respones.
23//
24// IMPORTANT NOTE: This is a very crappy webserver, don't even think about use it
25// as a skeleton.
26
27#include <arpa/inet.h>
28#include <fcntl.h>
29#include <netinet/in.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <sys/socket.h>
33#include <unistd.h>
34
35#include <serene/rt/fiber/io.h>
36#include <serene/rt/trace.h>
37#include <serene/runtime.h>
38
39#include "serene/utils.h"
40
41static atomic_uint counter;
42
43typedef struct server_args_t {
44 int listener;
46
47typedef struct worker_args_t {
48 int fd;
49 uint64_t id;
51
53
54 worker_args_t *wargs = arg;
55 int client_fd = wargs->fd;
56
57 int rd_num = (rand() % (5 - 1 + 1)) + 1;
59 auto worker_id = srn_sched_current_worker_id();
60
61 SRN_TRACEPOINT(http_worker_start, worker_id, wargs->id, self->name, rd_num);
62
63 // Drain the request first. Closing a socket with unread bytes makes the
64 // kernel answer with RST instead of FIN and the client reports a reset.
65 char req[1024];
66
67 (void)srn_fiber_read(client_fd, req, sizeof(req), -1);
68
69 worker_id = srn_sched_current_worker_id();
70 SRN_TRACEPOINT(http_worker_switch, worker_id, wargs->id, self->name, rd_num);
71
72 char body[256];
73 int body_len = snprintf(
74 body, sizeof(body),
75 "<!DOCTYPE html><html><head><title>12-http-server-example</title>"
76 "<body><h1>%d</h1></body></html>",
77 rd_num
78 );
79
80 // Content-Length tells the client where the body ends, so it does not
81 // depend on how the connection is torn down to finish the read.
82 char response[1024];
83 int len = snprintf(
84 response, sizeof(response),
85 "HTTP/1.1 200 OK\r\n"
86 "Content-Type: text/html; charset=UTF-8\r\n"
87 "Content-Length: %d\r\n"
88 "Connection: close\r\n\r\n"
89 "%s",
90 body_len, body
91 );
92
93 printf("[%s] sleeping\n", self->name);
94 srn_fiber_sleep(rd_num);
95
96 worker_id = srn_sched_current_worker_id();
97 SRN_TRACEPOINT(http_worker_switch, worker_id, wargs->id, self->name, rd_num);
98
99 auto result = srn_fiber_write(client_fd, response, (size_t)len, -1);
100 if (HAS_ERROR(result)) {
101 printf("Failed to write: %s\n", result.maybe_error->msg);
102 }
103
104 worker_id = srn_sched_current_worker_id();
105 SRN_TRACEPOINT(http_worker_end, worker_id, wargs->id, self->name, rd_num);
106
107 printf("[%s] Replied back to the client\n", self->name);
108 close(client_fd);
109
110 return nullptr;
111}
112
113static srn_fiber_result_t server(srn_context_t *ctx, void *arg) {
114 (void)ctx;
115 server_args_t *s = arg;
116
117 for (;;) {
118 srn_io_accept_result_t conn = srn_fiber_accept(s->listener, nullptr, 0);
119
120 if (HAS_ERROR(conn)) {
121 printf("server: accept failed: %s\n", conn.maybe_error->msg);
122 return nullptr;
123 }
124
125 worker_args_t *wargs = ALLOC(ctx, worker_args_t);
126 PANIC_IF_NULL(wargs);
127 wargs->fd = conn.fd;
128 wargs->id = atomic_fetch_add(&counter, 1);
129 (void)srn_fiber_spawn(ctx, srn_fiber_web_worker, (void *)wargs);
130 }
131
132 return nullptr;
133}
134
135static void set_nonblock(int fd) {
136 int fl = fcntl(fd, F_GETFL, 0);
137 (void)fcntl(fd, F_SETFL, fl | O_NONBLOCK);
138}
139
140int main(void) {
141 SERENE_RUNTIME_INIT_WITH_SCHED(engine, sched, nullptr);
142 srn_context_t *ctx = srn_context_make(engine);
143
144 atomic_init(&counter, 0);
145
146 int one = 1;
147 int sock = socket(AF_INET, SOCK_STREAM, 0);
148 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
149
150 int port = 8080;
151 struct sockaddr_in a = {
152 .sin_family = AF_INET,
153 .sin_addr = {INADDR_ANY},
154 .sin_port = htons(port),
155 };
156
157 if (sock < 0 || bind(sock, (struct sockaddr *)&a, sizeof(a)) != 0 || listen(sock, 128) != 0) {
158 perror("listener setup");
159 return 1;
160 }
161
162 set_nonblock(sock);
163 socklen_t al = sizeof(a);
164 if (getsockname(sock, (struct sockaddr *)&a, &al) != 0) {
165 perror("getsockname");
166 return 1;
167 }
168
169 printf("Running the webserver on 0.0.0.0:%d\n", port);
170 server_args_t s = {sock};
171 SRN_TRACEPOINT(http_main_start);
172 (void)srn_fiber_spawn(ctx, server, &s);
173
174 srn_sched_run(sched, 0);
175 close(sock);
176
179 return 0;
180}
static srn_fiber_result_t server(srn_context_t *ctx, void *arg)
Definition 09_tcp_echo.c:56
static void set_nonblock(int fd)
int main(void)
static atomic_uint counter
static srn_fiber_result_t srn_fiber_web_worker(srn_context_t *ctx, void *arg)
static srn_fiber_result_t server(srn_context_t *ctx, void *arg)
srn_context_t * srn_context_make(srn_engine_t *engine)
Make an empty context, by allocating a new memory block.
Definition context.c:39
int srn_context_release(srn_context_t *ctx)
Definition context.c:64
#define ALLOC(ctx, T)
Definition context.h:84
#define HAS_ERROR(x)
True when x – a value carrying an ERROR_HEADER – has an error attached.
Definition errors.h:63
srn_fiber_t * srn_fiber_spawn(srn_context_t *ctx, srn_fiber_entry_t entry, void *arg)
Make and schedule a fiber with every default, the engine's scheduler, the configured stack size,...
Definition fiber.c:245
void * srn_fiber_result_t
What a fiber's entry produces, type-erased.
Definition fiber.h:161
srn_io_size_result_t srn_fiber_write(int fd, const void *buf, size_t len, int64_t offset)
Write len bytes from buf to fd, suspending the calling fiber until the operation completes rather tha...
Definition io.c:315
srn_io_accept_result_t srn_fiber_accept(int fd, struct sockaddr *addr, socklen_t addr_cap)
Accept a pending connection on the listening socket fd, suspending the calling fiber until one arrive...
Definition io.c:327
srn_error_t * srn_fiber_sleep(uint64_t duration)
Suspend the calling fiber for at least duration seconds, letting its worker run other fibers meanwhil...
Definition io.c:301
srn_io_size_result_t srn_fiber_read(int fd, void *buf, size_t len, int64_t offset)
Read up to len bytes from fd into buf, suspending the calling fiber until the operation completes rat...
Definition io.c:303
The fiber facing IO API: blocking looking calls a fiber uses to do IO.
#define SERENE_RUNTIME_SHUTDOWN(engine)
Tear down what SERENE_RUNTIME_INIT brought up, in reverse order, the engine (reactor,...
Definition runtime.h:58
#define SERENE_RUNTIME_INIT_WITH_SCHED(engine, sched, config)
SERENE_RUNTIME_INIT plus a sched variable bound to the engine's scheduler, which every run and fiber ...
Definition runtime.h:46
srn_worker_id_t srn_sched_current_worker_id()
Return the id of the worker that the calling os thread is running, or SIZE_MAX when the calling threa...
Definition scheduler.c:1157
srn_fiber_t * srn_fiber_current(void)
The fiber currently running on this os thread, or null when the calling thread is not a worker or the...
Definition scheduler.c:1097
void srn_sched_run(srn_scheduler_t *sched, size_t nworkers)
Run the scheduler with nworkers os threads draining it, returning once the pool goes quiescent (every...
Definition scheduler.c:875
char name[SRN_FIBER_NAME_MAX]
Debug name, the caller's choice copied at creation, or autogenerated when the caller passed none (see...
Definition fiber.h:333
The result of srn_fiber_accept.
Definition io.h:86
Platform neutral static tracepoints.
#define SRN_TRACEPOINT(name,...)
Placement matters.
Definition trace.h:79
#define PANIC_IF_NULL(ptr)
Definition utils.h:66