/src/uWebSockets/fuzzing/EpollHelloWorld.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* We rely on wrapped syscalls */ |
2 | | #include "libEpollFuzzer/epoll_fuzzer.h" |
3 | | |
4 | | #include "App.h" |
5 | | |
6 | | /* We keep this one for teardown later on */ |
7 | | struct us_listen_socket_t *listen_socket; |
8 | | struct us_socket_t *client; |
9 | | |
10 | | /* This test is run by libEpollFuzzer */ |
11 | 5.93k | void test() { |
12 | | /* ws->getUserData returns one of these */ |
13 | 5.93k | struct PerSocketData { |
14 | | /* Fill with user data */ |
15 | 5.93k | }; |
16 | | |
17 | 5.93k | { |
18 | | /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. |
19 | | * You may swap to using uWS:App() if you don't need SSL */ |
20 | 5.93k | auto app = uWS::App({ |
21 | | /* There are example certificates in uWebSockets.js repo */ |
22 | 5.93k | .key_file_name = "../misc/key.pem", |
23 | 5.93k | .cert_file_name = "../misc/cert.pem", |
24 | 5.93k | .passphrase = "1234" |
25 | 5.93k | }).ws<PerSocketData>("/empty", { |
26 | | /* Having no handlers here should not crash */ |
27 | 7.76k | }).get("/*", [](auto *res, auto *req) { |
28 | 7.76k | if (req->getHeader("write").length()) { |
29 | 664 | res->writeStatus("200 OK")->writeHeader("write", "true")->write("Hello"); |
30 | 664 | res->write(" world!"); |
31 | 664 | res->end(); |
32 | 7.10k | } else if (req->getQuery().length()) { |
33 | 731 | res->close(); |
34 | 6.36k | } else { |
35 | 6.36k | res->end("Hello world!"); |
36 | 6.36k | } |
37 | 7.76k | }).post("/*", [](auto *res, auto *req) { |
38 | 3.15k | res->onAborted([]() { |
39 | | /* We might as well use this opportunity to stress the loop a bit */ |
40 | 1.67k | uWS::Loop::get()->defer([]() { |
41 | | |
42 | 1.56k | }); |
43 | 1.67k | }); |
44 | 6.01k | res->onData([res](std::string_view chunk, bool isEnd) { |
45 | 6.01k | if (isEnd) { |
46 | 1.48k | res->cork([res, chunk]() { |
47 | 1.48k | res->write("something ahead"); |
48 | 1.48k | res->end(chunk); |
49 | 1.48k | }); |
50 | 1.48k | } |
51 | 6.01k | }); |
52 | 4.10k | }).any("/:candy/*", [](auto *res, auto *req) { |
53 | 4.10k | if (req->getParameter(0).length() == 0) { |
54 | 0 | free((void *) -1); |
55 | 0 | } |
56 | | /* Some invalid queries */ |
57 | 4.10k | req->getParameter(30000); |
58 | 4.10k | req->getParameter((unsigned short) -34234); |
59 | 4.10k | req->getHeader("yhello"); |
60 | 4.10k | req->getQuery(); |
61 | 4.10k | req->getQuery("assd"); |
62 | | |
63 | 4.10k | res->end("done"); |
64 | 4.10k | }).ws<PerSocketData>("/*", { |
65 | | /* Settings */ |
66 | 5.93k | .compression = uWS::SHARED_COMPRESSOR, |
67 | 5.93k | .maxPayloadLength = 16 * 1024, |
68 | 5.93k | .idleTimeout = 12, |
69 | 5.93k | .maxBackpressure = 1024, |
70 | | /* Handlers */ |
71 | 34.7k | .open = [](auto *ws) { |
72 | | /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ |
73 | 34.7k | ws->getNativeHandle(); |
74 | 34.7k | ws->getRemoteAddressAsText(); |
75 | 34.7k | us_poll_ext((struct us_poll_t *) ws); |
76 | 34.7k | }, |
77 | 16.0k | .message = [](auto *ws, std::string_view message, uWS::OpCode opCode) { |
78 | 16.0k | ws->send(message, opCode, true); |
79 | 16.0k | }, |
80 | 5.93k | .drain = [](auto *ws) { |
81 | | /* Check ws->getBufferedAmount() here */ |
82 | 5.11k | }, |
83 | 8.89k | .ping = [](auto *ws, std::string_view) { |
84 | | /* We use this to trigger the async/wakeup feature */ |
85 | 8.89k | uWS::Loop::get()->defer([]() { |
86 | | /* Do nothing */ |
87 | 7.87k | }); |
88 | 8.89k | }, |
89 | 5.93k | .pong = [](auto *ws, std::string_view) { |
90 | | /* Not implemented yet */ |
91 | 1.00k | }, |
92 | 34.7k | .close = [](auto *ws, int code, std::string_view message) { |
93 | | /* You may access ws->getUserData() here */ |
94 | 34.7k | } |
95 | 5.93k | }).listen(9001, [](auto *listenSocket) { |
96 | 5.93k | listen_socket = listenSocket; |
97 | 5.93k | }); |
98 | | |
99 | | /* Here we want to stress the connect feature, since nothing else stresses it */ |
100 | 5.93k | struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get(); |
101 | | /* This function is stupid */ |
102 | 5.93k | us_loop_iteration_number(loop); |
103 | 5.93k | struct us_socket_context_t *client_context = us_create_socket_context(0, loop, 0, {}); |
104 | 5.93k | us_socket_context_timestamp(0, client_context); |
105 | 5.93k | client = us_socket_context_connect(0, client_context, "hostname", 5000, "localhost", 0, 0); |
106 | | |
107 | 5.93k | if (client) { |
108 | 3.04k | us_socket_is_established(0, client); |
109 | 3.04k | us_socket_local_port(0, client); |
110 | 3.04k | } |
111 | | |
112 | 5.93k | us_socket_context_on_connect_error(0, client_context, [](struct us_socket_t *s, int code) { |
113 | 2.67k | client = nullptr; |
114 | 2.67k | return s; |
115 | 2.67k | }); |
116 | | |
117 | 5.93k | us_socket_context_on_open(0, client_context, [](struct us_socket_t *s, int is_client, char *ip, int ip_length) { |
118 | 334 | us_socket_flush(0, s); |
119 | 334 | return s; |
120 | 334 | }); |
121 | | |
122 | 5.93k | us_socket_context_on_end(0, client_context, [](struct us_socket_t *s) { |
123 | | /* Someone sent is a FIN, but we can still send data */ |
124 | 770 | us_socket_write(0, s, "asdadasdasdasdaddfgdfhdfgdfg", 28, false); |
125 | 770 | return s; |
126 | 770 | }); |
127 | | |
128 | 5.93k | us_socket_context_on_data(0, client_context, [](struct us_socket_t *s, char *data, int length) { |
129 | 248 | return s; |
130 | 248 | }); |
131 | | |
132 | 5.93k | us_socket_context_on_writable(0, client_context, [](struct us_socket_t *s) { |
133 | | /* Let's defer a close here */ |
134 | 595 | us_socket_shutdown_read(0, s); |
135 | 595 | return s; |
136 | 595 | }); |
137 | | |
138 | 5.93k | us_socket_context_on_close(0, client_context, [](struct us_socket_t *s, int code, void *reason) { |
139 | 372 | client = NULL; |
140 | 372 | return s; |
141 | 372 | }); |
142 | | |
143 | | /* Trigger some context functions */ |
144 | 5.93k | app.addServerName("servername", {}); |
145 | 5.93k | app.removeServerName("servername"); |
146 | 5.93k | app.missingServerName(nullptr); |
147 | 5.93k | app.getNativeHandle(); |
148 | | |
149 | 5.93k | app.run(); |
150 | | |
151 | | /* After done we also free the client context */ |
152 | 5.93k | us_socket_context_free(0, client_context); |
153 | 5.93k | } |
154 | 5.93k | uWS::Loop::get()->setSilent(true); |
155 | 5.93k | uWS::Loop::get()->free(); |
156 | 5.93k | } |
157 | | |
158 | | /* Thus function should shutdown the event-loop and let the test fall through */ |
159 | 5.89k | void teardown() { |
160 | | /* If we are called twice there's a bug (it potentially could if |
161 | | * all open sockets cannot be error-closed in one epoll_wait call). |
162 | | * But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */ |
163 | 5.89k | if (!listen_socket && !client) { |
164 | 0 | exit(-1); |
165 | 0 | } |
166 | | |
167 | 5.89k | if (client) { |
168 | 135 | us_socket_close(0, client, 0, 0); |
169 | 135 | client = NULL; |
170 | 135 | } |
171 | | |
172 | | /* We might have open sockets still, and these will be error-closed by epoll_wait */ |
173 | | // us_socket_context_close - close all open sockets created with this socket context |
174 | 5.89k | if (listen_socket) { |
175 | 5.78k | us_listen_socket_close(0, listen_socket); |
176 | 5.78k | listen_socket = NULL; |
177 | 5.78k | } |
178 | 5.89k | } |