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