-
Notifications
You must be signed in to change notification settings - Fork 301
Expand file tree
/
Copy pathhammer_test.c
More file actions
465 lines (370 loc) · 15.3 KB
/
hammer_test.c
File metadata and controls
465 lines (370 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
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
360
361
362
363
364
365
366
367
368
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
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
/* This example, or test, is a moron test where the library is being hammered in all the possible ways randomly over time */
#include <libusockets.h>
const int SSL = 1;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define PBSTR "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
#define PBWIDTH 60
void print_progress(double percentage) {
static int last_val = -1;
int val = (int) (percentage * 100);
if (last_val != -1 && val == last_val) {
return;
}
last_val = val;
int lpad = (int) (percentage * PBWIDTH);
int rpad = PBWIDTH - lpad;
printf("\r%3d%% [%.*s%*s]", val, lpad, PBSTR, rpad, "");
fflush(stdout);
}
// todo: properly put all of these in various ext data so to test them!
int opened_connections, closed_connections, operations_done;
struct us_socket_context_t *http_context, *websocket_context;
struct us_listen_socket_t *listen_socket;
int opened_clients, opened_servers, closed_clients, closed_servers;
// put in loop ext data
void *long_buffer;
unsigned int long_length = 5 * 1024 * 1024;
// also make sure to have socket ext data
// and context ext data
// and loop ext data
const double pad_should_always_be = 14.652752;
/* We begin smaller than the WS so that resize must fail (opposite of what we have in uWS).
* This triggers the libuv resize crash that must be fixed. */
struct http_socket {
double pad_invariant;
int is_http;
double post_pad_invariant;
int is_client;
char content[128];
};
struct web_socket {
double pad_invariant;
int is_http;
double post_pad_invariant;
int is_client;
char content[1024];
};
/* This checks the ext data state according to callbacks */
void assume_state(struct us_socket_t *s, int is_http) {
struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s);
if (hs->pad_invariant != pad_should_always_be || hs->post_pad_invariant != pad_should_always_be) {
printf("ERROR: Pad invariant is not correct!\n");
abort();
}
if (hs->is_http != is_http) {
printf("ERROR: State is: %d should be: %d. Terminating now!\n", hs->is_http, is_http);
abort();
}
// try and cause havoc (different size)
if (hs->is_http) {
memset(hs->content, 0, 128);
} else {
memset(hs->content, 0, 1024);
}
}
struct http_context {
// link to the other context here
char content[1];
};
// todo: it would be nice to randomly select socket instead of
// using the one responsible for the event
struct us_socket_t *perform_random_operation(struct us_socket_t *s) {
switch (rand() % 5) {
case 0: {
// close
return us_socket_close(SSL, s, 0, NULL);
}
case 1: {
// adoption cannot happen if closed!
if (!us_socket_is_closed(SSL, s)) {
if (rand() % 2) {
s = us_socket_context_adopt_socket(SSL, websocket_context, s, sizeof(struct web_socket));
struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s);
hs->is_http = 0;
} else {
s = us_socket_context_adopt_socket(SSL, http_context, s, sizeof(struct http_socket));
struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s);
hs->is_http = 1;
}
}
return perform_random_operation(s);
}
case 2: {
// write - causes the other end to receive the data (event) and possibly us
// to receive on writable event - could it be that we get stuck if the other end is closed?
// no because, if we do not get ack in time we will timeout after some time
us_socket_write(SSL, s, (char *) long_buffer, rand() % long_length, 0);
}
break;
case 3: {
// shutdown (on macOS we can get stuck in fin_wait_2 for some weird reason!)
// if we send fin, the other end sends data but then on writable closes? then fin is not sent?
// so we need to timeout here to ensure we are closed if no fin is received within 30 seconds
us_socket_shutdown(SSL, s);
us_socket_timeout(SSL, s, 16);
}
break;
case 4: {
/* Triggers all timeouts next iteration */
us_socket_timeout(SSL, s, 4);
us_wakeup_loop(us_socket_context_loop(SSL, us_socket_context(SSL, s)));
}
break;
}
return s;
}
void on_wakeup(struct us_loop_t *loop) {
// note: we expose internal functions to trigger a timeout sweep to find bugs
extern void us_internal_timer_sweep(struct us_loop_t *loop);
//us_internal_timer_sweep(loop);
}
// maybe use thse to count spurious wakeups?
// that is, if we get tons of pre/post over and over without any events
// that would point towards 100% cpu usage kind of bugs
void on_pre(struct us_loop_t *loop) {
}
void on_post(struct us_loop_t *loop) {
// check if we did perform_random_operation
}
struct us_socket_t *on_web_socket_writable(struct us_socket_t *s) {
assume_state(s, 0);
return perform_random_operation(s);
}
struct us_socket_t *on_http_socket_writable(struct us_socket_t *s) {
assume_state(s, 1);
return perform_random_operation(s);
}
struct us_socket_t *on_web_socket_close(struct us_socket_t *s, int code, void *reason) {
assume_state(s, 0);
struct web_socket *ws = (struct web_socket *) us_socket_ext(SSL, s);
if (ws->is_client) {
closed_clients++;
} else {
closed_servers++;
}
closed_connections++;
print_progress((double) closed_connections / 10000);
if (closed_connections == 10000) {
if (opened_clients != 5000) {
printf("VA I HELVETE STÄNGER LISTEN INNAN ÖPPNAT ALLA CLIENTS! %d\n", opened_clients);
exit(1);
}
us_listen_socket_close(SSL, listen_socket);
} else {
return perform_random_operation(s);
}
return s;
}
struct us_socket_t *on_http_socket_close(struct us_socket_t *s, int code, void *reason) {
assume_state(s, 1);
struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s);
if (hs->is_client) {
closed_clients++;
} else {
closed_servers++;
}
print_progress((double) closed_connections / 10000);
closed_connections++;
if (closed_connections == 10000) {
if (opened_clients != 5000) {
printf("VA I HELVETE STÄNGER LISTEN INNAN ÖPPNAT ALLA CLIENTS! %d\n", opened_clients);
exit(1);
}
us_listen_socket_close(SSL, listen_socket);
} else {
return perform_random_operation(s);
}
return s;
}
/* Same as below */
struct us_socket_t *on_web_socket_end(struct us_socket_t *s) {
assume_state(s, 0);
// we need to close on shutdown
s = us_socket_close(SSL, s, 0, NULL);
return perform_random_operation(s);
}
struct us_socket_t *on_http_socket_end(struct us_socket_t *s) {
assume_state(s, 1);
/* Getting a FIN and we just close down */
s = us_socket_close(SSL, s, 0, NULL);
/* I guess we do this, to extra check that following actions are ignored,
* we really should just return s here, but to stress the lib we do this */
return perform_random_operation(s);
}
struct us_socket_t *on_web_socket_data(struct us_socket_t *s, char *data, int length) {
assume_state(s, 0);
if (length == 0) {
printf("ERROR: Got data event with no data\n");
exit(-1);
}
return perform_random_operation(s);
}
struct us_socket_t *on_http_socket_data(struct us_socket_t *s, char *data, int length) {
assume_state(s, 1);
if (length == 0) {
printf("ERROR: Got data event with no data\n");
exit(-1);
}
return perform_random_operation(s);
}
struct us_socket_t *on_web_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
// fail here, this can never happen!
printf("ERROR: on_web_socket_open called!\n");
exit(-2);
}
/* This one drives progress, or well close actually drives progress but this allows sockets to close */
struct us_socket_t *next_connection() {
if (opened_clients == 5000) {
printf("ERROR! next_connection called when already having made all!\n");
abort();
}
struct us_socket_t *connection_socket;
if (!(connection_socket = us_socket_context_connect(SSL, http_context, "127.0.0.1", 3000, NULL, 0, sizeof(struct http_socket)))) {
/* This one we do not deal with, so just exit */
printf("FAILED TO START CONNECTION, WILL EXIT NOW\n");
exit(1);
}
/* Typically, in real applications you would want to us_socket_timeout the connection_socket
* to track a maximal connection timeout. However, because this test relies on perfect sync
* between number of server side sockets and number of client side sockets, we cannot use this
* feature since it is possible we send a SYN, timeout and close the client socket while
* the listen side accepts the SYN and opens up while the client side is missing, causing the
* test to fail. */
return connection_socket;
}
struct us_socket_t *on_http_socket_connect_error(struct us_socket_t *s, int code) {
/* On macOS it is common for a connect to end up here. Even though we have no real timeout
* it seems the system has a default connect timeout and we end up here. */
next_connection();
return s;
}
struct us_socket_t *on_web_socket_connect_error(struct us_socket_t *s, int code) {
printf("ERROR: WebSocket can never get connect errors!\n");
exit(1);
return s;
}
struct us_socket_t *on_http_socket_open(struct us_socket_t *s, int is_client, char *ip, int ip_length) {
struct http_socket *hs = (struct http_socket *) us_socket_ext(SSL, s);
hs->is_http = 1;
hs->pad_invariant = pad_should_always_be;
hs->post_pad_invariant = pad_should_always_be;
hs->is_client = is_client;
assume_state(s, 1);
opened_connections++;
if (is_client) {
opened_clients++;
} else {
opened_servers++;
}
if (is_client && opened_clients < 5000) {
next_connection();
}
return perform_random_operation(s);
}
struct us_socket_t *on_web_socket_timeout(struct us_socket_t *s) {
assume_state(s, 0);
return perform_random_operation(s);
}
struct us_socket_t *on_http_socket_timeout(struct us_socket_t *s) {
/* If we timed out before being established, we should always cancel connecting and connect again */
if (!us_socket_is_established(SSL, s)) {
if (s != (struct us_socket_t *) listen_socket) {
/* Connect sockets are not allowed to timeout since, in this hammer_test
* we can get a connection server-side yet no connection client side due
* to sending a SYN, the closing due to timeout, yet having the server side
* eventually accept and open its side causing out of sync */
printf("CONNECTION TIMEOUT!!! CANNOT HAPPEN!!\n");
exit(1);
/* It would be perfectly valid to perform the following if we did not care for
* having number of opened sockets server side synced with number of opened sockets
* client side (the following is typically what you would do) */
us_socket_close_connecting(SSL, s);
next_connection();
}
/* Okay, so this is the listen_socket */
static time_t last_time;
if (last_time && time(0) - last_time == 0) {
printf("TIMER IS FIRING TOO FAST!!!\n");
exit(1);
}
last_time = time(0);
print_progress((double) closed_connections / 10000);
us_socket_timeout(SSL, s, 16);
return s;
}
/* Assume this established socket is in the state of http */
assume_state(s, 1);
/* An established socket has timed out */
if (us_socket_is_shut_down(SSL, s)) {
/* If we have sent FIN but not received a FIN on the other side,
* we can actually end up stuck in FIN_WAIT_2 without seeing any
* progress (FIN_WAIT_2 is not a state kqueue will report as we
* still can get data). macOS does not seem to send FIN for closed
* sockets in all cases. */
return us_socket_close(SSL, s, 0, 0);
}
return perform_random_operation(s);
}
int main() {
fprintf(stderr, "Hello hammer test\n");
printf("Helloui\n");
srand(time(0));
long_buffer = calloc(long_length, 1);
struct us_loop_t *loop = us_create_loop(0, on_wakeup, on_pre, on_post, 0);
// us_loop_on_wakeup()
// us_loop_on_pre()
// us_loop_on_post()
// us_loop_on_poll()
// us_loop_on_timer()
// these are ignored for non-SSL
struct us_socket_context_options_t options;
memset(&options, 0, sizeof(struct us_socket_context_options_t));
options.key_file_name = "/home/alexhultman/uWebSockets.js/misc/key.pem";
options.cert_file_name = "/home/alexhultman/uWebSockets.js/misc/cert.pem";
options.passphrase = "1234";
http_context = us_create_socket_context(SSL, loop, sizeof(struct http_context), options);
us_socket_context_on_open(SSL, http_context, on_http_socket_open);
us_socket_context_on_data(SSL, http_context, on_http_socket_data);
us_socket_context_on_writable(SSL, http_context, on_http_socket_writable);
us_socket_context_on_close(SSL, http_context, on_http_socket_close);
us_socket_context_on_timeout(SSL, http_context, on_http_socket_timeout);
us_socket_context_on_end(SSL, http_context, on_http_socket_end);
us_socket_context_on_connect_error(SSL, http_context, on_http_socket_connect_error);
websocket_context = us_create_child_socket_context(SSL, http_context, sizeof(struct http_context));
us_socket_context_on_open(SSL, websocket_context, on_web_socket_open);
us_socket_context_on_data(SSL, websocket_context, on_web_socket_data);
us_socket_context_on_writable(SSL, websocket_context, on_web_socket_writable);
us_socket_context_on_close(SSL, websocket_context, on_web_socket_close);
us_socket_context_on_timeout(SSL, websocket_context, on_web_socket_timeout);
us_socket_context_on_end(SSL, websocket_context, on_web_socket_end);
us_socket_context_on_connect_error(SSL, websocket_context, on_web_socket_connect_error);
listen_socket = us_socket_context_listen(SSL, http_context, "127.0.0.1", 3000, 0, sizeof(struct http_socket));
/* We use the listen socket as a way to check so that timeout stamps don't
* deviate from wallclock time - let's use 16 seconds and check that we have
* at least 1 second diff since last trigger (allows iteration lag of 15 seconds) */
us_socket_timeout(SSL, (struct us_socket_t *) listen_socket, 16);
if (listen_socket) {
printf("Running hammer test over tcpip\n");
print_progress(0);
next_connection();
us_loop_run(loop);
} else {
printf("Cannot listen to port 3000!\n");
}
us_socket_context_free(SSL, websocket_context);
us_socket_context_free(SSL, http_context);
us_loop_free(loop);
free(long_buffer);
print_progress(1);
printf("\n");
if (opened_clients == 5000 && closed_clients == 5000 && opened_servers == 5000 && closed_servers == 5000) {
printf("ALL GOOD\n");
return 0;
} else {
printf("MISMATCHING! FAILED!\n");
return 1;
}
}