/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 | 6.94k | void test() { |
12 | | /* ws->getUserData returns one of these */ |
13 | 6.94k | struct PerSocketData { |
14 | | /* Fill with user data */ |
15 | 6.94k | }; |
16 | | |
17 | 6.94k | { |
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 | 6.94k | auto app = uWS::App({ |
21 | | /* There are example certificates in uWebSockets.js repo */ |
22 | 6.94k | .key_file_name = "../misc/key.pem", |
23 | 6.94k | .cert_file_name = "../misc/cert.pem", |
24 | 6.94k | .passphrase = "1234" |
25 | 6.94k | }).ws<PerSocketData>("/empty", { |
26 | | /* Having no handlers here should not crash */ |
27 | 11.1k | }).get("/*", [](auto *res, auto *req) { |
28 | 11.1k | if (req->getHeader("write").length()) { |
29 | 2.55k | res->writeStatus("200 OK")->writeHeader("write", "true")->write("Hello"); |
30 | 2.55k | res->write(" world!"); |
31 | 2.55k | res->end(); |
32 | 8.63k | } else if (req->getQuery().length()) { |
33 | 953 | res->close(); |
34 | 7.68k | } else { |
35 | 7.68k | res->end("Hello world!"); |
36 | 7.68k | } |
37 | 11.1k | }).post("/*", [](auto *res, auto *req) { |
38 | 4.44k | res->onAborted([]() { |
39 | | /* We might as well use this opportunity to stress the loop a bit */ |
40 | 3.00k | uWS::Loop::get()->defer([]() { |
41 | | |
42 | 2.78k | }); |
43 | 3.00k | }); |
44 | 33.5k | res->onData([res](std::string_view chunk, bool isEnd) { |
45 | 33.5k | if (isEnd) { |
46 | 1.43k | res->cork([res, chunk]() { |
47 | 1.43k | res->write("something ahead"); |
48 | 1.43k | res->end(chunk); |
49 | 1.43k | }); |
50 | 1.43k | } |
51 | 33.5k | }); |
52 | 4.44k | }).any("/:candy/*", [](auto *res, auto *req) { |
53 | 2.31k | if (req->getParameter(0).length() == 0) { |
54 | 0 | free((void *) -1); |
55 | 0 | } |
56 | | /* Some invalid queries */ |
57 | 2.31k | req->getParameter(30000); |
58 | 2.31k | req->getParameter((unsigned short) -34234); |
59 | 2.31k | req->getHeader("yhello"); |
60 | 2.31k | req->getQuery(); |
61 | 2.31k | req->getQuery("assd"); |
62 | | |
63 | 2.31k | res->end("done"); |
64 | 2.31k | }).ws<PerSocketData>("/*", { |
65 | | /* Settings */ |
66 | 6.94k | .compression = uWS::SHARED_COMPRESSOR, |
67 | 6.94k | .maxPayloadLength = 16 * 1024, |
68 | 6.94k | .idleTimeout = 12, |
69 | 6.94k | .maxBackpressure = 1024, |
70 | | /* Handlers */ |
71 | 39.0k | .open = [](auto *ws) { |
72 | | /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ |
73 | 39.0k | ws->getNativeHandle(); |
74 | 39.0k | ws->getRemoteAddressAsText(); |
75 | 39.0k | us_poll_ext((struct us_poll_t *) ws); |
76 | 39.0k | }, |
77 | 10.4k | .message = [](auto *ws, std::string_view message, uWS::OpCode opCode) { |
78 | 10.4k | ws->send(message, opCode, true); |
79 | 10.4k | }, |
80 | 6.94k | .drain = [](auto *ws) { |
81 | | /* Check ws->getBufferedAmount() here */ |
82 | 4.10k | }, |
83 | 6.97k | .ping = [](auto *ws, std::string_view) { |
84 | | /* We use this to trigger the async/wakeup feature */ |
85 | 6.97k | uWS::Loop::get()->defer([]() { |
86 | | /* Do nothing */ |
87 | 5.08k | }); |
88 | 6.97k | }, |
89 | 6.94k | .pong = [](auto *ws, std::string_view) { |
90 | | /* Not implemented yet */ |
91 | 1.22k | }, |
92 | 39.0k | .close = [](auto *ws, int code, std::string_view message) { |
93 | | /* You may access ws->getUserData() here */ |
94 | 39.0k | } |
95 | 6.94k | }).listen(9001, [](auto *listenSocket) { |
96 | 6.94k | listen_socket = listenSocket; |
97 | 6.94k | }); |
98 | | |
99 | | /* Here we want to stress the connect feature, since nothing else stresses it */ |
100 | 6.94k | struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get(); |
101 | | /* This function is stupid */ |
102 | 6.94k | us_loop_iteration_number(loop); |
103 | 6.94k | struct us_socket_context_t *client_context = us_create_socket_context(0, loop, 0, {}); |
104 | 6.94k | us_socket_context_timestamp(0, client_context); |
105 | 6.94k | client = us_socket_context_connect(0, client_context, "hostname", 5000, "localhost", 0, 0); |
106 | | |
107 | 6.94k | if (client) { |
108 | 2.03k | us_socket_is_established(0, client); |
109 | 2.03k | us_socket_local_port(0, client); |
110 | 2.03k | } |
111 | | |
112 | 6.94k | us_socket_context_on_connect_error(0, client_context, [](struct us_socket_t *s, int code) { |
113 | 1.76k | client = nullptr; |
114 | 1.76k | return s; |
115 | 1.76k | }); |
116 | | |
117 | 6.94k | us_socket_context_on_open(0, client_context, [](struct us_socket_t *s, int is_client, char *ip, int ip_length) { |
118 | 225 | us_socket_flush(0, s); |
119 | 225 | return s; |
120 | 225 | }); |
121 | | |
122 | 6.94k | 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 | 896 | us_socket_write(0, s, "asdadasdasdasdaddfgdfhdfgdfg", 28, false); |
125 | 896 | return s; |
126 | 896 | }); |
127 | | |
128 | 6.94k | us_socket_context_on_data(0, client_context, [](struct us_socket_t *s, char *data, int length) { |
129 | 295 | return s; |
130 | 295 | }); |
131 | | |
132 | 6.94k | us_socket_context_on_writable(0, client_context, [](struct us_socket_t *s) { |
133 | | /* Let's defer a close here */ |
134 | 844 | us_socket_shutdown_read(0, s); |
135 | 844 | return s; |
136 | 844 | }); |
137 | | |
138 | 6.94k | us_socket_context_on_close(0, client_context, [](struct us_socket_t *s, int code, void *reason) { |
139 | 269 | client = NULL; |
140 | 269 | return s; |
141 | 269 | }); |
142 | | |
143 | | /* Trigger some context functions */ |
144 | 6.94k | app.addServerName("servername", {}); |
145 | 6.94k | app.removeServerName("servername"); |
146 | 6.94k | app.missingServerName(nullptr); |
147 | 6.94k | app.getNativeHandle(); |
148 | | |
149 | 6.94k | app.run(); |
150 | | |
151 | | /* After done we also free the client context */ |
152 | 6.94k | us_socket_context_free(0, client_context); |
153 | 6.94k | } |
154 | 6.94k | uWS::Loop::get()->setSilent(true); |
155 | 6.94k | uWS::Loop::get()->free(); |
156 | 6.94k | } |
157 | | |
158 | | /* Thus function should shutdown the event-loop and let the test fall through */ |
159 | 6.90k | 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 | 6.90k | if (!listen_socket && !client) { |
164 | 0 | exit(-1); |
165 | 0 | } |
166 | | |
167 | 6.90k | if (client) { |
168 | 133 | us_socket_close(0, client, 0, 0); |
169 | 133 | client = NULL; |
170 | 133 | } |
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 | 6.90k | if (listen_socket) { |
175 | 6.80k | us_listen_socket_close(0, listen_socket); |
176 | 6.80k | listen_socket = NULL; |
177 | 6.80k | } |
178 | 6.90k | } |