@velox0/cerver 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +35 -0
- package/.github/workflows/publish.yml +6 -4
- package/Makefile +26 -0
- package/bin/cerver.js +11 -0
- package/lib/commands/dev.js +23 -9
- package/lib/commands/new.js +273 -79
- package/lib/ir/transform.js +14 -20
- package/package.json +2 -2
- package/runtime/cerver.h +139 -129
- package/runtime/http_parser.c +153 -152
- package/runtime/http_writer.c +136 -126
- package/runtime/mime.c +49 -49
- package/runtime/router.c +99 -98
- package/runtime/server.c +594 -436
- package/runtime/static.c +174 -183
- package/runtime/tests/minunit.c +76 -0
- package/runtime/tests/minunit.h +64 -0
- package/runtime/tests/runtime_tests.c +414 -0
package/runtime/server.c
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
#include <fcntl.h>
|
|
21
21
|
#include <time.h>
|
|
22
22
|
#include <sys/mman.h>
|
|
23
|
+
#include <sys/time.h>
|
|
23
24
|
#include <sys/socket.h>
|
|
24
25
|
#include <sys/types.h>
|
|
25
26
|
#include <netinet/in.h>
|
|
@@ -28,63 +29,62 @@
|
|
|
28
29
|
#include <pthread.h>
|
|
29
30
|
|
|
30
31
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
#define CERVER_USE_KQUEUE 1
|
|
33
|
+
#include <sys/event.h>
|
|
33
34
|
#elif defined(__linux__)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
#define CERVER_USE_EPOLL 1
|
|
36
|
+
#include <sys/epoll.h>
|
|
37
|
+
#include <sys/sendfile.h>
|
|
37
38
|
#else
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
#define CERVER_USE_SELECT 1
|
|
40
|
+
#include <sys/select.h>
|
|
40
41
|
#endif
|
|
41
42
|
|
|
42
43
|
#ifdef __linux__
|
|
43
|
-
|
|
44
|
+
#include <sched.h>
|
|
44
45
|
#endif
|
|
45
46
|
|
|
46
47
|
/* Connection pool sizing */
|
|
47
|
-
#define CERVER_CONN_POOL_SIZE
|
|
48
|
-
#define CERVER_CONN_QUEUE_SIZE
|
|
48
|
+
#define CERVER_CONN_POOL_SIZE 128
|
|
49
|
+
#define CERVER_CONN_QUEUE_SIZE 4096
|
|
49
50
|
|
|
50
51
|
/* ------------------------------------------------------------------ */
|
|
51
52
|
/* memmem fallback */
|
|
52
53
|
/* ------------------------------------------------------------------ */
|
|
53
54
|
|
|
54
55
|
#if !defined(__APPLE__) && !defined(__linux__) && !defined(_GNU_SOURCE)
|
|
55
|
-
static void
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return NULL;
|
|
56
|
+
static void* cerver_memmem(const void* hay, size_t haylen, const void* needle, size_t nlen) {
|
|
57
|
+
if (nlen == 0) return (void*)hay;
|
|
58
|
+
if (nlen > haylen) return NULL;
|
|
59
|
+
const char* p = (const char*)hay;
|
|
60
|
+
const char* end = p + haylen - nlen;
|
|
61
|
+
for (; p <= end; p++) {
|
|
62
|
+
if (memcmp(p, needle, nlen) == 0) return (void*)p;
|
|
63
|
+
}
|
|
64
|
+
return NULL;
|
|
65
65
|
}
|
|
66
66
|
#define memmem cerver_memmem
|
|
67
67
|
#endif
|
|
68
68
|
|
|
69
69
|
/* Global for signal handler */
|
|
70
|
-
static cerver_server_t
|
|
70
|
+
static cerver_server_t* g_srv = NULL;
|
|
71
71
|
|
|
72
72
|
static void signal_handler(int sig) {
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
(void)sig;
|
|
74
|
+
if (g_srv) g_srv->running = 0;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
static int set_nonblocking(int fd) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
int flags = fcntl(fd, F_GETFL, 0);
|
|
79
|
+
if (flags < 0) return -1;
|
|
80
|
+
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
static int get_cpu_count(void) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
long n = sysconf(_SC_NPROCESSORS_ONLN);
|
|
85
|
+
if (n < 1) n = 1;
|
|
86
|
+
if (n > 64) n = 64;
|
|
87
|
+
return (int)n;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/* ------------------------------------------------------------------ */
|
|
@@ -92,178 +92,246 @@ static int get_cpu_count(void) {
|
|
|
92
92
|
/* ------------------------------------------------------------------ */
|
|
93
93
|
|
|
94
94
|
typedef struct {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
95
|
+
int fds[CERVER_CONN_QUEUE_SIZE];
|
|
96
|
+
int head;
|
|
97
|
+
int tail;
|
|
98
|
+
int count;
|
|
99
|
+
pthread_mutex_t lock;
|
|
100
|
+
pthread_cond_t not_empty;
|
|
101
101
|
} conn_queue_t;
|
|
102
102
|
|
|
103
|
-
static void cq_init(conn_queue_t
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
static void cq_init(conn_queue_t* q) {
|
|
104
|
+
memset(q->fds, -1, sizeof(q->fds));
|
|
105
|
+
q->head = q->tail = q->count = 0;
|
|
106
|
+
pthread_mutex_init(&q->lock, NULL);
|
|
107
|
+
pthread_cond_init(&q->not_empty, NULL);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
static void cq_destroy(conn_queue_t
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
static void cq_destroy(conn_queue_t* q) {
|
|
111
|
+
pthread_mutex_destroy(&q->lock);
|
|
112
|
+
pthread_cond_destroy(&q->not_empty);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
/* Returns 0 on success, -1 if queue is full (drop connection). */
|
|
116
|
-
static int cq_push(conn_queue_t
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
pthread_mutex_unlock(&q->lock);
|
|
120
|
-
return -1;
|
|
121
|
-
}
|
|
122
|
-
q->fds[q->tail] = fd;
|
|
123
|
-
q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
|
|
124
|
-
q->count++;
|
|
125
|
-
pthread_cond_signal(&q->not_empty);
|
|
116
|
+
static int cq_push(conn_queue_t* q, int fd) {
|
|
117
|
+
pthread_mutex_lock(&q->lock);
|
|
118
|
+
if (q->count >= CERVER_CONN_QUEUE_SIZE) {
|
|
126
119
|
pthread_mutex_unlock(&q->lock);
|
|
127
|
-
return
|
|
120
|
+
return -1;
|
|
121
|
+
}
|
|
122
|
+
q->fds[q->tail] = fd;
|
|
123
|
+
q->tail = (q->tail + 1) % CERVER_CONN_QUEUE_SIZE;
|
|
124
|
+
q->count++;
|
|
125
|
+
pthread_cond_signal(&q->not_empty);
|
|
126
|
+
pthread_mutex_unlock(&q->lock);
|
|
127
|
+
return 0;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
/* Returns fd, or -1 on shutdown. */
|
|
131
|
-
static int cq_pop(conn_queue_t
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
pthread_mutex_unlock(&q->lock);
|
|
142
|
-
return -1;
|
|
143
|
-
}
|
|
144
|
-
int fd = q->fds[q->head];
|
|
145
|
-
q->head = (q->head + 1) % CERVER_CONN_QUEUE_SIZE;
|
|
146
|
-
q->count--;
|
|
131
|
+
static int cq_pop(conn_queue_t* q, volatile int* running) {
|
|
132
|
+
pthread_mutex_lock(&q->lock);
|
|
133
|
+
while (q->count == 0 && *running) {
|
|
134
|
+
/* Timed wait so we can check running flag periodically */
|
|
135
|
+
struct timespec ts;
|
|
136
|
+
clock_gettime(CLOCK_REALTIME, &ts);
|
|
137
|
+
ts.tv_sec += 1;
|
|
138
|
+
pthread_cond_timedwait(&q->not_empty, &q->lock, &ts);
|
|
139
|
+
}
|
|
140
|
+
if (q->count == 0) {
|
|
147
141
|
pthread_mutex_unlock(&q->lock);
|
|
148
|
-
return
|
|
142
|
+
return -1;
|
|
143
|
+
}
|
|
144
|
+
int fd = q->fds[q->head];
|
|
145
|
+
q->head = (q->head + 1) % CERVER_CONN_QUEUE_SIZE;
|
|
146
|
+
q->count--;
|
|
147
|
+
pthread_mutex_unlock(&q->lock);
|
|
148
|
+
return fd;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
/* Global connection queue */
|
|
152
152
|
static conn_queue_t g_conn_queue;
|
|
153
153
|
|
|
154
|
+
/* Global worker readiness tracking */
|
|
155
|
+
typedef struct {
|
|
156
|
+
int workers_ready;
|
|
157
|
+
int workers_expected;
|
|
158
|
+
pthread_mutex_t lock;
|
|
159
|
+
pthread_cond_t ready_cv;
|
|
160
|
+
} worker_readiness_t;
|
|
161
|
+
|
|
162
|
+
static worker_readiness_t g_worker_readiness = {.workers_ready = 0,
|
|
163
|
+
.workers_expected = 0,
|
|
164
|
+
.lock = PTHREAD_MUTEX_INITIALIZER,
|
|
165
|
+
.ready_cv = PTHREAD_COND_INITIALIZER};
|
|
166
|
+
|
|
167
|
+
/* Global acceptor startup tracking */
|
|
168
|
+
typedef struct {
|
|
169
|
+
int acceptors_ready;
|
|
170
|
+
int start_accepting;
|
|
171
|
+
pthread_mutex_t lock;
|
|
172
|
+
pthread_cond_t ready_cv;
|
|
173
|
+
pthread_cond_t start_cv;
|
|
174
|
+
} acceptor_readiness_t;
|
|
175
|
+
|
|
176
|
+
static acceptor_readiness_t g_acceptor_readiness = {.acceptors_ready = 0,
|
|
177
|
+
.start_accepting = 0,
|
|
178
|
+
.lock = PTHREAD_MUTEX_INITIALIZER,
|
|
179
|
+
.ready_cv = PTHREAD_COND_INITIALIZER,
|
|
180
|
+
.start_cv = PTHREAD_COND_INITIALIZER};
|
|
181
|
+
|
|
154
182
|
/* ------------------------------------------------------------------ */
|
|
155
183
|
/* Buffered read */
|
|
156
184
|
/* ------------------------------------------------------------------ */
|
|
157
185
|
|
|
158
|
-
static char
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
186
|
+
static char* read_full_request(int fd, size_t* out_len) {
|
|
187
|
+
size_t cap = CERVER_READ_BUF;
|
|
188
|
+
size_t len = 0;
|
|
189
|
+
char* buf = malloc(cap + 1);
|
|
190
|
+
if (!buf) return NULL;
|
|
191
|
+
|
|
192
|
+
while (len < (size_t)CERVER_READ_BUF_MAX) {
|
|
193
|
+
ssize_t n = read(fd, buf + len, cap - len);
|
|
194
|
+
if (n <= 0) break;
|
|
195
|
+
len += (size_t)n;
|
|
196
|
+
if (len >= 4 && memmem(buf, len, "\r\n\r\n", 4)) break;
|
|
197
|
+
if (len == cap) {
|
|
198
|
+
size_t newcap = cap * 2;
|
|
199
|
+
if (newcap > (size_t)CERVER_READ_BUF_MAX) newcap = (size_t)CERVER_READ_BUF_MAX;
|
|
200
|
+
char* tmp = realloc(buf, newcap + 1);
|
|
201
|
+
if (!tmp) {
|
|
202
|
+
free(buf);
|
|
203
|
+
return NULL;
|
|
204
|
+
}
|
|
205
|
+
buf = tmp;
|
|
206
|
+
cap = newcap;
|
|
178
207
|
}
|
|
208
|
+
}
|
|
179
209
|
|
|
180
|
-
|
|
181
|
-
buf
|
|
182
|
-
|
|
183
|
-
|
|
210
|
+
if (len == 0) {
|
|
211
|
+
free(buf);
|
|
212
|
+
return NULL;
|
|
213
|
+
}
|
|
214
|
+
buf[len] = '\0';
|
|
215
|
+
*out_len = len;
|
|
216
|
+
return buf;
|
|
184
217
|
}
|
|
185
218
|
|
|
186
219
|
/* ------------------------------------------------------------------ */
|
|
187
220
|
/* Handle connection with keep-alive */
|
|
188
221
|
/* ------------------------------------------------------------------ */
|
|
189
222
|
|
|
190
|
-
static void handle_connection(cerver_server_t
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
fcntl(client_fd, F_SETFL, flags & ~O_NONBLOCK);
|
|
223
|
+
static void handle_connection(cerver_server_t* srv, int client_fd) {
|
|
224
|
+
int flags = fcntl(client_fd, F_GETFL, 0);
|
|
225
|
+
if (flags >= 0 && (flags & O_NONBLOCK)) fcntl(client_fd, F_SETFL, flags & ~O_NONBLOCK);
|
|
194
226
|
|
|
195
|
-
|
|
196
|
-
|
|
227
|
+
int nodelay = 1;
|
|
228
|
+
setsockopt(client_fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
|
|
197
229
|
|
|
198
|
-
|
|
199
|
-
|
|
230
|
+
int request_count = 0;
|
|
231
|
+
int keepalive = 1;
|
|
200
232
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
233
|
+
while (keepalive && srv->running && request_count < CERVER_KEEPALIVE_MAX) {
|
|
234
|
+
struct timeval tv;
|
|
235
|
+
tv.tv_sec = (request_count == 0) ? 5 : CERVER_KEEPALIVE_TIMEOUT;
|
|
236
|
+
tv.tv_usec = 0;
|
|
237
|
+
setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
206
238
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
239
|
+
size_t req_len = 0;
|
|
240
|
+
char* buf = read_full_request(client_fd, &req_len);
|
|
241
|
+
if (!buf || req_len == 0) {
|
|
242
|
+
if (buf) free(buf);
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
213
245
|
|
|
214
|
-
|
|
215
|
-
|
|
246
|
+
cerver_request_t req;
|
|
247
|
+
memset(&req, 0, sizeof(req));
|
|
216
248
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
249
|
+
if (cerver_parse_request(buf, req_len, &req) < 0) {
|
|
250
|
+
const char* resp =
|
|
251
|
+
"HTTP/1.1 400 Bad Request\r\n"
|
|
252
|
+
"Content-Length: 11\r\nConnection: close\r\n\r\nBad Request";
|
|
253
|
+
write(client_fd, resp, strlen(resp));
|
|
254
|
+
free(buf);
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
224
257
|
|
|
225
|
-
|
|
226
|
-
|
|
258
|
+
request_count++;
|
|
259
|
+
keepalive = !cerver_req_wants_close(&req);
|
|
227
260
|
|
|
228
|
-
|
|
229
|
-
|
|
261
|
+
cerver_response_t res;
|
|
262
|
+
memset(&res, 0, sizeof(res));
|
|
230
263
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
264
|
+
if (cerver_serve_static(srv, &req, &res) < 0) {
|
|
265
|
+
cerver_handler_fn handler = cerver_dispatch(srv, &req);
|
|
266
|
+
if (handler) {
|
|
267
|
+
handler(&req, &res);
|
|
268
|
+
} else {
|
|
269
|
+
cerver_res_text(&res, 404, "Not Found");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
239
272
|
|
|
240
|
-
|
|
273
|
+
if (res._force_close) keepalive = 0;
|
|
241
274
|
|
|
242
|
-
|
|
275
|
+
int write_err = cerver_write_response(client_fd, &res, keepalive);
|
|
243
276
|
|
|
244
|
-
|
|
245
|
-
|
|
277
|
+
if (res._body_owned == 1 && res.body)
|
|
278
|
+
free((void*)res.body);
|
|
279
|
+
else if (res._body_owned == 2 && res.body)
|
|
280
|
+
munmap((void*)res.body, res.body_len);
|
|
246
281
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
282
|
+
free(buf);
|
|
283
|
+
if (write_err < 0) break;
|
|
284
|
+
}
|
|
250
285
|
|
|
251
|
-
|
|
286
|
+
close(client_fd);
|
|
252
287
|
}
|
|
253
288
|
|
|
254
289
|
/* ------------------------------------------------------------------ */
|
|
255
290
|
/* Connection pool worker thread */
|
|
256
291
|
/* ------------------------------------------------------------------ */
|
|
257
292
|
|
|
258
|
-
static void
|
|
259
|
-
|
|
293
|
+
static void* conn_pool_worker(void* arg) {
|
|
294
|
+
cerver_server_t* srv = (cerver_server_t*)arg;
|
|
295
|
+
|
|
296
|
+
/* Signal that this worker is ready */
|
|
297
|
+
pthread_mutex_lock(&g_worker_readiness.lock);
|
|
298
|
+
g_worker_readiness.workers_ready++;
|
|
299
|
+
pthread_cond_broadcast(&g_worker_readiness.ready_cv);
|
|
300
|
+
pthread_mutex_unlock(&g_worker_readiness.lock);
|
|
301
|
+
|
|
302
|
+
while (srv->running) {
|
|
303
|
+
int fd = cq_pop(&g_conn_queue, &srv->running);
|
|
304
|
+
if (fd < 0) continue;
|
|
305
|
+
handle_connection(srv, fd);
|
|
306
|
+
}
|
|
307
|
+
return NULL;
|
|
308
|
+
}
|
|
260
309
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
310
|
+
static int wait_for_pool_workers_ready(cerver_server_t* srv, int expected) {
|
|
311
|
+
pthread_mutex_lock(&g_worker_readiness.lock);
|
|
312
|
+
while (g_worker_readiness.workers_ready < expected && srv->running) {
|
|
313
|
+
pthread_cond_wait(&g_worker_readiness.ready_cv, &g_worker_readiness.lock);
|
|
314
|
+
}
|
|
315
|
+
int ready = (g_worker_readiness.workers_ready >= expected);
|
|
316
|
+
pthread_mutex_unlock(&g_worker_readiness.lock);
|
|
317
|
+
return ready;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
static int wait_for_acceptors_ready(cerver_server_t* srv, int expected) {
|
|
321
|
+
pthread_mutex_lock(&g_acceptor_readiness.lock);
|
|
322
|
+
while (g_acceptor_readiness.acceptors_ready < expected && srv->running) {
|
|
323
|
+
pthread_cond_wait(&g_acceptor_readiness.ready_cv, &g_acceptor_readiness.lock);
|
|
324
|
+
}
|
|
325
|
+
int ready = (g_acceptor_readiness.acceptors_ready >= expected);
|
|
326
|
+
pthread_mutex_unlock(&g_acceptor_readiness.lock);
|
|
327
|
+
return ready;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
static void release_acceptors(void) {
|
|
331
|
+
pthread_mutex_lock(&g_acceptor_readiness.lock);
|
|
332
|
+
g_acceptor_readiness.start_accepting = 1;
|
|
333
|
+
pthread_cond_broadcast(&g_acceptor_readiness.start_cv);
|
|
334
|
+
pthread_mutex_unlock(&g_acceptor_readiness.lock);
|
|
267
335
|
}
|
|
268
336
|
|
|
269
337
|
/* ------------------------------------------------------------------ */
|
|
@@ -271,34 +339,40 @@ static void *conn_pool_worker(void *arg) {
|
|
|
271
339
|
/* ------------------------------------------------------------------ */
|
|
272
340
|
|
|
273
341
|
static int create_listener(int port, int reuseport) {
|
|
274
|
-
|
|
275
|
-
|
|
342
|
+
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
343
|
+
if (fd < 0) {
|
|
344
|
+
perror("cerver: socket");
|
|
345
|
+
return -1;
|
|
346
|
+
}
|
|
276
347
|
|
|
277
|
-
|
|
278
|
-
|
|
348
|
+
int opt = 1;
|
|
349
|
+
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
|
279
350
|
|
|
280
351
|
#ifdef __linux__
|
|
281
|
-
|
|
282
|
-
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
|
|
352
|
+
if (reuseport) setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
|
|
283
353
|
#else
|
|
284
|
-
|
|
354
|
+
(void)reuseport;
|
|
285
355
|
#endif
|
|
286
356
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
357
|
+
struct sockaddr_in addr;
|
|
358
|
+
memset(&addr, 0, sizeof(addr));
|
|
359
|
+
addr.sin_family = AF_INET;
|
|
360
|
+
addr.sin_addr.s_addr = INADDR_ANY;
|
|
361
|
+
addr.sin_port = htons((uint16_t)port);
|
|
292
362
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
363
|
+
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
|
364
|
+
perror("cerver: bind");
|
|
365
|
+
close(fd);
|
|
366
|
+
return -1;
|
|
367
|
+
}
|
|
368
|
+
if (listen(fd, CERVER_LISTEN_BACKLOG) < 0) {
|
|
369
|
+
perror("cerver: listen");
|
|
370
|
+
close(fd);
|
|
371
|
+
return -1;
|
|
372
|
+
}
|
|
299
373
|
|
|
300
|
-
|
|
301
|
-
|
|
374
|
+
set_nonblocking(fd);
|
|
375
|
+
return fd;
|
|
302
376
|
}
|
|
303
377
|
|
|
304
378
|
/* ------------------------------------------------------------------ */
|
|
@@ -306,12 +380,12 @@ static int create_listener(int port, int reuseport) {
|
|
|
306
380
|
/* ------------------------------------------------------------------ */
|
|
307
381
|
|
|
308
382
|
static int accept_connection(int listen_fd) {
|
|
309
|
-
|
|
310
|
-
|
|
383
|
+
struct sockaddr_in ca;
|
|
384
|
+
socklen_t cl = sizeof(ca);
|
|
311
385
|
#ifdef __linux__
|
|
312
|
-
|
|
386
|
+
return accept4(listen_fd, (struct sockaddr*)&ca, &cl, SOCK_CLOEXEC);
|
|
313
387
|
#else
|
|
314
|
-
|
|
388
|
+
return accept(listen_fd, (struct sockaddr*)&ca, &cl);
|
|
315
389
|
#endif
|
|
316
390
|
}
|
|
317
391
|
|
|
@@ -320,114 +394,155 @@ static int accept_connection(int listen_fd) {
|
|
|
320
394
|
/* ------------------------------------------------------------------ */
|
|
321
395
|
|
|
322
396
|
#if CERVER_USE_KQUEUE
|
|
323
|
-
static void
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
397
|
+
static void* acceptor_loop(void* arg) {
|
|
398
|
+
cerver_worker_t* w = (cerver_worker_t*)arg;
|
|
399
|
+
cerver_server_t* srv = w->srv;
|
|
400
|
+
|
|
401
|
+
int kq = kqueue();
|
|
402
|
+
if (kq < 0) {
|
|
403
|
+
perror("cerver: kqueue");
|
|
404
|
+
return NULL;
|
|
405
|
+
}
|
|
406
|
+
w->event_fd = kq;
|
|
407
|
+
|
|
408
|
+
struct kevent change;
|
|
409
|
+
EV_SET(&change, w->listen_fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
|
410
|
+
kevent(kq, &change, 1, NULL, 0, NULL);
|
|
411
|
+
|
|
412
|
+
struct kevent events[CERVER_MAX_EVENTS];
|
|
413
|
+
|
|
414
|
+
pthread_mutex_lock(&g_acceptor_readiness.lock);
|
|
415
|
+
g_acceptor_readiness.acceptors_ready++;
|
|
416
|
+
pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
|
|
417
|
+
while (!g_acceptor_readiness.start_accepting && srv->running) {
|
|
418
|
+
pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
|
|
419
|
+
}
|
|
420
|
+
pthread_mutex_unlock(&g_acceptor_readiness.lock);
|
|
421
|
+
|
|
422
|
+
while (srv->running) {
|
|
423
|
+
struct timespec ts = {1, 0};
|
|
424
|
+
int nev = kevent(kq, NULL, 0, events, CERVER_MAX_EVENTS, &ts);
|
|
425
|
+
if (nev < 0) {
|
|
426
|
+
if (errno == EINTR) continue;
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
for (int i = 0; i < nev; i++) {
|
|
431
|
+
if ((int)events[i].ident == w->listen_fd) {
|
|
432
|
+
while (1) {
|
|
433
|
+
int cfd = accept_connection(w->listen_fd);
|
|
434
|
+
if (cfd < 0) {
|
|
435
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
if (cq_push(&g_conn_queue, cfd) < 0) {
|
|
439
|
+
/* Queue full — send 503 and close */
|
|
440
|
+
const char* r =
|
|
441
|
+
"HTTP/1.1 503 Service Unavailable\r\n"
|
|
442
|
+
"Content-Length: 19\r\nConnection: close\r\n\r\n"
|
|
443
|
+
"Service Unavailable";
|
|
444
|
+
write(cfd, r, strlen(r));
|
|
445
|
+
close(cfd);
|
|
446
|
+
}
|
|
360
447
|
}
|
|
448
|
+
}
|
|
361
449
|
}
|
|
450
|
+
}
|
|
362
451
|
|
|
363
|
-
|
|
364
|
-
|
|
452
|
+
close(kq);
|
|
453
|
+
return NULL;
|
|
365
454
|
}
|
|
366
455
|
|
|
367
456
|
#elif CERVER_USE_EPOLL
|
|
368
|
-
static void
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
457
|
+
static void* acceptor_loop(void* arg) {
|
|
458
|
+
cerver_worker_t* w = (cerver_worker_t*)arg;
|
|
459
|
+
cerver_server_t* srv = w->srv;
|
|
460
|
+
|
|
461
|
+
int ep = epoll_create1(EPOLL_CLOEXEC);
|
|
462
|
+
if (ep < 0) {
|
|
463
|
+
perror("cerver: epoll");
|
|
464
|
+
return NULL;
|
|
465
|
+
}
|
|
466
|
+
w->event_fd = ep;
|
|
467
|
+
|
|
468
|
+
struct epoll_event ev = {.events = EPOLLIN, .data.fd = w->listen_fd};
|
|
469
|
+
epoll_ctl(ep, EPOLL_CTL_ADD, w->listen_fd, &ev);
|
|
470
|
+
|
|
471
|
+
struct epoll_event events[CERVER_MAX_EVENTS];
|
|
472
|
+
|
|
473
|
+
pthread_mutex_lock(&g_acceptor_readiness.lock);
|
|
474
|
+
g_acceptor_readiness.acceptors_ready++;
|
|
475
|
+
pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
|
|
476
|
+
while (!g_acceptor_readiness.start_accepting && srv->running) {
|
|
477
|
+
pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
|
|
478
|
+
}
|
|
479
|
+
pthread_mutex_unlock(&g_acceptor_readiness.lock);
|
|
480
|
+
|
|
481
|
+
while (srv->running) {
|
|
482
|
+
int nev = epoll_wait(ep, events, CERVER_MAX_EVENTS, 1000);
|
|
483
|
+
if (nev < 0) {
|
|
484
|
+
if (errno == EINTR) continue;
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
for (int i = 0; i < nev; i++) {
|
|
489
|
+
if (events[i].data.fd == w->listen_fd) {
|
|
490
|
+
while (1) {
|
|
491
|
+
int cfd = accept_connection(w->listen_fd);
|
|
492
|
+
if (cfd < 0) {
|
|
493
|
+
if (errno == EAGAIN || errno == EWOULDBLOCK) break;
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
if (cq_push(&g_conn_queue, cfd) < 0) {
|
|
497
|
+
const char* r =
|
|
498
|
+
"HTTP/1.1 503 Service Unavailable\r\n"
|
|
499
|
+
"Content-Length: 19\r\nConnection: close\r\n\r\n"
|
|
500
|
+
"Service Unavailable";
|
|
501
|
+
write(cfd, r, strlen(r));
|
|
502
|
+
close(cfd);
|
|
503
|
+
}
|
|
402
504
|
}
|
|
505
|
+
}
|
|
403
506
|
}
|
|
507
|
+
}
|
|
404
508
|
|
|
405
|
-
|
|
406
|
-
|
|
509
|
+
close(ep);
|
|
510
|
+
return NULL;
|
|
407
511
|
}
|
|
408
512
|
|
|
409
513
|
#else /* SELECT */
|
|
410
|
-
static void
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
514
|
+
static void* acceptor_loop(void* arg) {
|
|
515
|
+
cerver_worker_t* w = (cerver_worker_t*)arg;
|
|
516
|
+
cerver_server_t* srv = w->srv;
|
|
517
|
+
|
|
518
|
+
pthread_mutex_lock(&g_acceptor_readiness.lock);
|
|
519
|
+
g_acceptor_readiness.acceptors_ready++;
|
|
520
|
+
pthread_cond_broadcast(&g_acceptor_readiness.ready_cv);
|
|
521
|
+
while (!g_acceptor_readiness.start_accepting && srv->running) {
|
|
522
|
+
pthread_cond_wait(&g_acceptor_readiness.start_cv, &g_acceptor_readiness.lock);
|
|
523
|
+
}
|
|
524
|
+
pthread_mutex_unlock(&g_acceptor_readiness.lock);
|
|
525
|
+
|
|
526
|
+
while (srv->running) {
|
|
527
|
+
fd_set rfds;
|
|
528
|
+
FD_ZERO(&rfds);
|
|
529
|
+
FD_SET(w->listen_fd, &rfds);
|
|
530
|
+
struct timeval tv = {1, 0};
|
|
531
|
+
int ret = select(w->listen_fd + 1, &rfds, NULL, NULL, &tv);
|
|
532
|
+
if (ret < 0) {
|
|
533
|
+
if (errno == EINTR) continue;
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
if (ret > 0 && FD_ISSET(w->listen_fd, &rfds)) {
|
|
537
|
+
int cfd = accept_connection(w->listen_fd);
|
|
538
|
+
if (cfd >= 0) {
|
|
539
|
+
if (cq_push(&g_conn_queue, cfd) < 0) {
|
|
540
|
+
close(cfd);
|
|
428
541
|
}
|
|
542
|
+
}
|
|
429
543
|
}
|
|
430
|
-
|
|
544
|
+
}
|
|
545
|
+
return NULL;
|
|
431
546
|
}
|
|
432
547
|
#endif
|
|
433
548
|
|
|
@@ -435,221 +550,264 @@ static void *acceptor_loop(void *arg) {
|
|
|
435
550
|
/* Stat cache */
|
|
436
551
|
/* ------------------------------------------------------------------ */
|
|
437
552
|
|
|
438
|
-
void cerver_stat_cache_init(cerver_stat_cache_t
|
|
439
|
-
|
|
440
|
-
|
|
553
|
+
void cerver_stat_cache_init(cerver_stat_cache_t* cache) {
|
|
554
|
+
memset(cache, 0, sizeof(*cache));
|
|
555
|
+
pthread_mutex_init(&cache->lock, NULL);
|
|
441
556
|
}
|
|
442
557
|
|
|
443
|
-
int cerver_stat_cache_lookup(cerver_stat_cache_t
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
break;
|
|
457
|
-
}
|
|
558
|
+
int cerver_stat_cache_lookup(cerver_stat_cache_t* cache, const char* path, size_t* file_size) {
|
|
559
|
+
time_t now = time(NULL);
|
|
560
|
+
pthread_mutex_lock(&cache->lock);
|
|
561
|
+
for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
|
|
562
|
+
cerver_stat_entry_t* e = &cache->entries[i];
|
|
563
|
+
if (e->valid && strcmp(e->path, path) == 0) {
|
|
564
|
+
if (now - e->cached_at < CERVER_STAT_CACHE_TTL) {
|
|
565
|
+
*file_size = e->file_size;
|
|
566
|
+
pthread_mutex_unlock(&cache->lock);
|
|
567
|
+
return 0;
|
|
568
|
+
}
|
|
569
|
+
e->valid = 0;
|
|
570
|
+
break;
|
|
458
571
|
}
|
|
459
|
-
|
|
460
|
-
|
|
572
|
+
}
|
|
573
|
+
pthread_mutex_unlock(&cache->lock);
|
|
574
|
+
return -1;
|
|
461
575
|
}
|
|
462
576
|
|
|
463
|
-
void cerver_stat_cache_store(cerver_stat_cache_t
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
577
|
+
void cerver_stat_cache_store(cerver_stat_cache_t* cache, const char* path, size_t file_size,
|
|
578
|
+
time_t mtime) {
|
|
579
|
+
time_t now = time(NULL);
|
|
580
|
+
pthread_mutex_lock(&cache->lock);
|
|
581
|
+
int best = 0;
|
|
582
|
+
time_t oldest = cache->entries[0].cached_at;
|
|
583
|
+
for (int i = 0; i < CERVER_STAT_CACHE_SIZE; i++) {
|
|
584
|
+
cerver_stat_entry_t* e = &cache->entries[i];
|
|
585
|
+
if (!e->valid) {
|
|
586
|
+
best = i;
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
if (e->cached_at < oldest) {
|
|
590
|
+
oldest = e->cached_at;
|
|
591
|
+
best = i;
|
|
473
592
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
593
|
+
}
|
|
594
|
+
cerver_stat_entry_t* slot = &cache->entries[best];
|
|
595
|
+
strncpy(slot->path, path, sizeof(slot->path) - 1);
|
|
596
|
+
slot->path[sizeof(slot->path) - 1] = '\0';
|
|
597
|
+
slot->file_size = file_size;
|
|
598
|
+
slot->mtime = mtime;
|
|
599
|
+
slot->cached_at = now;
|
|
600
|
+
slot->valid = 1;
|
|
601
|
+
pthread_mutex_unlock(&cache->lock);
|
|
482
602
|
}
|
|
483
603
|
|
|
484
604
|
/* ------------------------------------------------------------------ */
|
|
485
605
|
/* Server init */
|
|
486
606
|
/* ------------------------------------------------------------------ */
|
|
487
607
|
|
|
488
|
-
int cerver_init(cerver_server_t
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
608
|
+
int cerver_init(cerver_server_t* srv, int port, int threads) {
|
|
609
|
+
memset(srv, 0, sizeof(*srv));
|
|
610
|
+
srv->port = port;
|
|
611
|
+
srv->sock_fd = -1;
|
|
612
|
+
srv->running = 0;
|
|
613
|
+
srv->public_dir = NULL;
|
|
614
|
+
srv->dispatch_override = NULL;
|
|
615
|
+
srv->worker_count = (threads > 0) ? threads : get_cpu_count();
|
|
616
|
+
srv->workers = NULL;
|
|
617
|
+
cerver_stat_cache_init(&srv->stat_cache);
|
|
618
|
+
return 0;
|
|
499
619
|
}
|
|
500
620
|
|
|
501
|
-
int cerver_add_routes(cerver_server_t
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
621
|
+
int cerver_add_routes(cerver_server_t* srv, cerver_route_t* routes, int count) {
|
|
622
|
+
srv->routes = routes;
|
|
623
|
+
srv->route_count = count;
|
|
624
|
+
return 0;
|
|
505
625
|
}
|
|
506
626
|
|
|
507
|
-
int cerver_set_assets(cerver_server_t
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
627
|
+
int cerver_set_assets(cerver_server_t* srv, cerver_asset_t* assets, int count) {
|
|
628
|
+
srv->assets = assets;
|
|
629
|
+
srv->asset_count = count;
|
|
630
|
+
return 0;
|
|
511
631
|
}
|
|
512
632
|
|
|
513
|
-
void cerver_set_public_dir(cerver_server_t
|
|
514
|
-
srv->public_dir = dir;
|
|
515
|
-
}
|
|
633
|
+
void cerver_set_public_dir(cerver_server_t* srv, const char* dir) { srv->public_dir = dir; }
|
|
516
634
|
|
|
517
635
|
/* ------------------------------------------------------------------ */
|
|
518
636
|
/* Server listen */
|
|
519
637
|
/* ------------------------------------------------------------------ */
|
|
520
638
|
|
|
521
|
-
int cerver_listen(cerver_server_t
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
639
|
+
int cerver_listen(cerver_server_t* srv) {
|
|
640
|
+
g_srv = srv;
|
|
641
|
+
signal(SIGINT, signal_handler);
|
|
642
|
+
signal(SIGTERM, signal_handler);
|
|
643
|
+
signal(SIGPIPE, SIG_IGN);
|
|
644
|
+
|
|
645
|
+
srv->running = 1;
|
|
646
|
+
|
|
647
|
+
/* Determine pool and acceptor counts.
|
|
648
|
+
* Acceptors: min(worker_count, cpu_count) — one per core for accept.
|
|
649
|
+
* Pool workers: worker_count * 16 — enough to cover concurrent keep-alive. */
|
|
650
|
+
int cpu_count = get_cpu_count();
|
|
651
|
+
int acceptor_count = cpu_count;
|
|
652
|
+
if (acceptor_count > srv->worker_count) acceptor_count = srv->worker_count;
|
|
653
|
+
if (acceptor_count < 1) acceptor_count = 1;
|
|
654
|
+
|
|
655
|
+
int pool_size = srv->worker_count * 16;
|
|
656
|
+
if (pool_size < CERVER_CONN_POOL_SIZE) pool_size = CERVER_CONN_POOL_SIZE;
|
|
657
|
+
if (pool_size > 1024) pool_size = 1024;
|
|
658
|
+
|
|
659
|
+
/* Init shared connection queue */
|
|
660
|
+
cq_init(&g_conn_queue);
|
|
661
|
+
|
|
662
|
+
/* Initialize worker readiness tracking */
|
|
663
|
+
pthread_mutex_lock(&g_worker_readiness.lock);
|
|
664
|
+
g_worker_readiness.workers_ready = 0;
|
|
665
|
+
g_worker_readiness.workers_expected = pool_size;
|
|
666
|
+
pthread_mutex_unlock(&g_worker_readiness.lock);
|
|
667
|
+
|
|
668
|
+
pthread_mutex_lock(&g_acceptor_readiness.lock);
|
|
669
|
+
g_acceptor_readiness.acceptors_ready = 0;
|
|
670
|
+
g_acceptor_readiness.start_accepting = 0;
|
|
671
|
+
pthread_mutex_unlock(&g_acceptor_readiness.lock);
|
|
672
|
+
|
|
673
|
+
/* Start connection pool workers */
|
|
674
|
+
pthread_t* pool_threads = calloc((size_t)pool_size, sizeof(pthread_t));
|
|
675
|
+
if (!pool_threads) {
|
|
676
|
+
perror("cerver: calloc pool");
|
|
677
|
+
if (srv->sock_fd >= 0) close(srv->sock_fd);
|
|
678
|
+
return -1;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
pthread_attr_t attr;
|
|
682
|
+
pthread_attr_init(&attr);
|
|
683
|
+
pthread_attr_setstacksize(&attr, 2 * 1024 * 1024);
|
|
684
|
+
|
|
685
|
+
for (int i = 0; i < pool_size; i++) {
|
|
686
|
+
if (pthread_create(&pool_threads[i], &attr, conn_pool_worker, srv) != 0) {
|
|
687
|
+
perror("cerver: pool thread create");
|
|
688
|
+
srv->running = 0;
|
|
689
|
+
for (int j = 0; j < i; j++) pthread_join(pool_threads[j], NULL);
|
|
690
|
+
free(pool_threads);
|
|
691
|
+
if (srv->sock_fd >= 0) close(srv->sock_fd);
|
|
692
|
+
pthread_attr_destroy(&attr);
|
|
693
|
+
return -1;
|
|
569
694
|
}
|
|
695
|
+
}
|
|
570
696
|
|
|
571
|
-
|
|
572
|
-
srv->
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
|
|
579
|
-
free(pool_threads);
|
|
580
|
-
close(srv->sock_fd);
|
|
581
|
-
pthread_attr_destroy(&attr);
|
|
582
|
-
return -1;
|
|
583
|
-
}
|
|
697
|
+
if (!wait_for_pool_workers_ready(srv, pool_size)) {
|
|
698
|
+
srv->running = 0;
|
|
699
|
+
for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
|
|
700
|
+
free(pool_threads);
|
|
701
|
+
pthread_attr_destroy(&attr);
|
|
702
|
+
return -1;
|
|
703
|
+
}
|
|
584
704
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
705
|
+
srv->sock_fd = create_listener(srv->port, 0);
|
|
706
|
+
if (srv->sock_fd < 0) {
|
|
707
|
+
srv->running = 0;
|
|
708
|
+
for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
|
|
709
|
+
free(pool_threads);
|
|
710
|
+
pthread_attr_destroy(&attr);
|
|
711
|
+
return -1;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/* Start acceptor threads */
|
|
715
|
+
srv->workers = calloc((size_t)acceptor_count, sizeof(cerver_worker_t));
|
|
716
|
+
srv->worker_count = acceptor_count;
|
|
717
|
+
if (!srv->workers) {
|
|
718
|
+
perror("cerver: calloc acceptors");
|
|
719
|
+
srv->running = 0;
|
|
720
|
+
pthread_cond_broadcast(&g_conn_queue.not_empty);
|
|
721
|
+
for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
|
|
722
|
+
free(pool_threads);
|
|
723
|
+
close(srv->sock_fd);
|
|
724
|
+
pthread_attr_destroy(&attr);
|
|
725
|
+
return -1;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
for (int i = 0; i < acceptor_count; i++) {
|
|
729
|
+
cerver_worker_t* w = &srv->workers[i];
|
|
730
|
+
w->id = i;
|
|
731
|
+
w->srv = srv;
|
|
732
|
+
w->event_fd = -1;
|
|
590
733
|
#ifdef __linux__
|
|
591
|
-
|
|
592
|
-
|
|
734
|
+
w->listen_fd = create_listener(srv->port, 1);
|
|
735
|
+
if (w->listen_fd < 0) w->listen_fd = srv->sock_fd;
|
|
593
736
|
#else
|
|
594
|
-
|
|
737
|
+
w->listen_fd = srv->sock_fd;
|
|
595
738
|
#endif
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
739
|
+
if (pthread_create(&w->thread, &attr, acceptor_loop, w) != 0) {
|
|
740
|
+
perror("cerver: acceptor create");
|
|
741
|
+
srv->running = 0;
|
|
742
|
+
for (int j = 0; j < i; j++) pthread_join(srv->workers[j].thread, NULL);
|
|
743
|
+
break;
|
|
602
744
|
}
|
|
745
|
+
}
|
|
603
746
|
|
|
747
|
+
if (!wait_for_acceptors_ready(srv, acceptor_count)) {
|
|
748
|
+
srv->running = 0;
|
|
749
|
+
release_acceptors();
|
|
750
|
+
for (int i = 0; i < acceptor_count; i++) pthread_join(srv->workers[i].thread, NULL);
|
|
751
|
+
for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
|
|
752
|
+
free(pool_threads);
|
|
604
753
|
pthread_attr_destroy(&attr);
|
|
754
|
+
return -1;
|
|
755
|
+
}
|
|
605
756
|
|
|
606
|
-
|
|
607
|
-
|
|
757
|
+
printf("cerver: listening on http://localhost:%d\n", srv->port);
|
|
758
|
+
printf("cerver: %d acceptor(s), %d connection workers, keep-alive max %d req/conn\n",
|
|
759
|
+
acceptor_count, pool_size, CERVER_KEEPALIVE_MAX);
|
|
608
760
|
|
|
609
|
-
|
|
610
|
-
srv->running = 0;
|
|
761
|
+
release_acceptors();
|
|
611
762
|
|
|
612
|
-
|
|
613
|
-
pthread_join(srv->workers[i].thread, NULL);
|
|
763
|
+
pthread_attr_destroy(&attr);
|
|
614
764
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
pthread_cond_broadcast(&g_conn_queue.not_empty);
|
|
618
|
-
pthread_mutex_unlock(&g_conn_queue.lock);
|
|
765
|
+
/* Main thread waits for shutdown */
|
|
766
|
+
while (srv->running) sleep(1);
|
|
619
767
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
768
|
+
/* Shutdown: stop acceptors first, then drain pool */
|
|
769
|
+
srv->running = 0;
|
|
770
|
+
|
|
771
|
+
for (int i = 0; i < acceptor_count; i++) pthread_join(srv->workers[i].thread, NULL);
|
|
772
|
+
|
|
773
|
+
/* Wake all pool workers */
|
|
774
|
+
pthread_mutex_lock(&g_conn_queue.lock);
|
|
775
|
+
pthread_cond_broadcast(&g_conn_queue.not_empty);
|
|
776
|
+
pthread_mutex_unlock(&g_conn_queue.lock);
|
|
623
777
|
|
|
624
|
-
|
|
625
|
-
|
|
778
|
+
for (int i = 0; i < pool_size; i++) pthread_join(pool_threads[i], NULL);
|
|
779
|
+
free(pool_threads);
|
|
780
|
+
|
|
781
|
+
cerver_shutdown(srv);
|
|
782
|
+
return 0;
|
|
626
783
|
}
|
|
627
784
|
|
|
628
785
|
/* ------------------------------------------------------------------ */
|
|
629
786
|
/* Shutdown */
|
|
630
787
|
/* ------------------------------------------------------------------ */
|
|
631
788
|
|
|
632
|
-
void cerver_shutdown(cerver_server_t
|
|
633
|
-
|
|
789
|
+
void cerver_shutdown(cerver_server_t* srv) {
|
|
790
|
+
srv->running = 0;
|
|
634
791
|
|
|
635
|
-
|
|
636
|
-
|
|
792
|
+
if (srv->workers) {
|
|
793
|
+
for (int i = 0; i < srv->worker_count; i++) {
|
|
637
794
|
#ifdef __linux__
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
close(srv->workers[i].listen_fd);
|
|
795
|
+
if (srv->workers[i].listen_fd != srv->sock_fd && srv->workers[i].listen_fd >= 0)
|
|
796
|
+
close(srv->workers[i].listen_fd);
|
|
641
797
|
#endif
|
|
642
|
-
|
|
643
|
-
close(srv->workers[i].event_fd);
|
|
644
|
-
}
|
|
645
|
-
free(srv->workers);
|
|
646
|
-
srv->workers = NULL;
|
|
798
|
+
if (srv->workers[i].event_fd >= 0) close(srv->workers[i].event_fd);
|
|
647
799
|
}
|
|
800
|
+
free(srv->workers);
|
|
801
|
+
srv->workers = NULL;
|
|
802
|
+
}
|
|
648
803
|
|
|
649
|
-
|
|
804
|
+
if (srv->sock_fd >= 0) {
|
|
805
|
+
close(srv->sock_fd);
|
|
806
|
+
srv->sock_fd = -1;
|
|
807
|
+
}
|
|
650
808
|
|
|
651
|
-
|
|
652
|
-
|
|
809
|
+
cq_destroy(&g_conn_queue);
|
|
810
|
+
pthread_mutex_destroy(&srv->stat_cache.lock);
|
|
653
811
|
|
|
654
|
-
|
|
812
|
+
printf("\ncerver: server stopped\n");
|
|
655
813
|
}
|