_ZN3uWS12TemplatedAppILb0EEC2ENS_20SocketContextOptionsE: 217| 2.25k| TemplatedApp(SocketContextOptions options = {}) { 218| 2.25k| httpContext = HttpContext::create(Loop::get(), options); 219| | 220| | /* Register default handler for 404 (can be overridden by user) */ 221| 2.25k| this->any("/*", [](auto *res, auto */*req*/) { 222| 2.25k| res->writeStatus("404 File Not Found"); 223| 2.25k| res->end("

File Not Found


uWebSockets/20 Server"); 224| 2.25k| }); 225| 2.25k| } _ZNK3uWS20SocketContextOptionscv27us_socket_context_options_tEv: 71| 2.25k| operator struct us_socket_context_options_t() const { 72| 2.25k| struct us_socket_context_options_t socket_context_options; 73| 2.25k| memcpy(&socket_context_options, this, sizeof(SocketContextOptions)); 74| 2.25k| return socket_context_options; 75| 2.25k| } _ZN3uWS12TemplatedAppILb0EE3anyENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEON5ofats13any_invocableIFvPNS_12HttpResponseILb0EEEPNS_11HttpRequestEEEE: 529| 2.25k| TemplatedApp &&any(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { 530| 2.25k| if (httpContext) { ------------------ | Branch (530:13): [True: 2.25k, False: 0] ------------------ 531| 2.25k| httpContext->onHttp("*", pattern, std::move(handler)); 532| 2.25k| } 533| 2.25k| return std::move(*this); 534| 2.25k| } _ZZN3uWS12TemplatedAppILb0EEC1ENS_20SocketContextOptionsEENKUlPT_PT0_E_clINS_12HttpResponseILb0EEENS_11HttpRequestEEEDaS4_S6_: 221| 14.7k| this->any("/*", [](auto *res, auto */*req*/) { 222| 14.7k| res->writeStatus("404 File Not Found"); 223| 14.7k| res->end("

File Not Found


uWebSockets/20 Server"); 224| 14.7k| }); _ZN3uWS12TemplatedAppILb0EED2Ev: 177| 4.50k| ~TemplatedApp() { 178| | /* Let's just put everything here */ 179| 4.50k| if (httpContext) { ------------------ | Branch (179:13): [True: 2.25k, False: 2.25k] ------------------ 180| 2.25k| httpContext->free(); 181| | 182| | /* Free all our webSocketContexts in a type less way */ 183| 2.25k| for (auto &webSocketContextDeleter : webSocketContextDeleters) { ------------------ | Branch (183:48): [True: 0, False: 2.25k] ------------------ 184| 0| webSocketContextDeleter(); 185| 0| } 186| 2.25k| } 187| | 188| | /* Delete TopicTree */ 189| 4.50k| if (topicTree) { ------------------ | Branch (189:13): [True: 0, False: 4.50k] ------------------ 190| 0| delete topicTree; 191| | 192| | /* And unregister loop callbacks */ 193| | /* We must unregister any loop post handler here */ 194| 0| Loop::get()->removePostHandler(topicTree); 195| 0| Loop::get()->removePreHandler(topicTree); 196| 0| } 197| 4.50k| } _ZN3uWS12TemplatedAppILb0EE3getENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEON5ofats13any_invocableIFvPNS_12HttpResponseILb0EEEPNS_11HttpRequestEEEE: 465| 2.25k| TemplatedApp &&get(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { 466| 2.25k| if (httpContext) { ------------------ | Branch (466:13): [True: 2.25k, False: 0] ------------------ 467| 2.25k| httpContext->onHttp("GET", pattern, std::move(handler)); 468| 2.25k| } 469| 2.25k| return std::move(*this); 470| 2.25k| } _ZN3uWS12TemplatedAppILb0EE6listenEiON5ofats13any_invocableIFvP18us_listen_socket_tEEE: 555| 2.25k| TemplatedApp &&listen(int port, MoveOnlyFunction &&handler) { 556| 2.25k| handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr); ------------------ | Branch (556:17): [True: 2.25k, False: 0] ------------------ 557| 2.25k| return std::move(*this); 558| 2.25k| } _ZN3uWS12TemplatedAppILb0EEC2EOS1_: 202| 2.25k| TemplatedApp(TemplatedApp &&other) { 203| | /* Move HttpContext */ 204| 2.25k| httpContext = other.httpContext; 205| 2.25k| other.httpContext = nullptr; 206| | 207| | /* Move webSocketContextDeleters */ 208| 2.25k| webSocketContextDeleters = std::move(other.webSocketContextDeleters); 209| | 210| 2.25k| webSocketContexts = std::move(other.webSocketContexts); 211| | 212| | /* Move TopicTree */ 213| 2.25k| topicTree = other.topicTree; 214| 2.25k| other.topicTree = nullptr; 215| 2.25k| } _ZN3uWS12TemplatedAppILb0EE3runEv: 590| 2.25k| TemplatedApp &&run() { 591| 2.25k| uWS::run(); 592| 2.25k| return std::move(*this); 593| 2.25k| } _ZN3uWS11AsyncSocketILb0EE4corkEv: 133| 629k| void cork() { 134| | /* Extra check for invalid corking of others */ 135| 629k| if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) { ------------------ | Branch (135:13): [True: 0, False: 629k] | Branch (135:42): [True: 0, False: 0] ------------------ 136| 0| std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl; 137| 0| std::terminate(); 138| 0| } 139| | 140| | /* What if another socket is corked? */ 141| 629k| getLoopData()->corkedSocket = this; 142| 629k| } _ZN3uWS11AsyncSocketILb0EE11getLoopDataEv: 85| 2.57M| LoopData *getLoopData() { 86| 2.57M| return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); 87| 2.57M| } _ZN3uWS11AsyncSocketILb0EE6uncorkEPKcib: 327| 629k| std::pair uncork(const char *src = nullptr, int length = 0, bool optionally = false) { 328| 629k| LoopData *loopData = getLoopData(); 329| | 330| 629k| if (loopData->corkedSocket == this) { ------------------ | Branch (330:13): [True: 629k, False: 0] ------------------ 331| 629k| loopData->corkedSocket = nullptr; 332| | 333| 629k| if (loopData->corkOffset) { ------------------ | Branch (333:17): [True: 29.5k, False: 599k] ------------------ 334| | /* Corked data is already accounted for via its write call */ 335| 29.5k| auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); 336| 29.5k| loopData->corkOffset = 0; 337| | 338| 29.5k| if (failed) { ------------------ | Branch (338:21): [True: 14.7k, False: 14.8k] ------------------ 339| | /* We do not need to care for buffering here, write does that */ 340| 14.7k| return {0, true}; 341| 14.7k| } 342| 29.5k| } 343| | 344| | /* We should only return with new writes, not things written to cork already */ 345| 614k| return write(src, length, optionally, 0); 346| 629k| } else { 347| | /* We are not even corked! */ 348| 0| return {0, false}; 349| 0| } 350| 629k| } _ZN3uWS11AsyncSocketILb0EE5writeEPKcibi: 236| 1.13M| std::pair write(const char *src, int length, bool optionally = false, int nextLength = 0) { 237| | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ 238| 1.13M| if (us_socket_is_closed(SSL, (us_socket_t *) this)) { ------------------ | Branch (238:13): [True: 570k, False: 565k] ------------------ 239| 570k| return {length, false}; 240| 570k| } 241| | 242| 565k| LoopData *loopData = getLoopData(); 243| 565k| AsyncSocketData *asyncSocketData = getAsyncSocketData(); 244| | 245| | /* We are limited if we have a per-socket buffer */ 246| 565k| if (asyncSocketData->buffer.length()) { ------------------ | Branch (246:13): [True: 52.2k, False: 512k] ------------------ 247| | /* Write off as much as we can */ 248| 52.2k| int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); 249| | 250| | /* On failure return, otherwise continue down the function */ 251| 52.2k| if ((unsigned int) written < asyncSocketData->buffer.length()) { ------------------ | Branch (251:17): [True: 48.1k, False: 4.15k] ------------------ 252| | 253| | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ 254| 48.1k| asyncSocketData->buffer.erase((unsigned int) written); 255| | 256| 48.1k| if (optionally) { ------------------ | Branch (256:21): [True: 6.70k, False: 41.4k] ------------------ 257| | /* Thankfully we can exit early here */ 258| 6.70k| return {0, true}; 259| 41.4k| } else { 260| | /* This path is horrible and points towards erroneous usage */ 261| 41.4k| asyncSocketData->buffer.append(src, (unsigned int) length); 262| | 263| 41.4k| return {length, true}; 264| 41.4k| } 265| 48.1k| } 266| | 267| | /* At this point we simply have no buffer and can continue as normal */ 268| 4.15k| asyncSocketData->buffer.clear(); 269| 4.15k| } 270| | 271| 517k| if (length) { ------------------ | Branch (271:13): [True: 476k, False: 40.8k] ------------------ 272| 476k| if (loopData->corkedSocket == this) { ------------------ | Branch (272:17): [True: 455k, False: 21.0k] ------------------ 273| | /* We are corked */ 274| 455k| if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { ------------------ | Branch (274:21): [True: 455k, False: 0] ------------------ 275| | /* If the entire chunk fits in cork buffer */ 276| 455k| memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); 277| 455k| loopData->corkOffset += (unsigned int) length; 278| | /* Fall through to default return */ 279| 455k| } else { 280| | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ 281| 0| if constexpr (false) { ------------------ | Branch (281:35): [Folded - Ignored] ------------------ 282| | /* Cork up as much as we can */ 283| 0| unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; 284| 0| memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); 285| 0| loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; 286| | 287| 0| auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); 288| 0| return {written + (int) stripped, failed}; 289| 0| } 290| | 291| | /* For non-SSL we take the penalty of two syscalls */ 292| 0| return uncork(src, length, optionally); 293| 0| } 294| 455k| } else { 295| | /* We are not corked */ 296| 21.0k| int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); 297| | 298| | /* Did we fail? */ 299| 21.0k| if (written < length) { ------------------ | Branch (299:21): [True: 14.7k, False: 6.36k] ------------------ 300| | /* If the write was optional then just bail out */ 301| 14.7k| if (optionally) { ------------------ | Branch (301:25): [True: 0, False: 14.7k] ------------------ 302| 0| return {written, true}; 303| 0| } 304| | 305| | /* Fall back to worst possible case (should be very rare for HTTP) */ 306| | /* At least we can reserve room for next chunk if we know it up front */ 307| 14.7k| if (nextLength) { ------------------ | Branch (307:25): [True: 0, False: 14.7k] ------------------ 308| 0| asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); 309| 0| } 310| | 311| | /* Buffer this chunk */ 312| 14.7k| asyncSocketData->buffer.append(src + written, (size_t) (length - written)); 313| | 314| | /* Return the failure */ 315| 14.7k| return {length, true}; 316| 14.7k| } 317| | /* Fall through to default return */ 318| 21.0k| } 319| 476k| } 320| | 321| | /* Default fall through return */ 322| 502k| return {length, false}; 323| 517k| } _ZN3uWS11AsyncSocketILb0EE7timeoutEj: 95| 69.9k| void timeout(unsigned int seconds) { 96| 69.9k| us_socket_timeout(SSL, (us_socket_t *) this, seconds); 97| 69.9k| } _ZN3uWS11AsyncSocketILb0EE17getBufferedAmountEv: 193| 8.13k| unsigned int getBufferedAmount() { 194| | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ 195| 8.13k| return (unsigned int) getAsyncSocketData()->buffer.totalLength(); 196| 8.13k| } _ZN3uWS11AsyncSocketILb0EE8shutdownEv: 100| 3.48k| void shutdown() { 101| 3.48k| us_socket_shutdown(SSL, (us_socket_t *) this); 102| 3.48k| } _ZN3uWS11AsyncSocketILb0EE5closeEv: 117| 41.5k| us_socket_t *close() { 118| 41.5k| return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); 119| 41.5k| } _ZN3uWS11AsyncSocketILb0EE18getAsyncSocketDataEv: 90| 875k| AsyncSocketData *getAsyncSocketData() { 91| 875k| return (AsyncSocketData *) us_socket_ext(SSL, (us_socket_t *) this); 92| 875k| } _ZN3uWS11AsyncSocketILb0EE8isCorkedEv: 145| 49.5k| bool isCorked() { 146| 49.5k| return getLoopData()->corkedSocket == this; 147| 49.5k| } _ZN3uWS11AsyncSocketILb0EE7canCorkEv: 150| 17.3k| bool canCork() { 151| 17.3k| return getLoopData()->corkedSocket == nullptr; 152| 17.3k| } _ZN3uWS15AsyncSocketDataILb0EEC2Ev: 82| 1.44M| AsyncSocketData() = default; _ZN3uWS12BackPressureC2Ev: 32| 1.44M| BackPressure() = default; _ZN3uWS12BackPressure6lengthEv: 44| 669k| size_t length() { 45| 669k| return buffer.length() - pendingRemoval; 46| 669k| } _ZN3uWS12BackPressure4dataEv: 57| 52.2k| const char *data() { 58| 52.2k| return buffer.data() + pendingRemoval; 59| 52.2k| } _ZN3uWS12BackPressure5eraseEj: 36| 48.1k| void erase(unsigned int length) { 37| 48.1k| pendingRemoval += length; 38| | /* Always erase a minimum of 1/32th the current backpressure */ 39| 48.1k| if (pendingRemoval > (buffer.length() >> 5)) { ------------------ | Branch (39:13): [True: 16.3k, False: 31.7k] ------------------ 40| 16.3k| buffer.erase(0, pendingRemoval); 41| 16.3k| pendingRemoval = 0; 42| 16.3k| } 43| 48.1k| } _ZN3uWS12BackPressure6appendEPKcm: 33| 56.1k| void append(const char *data, size_t length) { 34| 56.1k| buffer.append(data, length); 35| 56.1k| } _ZN3uWS12BackPressure5clearEv: 47| 4.15k| void clear() { 48| 4.15k| pendingRemoval = 0; 49| 4.15k| buffer.clear(); 50| 4.15k| } _ZN3uWS12BackPressure11totalLengthEv: 64| 8.13k| size_t totalLength() { 65| 8.13k| return buffer.length(); 66| 8.13k| } _ZN3uWS11BloomFilter5resetEv: 75| 51.9k| void reset() { 76| 51.9k| filter.reset(); 77| 51.9k| } _ZN3uWS11BloomFilter3addENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE: 64| 70.4k| void add(std::string_view key) { 65| 70.4k| if (key.length() >= 2) { ------------------ | Branch (65:13): [True: 69.1k, False: 1.29k] ------------------ 66| 69.1k| ScrambleArea s = getFeatures(key); 67| 69.1k| s.val = perfectHash(s.val); 68| 69.1k| filter[s.p[0]] = 1; 69| 69.1k| filter[s.p[1]] = 1; 70| 69.1k| filter[s.p[2]] = 1; 71| 69.1k| filter[s.p[3]] = 1; 72| 69.1k| } 73| 70.4k| } _ZN3uWS11BloomFilter11getFeaturesENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE: 41| 320k| ScrambleArea getFeatures(std::string_view key) { 42| 320k| ScrambleArea s; 43| 320k| s.p[0] = reinterpret_cast(key[0]); 44| 320k| s.p[1] = reinterpret_cast(key[key.length() - 1]); 45| 320k| s.p[2] = reinterpret_cast(key[key.length() - 2]); 46| 320k| s.p[3] = reinterpret_cast(key[key.length() >> 1]); 47| 320k| return s; 48| 320k| } _ZN3uWS11BloomFilter11perfectHashEj: 32| 320k| static inline uint32_t perfectHash(uint32_t features) { 33| 320k| return features *= 1843993368; 34| 320k| } _ZN3uWS11BloomFilter9mightHaveENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE: 51| 251k| bool mightHave(std::string_view key) { 52| 251k| if (key.length() < 2) { ------------------ | Branch (52:13): [True: 0, False: 251k] ------------------ 53| 0| return true; 54| 0| } 55| | 56| 251k| ScrambleArea s = getFeatures(key); 57| 251k| s.val = perfectHash(s.val); 58| 251k| return filter[s.p[0]] && ------------------ | Branch (58:16): [True: 86.6k, False: 164k] ------------------ 59| 251k| filter[s.p[1]] && ------------------ | Branch (59:9): [True: 73.6k, False: 13.0k] ------------------ 60| 251k| filter[s.p[2]] && ------------------ | Branch (60:9): [True: 62.5k, False: 11.0k] ------------------ 61| 251k| filter[s.p[3]]; ------------------ | Branch (61:9): [True: 62.3k, False: 240] ------------------ 62| 251k| } _ZN3uWS24isParsingChunkedEncodingEm: 95| 4.33k| inline bool isParsingChunkedEncoding(uint64_t state) { 96| 4.33k| return state & ~STATE_SIZE_MASK; 97| 4.33k| } _ZN3uWS11HttpContextILb0EE6createEPNS_4LoopE27us_socket_context_options_t: 394| 2.25k| static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) { 395| 2.25k| HttpContext *httpContext; 396| | 397| 2.25k| httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData), options); 398| | 399| 2.25k| if (!httpContext) { ------------------ | Branch (399:13): [True: 0, False: 2.25k] ------------------ 400| 0| return nullptr; 401| 0| } 402| | 403| | /* Init socket context data */ 404| 2.25k| new ((HttpContextData *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData(); 405| 2.25k| return httpContext->init(); 406| 2.25k| } _ZN3uWS11HttpContextILb0EE4initEv: 70| 2.25k| HttpContext *init() { 71| | /* Handle socket connections */ 72| 2.25k| us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) { 73| | /* Any connected socket should timeout until it has a request */ 74| 2.25k| us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); 75| | 76| | /* Init socket ext */ 77| 2.25k| new (us_socket_ext(SSL, s)) HttpResponseData; 78| | 79| | /* Call filter */ 80| 2.25k| HttpContextData *httpContextData = getSocketContextDataS(s); 81| 2.25k| for (auto &f : httpContextData->filterHandlers) { 82| 2.25k| f((HttpResponse *) s, 1); 83| 2.25k| } 84| | 85| 2.25k| return s; 86| 2.25k| }); 87| | 88| | /* Handle socket disconnections */ 89| 2.25k| us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { 90| | /* Get socket ext */ 91| 2.25k| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, s); 92| | 93| | /* Call filter */ 94| 2.25k| HttpContextData *httpContextData = getSocketContextDataS(s); 95| 2.25k| for (auto &f : httpContextData->filterHandlers) { 96| 2.25k| f((HttpResponse *) s, -1); 97| 2.25k| } 98| | 99| | /* Signal broken HTTP request only if we have a pending request */ 100| 2.25k| if (httpResponseData->onAborted) { 101| 2.25k| httpResponseData->onAborted(); 102| 2.25k| } 103| | 104| | /* Destruct socket ext */ 105| 2.25k| httpResponseData->~HttpResponseData(); 106| | 107| 2.25k| return s; 108| 2.25k| }); 109| | 110| | /* Handle HTTP data streams */ 111| 2.25k| us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) { 112| | 113| | // total overhead is about 210k down to 180k 114| | // ~210k req/sec is the original perf with write in data 115| | // ~200k req/sec is with cork and formatting 116| | // ~190k req/sec is with http parsing 117| | // ~180k - 190k req/sec is with varying routing 118| | 119| 2.25k| HttpContextData *httpContextData = getSocketContextDataS(s); 120| | 121| | /* Do not accept any data while in shutdown state */ 122| 2.25k| if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { 123| 2.25k| return s; 124| 2.25k| } 125| | 126| 2.25k| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, s); 127| | 128| | /* Cork this socket */ 129| 2.25k| ((AsyncSocket *) s)->cork(); 130| | 131| | /* Mark that we are inside the parser now */ 132| 2.25k| httpContextData->isParsingHttp = true; 133| | 134| | // clients need to know the cursor after http parse, not servers! 135| | // how far did we read then? we need to know to continue with websocket parsing data? or? 136| | 137| 2.25k| void *proxyParser = nullptr; 138| |#ifdef UWS_WITH_PROXY 139| | proxyParser = &httpResponseData->proxyParser; 140| |#endif 141| | 142| | /* The return value is entirely up to us to interpret. The HttpParser only care for whether the returned value is DIFFERENT or not from passed user */ 143| 2.25k| auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * { 144| | /* For every request we reset the timeout and hang until user makes action */ 145| | /* Warning: if we are in shutdown state, resetting the timer is a security issue! */ 146| 2.25k| us_socket_timeout(SSL, (us_socket_t *) s, 0); 147| | 148| | /* Reset httpResponse */ 149| 2.25k| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, (us_socket_t *) s); 150| 2.25k| httpResponseData->offset = 0; 151| | 152| | /* Are we not ready for another request yet? Terminate the connection. */ 153| 2.25k| if (httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) { 154| 2.25k| us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); 155| 2.25k| return nullptr; 156| 2.25k| } 157| | 158| | /* Mark pending request and emit it */ 159| 2.25k| httpResponseData->state = HttpResponseData::HTTP_RESPONSE_PENDING; 160| | 161| | /* Mark this response as connectionClose if ancient or connection: close */ 162| 2.25k| if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { 163| 2.25k| httpResponseData->state |= HttpResponseData::HTTP_CONNECTION_CLOSE; 164| 2.25k| } 165| | 166| | /* Select the router based on SNI (only possible for SSL) */ 167| 2.25k| auto *selectedRouter = &httpContextData->router; 168| 2.25k| if constexpr (SSL) { 169| 2.25k| void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); 170| 2.25k| if (domainRouter) { 171| 2.25k| selectedRouter = (decltype(selectedRouter)) domainRouter; 172| 2.25k| } 173| 2.25k| } 174| | 175| | /* Route the method and URL */ 176| 2.25k| selectedRouter->getUserData() = {(HttpResponse *) s, httpRequest}; 177| 2.25k| if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { 178| | /* We have to force close this socket as we have no handler for it */ 179| 2.25k| us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); 180| 2.25k| return nullptr; 181| 2.25k| } 182| | 183| | /* First of all we need to check if this socket was deleted due to upgrade */ 184| 2.25k| if (httpContextData->upgradedWebSocket) { 185| | /* We differ between closed and upgraded below */ 186| 2.25k| return nullptr; 187| 2.25k| } 188| | 189| | /* Was the socket closed? */ 190| 2.25k| if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { 191| 2.25k| return nullptr; 192| 2.25k| } 193| | 194| | /* We absolutely have to terminate parsing if shutdown */ 195| 2.25k| if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { 196| 2.25k| return nullptr; 197| 2.25k| } 198| | 199| | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ 200| 2.25k| if (!((HttpResponse *) s)->hasResponded() && !httpResponseData->onAborted) { 201| | /* Throw exception here? */ 202| 2.25k| std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; 203| 2.25k| std::terminate(); 204| 2.25k| } 205| | 206| | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ 207| 2.25k| if (!((HttpResponse *) s)->hasResponded() && httpResponseData->inStream) { 208| 2.25k| us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); 209| 2.25k| } 210| | 211| | /* Continue parsing */ 212| 2.25k| return s; 213| | 214| 2.25k| }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { 215| | /* We always get an empty chunk even if there is no data */ 216| 2.25k| if (httpResponseData->inStream) { 217| | 218| | /* Todo: can this handle timeout for non-post as well? */ 219| 2.25k| if (fin) { 220| | /* If we just got the last chunk (or empty chunk), disable timeout */ 221| 2.25k| us_socket_timeout(SSL, (struct us_socket_t *) user, 0); 222| 2.25k| } else { 223| | /* We still have some more data coming in later, so reset timeout */ 224| | /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */ 225| 2.25k| httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); 226| 2.25k| if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { 227| 2.25k| us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); 228| 2.25k| httpResponseData->received_bytes_per_timeout = 0; 229| 2.25k| } 230| 2.25k| } 231| | 232| | /* We might respond in the handler, so do not change timeout after this */ 233| 2.25k| httpResponseData->inStream(data, fin); 234| | 235| | /* Was the socket closed? */ 236| 2.25k| if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { 237| 2.25k| return nullptr; 238| 2.25k| } 239| | 240| | /* We absolutely have to terminate parsing if shutdown */ 241| 2.25k| if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { 242| 2.25k| return nullptr; 243| 2.25k| } 244| | 245| | /* If we were given the last data chunk, reset data handler to ensure following 246| | * requests on the same socket won't trigger any previously registered behavior */ 247| 2.25k| if (fin) { 248| 2.25k| httpResponseData->inStream = nullptr; 249| 2.25k| } 250| 2.25k| } 251| 2.25k| return user; 252| 2.25k| }); 253| | 254| | /* Mark that we are no longer parsing Http */ 255| 2.25k| httpContextData->isParsingHttp = false; 256| | 257| | /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */ 258| 2.25k| if (returnedSocket == FULLPTR) { 259| | /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */ 260| 2.25k| us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); 261| 2.25k| us_socket_shutdown(SSL, s); 262| | /* Close any socket on HTTP errors */ 263| 2.25k| us_socket_close(SSL, s, 0, nullptr); 264| | /* This just makes the following code act as if the socket was closed from error inside the parser. */ 265| 2.25k| returnedSocket = nullptr; 266| 2.25k| } 267| | 268| | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ 269| 2.25k| if (returnedSocket != nullptr) { 270| | /* Timeout on uncork failure */ 271| 2.25k| auto [written, failed] = ((AsyncSocket *) returnedSocket)->uncork(); 272| 2.25k| if (failed) { 273| | /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */ 274| | /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */ 275| 2.25k| ((AsyncSocket *) s)->timeout(HTTP_IDLE_TIMEOUT_S); 276| 2.25k| } 277| | 278| | /* We need to check if we should close this socket here now */ 279| 2.25k| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { 280| 2.25k| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { 281| 2.25k| if (((AsyncSocket *) s)->getBufferedAmount() == 0) { 282| 2.25k| ((AsyncSocket *) s)->shutdown(); 283| | /* We need to force close after sending FIN since we want to hinder 284| | * clients from keeping to send their huge data */ 285| 2.25k| ((AsyncSocket *) s)->close(); 286| 2.25k| } 287| 2.25k| } 288| 2.25k| } 289| | 290| 2.25k| return (us_socket_t *) returnedSocket; 291| 2.25k| } 292| | 293| | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ 294| 2.25k| if (httpContextData->upgradedWebSocket) { 295| | /* This path is only for upgraded websockets */ 296| 2.25k| AsyncSocket *asyncSocket = (AsyncSocket *) httpContextData->upgradedWebSocket; 297| | 298| | /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */ 299| 2.25k| auto [written, failed] = asyncSocket->uncork(); 300| | 301| | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ 302| 2.25k| if (!failed) { 303| 2.25k| WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); 304| 2.25k| if (webSocketData->isShuttingDown) { 305| | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ 306| 2.25k| asyncSocket->shutdown(); 307| 2.25k| } 308| 2.25k| } 309| | 310| | /* Reset upgradedWebSocket before we return */ 311| 2.25k| httpContextData->upgradedWebSocket = nullptr; 312| | 313| | /* Return the new upgraded websocket */ 314| 2.25k| return (us_socket_t *) asyncSocket; 315| 2.25k| } 316| | 317| | /* It is okay to uncork a closed socket and we need to */ 318| 2.25k| ((AsyncSocket *) s)->uncork(); 319| | 320| | /* We cannot return nullptr to the underlying stack in any case */ 321| 2.25k| return s; 322| 2.25k| }); 323| | 324| | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ 325| 2.25k| us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { 326| | 327| 2.25k| AsyncSocket *asyncSocket = (AsyncSocket *) s; 328| 2.25k| HttpResponseData *httpResponseData = (HttpResponseData *) asyncSocket->getAsyncSocketData(); 329| | 330| | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ 331| 2.25k| if (httpResponseData->onWritable) { 332| | /* We are now writable, so hang timeout again, the user does not have to do anything so we should hang until end or tryEnd rearms timeout */ 333| 2.25k| us_socket_timeout(SSL, s, 0); 334| | 335| | /* We expect the developer to return whether or not write was successful (true). 336| | * If write was never called, the developer should still return true so that we may drain. */ 337| 2.25k| bool success = httpResponseData->callOnWritable(httpResponseData->offset); 338| | 339| | /* The developer indicated that their onWritable failed. */ 340| 2.25k| if (!success) { 341| | /* Skip testing if we can drain anything since that might perform an extra syscall */ 342| 2.25k| return s; 343| 2.25k| } 344| | 345| | /* We don't want to fall through since we don't want to mess with timeout. 346| | * It makes little sense to drain any backpressure when the user has registered onWritable. */ 347| 2.25k| return s; 348| 2.25k| } 349| | 350| | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ 351| 2.25k| /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0); 352| | 353| | /* Should we close this connection after a response - and is this response really done? */ 354| 2.25k| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { 355| 2.25k| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { 356| 2.25k| if (asyncSocket->getBufferedAmount() == 0) { 357| 2.25k| asyncSocket->shutdown(); 358| | /* We need to force close after sending FIN since we want to hinder 359| | * clients from keeping to send their huge data */ 360| 2.25k| asyncSocket->close(); 361| 2.25k| } 362| 2.25k| } 363| 2.25k| } 364| | 365| | /* Expect another writable event, or another request within the timeout */ 366| 2.25k| asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); 367| | 368| 2.25k| return s; 369| 2.25k| }); 370| | 371| | /* Handle FIN, HTTP does not support half-closed sockets, so simply close */ 372| 2.25k| us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { 373| | 374| | /* We do not care for half closed sockets */ 375| 2.25k| AsyncSocket *asyncSocket = (AsyncSocket *) s; 376| 2.25k| return asyncSocket->close(); 377| | 378| 2.25k| }); 379| | 380| | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ 381| 2.25k| us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { 382| | 383| | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ 384| 2.25k| AsyncSocket *asyncSocket = (AsyncSocket *) s; 385| 2.25k| return asyncSocket->close(); 386| | 387| 2.25k| }); 388| | 389| 2.25k| return this; 390| 2.25k| } _ZN3uWS11HttpContextILb0EE16getSocketContextEv: 53| 24.7k| us_socket_context_t *getSocketContext() { 54| 24.7k| return (us_socket_context_t *) this; 55| 24.7k| } _ZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tiPciE_clES3_iS4_i: 72| 1.44M| us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) { 73| | /* Any connected socket should timeout until it has a request */ 74| 1.44M| us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); 75| | 76| | /* Init socket ext */ 77| 1.44M| new (us_socket_ext(SSL, s)) HttpResponseData; 78| | 79| | /* Call filter */ 80| 1.44M| HttpContextData *httpContextData = getSocketContextDataS(s); 81| 1.44M| for (auto &f : httpContextData->filterHandlers) { ------------------ | Branch (81:26): [True: 0, False: 1.44M] ------------------ 82| 0| f((HttpResponse *) s, 1); 83| 0| } 84| | 85| 1.44M| return s; 86| 1.44M| }); _ZN3uWS11HttpContextILb0EE21getSocketContextDataSEP11us_socket_t: 65| 3.49M| static HttpContextData *getSocketContextDataS(us_socket_t *s) { 66| 3.49M| return (HttpContextData *) us_socket_context_ext(SSL, getSocketContext(s)); 67| 3.49M| } _ZN3uWS11HttpContextILb0EE16getSocketContextEP11us_socket_t: 57| 3.49M| static us_socket_context_t *getSocketContext(us_socket_t *s) { 58| 3.49M| return (us_socket_context_t *) us_socket_context(SSL, s); 59| 3.49M| } _ZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tiPvE_clES3_iS4_: 89| 1.44M| us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { 90| | /* Get socket ext */ 91| 1.44M| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, s); 92| | 93| | /* Call filter */ 94| 1.44M| HttpContextData *httpContextData = getSocketContextDataS(s); 95| 1.44M| for (auto &f : httpContextData->filterHandlers) { ------------------ | Branch (95:26): [True: 0, False: 1.44M] ------------------ 96| 0| f((HttpResponse *) s, -1); 97| 0| } 98| | 99| | /* Signal broken HTTP request only if we have a pending request */ 100| 1.44M| if (httpResponseData->onAborted) { ------------------ | Branch (100:17): [True: 16.8k, False: 1.42M] ------------------ 101| 16.8k| httpResponseData->onAborted(); 102| 16.8k| } 103| | 104| | /* Destruct socket ext */ 105| 1.44M| httpResponseData->~HttpResponseData(); 106| | 107| 1.44M| return s; 108| 1.44M| }); _ZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clES3_S4_i: 111| 612k| us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) { 112| | 113| | // total overhead is about 210k down to 180k 114| | // ~210k req/sec is the original perf with write in data 115| | // ~200k req/sec is with cork and formatting 116| | // ~190k req/sec is with http parsing 117| | // ~180k - 190k req/sec is with varying routing 118| | 119| 612k| HttpContextData *httpContextData = getSocketContextDataS(s); 120| | 121| | /* Do not accept any data while in shutdown state */ 122| 612k| if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { ------------------ | Branch (122:17): [True: 0, False: 612k] ------------------ 123| 0| return s; 124| 0| } 125| | 126| 612k| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, s); 127| | 128| | /* Cork this socket */ 129| 612k| ((AsyncSocket *) s)->cork(); 130| | 131| | /* Mark that we are inside the parser now */ 132| 612k| httpContextData->isParsingHttp = true; 133| | 134| | // clients need to know the cursor after http parse, not servers! 135| | // how far did we read then? we need to know to continue with websocket parsing data? or? 136| | 137| 612k| void *proxyParser = nullptr; 138| |#ifdef UWS_WITH_PROXY 139| | proxyParser = &httpResponseData->proxyParser; 140| |#endif 141| | 142| | /* The return value is entirely up to us to interpret. The HttpParser only care for whether the returned value is DIFFERENT or not from passed user */ 143| 612k| auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * { 144| | /* For every request we reset the timeout and hang until user makes action */ 145| | /* Warning: if we are in shutdown state, resetting the timer is a security issue! */ 146| 612k| us_socket_timeout(SSL, (us_socket_t *) s, 0); 147| | 148| | /* Reset httpResponse */ 149| 612k| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, (us_socket_t *) s); 150| 612k| httpResponseData->offset = 0; 151| | 152| | /* Are we not ready for another request yet? Terminate the connection. */ 153| 612k| if (httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) { 154| 612k| us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); 155| 612k| return nullptr; 156| 612k| } 157| | 158| | /* Mark pending request and emit it */ 159| 612k| httpResponseData->state = HttpResponseData::HTTP_RESPONSE_PENDING; 160| | 161| | /* Mark this response as connectionClose if ancient or connection: close */ 162| 612k| if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { 163| 612k| httpResponseData->state |= HttpResponseData::HTTP_CONNECTION_CLOSE; 164| 612k| } 165| | 166| | /* Select the router based on SNI (only possible for SSL) */ 167| 612k| auto *selectedRouter = &httpContextData->router; 168| 612k| if constexpr (SSL) { 169| 612k| void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); 170| 612k| if (domainRouter) { 171| 612k| selectedRouter = (decltype(selectedRouter)) domainRouter; 172| 612k| } 173| 612k| } 174| | 175| | /* Route the method and URL */ 176| 612k| selectedRouter->getUserData() = {(HttpResponse *) s, httpRequest}; 177| 612k| if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { 178| | /* We have to force close this socket as we have no handler for it */ 179| 612k| us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); 180| 612k| return nullptr; 181| 612k| } 182| | 183| | /* First of all we need to check if this socket was deleted due to upgrade */ 184| 612k| if (httpContextData->upgradedWebSocket) { 185| | /* We differ between closed and upgraded below */ 186| 612k| return nullptr; 187| 612k| } 188| | 189| | /* Was the socket closed? */ 190| 612k| if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { 191| 612k| return nullptr; 192| 612k| } 193| | 194| | /* We absolutely have to terminate parsing if shutdown */ 195| 612k| if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { 196| 612k| return nullptr; 197| 612k| } 198| | 199| | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ 200| 612k| if (!((HttpResponse *) s)->hasResponded() && !httpResponseData->onAborted) { 201| | /* Throw exception here? */ 202| 612k| std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; 203| 612k| std::terminate(); 204| 612k| } 205| | 206| | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ 207| 612k| if (!((HttpResponse *) s)->hasResponded() && httpResponseData->inStream) { 208| 612k| us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); 209| 612k| } 210| | 211| | /* Continue parsing */ 212| 612k| return s; 213| | 214| 612k| }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { 215| | /* We always get an empty chunk even if there is no data */ 216| 612k| if (httpResponseData->inStream) { 217| | 218| | /* Todo: can this handle timeout for non-post as well? */ 219| 612k| if (fin) { 220| | /* If we just got the last chunk (or empty chunk), disable timeout */ 221| 612k| us_socket_timeout(SSL, (struct us_socket_t *) user, 0); 222| 612k| } else { 223| | /* We still have some more data coming in later, so reset timeout */ 224| | /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */ 225| 612k| httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); 226| 612k| if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { 227| 612k| us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); 228| 612k| httpResponseData->received_bytes_per_timeout = 0; 229| 612k| } 230| 612k| } 231| | 232| | /* We might respond in the handler, so do not change timeout after this */ 233| 612k| httpResponseData->inStream(data, fin); 234| | 235| | /* Was the socket closed? */ 236| 612k| if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { 237| 612k| return nullptr; 238| 612k| } 239| | 240| | /* We absolutely have to terminate parsing if shutdown */ 241| 612k| if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { 242| 612k| return nullptr; 243| 612k| } 244| | 245| | /* If we were given the last data chunk, reset data handler to ensure following 246| | * requests on the same socket won't trigger any previously registered behavior */ 247| 612k| if (fin) { 248| 612k| httpResponseData->inStream = nullptr; 249| 612k| } 250| 612k| } 251| 612k| return user; 252| 612k| }); 253| | 254| | /* Mark that we are no longer parsing Http */ 255| 612k| httpContextData->isParsingHttp = false; 256| | 257| | /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */ 258| 612k| if (returnedSocket == FULLPTR) { ------------------ | Branch (258:17): [True: 560k, False: 51.4k] ------------------ 259| | /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */ 260| 560k| us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); 261| 560k| us_socket_shutdown(SSL, s); 262| | /* Close any socket on HTTP errors */ 263| 560k| us_socket_close(SSL, s, 0, nullptr); 264| | /* This just makes the following code act as if the socket was closed from error inside the parser. */ 265| 560k| returnedSocket = nullptr; 266| 560k| } 267| | 268| | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ 269| 612k| if (returnedSocket != nullptr) { ------------------ | Branch (269:17): [True: 49.8k, False: 562k] ------------------ 270| | /* Timeout on uncork failure */ 271| 49.8k| auto [written, failed] = ((AsyncSocket *) returnedSocket)->uncork(); 272| 49.8k| if (failed) { ------------------ | Branch (272:21): [True: 14.6k, False: 35.2k] ------------------ 273| | /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */ 274| | /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */ 275| 14.6k| ((AsyncSocket *) s)->timeout(HTTP_IDLE_TIMEOUT_S); 276| 14.6k| } 277| | 278| | /* We need to check if we should close this socket here now */ 279| 49.8k| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { ------------------ | Branch (279:21): [True: 5.69k, False: 44.1k] ------------------ 280| 5.69k| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { ------------------ | Branch (280:25): [True: 1.92k, False: 3.77k] ------------------ 281| 1.92k| if (((AsyncSocket *) s)->getBufferedAmount() == 0) { ------------------ | Branch (281:29): [True: 1.38k, False: 540] ------------------ 282| 1.38k| ((AsyncSocket *) s)->shutdown(); 283| | /* We need to force close after sending FIN since we want to hinder 284| | * clients from keeping to send their huge data */ 285| 1.38k| ((AsyncSocket *) s)->close(); 286| 1.38k| } 287| 1.92k| } 288| 5.69k| } 289| | 290| 49.8k| return (us_socket_t *) returnedSocket; 291| 49.8k| } 292| | 293| | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ 294| 562k| if (httpContextData->upgradedWebSocket) { ------------------ | Branch (294:17): [True: 0, False: 562k] ------------------ 295| | /* This path is only for upgraded websockets */ 296| 0| AsyncSocket *asyncSocket = (AsyncSocket *) httpContextData->upgradedWebSocket; 297| | 298| | /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */ 299| 0| auto [written, failed] = asyncSocket->uncork(); 300| | 301| | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ 302| 0| if (!failed) { ------------------ | Branch (302:21): [True: 0, False: 0] ------------------ 303| 0| WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); 304| 0| if (webSocketData->isShuttingDown) { ------------------ | Branch (304:25): [True: 0, False: 0] ------------------ 305| | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ 306| 0| asyncSocket->shutdown(); 307| 0| } 308| 0| } 309| | 310| | /* Reset upgradedWebSocket before we return */ 311| 0| httpContextData->upgradedWebSocket = nullptr; 312| | 313| | /* Return the new upgraded websocket */ 314| 0| return (us_socket_t *) asyncSocket; 315| 0| } 316| | 317| | /* It is okay to uncork a closed socket and we need to */ 318| 562k| ((AsyncSocket *) s)->uncork(); 319| | 320| | /* We cannot return nullptr to the underlying stack in any case */ 321| 562k| return s; 322| 562k| }); _ZZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clES3_S4_iENKUlPvPNS_11HttpRequestEE_clES6_S8_: 143| 50.5k| auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * { 144| | /* For every request we reset the timeout and hang until user makes action */ 145| | /* Warning: if we are in shutdown state, resetting the timer is a security issue! */ 146| 50.5k| us_socket_timeout(SSL, (us_socket_t *) s, 0); 147| | 148| | /* Reset httpResponse */ 149| 50.5k| HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, (us_socket_t *) s); 150| 50.5k| httpResponseData->offset = 0; 151| | 152| | /* Are we not ready for another request yet? Terminate the connection. */ 153| 50.5k| if (httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) { ------------------ | Branch (153:21): [True: 1.59k, False: 48.9k] ------------------ 154| 1.59k| us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); 155| 1.59k| return nullptr; 156| 1.59k| } 157| | 158| | /* Mark pending request and emit it */ 159| 48.9k| httpResponseData->state = HttpResponseData::HTTP_RESPONSE_PENDING; 160| | 161| | /* Mark this response as connectionClose if ancient or connection: close */ 162| 48.9k| if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { ------------------ | Branch (162:21): [True: 0, False: 48.9k] | Branch (162:21): [True: 6.63k, False: 42.3k] | Branch (162:49): [True: 6.63k, False: 42.3k] ------------------ 163| 6.63k| httpResponseData->state |= HttpResponseData::HTTP_CONNECTION_CLOSE; 164| 6.63k| } 165| | 166| | /* Select the router based on SNI (only possible for SSL) */ 167| 48.9k| auto *selectedRouter = &httpContextData->router; 168| 48.9k| if constexpr (SSL) { ------------------ | Branch (168:31): [Folded - Ignored] ------------------ 169| 48.9k| void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); 170| 48.9k| if (domainRouter) { 171| 48.9k| selectedRouter = (decltype(selectedRouter)) domainRouter; 172| 48.9k| } 173| 48.9k| } 174| | 175| | /* Route the method and URL */ 176| 48.9k| selectedRouter->getUserData() = {(HttpResponse *) s, httpRequest}; 177| 48.9k| if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { ------------------ | Branch (177:21): [True: 0, False: 48.9k] ------------------ 178| | /* We have to force close this socket as we have no handler for it */ 179| 0| us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); 180| 0| return nullptr; 181| 0| } 182| | 183| | /* First of all we need to check if this socket was deleted due to upgrade */ 184| 48.9k| if (httpContextData->upgradedWebSocket) { ------------------ | Branch (184:21): [True: 0, False: 48.9k] ------------------ 185| | /* We differ between closed and upgraded below */ 186| 0| return nullptr; 187| 0| } 188| | 189| | /* Was the socket closed? */ 190| 48.9k| if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { ------------------ | Branch (190:21): [True: 0, False: 48.9k] ------------------ 191| 0| return nullptr; 192| 0| } 193| | 194| | /* We absolutely have to terminate parsing if shutdown */ 195| 48.9k| if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { ------------------ | Branch (195:21): [True: 0, False: 48.9k] ------------------ 196| 0| return nullptr; 197| 0| } 198| | 199| | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ 200| 48.9k| if (!((HttpResponse *) s)->hasResponded() && !httpResponseData->onAborted) { ------------------ | Branch (200:21): [True: 34.2k, False: 14.7k] | Branch (200:67): [True: 0, False: 34.2k] ------------------ 201| | /* Throw exception here? */ 202| 0| std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; 203| 0| std::terminate(); 204| 0| } 205| | 206| | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ 207| 48.9k| if (!((HttpResponse *) s)->hasResponded() && httpResponseData->inStream) { ------------------ | Branch (207:21): [True: 34.2k, False: 14.7k] | Branch (207:67): [True: 0, False: 34.2k] ------------------ 208| 0| us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); 209| 0| } 210| | 211| | /* Continue parsing */ 212| 48.9k| return s; 213| | 214| 48.9k| }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { _ZZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clES3_S4_iENKUlPvNSt3__117basic_string_viewIcNS7_11char_traitsIcEEEEbE_clES6_SB_b: 214| 49.9k| }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { 215| | /* We always get an empty chunk even if there is no data */ 216| 49.9k| if (httpResponseData->inStream) { ------------------ | Branch (216:21): [True: 0, False: 49.9k] ------------------ 217| | 218| | /* Todo: can this handle timeout for non-post as well? */ 219| 0| if (fin) { ------------------ | Branch (219:25): [True: 0, False: 0] ------------------ 220| | /* If we just got the last chunk (or empty chunk), disable timeout */ 221| 0| us_socket_timeout(SSL, (struct us_socket_t *) user, 0); 222| 0| } else { 223| | /* We still have some more data coming in later, so reset timeout */ 224| | /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */ 225| 0| httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); 226| 0| if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { ------------------ | Branch (226:29): [True: 0, False: 0] ------------------ 227| 0| us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); 228| 0| httpResponseData->received_bytes_per_timeout = 0; 229| 0| } 230| 0| } 231| | 232| | /* We might respond in the handler, so do not change timeout after this */ 233| 0| httpResponseData->inStream(data, fin); 234| | 235| | /* Was the socket closed? */ 236| 0| if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { ------------------ | Branch (236:25): [True: 0, False: 0] ------------------ 237| 0| return nullptr; 238| 0| } 239| | 240| | /* We absolutely have to terminate parsing if shutdown */ 241| 0| if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { ------------------ | Branch (241:25): [True: 0, False: 0] ------------------ 242| 0| return nullptr; 243| 0| } 244| | 245| | /* If we were given the last data chunk, reset data handler to ensure following 246| | * requests on the same socket won't trigger any previously registered behavior */ 247| 0| if (fin) { ------------------ | Branch (247:25): [True: 0, False: 0] ------------------ 248| 0| httpResponseData->inStream = nullptr; 249| 0| } 250| 0| } 251| 49.9k| return user; 252| 49.9k| }); _ZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tE_clES3_: 325| 9.03k| us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { 326| | 327| 9.03k| AsyncSocket *asyncSocket = (AsyncSocket *) s; 328| 9.03k| HttpResponseData *httpResponseData = (HttpResponseData *) asyncSocket->getAsyncSocketData(); 329| | 330| | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ 331| 9.03k| if (httpResponseData->onWritable) { ------------------ | Branch (331:17): [True: 0, False: 9.03k] ------------------ 332| | /* We are now writable, so hang timeout again, the user does not have to do anything so we should hang until end or tryEnd rearms timeout */ 333| 0| us_socket_timeout(SSL, s, 0); 334| | 335| | /* We expect the developer to return whether or not write was successful (true). 336| | * If write was never called, the developer should still return true so that we may drain. */ 337| 0| bool success = httpResponseData->callOnWritable(httpResponseData->offset); 338| | 339| | /* The developer indicated that their onWritable failed. */ 340| 0| if (!success) { ------------------ | Branch (340:21): [True: 0, False: 0] ------------------ 341| | /* Skip testing if we can drain anything since that might perform an extra syscall */ 342| 0| return s; 343| 0| } 344| | 345| | /* We don't want to fall through since we don't want to mess with timeout. 346| | * It makes little sense to drain any backpressure when the user has registered onWritable. */ 347| 0| return s; 348| 0| } 349| | 350| | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ 351| 9.03k| /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0); 352| | 353| | /* Should we close this connection after a response - and is this response really done? */ 354| 9.03k| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { ------------------ | Branch (354:17): [True: 2.71k, False: 6.32k] ------------------ 355| 2.71k| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { ------------------ | Branch (355:21): [True: 2.51k, False: 194] ------------------ 356| 2.51k| if (asyncSocket->getBufferedAmount() == 0) { ------------------ | Branch (356:25): [True: 1.86k, False: 649] ------------------ 357| 1.86k| asyncSocket->shutdown(); 358| | /* We need to force close after sending FIN since we want to hinder 359| | * clients from keeping to send their huge data */ 360| 1.86k| asyncSocket->close(); 361| 1.86k| } 362| 2.51k| } 363| 2.71k| } 364| | 365| | /* Expect another writable event, or another request within the timeout */ 366| 9.03k| asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); 367| | 368| 9.03k| return s; 369| 9.03k| }); _ZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tE0_clES3_: 372| 32.4k| us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { 373| | 374| | /* We do not care for half closed sockets */ 375| 32.4k| AsyncSocket *asyncSocket = (AsyncSocket *) s; 376| 32.4k| return asyncSocket->close(); 377| | 378| 32.4k| }); _ZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tE1_clES3_: 381| 5.64k| us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { 382| | 383| | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ 384| 5.64k| AsyncSocket *asyncSocket = (AsyncSocket *) s; 385| 5.64k| return asyncSocket->close(); 386| | 387| 5.64k| }); _ZN3uWS11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEES8_ON5ofats13any_invocableIFvPNS_12HttpResponseILb0EEEPNS_11HttpRequestEEEEb: 423| 4.50k| void onHttp(std::string method, std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler, bool upgrade = false) { 424| 4.50k| HttpContextData *httpContextData = getSocketContextData(); 425| | 426| | /* Todo: This is ugly, fix */ 427| 4.50k| std::vector methods; 428| 4.50k| if (method == "*") { ------------------ | Branch (428:13): [True: 2.25k, False: 2.25k] ------------------ 429| 2.25k| methods = {"*"}; 430| 2.25k| } else { 431| 2.25k| methods = {method}; 432| 2.25k| } 433| | 434| 4.50k| uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY); ------------------ | Branch (434:29): [True: 2.25k, False: 2.25k] | Branch (434:93): [True: 0, False: 2.25k] ------------------ 435| | 436| | /* If we are passed nullptr then remove this */ 437| 4.50k| if (!handler) { ------------------ | Branch (437:13): [True: 0, False: 4.50k] ------------------ 438| 0| httpContextData->currentRouter->remove(methods[0], pattern, priority); 439| 0| return; 440| 0| } 441| | 442| | /* Record this route's parameter offsets */ 443| 4.50k| std::map> parameterOffsets; 444| 4.50k| unsigned short offset = 0; 445| 13.5k| for (unsigned int i = 0; i < pattern.length(); i++) { ------------------ | Branch (445:34): [True: 9.01k, False: 4.50k] ------------------ 446| 9.01k| if (pattern[i] == ':') { ------------------ | Branch (446:17): [True: 0, False: 9.01k] ------------------ 447| 0| i++; 448| 0| unsigned int start = i; 449| 0| while (i < pattern.length() && pattern[i] != '/') { ------------------ | Branch (449:24): [True: 0, False: 0] | Branch (449:48): [True: 0, False: 0] ------------------ 450| 0| i++; 451| 0| } 452| 0| parameterOffsets[std::string(pattern.data() + start, i - start)] = offset; 453| | //std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset; 454| 0| offset++; 455| 0| } 456| 9.01k| } 457| | 458| 4.50k| httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable { 459| 4.50k| auto user = r->getUserData(); 460| 4.50k| user.httpRequest->setYield(false); 461| 4.50k| user.httpRequest->setParameters(r->getParameters()); 462| 4.50k| user.httpRequest->setParameterOffsets(¶meterOffsets); 463| | 464| | /* Middleware? Automatically respond to expectations */ 465| 4.50k| std::string_view expect = user.httpRequest->getHeader("expect"); 466| 4.50k| if (expect.length() && expect == "100-continue") { 467| 4.50k| user.httpResponse->writeContinue(); 468| 4.50k| } 469| | 470| 4.50k| handler(user.httpResponse, user.httpRequest); 471| | 472| | /* If any handler yielded, the router will keep looking for a suitable handler. */ 473| 4.50k| if (user.httpRequest->getYield()) { 474| 4.50k| return false; 475| 4.50k| } 476| 4.50k| return true; 477| 4.50k| }, priority); 478| 4.50k| } _ZN3uWS11HttpContextILb0EE20getSocketContextDataEv: 61| 6.76k| HttpContextData *getSocketContextData() { 62| 6.76k| return (HttpContextData *) us_socket_context_ext(SSL, getSocketContext()); 63| 6.76k| } _ZZN3uWS11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEES8_ON5ofats13any_invocableIFvPNS_12HttpResponseILb0EEEPNS_11HttpRequestEEEEbENUlPT_E_clINS_10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEEEEEDaSK_: 458| 48.9k| httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable { 459| 48.9k| auto user = r->getUserData(); 460| 48.9k| user.httpRequest->setYield(false); 461| 48.9k| user.httpRequest->setParameters(r->getParameters()); 462| 48.9k| user.httpRequest->setParameterOffsets(¶meterOffsets); 463| | 464| | /* Middleware? Automatically respond to expectations */ 465| 48.9k| std::string_view expect = user.httpRequest->getHeader("expect"); 466| 48.9k| if (expect.length() && expect == "100-continue") { ------------------ | Branch (466:17): [True: 0, False: 48.9k] | Branch (466:36): [True: 0, False: 0] ------------------ 467| 0| user.httpResponse->writeContinue(); 468| 0| } 469| | 470| 48.9k| handler(user.httpResponse, user.httpRequest); 471| | 472| | /* If any handler yielded, the router will keep looking for a suitable handler. */ 473| 48.9k| if (user.httpRequest->getYield()) { ------------------ | Branch (473:17): [True: 0, False: 48.9k] ------------------ 474| 0| return false; 475| 0| } 476| 48.9k| return true; 477| 48.9k| }, priority); _ZN3uWS11HttpContextILb0EE4freeEv: 409| 2.25k| void free() { 410| | /* Destruct socket context data */ 411| 2.25k| HttpContextData *httpContextData = getSocketContextData(); 412| 2.25k| httpContextData->~HttpContextData(); 413| | 414| | /* Free the socket context in whole */ 415| 2.25k| us_socket_context_free(SSL, getSocketContext()); 416| 2.25k| } _ZN3uWS11HttpContextILb0EE6listenEPKcii: 481| 2.25k| us_listen_socket_t *listen(const char *host, int port, int options) { 482| 2.25k| return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData)); 483| 2.25k| } _ZN3uWS12optional_ptrIcEENSt3__18optionalIPT_EES4_: 47| 2|std::optional optional_ptr(T *ptr) { 48| 2| return ptr ? std::optional(ptr) : std::nullopt; ------------------ | Branch (48:12): [True: 0, False: 2] ------------------ 49| 2|} _ZN3uWS10HttpParser17consumePostPaddedEPcjPvS2_ON5ofats13any_invocableIFS2_S2_PNS_11HttpRequestEEEEONS4_IFS2_S2_NSt3__117basic_string_viewIcNSA_11char_traitsIcEEEEbEEE: 583| 612k| std::pair consumePostPadded(char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction &&requestHandler, MoveOnlyFunction &&dataHandler) { 584| | 585| | /* This resets BloomFilter by construction, but later we also reset it again. 586| | * Optimize this to skip resetting twice (req could be made global) */ 587| 612k| HttpRequest req; 588| | 589| 612k| if (remainingStreamingBytes) { ------------------ | Branch (589:13): [True: 2.09k, False: 610k] ------------------ 590| | 591| | /* It's either chunked or with a content-length */ 592| 2.09k| if (isParsingChunkedEncoding(remainingStreamingBytes)) { ------------------ | Branch (592:17): [True: 0, False: 2.09k] ------------------ 593| 0| std::string_view dataToConsume(data, length); 594| 0| for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { ------------------ | Branch (594:33): [True: 0, False: 0] ------------------ 595| 0| dataHandler(user, chunk, chunk.length() == 0); 596| 0| } 597| 0| if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { ------------------ | Branch (597:21): [True: 0, False: 0] ------------------ 598| 0| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 599| 0| } 600| 0| data = (char *) dataToConsume.data(); 601| 0| length = (unsigned int) dataToConsume.length(); 602| 2.09k| } else { 603| | // this is exactly the same as below! 604| | // todo: refactor this 605| 2.09k| if (remainingStreamingBytes >= length) { ------------------ | Branch (605:21): [True: 383, False: 1.70k] ------------------ 606| 383| void *returnedUser = dataHandler(user, std::string_view(data, length), remainingStreamingBytes == length); 607| 383| remainingStreamingBytes -= length; 608| 383| return {0, returnedUser}; 609| 1.70k| } else { 610| 1.70k| void *returnedUser = dataHandler(user, std::string_view(data, remainingStreamingBytes), true); 611| | 612| 1.70k| data += (unsigned int) remainingStreamingBytes; 613| 1.70k| length -= (unsigned int) remainingStreamingBytes; 614| | 615| 1.70k| remainingStreamingBytes = 0; 616| | 617| 1.70k| if (returnedUser != user) { ------------------ | Branch (617:25): [True: 0, False: 1.70k] ------------------ 618| 0| return {0, returnedUser}; 619| 0| } 620| 1.70k| } 621| 2.09k| } 622| | 623| 610k| } else if (fallback.length()) { ------------------ | Branch (623:20): [True: 24.3k, False: 585k] ------------------ 624| 24.3k| unsigned int had = (unsigned int) fallback.length(); 625| | 626| 24.3k| size_t maxCopyDistance = std::min(MAX_FALLBACK_SIZE - fallback.length(), (size_t) length); 627| | 628| | /* We don't want fallback to be short string optimized, since we want to move it */ 629| 24.3k| fallback.reserve(fallback.length() + maxCopyDistance + std::max(MINIMUM_HTTP_POST_PADDING, sizeof(std::string))); 630| 24.3k| fallback.append(data, maxCopyDistance); 631| | 632| | // break here on break 633| 24.3k| std::pair consumed = fenceAndConsumePostPadded(fallback.data(), (unsigned int) fallback.length(), user, reserved, &req, requestHandler, dataHandler); 634| 24.3k| if (consumed.second != user) { ------------------ | Branch (634:17): [True: 1.14k, False: 23.2k] ------------------ 635| 1.14k| return consumed; 636| 1.14k| } 637| | 638| 23.2k| if (consumed.first) { ------------------ | Branch (638:17): [True: 8.51k, False: 14.7k] ------------------ 639| | 640| | /* This logic assumes that we consumed everything in fallback buffer. 641| | * This is critically important, as we will get an integer overflow in case 642| | * of "had" being larger than what we consumed, and that we would drop data */ 643| 8.51k| fallback.clear(); 644| 8.51k| data += consumed.first - had; 645| 8.51k| length -= consumed.first - had; 646| | 647| 8.51k| if (remainingStreamingBytes) { ------------------ | Branch (647:21): [True: 2.24k, False: 6.26k] ------------------ 648| | /* It's either chunked or with a content-length */ 649| 2.24k| if (isParsingChunkedEncoding(remainingStreamingBytes)) { ------------------ | Branch (649:25): [True: 0, False: 2.24k] ------------------ 650| 0| std::string_view dataToConsume(data, length); 651| 0| for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { ------------------ | Branch (651:41): [True: 0, False: 0] ------------------ 652| 0| dataHandler(user, chunk, chunk.length() == 0); 653| 0| } 654| 0| if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { ------------------ | Branch (654:29): [True: 0, False: 0] ------------------ 655| 0| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 656| 0| } 657| 0| data = (char *) dataToConsume.data(); 658| 0| length = (unsigned int) dataToConsume.length(); 659| 2.24k| } else { 660| | // this is exactly the same as above! 661| 2.24k| if (remainingStreamingBytes >= (unsigned int) length) { ------------------ | Branch (661:29): [True: 2.01k, False: 229] ------------------ 662| 2.01k| void *returnedUser = dataHandler(user, std::string_view(data, length), remainingStreamingBytes == (unsigned int) length); 663| 2.01k| remainingStreamingBytes -= length; 664| 2.01k| return {0, returnedUser}; 665| 2.01k| } else { 666| 229| void *returnedUser = dataHandler(user, std::string_view(data, remainingStreamingBytes), true); 667| | 668| 229| data += (unsigned int) remainingStreamingBytes; 669| 229| length -= (unsigned int) remainingStreamingBytes; 670| | 671| 229| remainingStreamingBytes = 0; 672| | 673| 229| if (returnedUser != user) { ------------------ | Branch (673:33): [True: 0, False: 229] ------------------ 674| 0| return {0, returnedUser}; 675| 0| } 676| 229| } 677| 2.24k| } 678| 2.24k| } 679| | 680| 14.7k| } else { 681| 14.7k| if (fallback.length() == MAX_FALLBACK_SIZE) { ------------------ | Branch (681:21): [True: 200, False: 14.5k] ------------------ 682| 200| return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; 683| 200| } 684| 14.5k| return {0, user}; 685| 14.7k| } 686| 23.2k| } 687| | 688| 593k| std::pair consumed = fenceAndConsumePostPadded(data, length, user, reserved, &req, requestHandler, dataHandler); 689| 593k| if (consumed.second != user) { ------------------ | Branch (689:13): [True: 560k, False: 32.9k] ------------------ 690| 560k| return consumed; 691| 560k| } 692| | 693| 32.9k| data += consumed.first; 694| 32.9k| length -= consumed.first; 695| | 696| 32.9k| if (length) { ------------------ | Branch (696:13): [True: 15.5k, False: 17.3k] ------------------ 697| 15.5k| if (length < MAX_FALLBACK_SIZE) { ------------------ | Branch (697:17): [True: 15.5k, False: 0] ------------------ 698| 15.5k| fallback.append(data, length); 699| 15.5k| } else { 700| 0| return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; 701| 0| } 702| 15.5k| } 703| | 704| | // added for now 705| 32.9k| return {0, user}; 706| 32.9k| } _ZN3uWS10HttpParser25fenceAndConsumePostPaddedILi1EEENSt3__14pairIjPvEEPcjS4_S4_PNS_11HttpRequestERN5ofats13any_invocableIFS4_S4_S8_EEERNSA_IFS4_S4_NS2_17basic_string_viewIcNS2_11char_traitsIcEEEEbEEE: 448| 24.3k| std::pair fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction &requestHandler, MoveOnlyFunction &dataHandler) { 449| | 450| | /* How much data we CONSUMED (to throw away) */ 451| 24.3k| unsigned int consumedTotal = 0; 452| 24.3k| unsigned int err = 0; 453| | 454| | /* Fence two bytes past end of our buffer (buffer has post padded margins). 455| | * This is to always catch scan for \r but not for \r\n. */ 456| 24.3k| data[length] = '\r'; 457| 24.3k| data[length + 1] = 'a'; /* Anything that is not \n, to trigger "invalid request" */ 458| | 459| 24.3k| for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved, err)); ) { ------------------ | Branch (459:37): [True: 24.3k, False: 0] | Branch (459:47): [True: 9.66k, False: 14.7k] ------------------ 460| 9.66k| data += consumed; 461| 9.66k| length -= consumed; 462| 9.66k| consumedTotal += consumed; 463| | 464| | /* Even if we could parse it, check for length here as well */ 465| 9.66k| if (consumed > MAX_FALLBACK_SIZE) { ------------------ | Branch (465:17): [True: 0, False: 9.66k] ------------------ 466| 0| return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; 467| 0| } 468| | 469| | /* Store HTTP version (ancient 1.0 or 1.1) */ 470| 9.66k| req->ancientHttp = false; 471| | 472| | /* Add all headers to bloom filter */ 473| 9.66k| req->bf.reset(); 474| 25.2k| for (HttpRequest::Header *h = req->headers; (++h)->key.length(); ) { ------------------ | Branch (474:57): [True: 15.6k, False: 9.66k] ------------------ 475| 15.6k| req->bf.add(h->key); 476| 15.6k| } 477| | 478| | /* Break if no host header (but we can have empty string which is different from nullptr) */ 479| 9.66k| if (!req->getHeader("host").data()) { ------------------ | Branch (479:17): [True: 467, False: 9.19k] ------------------ 480| 467| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 481| 467| } 482| | 483| | /* RFC 9112 6.3 484| | * If a message is received with both a Transfer-Encoding and a Content-Length header field, 485| | * the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt 486| | * to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and 487| | * ought to be handled as an error. */ 488| 9.19k| std::string_view transferEncodingString = req->getHeader("transfer-encoding"); 489| 9.19k| std::string_view contentLengthString = req->getHeader("content-length"); 490| 9.19k| if (transferEncodingString.length() && contentLengthString.length()) { ------------------ | Branch (490:17): [True: 0, False: 9.19k] | Branch (490:52): [True: 0, False: 0] ------------------ 491| | /* Returning fullptr is the same as calling the errorHandler */ 492| | /* We could be smart and set an error in the context along with this, to indicate what 493| | * http error response we might want to return */ 494| 0| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 495| 0| } 496| | 497| | /* Parse query */ 498| 9.19k| const char *querySeparatorPtr = (const char *) memchr(req->headers->value.data(), '?', req->headers->value.length()); 499| 9.19k| req->querySeparator = (unsigned int) ((querySeparatorPtr ? querySeparatorPtr : req->headers->value.data() + req->headers->value.length()) - req->headers->value.data()); ------------------ | Branch (499:52): [True: 333, False: 8.86k] ------------------ 500| | 501| | /* If returned socket is not what we put in we need 502| | * to break here as we either have upgraded to 503| | * WebSockets or otherwise closed the socket. */ 504| 9.19k| void *returnedUser = requestHandler(user, req); 505| 9.19k| if (returnedUser != user) { ------------------ | Branch (505:17): [True: 224, False: 8.96k] ------------------ 506| | /* We are upgraded to WebSocket or otherwise broken */ 507| 224| return {consumedTotal, returnedUser}; 508| 224| } 509| | 510| | /* The rules at play here according to RFC 9112 for requests are essentially: 511| | * If both content-length and transfer-encoding then invalid message; must break. 512| | * If has transfer-encoding then must be chunked regardless of value. 513| | * If content-length then fixed length even if 0. 514| | * If none of the above then fixed length is 0. */ 515| | 516| | /* RFC 9112 6.3 517| | * If a message is received with both a Transfer-Encoding and a Content-Length header field, 518| | * the Transfer-Encoding overrides the Content-Length. */ 519| 8.96k| if (transferEncodingString.length()) { ------------------ | Branch (519:17): [True: 0, False: 8.96k] ------------------ 520| | 521| | /* If a proxy sent us the transfer-encoding header that 100% means it must be chunked or else the proxy is 522| | * not RFC 9112 compliant. Therefore it is always better to assume this is the case, since that entirely eliminates 523| | * all forms of transfer-encoding obfuscation tricks. We just rely on the header. */ 524| | 525| | /* RFC 9112 6.3 526| | * If a Transfer-Encoding header field is present in a request and the chunked transfer coding is not the 527| | * final encoding, the message body length cannot be determined reliably; the server MUST respond with the 528| | * 400 (Bad Request) status code and then close the connection. */ 529| | 530| | /* In this case we fail later by having the wrong interpretation (assuming chunked). 531| | * This could be made stricter but makes no difference either way, unless forwarding the identical message as a proxy. */ 532| | 533| 0| remainingStreamingBytes = STATE_IS_CHUNKED; 534| | /* If consume minimally, we do not want to consume anything but we want to mark this as being chunked */ 535| 0| if (!CONSUME_MINIMALLY) { ------------------ | Branch (535:21): [Folded - Ignored] ------------------ 536| | /* Go ahead and parse it (todo: better heuristics for emitting FIN to the app level) */ 537| 0| std::string_view dataToConsume(data, length); 538| 0| for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { ------------------ | Branch (538:37): [True: 0, False: 0] ------------------ 539| 0| dataHandler(user, chunk, chunk.length() == 0); 540| 0| } 541| 0| if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { ------------------ | Branch (541:25): [True: 0, False: 0] ------------------ 542| 0| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 543| 0| } 544| 0| unsigned int consumed = (length - (unsigned int) dataToConsume.length()); 545| 0| data = (char *) dataToConsume.data(); 546| 0| length = (unsigned int) dataToConsume.length(); 547| 0| consumedTotal += consumed; 548| 0| } 549| 8.96k| } else if (contentLengthString.length()) { ------------------ | Branch (549:24): [True: 2.70k, False: 6.26k] ------------------ 550| 2.70k| remainingStreamingBytes = toUnsignedInteger(contentLengthString); 551| 2.70k| if (remainingStreamingBytes == UINT64_MAX) { ------------------ | Branch (551:21): [True: 458, False: 2.24k] ------------------ 552| | /* Parser error */ 553| 458| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 554| 458| } 555| | 556| 2.24k| if (!CONSUME_MINIMALLY) { ------------------ | Branch (556:21): [Folded - Ignored] ------------------ 557| 0| unsigned int emittable = (unsigned int) std::min(remainingStreamingBytes, length); 558| 0| dataHandler(user, std::string_view(data, emittable), emittable == remainingStreamingBytes); 559| 0| remainingStreamingBytes -= emittable; 560| | 561| 0| data += emittable; 562| 0| length -= emittable; 563| 0| consumedTotal += emittable; 564| 0| } 565| 6.26k| } else { 566| | /* If we came here without a body; emit an empty data chunk to signal no data */ 567| 6.26k| dataHandler(user, {}, true); 568| 6.26k| } 569| | 570| | /* Consume minimally should break as easrly as possible */ 571| 8.51k| if (CONSUME_MINIMALLY) { ------------------ | Branch (571:17): [Folded - Ignored] ------------------ 572| 8.51k| break; 573| 8.51k| } 574| 8.51k| } 575| | /* Whenever we return FULLPTR, the interpretation of "consumed" should be the HttpError enum. */ 576| 23.2k| if (err) { ------------------ | Branch (576:13): [True: 0, False: 23.2k] ------------------ 577| 0| return {err, FULLPTR}; 578| 0| } 579| 23.2k| return {consumedTotal, user}; 580| 23.2k| } _ZN3uWS10HttpParser10getHeadersEPcS1_PNS_11HttpRequest6HeaderEPvRj: 337| 640k| static unsigned int getHeaders(char *postPaddedBuffer, char *end, struct HttpRequest::Header *headers, void *reserved, unsigned int &err) { 338| 640k| char *preliminaryKey, *preliminaryValue, *start = postPaddedBuffer; 339| | 340| | #ifdef UWS_WITH_PROXY 341| | /* ProxyParser is passed as reserved parameter */ 342| | ProxyParser *pp = (ProxyParser *) reserved; 343| | 344| | /* Parse PROXY protocol */ 345| | auto [done, offset] = pp->parse({postPaddedBuffer, (size_t) (end - postPaddedBuffer)}); 346| | if (!done) { 347| | /* We do not reset the ProxyParser (on filure) since it is tied to this 348| | * connection, which is really only supposed to ever get one PROXY frame 349| | * anyways. We do however allow multiple PROXY frames to be sent (overwrites former). */ 350| | return 0; 351| | } else { 352| | /* We have consumed this data so skip it */ 353| | postPaddedBuffer += offset; 354| | } 355| | #else 356| | /* This one is unused */ 357| 640k| (void) reserved; 358| 640k| (void) end; 359| 640k| #endif 360| | 361| | /* It is critical for fallback buffering logic that we only return with success 362| | * if we managed to parse a complete HTTP request (minus data). Returning success 363| | * for PROXY means we can end up succeeding, yet leaving bytes in the fallback buffer 364| | * which is then removed, and our counters to flip due to overflow and we end up with a crash */ 365| | 366| | /* The request line is different from the field names / field values */ 367| 640k| if (!(postPaddedBuffer = consumeRequestLine(postPaddedBuffer, headers[0]))) { ------------------ | Branch (367:13): [True: 558k, False: 82.2k] ------------------ 368| | /* Error - invalid request line */ 369| | /* Assuming it is 505 HTTP Version Not Supported */ 370| 558k| err = HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED; 371| 558k| return 0; 372| 558k| } 373| 82.2k| headers++; 374| | 375| 124k| for (unsigned int i = 1; i < UWS_HTTP_MAX_HEADERS_COUNT - 1; i++) { ------------------ | | 53| 124k|#define UWS_HTTP_MAX_HEADERS_COUNT 100 ------------------ | Branch (375:34): [True: 123k, False: 194] ------------------ 376| | /* Lower case and consume the field name */ 377| 123k| preliminaryKey = postPaddedBuffer; 378| 123k| postPaddedBuffer = (char *) consumeFieldName(postPaddedBuffer); 379| 123k| headers->key = std::string_view(preliminaryKey, (size_t) (postPaddedBuffer - preliminaryKey)); 380| | 381| | /* We should not accept whitespace between key and colon, so colon must foloow immediately */ 382| 123k| if (postPaddedBuffer[0] != ':') { ------------------ | Branch (382:17): [True: 9.84k, False: 114k] ------------------ 383| | /* Error: invalid chars in field name */ 384| 9.84k| return 0; 385| 9.84k| } 386| 114k| postPaddedBuffer++; 387| | 388| 114k| preliminaryValue = postPaddedBuffer; 389| | /* The goal of this call is to find next "\r\n", or any invalid field value chars, fast */ 390| 114k| while (true) { ------------------ | Branch (390:20): [Folded - Ignored] ------------------ 391| 114k| postPaddedBuffer = (char *) tryConsumeFieldValue(postPaddedBuffer); 392| | /* If this is not CR then we caught some stinky invalid char on the way */ 393| 114k| if (postPaddedBuffer[0] != '\r') { ------------------ | Branch (393:21): [True: 2.84k, False: 111k] ------------------ 394| | /* If TAB then keep searching */ 395| 2.84k| if (postPaddedBuffer[0] == '\t') { ------------------ | Branch (395:25): [True: 335, False: 2.50k] ------------------ 396| 335| postPaddedBuffer++; 397| 335| continue; 398| 335| } 399| | /* Error - invalid chars in field value */ 400| 2.50k| return 0; 401| 2.84k| } 402| 111k| break; 403| 114k| } 404| | /* We fence end[0] with \r, followed by end[1] being something that is "not \n", to signify "not found". 405| | * This way we can have this one single check to see if we found \r\n WITHIN our allowed search space. */ 406| 111k| if (postPaddedBuffer[1] == '\n') { ------------------ | Branch (406:17): [True: 94.9k, False: 16.6k] ------------------ 407| | /* Store this header, it is valid */ 408| 94.9k| headers->value = std::string_view(preliminaryValue, (size_t) (postPaddedBuffer - preliminaryValue)); 409| 94.9k| postPaddedBuffer += 2; 410| | 411| | /* Trim trailing whitespace (SP, HTAB) */ 412| 121k| while (headers->value.length() && headers->value.back() < 33) { ------------------ | Branch (412:24): [True: 68.9k, False: 52.6k] | Branch (412:51): [True: 26.6k, False: 42.2k] ------------------ 413| 26.6k| headers->value.remove_suffix(1); 414| 26.6k| } 415| | 416| | /* Trim initial whitespace (SP, HTAB) */ 417| 103k| while (headers->value.length() && headers->value.front() < 33) { ------------------ | Branch (417:24): [True: 50.4k, False: 52.6k] | Branch (417:51): [True: 8.15k, False: 42.2k] ------------------ 418| 8.15k| headers->value.remove_prefix(1); 419| 8.15k| } 420| | 421| 94.9k| headers++; 422| | 423| | /* We definitely have at least one header (or request line), so check if we are done */ 424| 94.9k| if (*postPaddedBuffer == '\r') { ------------------ | Branch (424:21): [True: 52.9k, False: 41.9k] ------------------ 425| 52.9k| if (postPaddedBuffer[1] == '\n') { ------------------ | Branch (425:25): [True: 51.9k, False: 1.06k] ------------------ 426| | /* This cann take the very last header space */ 427| 51.9k| headers->key = std::string_view(nullptr, 0); 428| 51.9k| return (unsigned int) ((postPaddedBuffer + 2) - start); 429| 51.9k| } else { 430| | /* \r\n\r plus non-\n letter is malformed request, or simply out of search space */ 431| 1.06k| return 0; 432| 1.06k| } 433| 52.9k| } 434| 94.9k| } else { 435| | /* We are either out of search space or this is a malformed request */ 436| 16.6k| return 0; 437| 16.6k| } 438| 111k| } 439| | /* We ran out of header space, too large request */ 440| 194| return 0; 441| 82.2k| } _ZN3uWS10HttpParser18consumeRequestLineEPcRNS_11HttpRequest6HeaderE: 293| 640k| static inline char *consumeRequestLine(char *data, HttpRequest::Header &header) { 294| | /* Scan until single SP, assume next is / (origin request) */ 295| 640k| char *start = data; 296| | /* This catches the post padded CR and fails */ 297| 1.07M| while (data[0] > 32) data++; ------------------ | Branch (297:16): [True: 431k, False: 640k] ------------------ 298| 640k| if (data[0] == 32 && data[1] == '/') { ------------------ | Branch (298:13): [True: 95.1k, False: 545k] | Branch (298:30): [True: 89.9k, False: 5.12k] ------------------ 299| 89.9k| header.key = {start, (size_t) (data - start)}; 300| 89.9k| data++; 301| | /* Scan for less than 33 (catches post padded CR and fails) */ 302| 89.9k| start = data; 303| 92.6k| for (; true; data += 8) { ------------------ | Branch (303:20): [Folded - Ignored] ------------------ 304| 92.6k| uint64_t word; 305| 92.6k| memcpy(&word, data, sizeof(uint64_t)); 306| 92.6k| if (hasLess(word, 33)) { ------------------ | Branch (306:21): [True: 89.9k, False: 2.63k] ------------------ 307| 283k| while (*(unsigned char *)data > 32) data++; ------------------ | Branch (307:28): [True: 193k, False: 89.9k] ------------------ 308| | /* Now we stand on space */ 309| 89.9k| header.value = {start, (size_t) (data - start)}; 310| | /* Check that the following is http 1.1 */ 311| 89.9k| if (memcmp(" HTTP/1.1\r\n", data, 11) == 0) { ------------------ | Branch (311:25): [True: 82.2k, False: 7.77k] ------------------ 312| 82.2k| return data + 11; 313| 82.2k| } 314| 7.77k| return nullptr; 315| 89.9k| } 316| 92.6k| } 317| 89.9k| } 318| 550k| return nullptr; 319| 640k| } _ZN3uWS10HttpParser7hasLessEmm: 220| 370k| static inline uint64_t hasLess(uint64_t x, uint64_t n) { 221| 370k| return (((x)-~0ULL/255*(n))&~(x)&~0ULL/255*128); 222| 370k| } _ZN3uWS10HttpParser16consumeFieldNameEPc: 264| 123k| static inline void *consumeFieldName(char *p) { 265| | /* Best case fast path (particularly useful with clang) */ 266| 134k| while (true) { ------------------ | Branch (266:16): [Folded - Ignored] ------------------ 267| 167k| while ((*p >= 65) & (*p <= 90)) [[likely]] { ------------------ | Branch (267:20): [True: 33.4k, False: 134k] ------------------ 268| 33.4k| *p |= 32; 269| 33.4k| p++; 270| 33.4k| } 271| 631k| while (((*p >= 97) & (*p <= 122))) [[likely]] { ------------------ | Branch (271:20): [True: 497k, False: 134k] ------------------ 272| 497k| p++; 273| 497k| } 274| 134k| if (*p == ':') { ------------------ | Branch (274:17): [True: 113k, False: 20.9k] ------------------ 275| 113k| return (void *)p; 276| 113k| } 277| 20.9k| if (*p == '-') { ------------------ | Branch (277:17): [True: 5.56k, False: 15.3k] ------------------ 278| 5.56k| p++; 279| 15.3k| } else if (!((*p >= 65) & (*p <= 90))) { ------------------ | Branch (279:24): [True: 10.6k, False: 4.69k] ------------------ 280| | /* Exit fast path parsing */ 281| 10.6k| break; 282| 10.6k| } 283| 20.9k| } 284| | 285| | /* Generic */ 286| 130k| while (isFieldNameByteFastLowercased(*(unsigned char *)p)) { ------------------ | Branch (286:16): [True: 120k, False: 10.6k] ------------------ 287| 120k| p++; 288| 120k| } 289| 10.6k| return (void *)p; 290| 123k| } _ZN3uWS10HttpParser29isFieldNameByteFastLowercasedERh: 249| 130k| static inline bool isFieldNameByteFastLowercased(unsigned char &in) { 250| | /* Most common is lowercase alpha and hyphen */ 251| 130k| if (((in >= 97) & (in <= 122)) | (in == '-')) [[likely]] { ------------------ | Branch (251:13): [True: 112k, False: 17.9k] ------------------ 252| 112k| return true; 253| | /* Second is upper case alpha */ 254| 112k| } else if ((in >= 65) & (in <= 90)) [[unlikely]] { ------------------ | Branch (254:20): [True: 2.79k, False: 15.2k] ------------------ 255| 2.79k| in |= 32; 256| 2.79k| return true; 257| | /* These are rarely used but still valid */ 258| 15.2k| } else if (isUnlikelyFieldNameByte(in)) [[unlikely]] { ------------------ | Branch (258:20): [True: 4.50k, False: 10.6k] ------------------ 259| 4.50k| return true; 260| 4.50k| } 261| 10.6k| return false; 262| 130k| } _ZN3uWS10HttpParser23isUnlikelyFieldNameByteEh: 243| 15.2k| { 244| | /* Digits and 14 of the 15 non-alphanum characters (lacking hyphen) */ 245| 15.2k| return ((c == '~') | (c == '|') | (c == '`') | (c == '_') | (c == '^') | (c == '.') | (c == '+') ------------------ | Branch (245:16): [True: 1.97k, False: 13.2k] ------------------ 246| 15.2k| | (c == '*') | (c == '!')) || ((c >= 48) & (c <= 57)) || ((c <= 39) & (c >= 35)); ------------------ | Branch (246:43): [True: 1.60k, False: 11.6k] | Branch (246:70): [True: 930, False: 10.6k] ------------------ 247| 15.2k| } _ZN3uWS10HttpParser20tryConsumeFieldValueEPc: 325| 114k| static inline void *tryConsumeFieldValue(char *p) { 326| 278k| for (; true; p += 8) { ------------------ | Branch (326:16): [Folded - Ignored] ------------------ 327| 278k| uint64_t word; 328| 278k| memcpy(&word, p, sizeof(uint64_t)); 329| 278k| if (hasLess(word, 32)) { ------------------ | Branch (329:17): [True: 114k, False: 163k] ------------------ 330| 304k| while (*(unsigned char *)p > 31) p++; ------------------ | Branch (330:24): [True: 190k, False: 114k] ------------------ 331| 114k| return (void *)p; 332| 114k| } 333| 278k| } 334| 114k| } _ZN3uWS11HttpRequest9getHeaderENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE: 115| 251k| std::string_view getHeader(std::string_view lowerCasedHeader) { 116| 251k| if (bf.mightHave(lowerCasedHeader)) { ------------------ | Branch (116:13): [True: 62.3k, False: 188k] ------------------ 117| 81.2k| for (Header *h = headers; (++h)->key.length(); ) { ------------------ | Branch (117:39): [True: 80.5k, False: 694] ------------------ 118| 80.5k| if (h->key.length() == lowerCasedHeader.length() && !strncmp(h->key.data(), lowerCasedHeader.data(), lowerCasedHeader.length())) { ------------------ | Branch (118:21): [True: 66.1k, False: 14.3k] | Branch (118:69): [True: 61.6k, False: 4.49k] ------------------ 119| 61.6k| return h->value; 120| 61.6k| } 121| 80.5k| } 122| 62.3k| } 123| 189k| return std::string_view(nullptr, 0); 124| 251k| } _ZN3uWS10HttpParser17toUnsignedIntegerENSt3__117basic_string_viewIcNS1_11char_traitsIcEEEE: 203| 4.34k| static uint64_t toUnsignedInteger(std::string_view str) { 204| | /* We assume at least 64-bit integer giving us safely 999999999999999999 (18 number of 9s) */ 205| 4.34k| if (str.length() > 18) { ------------------ | Branch (205:13): [True: 586, False: 3.76k] ------------------ 206| 586| return UINT64_MAX; 207| 586| } 208| | 209| 3.76k| uint64_t unsignedIntegerValue = 0; 210| 10.1k| for (char c : str) { ------------------ | Branch (210:21): [True: 10.1k, False: 3.23k] ------------------ 211| | /* As long as the letter is 0-9 we cannot overflow. */ 212| 10.1k| if (c < '0' || c > '9') { ------------------ | Branch (212:17): [True: 346, False: 9.85k] | Branch (212:28): [True: 177, False: 9.67k] ------------------ 213| 523| return UINT64_MAX; 214| 523| } 215| 9.67k| unsignedIntegerValue = unsignedIntegerValue * 10ull + ((unsigned int) c - (unsigned int) '0'); 216| 9.67k| } 217| 3.23k| return unsignedIntegerValue; 218| 3.76k| } _ZN3uWS10HttpParser25fenceAndConsumePostPaddedILi0EEENSt3__14pairIjPvEEPcjS4_S4_PNS_11HttpRequestERN5ofats13any_invocableIFS4_S4_S8_EEERNSA_IFS4_S4_NS2_17basic_string_viewIcNS2_11char_traitsIcEEEEbEEE: 448| 593k| std::pair fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction &requestHandler, MoveOnlyFunction &dataHandler) { 449| | 450| | /* How much data we CONSUMED (to throw away) */ 451| 593k| unsigned int consumedTotal = 0; 452| 593k| unsigned int err = 0; 453| | 454| | /* Fence two bytes past end of our buffer (buffer has post padded margins). 455| | * This is to always catch scan for \r but not for \r\n. */ 456| 593k| data[length] = '\r'; 457| 593k| data[length + 1] = 'a'; /* Anything that is not \n, to trigger "invalid request" */ 458| | 459| 633k| for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved, err)); ) { ------------------ | Branch (459:37): [True: 615k, False: 17.3k] | Branch (459:47): [True: 42.2k, False: 573k] ------------------ 460| 42.2k| data += consumed; 461| 42.2k| length -= consumed; 462| 42.2k| consumedTotal += consumed; 463| | 464| | /* Even if we could parse it, check for length here as well */ 465| 42.2k| if (consumed > MAX_FALLBACK_SIZE) { ------------------ | Branch (465:17): [True: 0, False: 42.2k] ------------------ 466| 0| return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; 467| 0| } 468| | 469| | /* Store HTTP version (ancient 1.0 or 1.1) */ 470| 42.2k| req->ancientHttp = false; 471| | 472| | /* Add all headers to bloom filter */ 473| 42.2k| req->bf.reset(); 474| 97.0k| for (HttpRequest::Header *h = req->headers; (++h)->key.length(); ) { ------------------ | Branch (474:57): [True: 54.8k, False: 42.2k] ------------------ 475| 54.8k| req->bf.add(h->key); 476| 54.8k| } 477| | 478| | /* Break if no host header (but we can have empty string which is different from nullptr) */ 479| 42.2k| if (!req->getHeader("host").data()) { ------------------ | Branch (479:17): [True: 869, False: 41.3k] ------------------ 480| 869| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 481| 869| } 482| | 483| | /* RFC 9112 6.3 484| | * If a message is received with both a Transfer-Encoding and a Content-Length header field, 485| | * the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt 486| | * to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and 487| | * ought to be handled as an error. */ 488| 41.3k| std::string_view transferEncodingString = req->getHeader("transfer-encoding"); 489| 41.3k| std::string_view contentLengthString = req->getHeader("content-length"); 490| 41.3k| if (transferEncodingString.length() && contentLengthString.length()) { ------------------ | Branch (490:17): [True: 0, False: 41.3k] | Branch (490:52): [True: 0, False: 0] ------------------ 491| | /* Returning fullptr is the same as calling the errorHandler */ 492| | /* We could be smart and set an error in the context along with this, to indicate what 493| | * http error response we might want to return */ 494| 0| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 495| 0| } 496| | 497| | /* Parse query */ 498| 41.3k| const char *querySeparatorPtr = (const char *) memchr(req->headers->value.data(), '?', req->headers->value.length()); 499| 41.3k| req->querySeparator = (unsigned int) ((querySeparatorPtr ? querySeparatorPtr : req->headers->value.data() + req->headers->value.length()) - req->headers->value.data()); ------------------ | Branch (499:52): [True: 15.7k, False: 25.6k] ------------------ 500| | 501| | /* If returned socket is not what we put in we need 502| | * to break here as we either have upgraded to 503| | * WebSockets or otherwise closed the socket. */ 504| 41.3k| void *returnedUser = requestHandler(user, req); 505| 41.3k| if (returnedUser != user) { ------------------ | Branch (505:17): [True: 1.36k, False: 40.0k] ------------------ 506| | /* We are upgraded to WebSocket or otherwise broken */ 507| 1.36k| return {consumedTotal, returnedUser}; 508| 1.36k| } 509| | 510| | /* The rules at play here according to RFC 9112 for requests are essentially: 511| | * If both content-length and transfer-encoding then invalid message; must break. 512| | * If has transfer-encoding then must be chunked regardless of value. 513| | * If content-length then fixed length even if 0. 514| | * If none of the above then fixed length is 0. */ 515| | 516| | /* RFC 9112 6.3 517| | * If a message is received with both a Transfer-Encoding and a Content-Length header field, 518| | * the Transfer-Encoding overrides the Content-Length. */ 519| 40.0k| if (transferEncodingString.length()) { ------------------ | Branch (519:17): [True: 0, False: 40.0k] ------------------ 520| | 521| | /* If a proxy sent us the transfer-encoding header that 100% means it must be chunked or else the proxy is 522| | * not RFC 9112 compliant. Therefore it is always better to assume this is the case, since that entirely eliminates 523| | * all forms of transfer-encoding obfuscation tricks. We just rely on the header. */ 524| | 525| | /* RFC 9112 6.3 526| | * If a Transfer-Encoding header field is present in a request and the chunked transfer coding is not the 527| | * final encoding, the message body length cannot be determined reliably; the server MUST respond with the 528| | * 400 (Bad Request) status code and then close the connection. */ 529| | 530| | /* In this case we fail later by having the wrong interpretation (assuming chunked). 531| | * This could be made stricter but makes no difference either way, unless forwarding the identical message as a proxy. */ 532| | 533| 0| remainingStreamingBytes = STATE_IS_CHUNKED; 534| | /* If consume minimally, we do not want to consume anything but we want to mark this as being chunked */ 535| 0| if (!CONSUME_MINIMALLY) { ------------------ | Branch (535:21): [Folded - Ignored] ------------------ 536| | /* Go ahead and parse it (todo: better heuristics for emitting FIN to the app level) */ 537| 0| std::string_view dataToConsume(data, length); 538| 0| for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { ------------------ | Branch (538:37): [True: 0, False: 0] ------------------ 539| 0| dataHandler(user, chunk, chunk.length() == 0); 540| 0| } 541| 0| if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { ------------------ | Branch (541:25): [True: 0, False: 0] ------------------ 542| 0| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 543| 0| } 544| 0| unsigned int consumed = (length - (unsigned int) dataToConsume.length()); 545| 0| data = (char *) dataToConsume.data(); 546| 0| length = (unsigned int) dataToConsume.length(); 547| 0| consumedTotal += consumed; 548| 0| } 549| 40.0k| } else if (contentLengthString.length()) { ------------------ | Branch (549:24): [True: 1.64k, False: 38.3k] ------------------ 550| 1.64k| remainingStreamingBytes = toUnsignedInteger(contentLengthString); 551| 1.64k| if (remainingStreamingBytes == UINT64_MAX) { ------------------ | Branch (551:21): [True: 651, False: 989] ------------------ 552| | /* Parser error */ 553| 651| return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; 554| 651| } 555| | 556| 989| if (!CONSUME_MINIMALLY) { ------------------ | Branch (556:21): [Folded - Ignored] ------------------ 557| 989| unsigned int emittable = (unsigned int) std::min(remainingStreamingBytes, length); 558| 989| dataHandler(user, std::string_view(data, emittable), emittable == remainingStreamingBytes); 559| 989| remainingStreamingBytes -= emittable; 560| | 561| 989| data += emittable; 562| 989| length -= emittable; 563| 989| consumedTotal += emittable; 564| 989| } 565| 38.3k| } else { 566| | /* If we came here without a body; emit an empty data chunk to signal no data */ 567| 38.3k| dataHandler(user, {}, true); 568| 38.3k| } 569| | 570| | /* Consume minimally should break as easrly as possible */ 571| 39.3k| if (CONSUME_MINIMALLY) { ------------------ | Branch (571:17): [Folded - Ignored] ------------------ 572| 0| break; 573| 0| } 574| 39.3k| } 575| | /* Whenever we return FULLPTR, the interpretation of "consumed" should be the HttpError enum. */ 576| 590k| if (err) { ------------------ | Branch (576:13): [True: 558k, False: 32.9k] ------------------ 577| 558k| return {err, FULLPTR}; 578| 558k| } 579| 32.9k| return {consumedTotal, user}; 580| 590k| } _ZN3uWS11HttpRequest9isAncientEv: 72| 48.9k| bool isAncient() { 73| 48.9k| return ancientHttp; 74| 48.9k| } _ZN3uWS11HttpRequest22getCaseSensitiveMethodEv: 135| 48.9k| std::string_view getCaseSensitiveMethod() { 136| 48.9k| return std::string_view(headers->key.data(), headers->key.length()); 137| 48.9k| } _ZN3uWS11HttpRequest6getUrlEv: 126| 48.9k| std::string_view getUrl() { 127| 48.9k| return std::string_view(headers->value.data(), querySeparator); 128| 48.9k| } _ZN3uWS11HttpRequest8setYieldEb: 111| 48.9k| void setYield(bool yield) { 112| 48.9k| didYield = yield; 113| 48.9k| } _ZN3uWS11HttpRequest13setParametersENSt3__14pairIiPNS1_17basic_string_viewIcNS1_11char_traitsIcEEEEEE: 166| 48.9k| void setParameters(std::pair parameters) { 167| 48.9k| currentParameters = parameters; 168| 48.9k| } _ZN3uWS11HttpRequest19setParameterOffsetsEPNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEEtNS1_4lessIvEENS6_INS1_4pairIKS8_tEEEEEE: 170| 48.9k| void setParameterOffsets(std::map> *offsets) { 171| 48.9k| currentParameterOffsets = offsets; 172| 48.9k| } _ZN3uWS11HttpRequest8getYieldEv: 76| 48.9k| bool getYield() { 77| 48.9k| return didYield; 78| 48.9k| } _ZN3uWS12HttpResponseILb0EE12hasRespondedEv: 490| 97.9k| bool hasResponded() { 491| 97.9k| HttpResponseData *httpResponseData = getHttpResponseData(); 492| | 493| 97.9k| return !(httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING); 494| 97.9k| } _ZN3uWS12HttpResponseILb0EE19getHttpResponseDataEv: 52| 293k| HttpResponseData *getHttpResponseData() { 53| 293k| return (HttpResponseData *) Super::getAsyncSocketData(); 54| 293k| } _ZN3uWS12HttpResponseILb0EE11writeStatusENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEE: 383| 111k| HttpResponse *writeStatus(std::string_view status) { 384| 111k| HttpResponseData *httpResponseData = getHttpResponseData(); 385| | 386| | /* Do not allow writing more than one status */ 387| 111k| if (httpResponseData->state & HttpResponseData::HTTP_STATUS_CALLED) { ------------------ | Branch (387:13): [True: 79.0k, False: 32.1k] ------------------ 388| 79.0k| return this; 389| 79.0k| } 390| | 391| | /* Update status */ 392| 32.1k| httpResponseData->state |= HttpResponseData::HTTP_STATUS_CALLED; 393| | 394| 32.1k| Super::write("HTTP/1.1 ", 9); 395| 32.1k| Super::write(status.data(), (int) status.length()); 396| 32.1k| Super::write("\r\n", 2); 397| 32.1k| return this; 398| 111k| } _ZN3uWS12HttpResponseILb0EE3endENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEb: 432| 32.1k| void end(std::string_view data = {}, bool closeConnection = false) { 433| 32.1k| internalEnd(data, data.length(), false, true, closeConnection); 434| 32.1k| } _ZN3uWS12HttpResponseILb0EE11internalEndENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEmbbb: 92| 32.1k| bool internalEnd(std::string_view data, uintmax_t totalSize, bool optional, bool allowContentLength = true, bool closeConnection = false) { 93| | /* Write status if not already done */ 94| 32.1k| writeStatus(HTTP_200_OK); 95| | 96| | /* If no total size given then assume this chunk is everything */ 97| 32.1k| if (!totalSize) { ------------------ | Branch (97:13): [True: 0, False: 32.1k] ------------------ 98| 0| totalSize = data.length(); 99| 0| } 100| | 101| 32.1k| HttpResponseData *httpResponseData = getHttpResponseData(); 102| | 103| | /* In some cases, such as when refusing huge data we want to close the connection when drained */ 104| 32.1k| if (closeConnection) { ------------------ | Branch (104:13): [True: 0, False: 32.1k] ------------------ 105| | 106| | /* HTTP 1.1 must send this back unless the client already sent it to us. 107| | * It is a connection close when either of the two parties say so but the 108| | * one party must tell the other one so. 109| | * 110| | * This check also serves to limit writing the header only once. */ 111| 0| if ((httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) == 0) { ------------------ | Branch (111:17): [True: 0, False: 0] ------------------ 112| 0| writeHeader("Connection", "close"); 113| 0| } 114| | 115| 0| httpResponseData->state |= HttpResponseData::HTTP_CONNECTION_CLOSE; 116| 0| } 117| | 118| 32.1k| if (httpResponseData->state & HttpResponseData::HTTP_WRITE_CALLED) { ------------------ | Branch (118:13): [True: 0, False: 32.1k] ------------------ 119| | 120| | /* We do not have tryWrite-like functionalities, so ignore optional in this path */ 121| | 122| | /* Do not allow sending 0 chunk here */ 123| 0| if (data.length()) { ------------------ | Branch (123:17): [True: 0, False: 0] ------------------ 124| 0| Super::write("\r\n", 2); 125| 0| writeUnsignedHex((unsigned int) data.length()); 126| 0| Super::write("\r\n", 2); 127| | 128| | /* Ignoring optional for now */ 129| 0| Super::write(data.data(), (int) data.length()); 130| 0| } 131| | 132| | /* Terminating 0 chunk */ 133| 0| Super::write("\r\n0\r\n\r\n", 7); 134| | 135| 0| httpResponseData->markDone(); 136| | 137| | /* We need to check if we should close this socket here now */ 138| 0| if (!Super::isCorked()) { ------------------ | Branch (138:17): [True: 0, False: 0] ------------------ 139| 0| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { ------------------ | Branch (139:21): [True: 0, False: 0] ------------------ 140| 0| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { ------------------ | Branch (140:25): [True: 0, False: 0] ------------------ 141| 0| if (((AsyncSocket *) this)->getBufferedAmount() == 0) { ------------------ | Branch (141:29): [True: 0, False: 0] ------------------ 142| 0| ((AsyncSocket *) this)->shutdown(); 143| | /* We need to force close after sending FIN since we want to hinder 144| | * clients from keeping to send their huge data */ 145| 0| ((AsyncSocket *) this)->close(); 146| 0| return true; 147| 0| } 148| 0| } 149| 0| } 150| 0| } 151| | 152| | /* tryEnd can never fail when in chunked mode, since we do not have tryWrite (yet), only write */ 153| 0| Super::timeout(HTTP_TIMEOUT_S); 154| 0| return true; 155| 32.1k| } else { 156| | /* Write content-length on first call */ 157| 32.1k| if (!(httpResponseData->state & HttpResponseData::HTTP_END_CALLED)) { ------------------ | Branch (157:17): [True: 32.1k, False: 0] ------------------ 158| | /* Write mark, this propagates to WebSockets too */ 159| 32.1k| writeMark(); 160| | 161| | /* WebSocket upgrades does not allow content-length */ 162| 32.1k| if (allowContentLength) { ------------------ | Branch (162:21): [True: 32.1k, False: 0] ------------------ 163| | /* Even zero is a valid content-length */ 164| 32.1k| Super::write("Content-Length: ", 16); 165| 32.1k| writeUnsigned64(totalSize); 166| 32.1k| Super::write("\r\n\r\n", 4); 167| 32.1k| } else { 168| 0| Super::write("\r\n", 2); 169| 0| } 170| | 171| | /* Mark end called */ 172| 32.1k| httpResponseData->state |= HttpResponseData::HTTP_END_CALLED; 173| 32.1k| } 174| | 175| | /* Even if we supply no new data to write, its failed boolean is useful to know 176| | * if it failed to drain any prior failed header writes */ 177| | 178| | /* Write as much as possible without causing backpressure */ 179| 32.1k| size_t written = 0; 180| 32.1k| bool failed = false; 181| 64.3k| while (written < data.length() && !failed) { ------------------ | Branch (181:20): [True: 32.1k, False: 32.1k] | Branch (181:47): [True: 32.1k, False: 0] ------------------ 182| | /* uSockets only deals with int sizes, so pass chunks of max signed int size */ 183| 32.1k| auto writtenFailed = Super::write(data.data() + written, (int) std::min(data.length() - written, INT_MAX), optional); 184| | 185| 32.1k| written += (size_t) writtenFailed.first; 186| 32.1k| failed = writtenFailed.second; 187| 32.1k| } 188| | 189| 32.1k| httpResponseData->offset += written; 190| | 191| | /* Success is when we wrote the entire thing without any failures */ 192| 32.1k| bool success = written == data.length() && !failed; ------------------ | Branch (192:28): [True: 32.1k, False: 0] | Branch (192:56): [True: 30.6k, False: 1.48k] ------------------ 193| | 194| | /* If we are now at the end, start a timeout. Also start a timeout if we failed. */ 195| 32.1k| if (!success || httpResponseData->offset == totalSize) { ------------------ | Branch (195:17): [True: 1.48k, False: 30.6k] | Branch (195:29): [True: 30.6k, False: 0] ------------------ 196| 32.1k| Super::timeout(HTTP_TIMEOUT_S); 197| 32.1k| } 198| | 199| | /* Remove onAborted function if we reach the end */ 200| 32.1k| if (httpResponseData->offset == totalSize) { ------------------ | Branch (200:17): [True: 32.1k, False: 0] ------------------ 201| 32.1k| httpResponseData->markDone(); 202| | 203| | /* We need to check if we should close this socket here now */ 204| 32.1k| if (!Super::isCorked()) { ------------------ | Branch (204:21): [True: 0, False: 32.1k] ------------------ 205| 0| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { ------------------ | Branch (205:25): [True: 0, False: 0] ------------------ 206| 0| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { ------------------ | Branch (206:29): [True: 0, False: 0] ------------------ 207| 0| if (((AsyncSocket *) this)->getBufferedAmount() == 0) { ------------------ | Branch (207:33): [True: 0, False: 0] ------------------ 208| 0| ((AsyncSocket *) this)->shutdown(); 209| | /* We need to force close after sending FIN since we want to hinder 210| | * clients from keeping to send their huge data */ 211| 0| ((AsyncSocket *) this)->close(); 212| 0| } 213| 0| } 214| 0| } 215| 0| } 216| 32.1k| } 217| | 218| 32.1k| return success; 219| 32.1k| } 220| 32.1k| } _ZN3uWS12HttpResponseILb0EE11writeHeaderENSt3__117basic_string_viewIcNS2_11char_traitsIcEEEES6_: 401| 64.3k| HttpResponse *writeHeader(std::string_view key, std::string_view value) { 402| 64.3k| writeStatus(HTTP_200_OK); 403| | 404| 64.3k| Super::write(key.data(), (int) key.length()); 405| 64.3k| Super::write(": ", 2); 406| 64.3k| Super::write(value.data(), (int) value.length()); 407| 64.3k| Super::write("\r\n", 2); 408| 64.3k| return this; 409| 64.3k| } _ZN3uWS12HttpResponseILb0EE9writeMarkEv: 77| 32.1k| void writeMark() { 78| | /* Date is always written */ 79| 32.1k| writeHeader("Date", std::string_view(((LoopData *) us_loop_ext(us_socket_context_loop(SSL, (us_socket_context(SSL, (us_socket_t *) this)))))->date, 29)); 80| | 81| | /* You can disable this altogether */ 82| 32.1k|#ifndef UWS_HTTPRESPONSE_NO_WRITEMARK 83| 32.1k| if (!Super::getLoopData()->noMark) { ------------------ | Branch (83:13): [True: 32.1k, False: 0] ------------------ 84| | /* We only expose major version */ 85| 32.1k| writeHeader("uWebSockets", "20"); 86| 32.1k| } 87| 32.1k|#endif 88| 32.1k| } _ZN3uWS12HttpResponseILb0EE15writeUnsigned64Em: 68| 32.1k| void writeUnsigned64(uint64_t value) { 69| 32.1k| char buf[20]; 70| 32.1k| int length = utils::u64toa(value, buf); 71| | 72| | /* For now we do this copy */ 73| 32.1k| Super::write(buf, length); 74| 32.1k| } _ZN3uWS12HttpResponseILb0EE9onAbortedEON5ofats13any_invocableIFvvEEE: 559| 34.2k| HttpResponse *onAborted(MoveOnlyFunction &&handler) { 560| 34.2k| HttpResponseData *httpResponseData = getHttpResponseData(); 561| | 562| 34.2k| httpResponseData->onAborted = std::move(handler); 563| 34.2k| return this; 564| 34.2k| } _ZN3uWS12HttpResponseILb0EE4corkEON5ofats13any_invocableIFvvEEE: 497| 17.3k| HttpResponse *cork(MoveOnlyFunction &&handler) { 498| 17.3k| if (!Super::isCorked() && Super::canCork()) { ------------------ | Branch (498:13): [True: 17.3k, False: 0] | Branch (498:35): [True: 17.3k, False: 0] ------------------ 499| 17.3k| LoopData *loopData = Super::getLoopData(); 500| 17.3k| Super::cork(); 501| 17.3k| handler(); 502| | 503| | /* The only way we could possibly have changed the corked socket during handler call, would be if 504| | * the HTTP socket was upgraded to WebSocket and caused a realloc. Because of this we cannot use "this" 505| | * from here downwards. The corking is done with corkUnchecked() in upgrade. It steals cork. */ 506| 17.3k| auto *newCorkedSocket = loopData->corkedSocket; 507| | 508| | /* If nobody is corked, it means most probably that large amounts of data has 509| | * been written and the cork buffer has already been sent off and uncorked. 510| | * We are done here, if that is the case. */ 511| 17.3k| if (!newCorkedSocket) { ------------------ | Branch (511:17): [True: 0, False: 17.3k] ------------------ 512| 0| return this; 513| 0| } 514| | 515| | /* Timeout on uncork failure, since most writes will succeed while corked */ 516| 17.3k| auto [written, failed] = static_cast(newCorkedSocket)->uncork(); 517| | 518| | /* If we are no longer an HTTP socket then early return the new "this". 519| | * We don't want to even overwrite timeout as it is set in upgrade already. */ 520| 17.3k| if (this != newCorkedSocket) { ------------------ | Branch (520:17): [True: 0, False: 17.3k] ------------------ 521| 0| return static_cast(newCorkedSocket); 522| 0| } 523| | 524| 17.3k| if (failed) { ------------------ | Branch (524:17): [True: 14.1k, False: 3.25k] ------------------ 525| | /* For now we only have one single timeout so let's use it */ 526| | /* This behavior should equal the behavior in HttpContext when uncorking fails */ 527| 14.1k| Super::timeout(HTTP_TIMEOUT_S); 528| 14.1k| } 529| | 530| | /* If we have no backbuffer and we are connection close and we responded fully then close */ 531| 17.3k| HttpResponseData *httpResponseData = getHttpResponseData(); 532| 17.3k| if (httpResponseData->state & HttpResponseData::HTTP_CONNECTION_CLOSE) { ------------------ | Branch (532:17): [True: 3.69k, False: 13.7k] ------------------ 533| 3.69k| if ((httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) == 0) { ------------------ | Branch (533:21): [True: 3.69k, False: 0] ------------------ 534| 3.69k| if (((AsyncSocket *) this)->getBufferedAmount() == 0) { ------------------ | Branch (534:25): [True: 226, False: 3.46k] ------------------ 535| 226| ((AsyncSocket *) this)->shutdown(); 536| | /* We need to force close after sending FIN since we want to hinder 537| | * clients from keeping to send their huge data */ 538| 226| ((AsyncSocket *) this)->close(); 539| 226| } 540| 3.69k| } 541| 3.69k| } 542| 17.3k| } else { 543| | /* We are already corked, or can't cork so let's just call the handler */ 544| 0| handler(); 545| 0| } 546| | 547| 17.3k| return this; 548| 17.3k| } _ZN3uWS16HttpResponseDataILb0EE8markDoneEv: 37| 32.1k| void markDone() { 38| 32.1k| onAborted = nullptr; 39| | /* Also remove onWritable so that we do not emit when draining behind the scenes. */ 40| 32.1k| onWritable = nullptr; 41| | 42| | /* We are done with this request */ 43| 32.1k| state &= ~HttpResponseData::HTTP_RESPONSE_PENDING; 44| 32.1k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEEC2Ev: 243| 2.25k| HttpRouter() { 244| | /* Always have ANY route */ 245| 2.25k| getNode(&root, std::string(ANY_METHOD_TOKEN.data(), ANY_METHOD_TOKEN.length()), false); 246| 2.25k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE4NodeC2ENSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEE: 63| 11.2k| Node(std::string name) : name(name) {} _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE7getNodeEPNS4_4NodeENSt3__112basic_stringIcNS7_11char_traitsIcEENS7_9allocatorIcEEEEb: 81| 11.2k| Node *getNode(Node *parent, std::string child, bool isHighPriority) { 82| 11.2k| for (std::unique_ptr &node : parent->children) { ------------------ | Branch (82:42): [True: 4.50k, False: 9.01k] ------------------ 83| 4.50k| if (node->name == child && node->isHighPriority == isHighPriority) { ------------------ | Branch (83:17): [True: 2.25k, False: 2.25k] | Branch (83:40): [True: 2.25k, False: 0] ------------------ 84| 2.25k| return node.get(); 85| 2.25k| } 86| 4.50k| } 87| | 88| | /* Insert sorted, but keep order if parent is root (we sort methods by priority elsewhere) */ 89| 9.01k| std::unique_ptr newNode(new Node(child)); 90| 9.01k| newNode->isHighPriority = isHighPriority; 91| 9.01k| return parent->children.emplace(std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) { 92| | 93| 9.01k| if (a->isHighPriority != b->isHighPriority) { 94| 9.01k| return a->isHighPriority; 95| 9.01k| } 96| | 97| 9.01k| return b->name.length() && (parent != &root) && (lexicalOrder(b->name) < lexicalOrder(a->name)); 98| 9.01k| }), std::move(newNode))->get(); 99| 11.2k| } _ZZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE7getNodeEPNS4_4NodeENSt3__112basic_stringIcNS7_11char_traitsIcEENS7_9allocatorIcEEEEbENKUlRT_RT0_E_clIKNS7_10unique_ptrIS5_NS7_14default_deleteIS5_EEEESN_EEDaSF_SH_: 91| 2.25k| return parent->children.emplace(std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) { 92| | 93| 2.25k| if (a->isHighPriority != b->isHighPriority) { ------------------ | Branch (93:17): [True: 0, False: 2.25k] ------------------ 94| 0| return a->isHighPriority; 95| 0| } 96| | 97| 2.25k| return b->name.length() && (parent != &root) && (lexicalOrder(b->name) < lexicalOrder(a->name)); ------------------ | Branch (97:20): [True: 2.25k, False: 0] | Branch (97:40): [True: 0, False: 2.25k] | Branch (97:61): [True: 0, False: 0] ------------------ 98| 2.25k| }), std::move(newNode))->get(); _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE11getUserDataEv: 252| 97.9k| USERDATA &getUserData() { 253| 97.9k| return userData; 254| 97.9k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE5routeENSt3__117basic_string_viewIcNS5_11char_traitsIcEEEES9_: 257| 48.9k| bool route(std::string_view method, std::string_view url) { 258| | /* Reset url parsing cache */ 259| 48.9k| setUrl(url); 260| 48.9k| routeParameters.reset(); 261| | 262| | /* Begin by finding the method node */ 263| 63.7k| for (auto &p : root.children) { ------------------ | Branch (263:22): [True: 63.7k, False: 14.7k] ------------------ 264| 63.7k| if (p->name == method) { ------------------ | Branch (264:17): [True: 34.2k, False: 29.5k] ------------------ 265| | /* Then route the url */ 266| 34.2k| if (executeHandlers(p.get(), 0, userData)) { ------------------ | Branch (266:21): [True: 34.2k, False: 0] ------------------ 267| 34.2k| return true; 268| 34.2k| } else { 269| 0| break; 270| 0| } 271| 34.2k| } 272| 63.7k| } 273| | 274| | /* Always test any route last */ 275| 14.7k| return executeHandlers(root.children.back().get(), 0, userData); 276| 48.9k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE6setUrlENSt3__117basic_string_viewIcNS5_11char_traitsIcEEEE: 124| 55.7k| inline void setUrl(std::string_view url) { 125| | 126| | /* Todo: URL may also start with "http://domain/" or "*", not only "/" */ 127| | 128| | /* We expect to stand on a slash */ 129| 55.7k| currentUrl = url; 130| 55.7k| urlSegmentTop = -1; 131| 55.7k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE15RouteParameters5resetEv: 108| 48.9k| void reset() { 109| 48.9k| paramsTop = -1; 110| 48.9k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE15executeHandlersEPNS4_4NodeEiRS3_: 168| 48.9k| bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) { 169| | 170| 48.9k| auto [segment, isStop] = getUrlSegment(urlSegment); 171| | 172| | /* If we are on STOP, return where we may stand */ 173| 48.9k| if (isStop) { ------------------ | Branch (173:13): [True: 0, False: 48.9k] ------------------ 174| | /* We have reached accross the entire URL with no stoppage, execute */ 175| 0| for (uint32_t handler : parent->handlers) { ------------------ | Branch (175:35): [True: 0, False: 0] ------------------ 176| 0| if (handlers[handler & HANDLER_MASK](this)) { ------------------ | Branch (176:21): [True: 0, False: 0] ------------------ 177| 0| return true; 178| 0| } 179| 0| } 180| | /* We reached the end, so go back */ 181| 0| return false; 182| 0| } 183| | 184| 48.9k| for (auto &p : parent->children) { ------------------ | Branch (184:22): [True: 48.9k, False: 0] ------------------ 185| 48.9k| if (p->name.length() && p->name[0] == '*') { ------------------ | Branch (185:17): [True: 48.9k, False: 0] | Branch (185:37): [True: 48.9k, False: 0] ------------------ 186| | /* Wildcard match (can be seen as a shortcut) */ 187| 48.9k| for (uint32_t handler : p->handlers) { ------------------ | Branch (187:39): [True: 48.9k, False: 0] ------------------ 188| 48.9k| if (handlers[handler & HANDLER_MASK](this)) { ------------------ | Branch (188:25): [True: 48.9k, False: 0] ------------------ 189| 48.9k| return true; 190| 48.9k| } 191| 48.9k| } 192| 48.9k| } else if (p->name.length() && p->name[0] == ':' && segment.length()) { ------------------ | Branch (192:24): [True: 0, False: 0] | Branch (192:44): [True: 0, False: 0] | Branch (192:65): [True: 0, False: 0] ------------------ 193| | /* Parameter match */ 194| 0| routeParameters.push(segment); 195| 0| if (executeHandlers(p.get(), urlSegment + 1, userData)) { ------------------ | Branch (195:21): [True: 0, False: 0] ------------------ 196| 0| return true; 197| 0| } 198| 0| routeParameters.pop(); 199| 0| } else if (p->name == segment) { ------------------ | Branch (199:24): [True: 0, False: 0] ------------------ 200| | /* Static match */ 201| 0| if (executeHandlers(p.get(), urlSegment + 1, userData)) { ------------------ | Branch (201:21): [True: 0, False: 0] ------------------ 202| 0| return true; 203| 0| } 204| 0| } 205| 48.9k| } 206| 0| return false; 207| 48.9k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE13getUrlSegmentEi: 134| 67.0k| inline std::pair getUrlSegment(int urlSegment) { 135| 67.0k| if (urlSegment > urlSegmentTop) { ------------------ | Branch (135:13): [True: 60.2k, False: 6.76k] ------------------ 136| | /* Signal as STOP when we have no more URL or stack space */ 137| 60.2k| if (!currentUrl.length() || urlSegment > int(MAX_URL_SEGMENTS - 1)) { ------------------ | Branch (137:17): [True: 4.50k, False: 55.7k] | Branch (137:41): [True: 0, False: 55.7k] ------------------ 138| 4.50k| return {{}, true}; 139| 4.50k| } 140| | 141| | /* We always stand on a slash here, so step over it */ 142| 55.7k| currentUrl.remove_prefix(1); 143| | 144| 55.7k| auto segmentLength = currentUrl.find('/'); 145| 55.7k| if (segmentLength == std::string::npos) { ------------------ | Branch (145:17): [True: 50.1k, False: 5.64k] ------------------ 146| 50.1k| segmentLength = currentUrl.length(); 147| | 148| | /* Push to url segment vector */ 149| 50.1k| urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength); 150| 50.1k| urlSegmentTop++; 151| | 152| | /* Update currentUrl */ 153| 50.1k| currentUrl = currentUrl.substr(segmentLength); 154| 50.1k| } else { 155| | /* Push to url segment vector */ 156| 5.64k| urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength); 157| 5.64k| urlSegmentTop++; 158| | 159| | /* Update currentUrl */ 160| 5.64k| currentUrl = currentUrl.substr(segmentLength); 161| 5.64k| } 162| 55.7k| } 163| | /* In any case we return it */ 164| 62.5k| return {urlSegmentVector[urlSegment], false}; 165| 67.0k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE6removeENSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESB_j: 360| 4.50k| void remove(std::string method, std::string pattern, uint32_t priority) { 361| 4.50k| uint32_t handler = findHandler(method, pattern, priority); 362| 4.50k| if (handler == UINT32_MAX) { ------------------ | Branch (362:13): [True: 4.50k, False: 0] ------------------ 363| | /* Not found or already removed, do nothing */ 364| 4.50k| return; 365| 4.50k| } 366| | 367| | /* Cull the entire tree */ 368| | /* For all nodes in depth first tree traveral; 369| | * if node contains handler - remove the handler - 370| | * if node holds no handlers after removal, remove the node and return */ 371| 0| cullNode(nullptr, &root, handler); 372| | 373| | /* Now remove the actual handler */ 374| 0| handlers.erase(handlers.begin() + (handler & HANDLER_MASK)); 375| 0| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE11findHandlerENSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEESB_j: 210| 4.50k| uint32_t findHandler(std::string method, std::string pattern, uint32_t priority) { 211| 4.50k| for (std::unique_ptr &node : root.children) { ------------------ | Branch (211:42): [True: 4.50k, False: 2.25k] ------------------ 212| 4.50k| if (method == node->name) { ------------------ | Branch (212:17): [True: 2.25k, False: 2.25k] ------------------ 213| 2.25k| setUrl(pattern); 214| 2.25k| Node *n = node.get(); 215| 2.25k| for (int i = 0; !getUrlSegment(i).second; i++) { ------------------ | Branch (215:33): [True: 2.25k, False: 0] ------------------ 216| | /* Go to next segment or quit */ 217| 2.25k| std::string segment = std::string(getUrlSegment(i).first); 218| 2.25k| Node *next = nullptr; 219| 2.25k| for (std::unique_ptr &child : n->children) { ------------------ | Branch (219:55): [True: 0, False: 2.25k] ------------------ 220| 0| if (child->name == segment && child->isHighPriority == (priority == HIGH_PRIORITY)) { ------------------ | Branch (220:29): [True: 0, False: 0] | Branch (220:55): [True: 0, False: 0] ------------------ 221| 0| next = child.get(); 222| 0| break; 223| 0| } 224| 0| } 225| 2.25k| if (!next) { ------------------ | Branch (225:25): [True: 2.25k, False: 0] ------------------ 226| 2.25k| return UINT32_MAX; 227| 2.25k| } 228| 0| n = next; 229| 0| } 230| | /* Seek for a priority match in the found node */ 231| 0| for (unsigned int i = 0; i < n->handlers.size(); i++) { ------------------ | Branch (231:42): [True: 0, False: 0] ------------------ 232| 0| if ((n->handlers[i] & ~HANDLER_MASK) == priority) { ------------------ | Branch (232:25): [True: 0, False: 0] ------------------ 233| 0| return n->handlers[i]; 234| 0| } 235| 0| } 236| 0| return UINT32_MAX; 237| 0| } 238| 4.50k| } 239| 2.25k| return UINT32_MAX; 240| 4.50k| } _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE3addENSt3__16vectorINS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEENSA_ISC_EEEESC_ON5ofats13any_invocableIFbPS4_EEEj: 279| 4.50k| void add(std::vector methods, std::string pattern, MoveOnlyFunction &&handler, uint32_t priority = MEDIUM_PRIORITY) { 280| | /* First remove existing handler */ 281| 4.50k| remove(methods[0], pattern, priority); 282| | 283| 4.50k| for (std::string method : methods) { ------------------ | Branch (283:33): [True: 4.50k, False: 4.50k] ------------------ 284| | /* Lookup method */ 285| 4.50k| Node *node = getNode(&root, method, false); 286| | /* Iterate over all segments */ 287| 4.50k| setUrl(pattern); 288| 9.01k| for (int i = 0; !getUrlSegment(i).second; i++) { ------------------ | Branch (288:29): [True: 4.50k, False: 4.50k] ------------------ 289| 4.50k| std::string strippedSegment(getUrlSegment(i).first); 290| 4.50k| if (strippedSegment.length() && strippedSegment[0] == ':') { ------------------ | Branch (290:21): [True: 4.50k, False: 0] | Branch (290:49): [True: 0, False: 4.50k] ------------------ 291| | /* Parameter routes must be named only : */ 292| 0| strippedSegment = ":"; 293| 0| } 294| 4.50k| node = getNode(node, strippedSegment, priority == HIGH_PRIORITY); 295| 4.50k| } 296| | /* Insert handler in order sorted by priority (most significant 1 byte) */ 297| 4.50k| node->handlers.insert(std::upper_bound(node->handlers.begin(), node->handlers.end(), (uint32_t) (priority | handlers.size())), (uint32_t) (priority | handlers.size())); 298| 4.50k| } 299| | 300| | /* Alloate this handler */ 301| 4.50k| handlers.emplace_back(std::move(handler)); 302| | 303| | /* ANY method must be last, GET must be first */ 304| 4.50k| std::sort(root.children.begin(), root.children.end(), [](const auto &a, const auto &b) { 305| | /* Assuming the list of methods is unique, non-repeating */ 306| 4.50k| if (a->name == "GET") { 307| 4.50k| return true; 308| 4.50k| } else if (b->name == "GET") { 309| 4.50k| return false; 310| 4.50k| } else if (a->name == ANY_METHOD_TOKEN) { 311| 4.50k| return false; 312| 4.50k| } else if (b->name == ANY_METHOD_TOKEN) { 313| 4.50k| return true; 314| 4.50k| } else { 315| 4.50k| return a->name < b->name; 316| 4.50k| } 317| 4.50k| }); 318| 4.50k| } _ZZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE3addENSt3__16vectorINS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEENSA_ISC_EEEESC_ON5ofats13any_invocableIFbPS4_EEEjENKUlRKT_RKT0_E_clINS5_10unique_ptrINS4_4NodeENS5_14default_deleteISU_EEEESX_EEDaSN_SQ_: 304| 2.25k| std::sort(root.children.begin(), root.children.end(), [](const auto &a, const auto &b) { 305| | /* Assuming the list of methods is unique, non-repeating */ 306| 2.25k| if (a->name == "GET") { ------------------ | Branch (306:17): [True: 2.25k, False: 0] ------------------ 307| 2.25k| return true; 308| 2.25k| } else if (b->name == "GET") { ------------------ | Branch (308:24): [True: 0, False: 0] ------------------ 309| 0| return false; 310| 0| } else if (a->name == ANY_METHOD_TOKEN) { ------------------ | Branch (310:24): [True: 0, False: 0] ------------------ 311| 0| return false; 312| 0| } else if (b->name == ANY_METHOD_TOKEN) { ------------------ | Branch (312:24): [True: 0, False: 0] ------------------ 313| 0| return true; 314| 0| } else { 315| 0| return a->name < b->name; 316| 0| } 317| 2.25k| }); _ZN3uWS10HttpRouterINS_15HttpContextDataILb0EE10RouterDataEE13getParametersEv: 248| 48.9k| std::pair getParameters() { 249| 48.9k| return {routeParameters.paramsTop, routeParameters.params}; 250| 48.9k| } _ZN3uWS4Loop3getEPv: 111| 40.9k| static Loop *get(void *existingNativeLoop = nullptr) { 112| 40.9k| if (!getLazyLoop().loop) { ------------------ | Branch (112:13): [True: 2.25k, False: 38.7k] ------------------ 113| | /* If we are given a native loop pointer we pass that to uSockets and let it deal with it */ 114| 2.25k| if (existingNativeLoop) { ------------------ | Branch (114:17): [True: 0, False: 2.25k] ------------------ 115| | /* Todo: here we want to pass the pointer, not a boolean */ 116| 0| getLazyLoop().loop = create(existingNativeLoop); 117| | /* We cannot register automatic free here, must be manually done */ 118| 2.25k| } else { 119| 2.25k| getLazyLoop().loop = create(nullptr); 120| 2.25k| getLazyLoop().cleanMe = true; 121| 2.25k| } 122| 2.25k| } 123| | 124| 40.9k| return getLazyLoop().loop; 125| 40.9k| } _ZN3uWS4Loop11getLazyLoopEv: 103| 88.7k| static LoopCleaner &getLazyLoop() { 104| 88.7k| static thread_local LoopCleaner lazyLoop; 105| 88.7k| return lazyLoop; 106| 88.7k| } _ZN3uWS4Loop11LoopCleanerD2Ev: 94| 1| ~LoopCleaner() { 95| 1| if(loop && cleanMe) { ------------------ | Branch (95:16): [True: 0, False: 1] | Branch (95:24): [True: 0, False: 0] ------------------ 96| 0| loop->free(); 97| 0| } 98| 1| } _ZN3uWS4Loop6createEPv: 76| 2.25k| static Loop *create(void *hint) { 77| 2.25k| Loop *loop = ((Loop *) us_create_loop(hint, wakeupCb, preCb, postCb, sizeof(LoopData)))->init(); 78| | 79| | /* We also need some timers (should live off the one 4 second timer rather) */ 80| 2.25k| LoopData *loopData = (LoopData *) us_loop_ext((struct us_loop_t *) loop); 81| 2.25k| loopData->dateTimer = us_create_timer((struct us_loop_t *) loop, 1, sizeof(LoopData *)); 82| 2.25k| memcpy(us_timer_ext(loopData->dateTimer), &loopData, sizeof(LoopData *)); 83| 2.25k| us_timer_set(loopData->dateTimer, [](struct us_timer_t *t) { 84| 2.25k| LoopData *loopData; 85| 2.25k| memcpy(&loopData, us_timer_ext(t), sizeof(LoopData *)); 86| 2.25k| loopData->updateDate(); 87| 2.25k| }, 1000, 1000); 88| | 89| 2.25k| return loop; 90| 2.25k| } _ZN3uWS4Loop8wakeupCbEP9us_loop_t: 30| 1.88M| static void wakeupCb(us_loop_t *loop) { 31| 1.88M| LoopData *loopData = (LoopData *) us_loop_ext(loop); 32| | 33| | /* Swap current deferQueue */ 34| 1.88M| loopData->deferMutex.lock(); 35| 1.88M| int oldDeferQueue = loopData->currentDeferQueue; 36| 1.88M| loopData->currentDeferQueue = (loopData->currentDeferQueue + 1) % 2; 37| 1.88M| loopData->deferMutex.unlock(); 38| | 39| | /* Drain the queue */ 40| 1.88M| for (auto &x : loopData->deferQueues[oldDeferQueue]) { ------------------ | Branch (40:22): [True: 33.5k, False: 1.88M] ------------------ 41| 33.5k| x(); 42| 33.5k| } 43| 1.88M| loopData->deferQueues[oldDeferQueue].clear(); 44| 1.88M| } _ZN3uWS4Loop5preCbEP9us_loop_t: 46| 2.41M| static void preCb(us_loop_t *loop) { 47| 2.41M| LoopData *loopData = (LoopData *) us_loop_ext(loop); 48| | 49| 2.41M| for (auto &p : loopData->preHandlers) { ------------------ | Branch (49:22): [True: 0, False: 2.41M] ------------------ 50| 0| p.second((Loop *) loop); 51| 0| } 52| 2.41M| } _ZN3uWS4Loop6postCbEP9us_loop_t: 54| 2.41M| static void postCb(us_loop_t *loop) { 55| 2.41M| LoopData *loopData = (LoopData *) us_loop_ext(loop); 56| | 57| 2.41M| for (auto &p : loopData->postHandlers) { ------------------ | Branch (57:22): [True: 0, False: 2.41M] ------------------ 58| 0| p.second((Loop *) loop); 59| 0| } 60| | 61| | /* After every event loop iteration, we must not hold the cork buffer */ 62| 2.41M| if (loopData->corkedSocket) { ------------------ | Branch (62:13): [True: 0, False: 2.41M] ------------------ 63| 0| std::cerr << "Error: Cork buffer must not be held across event loop iterations!" << std::endl; 64| 0| std::terminate(); 65| 0| } 66| 2.41M| } _ZN3uWS4Loop4initEv: 71| 2.25k| Loop *init() { 72| 2.25k| new (us_loop_ext((us_loop_t *) this)) LoopData; 73| 2.25k| return this; 74| 2.25k| } _ZZN3uWS4Loop6createEPvENKUlP10us_timer_tE_clES3_: 83| 1.86M| us_timer_set(loopData->dateTimer, [](struct us_timer_t *t) { 84| 1.86M| LoopData *loopData; 85| 1.86M| memcpy(&loopData, us_timer_ext(t), sizeof(LoopData *)); 86| 1.86M| loopData->updateDate(); 87| 1.86M| }, 1000, 1000); _ZN3uWS4Loop4freeEv: 128| 2.25k| void free() { 129| 2.25k| LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this); 130| | 131| | /* Stop and free dateTimer first */ 132| 2.25k| us_timer_close(loopData->dateTimer); 133| | 134| 2.25k| loopData->~LoopData(); 135| | /* uSockets will track whether this loop is owned by us or a borrowed alien loop */ 136| 2.25k| us_loop_free((us_loop_t *) this); 137| | 138| | /* Reset lazyLoop */ 139| 2.25k| getLazyLoop().loop = nullptr; 140| 2.25k| } _ZN3uWS4Loop5deferEON5ofats13any_invocableIFvvEEE: 169| 34.2k| void defer(MoveOnlyFunction &&cb) { 170| 34.2k| LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this); 171| | 172| | //if (std::thread::get_id() == ) // todo: add fast path for same thread id 173| 34.2k| loopData->deferMutex.lock(); 174| 34.2k| loopData->deferQueues[loopData->currentDeferQueue].emplace_back(std::move(cb)); 175| 34.2k| loopData->deferMutex.unlock(); 176| | 177| 34.2k| us_wakeup_loop((us_loop_t *) this); 178| 34.2k| } _ZN3uWS3runEv: 198| 2.25k|inline void run() { 199| 2.25k| Loop::get()->run(); 200| 2.25k|} _ZN3uWS4Loop3runEv: 181| 2.25k| void run() { 182| 2.25k| us_loop_run((us_loop_t *) this); 183| 2.25k| } _ZN3uWS8LoopDataC2Ev: 49| 2.25k| LoopData() { 50| 2.25k| updateDate(); 51| 2.25k| } _ZN3uWS8LoopData10updateDateEv: 63| 1.86M| void updateDate() { 64| 1.86M| time_t now = time(0); 65| 1.86M| struct tm tstruct = {}; 66| |#ifdef _WIN32 67| | /* Micro, fucking soft never follows spec. */ 68| | gmtime_s(&tstruct, &now); 69| |#else 70| 1.86M| gmtime_r(&now, &tstruct); 71| 1.86M|#endif 72| 1.86M| static const char wday_name[][4] = { 73| 1.86M| "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 74| 1.86M| }; 75| 1.86M| static const char mon_name[][4] = { 76| 1.86M| "Jan", "Feb", "Mar", "Apr", "May", "Jun", 77| 1.86M| "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 78| 1.86M| }; 79| 1.86M| snprintf(date, 32, "%.3s, %.2u %.3s %.4u %.2u:%.2u:%.2u GMT", 80| 1.86M| wday_name[tstruct.tm_wday], 81| 1.86M| tstruct.tm_mday % 99, 82| 1.86M| mon_name[tstruct.tm_mon], 83| 1.86M| (1900 + tstruct.tm_year) % 9999, 84| 1.86M| tstruct.tm_hour % 99, 85| 1.86M| tstruct.tm_min % 99, 86| 1.86M| tstruct.tm_sec % 99); 87| 1.86M| } _ZN3uWS8LoopDataD2Ev: 53| 2.25k| ~LoopData() { 54| | /* If we have had App.ws called with compression we need to clear this */ 55| 2.25k| if (zlibContext) { ------------------ | Branch (55:13): [True: 0, False: 2.25k] ------------------ 56| 0| delete zlibContext; 57| 0| delete inflationStream; 58| 0| delete deflationStream; 59| 0| } 60| 2.25k| delete [] corkBuffer; 61| 2.25k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJP18us_listen_socket_tEED2Ev: 206| 2.25k| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIvLb0EJP18us_listen_socket_tEE7destroyEv: 239| 2.25k| void destroy() noexcept { 240| 2.25k| if (handle_) { ------------------ | Branch (240:9): [True: 2.25k, False: 0] ------------------ 241| 2.25k| handle_(action::destroy, &storage_, nullptr); 242| 2.25k| handle_ = nullptr; 243| 2.25k| } 244| 2.25k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEED2Ev: 206| 13.5k| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE7destroyEv: 239| 13.5k| void destroy() noexcept { 240| 13.5k| if (handle_) { ------------------ | Branch (240:9): [True: 4.50k, False: 9.01k] ------------------ 241| 4.50k| handle_(action::destroy, &storage_, nullptr); 242| 4.50k| handle_ = nullptr; 243| 4.50k| } 244| 13.5k| } _ZN5ofats13any_invocableIFvvEEclEv: 345| 67.7k| R operator()(ArgTypes... args) cv ref noexcept(noex) { \ 346| 67.7k| return base_type::call(std::forward(args)...); \ 347| 67.7k| } \ _ZN5ofats10any_detail18any_invocable_implIvLb0EJEE4callEv: 246| 67.7k| R call(ArgTypes... args) noexcept(is_noexcept) { 247| 67.7k| return call_(storage_, std::forward(args)...); 248| 67.7k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJEED2Ev: 206| 1.64M| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIvLb0EJEE7destroyEv: 239| 1.64M| void destroy() noexcept { 240| 1.64M| if (handle_) { ------------------ | Branch (240:9): [True: 85.8k, False: 1.56M] ------------------ 241| 85.8k| handle_(action::destroy, &storage_, nullptr); 242| 85.8k| handle_ = nullptr; 243| 85.8k| } 244| 1.64M| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJPKcEEC2Ev: 186| 2.25k| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIbLb0EJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEED2Ev: 206| 11.2k| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIbLb0EJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE7destroyEv: 239| 11.2k| void destroy() noexcept { 240| 11.2k| if (handle_) { ------------------ | Branch (240:9): [True: 4.50k, False: 6.76k] ------------------ 241| 4.50k| handle_(action::destroy, &storage_, nullptr); 242| 4.50k| handle_ = nullptr; 243| 4.50k| } 244| 11.2k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJPKcEED2Ev: 206| 2.25k| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIvLb0EJPKcEE7destroyEv: 239| 2.25k| void destroy() noexcept { 240| 2.25k| if (handle_) { ------------------ | Branch (240:9): [True: 0, False: 2.25k] ------------------ 241| 0| handle_(action::destroy, &storage_, nullptr); 242| 0| handle_ = nullptr; 243| 0| } 244| 2.25k| } _ZN5ofats10any_detail18any_invocable_implIbLb0EJmEEC2Ev: 186| 1.44M| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIvLb0EJEEC2Ev: 186| 1.52M| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIvLb0EJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEbEEC2Ev: 186| 1.44M| any_invocable_impl() noexcept = default; _ZNK5ofats10any_detail18any_invocable_implIvLb0EJEEcvbEv: 228| 1.47M| explicit operator bool() const noexcept { return handle_ != nullptr; } _ZN5ofats10any_detail18any_invocable_implIvLb0EJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEbEED2Ev: 206| 1.44M| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIvLb0EJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEbEE7destroyEv: 239| 1.44M| void destroy() noexcept { 240| 1.44M| if (handle_) { ------------------ | Branch (240:9): [True: 0, False: 1.44M] ------------------ 241| 0| handle_(action::destroy, &storage_, nullptr); 242| 0| handle_ = nullptr; 243| 0| } 244| 1.44M| } _ZN5ofats10any_detail18any_invocable_implIbLb0EJmEED2Ev: 206| 1.47M| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIbLb0EJmEE7destroyEv: 239| 1.47M| void destroy() noexcept { 240| 1.47M| if (handle_) { ------------------ | Branch (240:9): [True: 0, False: 1.47M] ------------------ 241| 0| handle_(action::destroy, &storage_, nullptr); 242| 0| handle_ = nullptr; 243| 0| } 244| 1.47M| } _ZN5ofats13any_invocableIFPvS1_NSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEbEEclES1_S6_b: 345| 49.9k| R operator()(ArgTypes... args) cv ref noexcept(noex) { \ 346| 49.9k| return base_type::call(std::forward(args)...); \ 347| 49.9k| } \ _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE4callES2_S7_b: 246| 49.9k| R call(ArgTypes... args) noexcept(is_noexcept) { 247| 49.9k| return call_(storage_, std::forward(args)...); 248| 49.9k| } _ZN5ofats13any_invocableIFPvS1_PN3uWS11HttpRequestEEEclES1_S4_: 345| 50.5k| R operator()(ArgTypes... args) cv ref noexcept(noex) { \ 346| 50.5k| return base_type::call(std::forward(args)...); \ 347| 50.5k| } \ _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_PN3uWS11HttpRequestEEE4callES2_S5_: 246| 50.5k| R call(ArgTypes... args) noexcept(is_noexcept) { 247| 50.5k| return call_(storage_, std::forward(args)...); 248| 50.5k| } _ZN5ofats13any_invocableIFPvS1_PN3uWS11HttpRequestEEEC2IZZNS2_11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESB_SC_iEUlS1_S4_E_vEEOT_: 303| 612k| any_invocable(F&& f) { \ 304| 612k| base_type::template create>(std::forward(f)); \ 305| 612k| } \ _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_PN3uWS11HttpRequestEEEC2Ev: 186| 612k| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_PN3uWS11HttpRequestEEE6createIZZNS3_11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESB_SC_iEUlS2_S5_E_JSE_EEEvDpOT0_: 232| 612k| void create(Args&&... args) { 233| 612k| using hdl = handler; 234| 612k| hdl::create(storage_, std::forward(args)...); 235| 612k| handle_ = &hdl::handle; 236| 612k| call_ = &hdl::call; 237| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_PN3uWS11HttpRequestEEE13small_handlerIZZNS3_11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESB_SC_iEUlS2_S5_E_E6createIJSE_EEEvRNS0_7storageEDpOT_: 118| 612k| static void create(storage& s, Args&&... args) { 119| 612k| new (static_cast(&s.buf_)) T(std::forward(args)...); 120| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_PN3uWS11HttpRequestEEE12handler_baseINS6_13small_handlerIZZNS3_11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESC_SD_iEUlS2_S5_E_EEE6handleENS0_6actionEPNS0_7storageESK_: 103| 612k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 612k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 612k] ------------------ 105| 612k| case (action::destroy): ------------------ | Branch (105:9): [True: 612k, False: 0] ------------------ 106| 612k| Derived::destroy(*current); 107| 612k| break; 108| 0| case (action::move): ------------------ | Branch (108:9): [True: 0, False: 612k] ------------------ 109| 0| Derived::move(*current, *other); 110| 0| break; 111| 612k| } 112| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_PN3uWS11HttpRequestEEE13small_handlerIZZNS3_11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESB_SC_iEUlS2_S5_E_E7destroyERNS0_7storageE: 122| 612k| static void destroy(storage& s) noexcept { 123| 612k| T& value = *static_cast(static_cast(&s.buf_)); 124| 612k| value.~T(); 125| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_PN3uWS11HttpRequestEEE13small_handlerIZZNS3_11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESB_SC_iEUlS2_S5_E_E4callERNS0_7storageES2_S5_: 132| 50.5k| static R call(storage& s, ArgTypes... args) { 133| 50.5k| return std::invoke(*static_cast(static_cast(&s.buf_)), 134| 50.5k| std::forward(args)...); 135| 50.5k| } _ZN5ofats13any_invocableIFbPN3uWS10HttpRouterINS1_15HttpContextDataILb0EE10RouterDataEEEEEclES7_: 345| 48.9k| R operator()(ArgTypes... args) cv ref noexcept(noex) { \ 346| 48.9k| return base_type::call(std::forward(args)...); \ 347| 48.9k| } \ _ZN5ofats10any_detail18any_invocable_implIbLb0EJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE4callES8_: 246| 48.9k| R call(ArgTypes... args) noexcept(is_noexcept) { 247| 48.9k| return call_(storage_, std::forward(args)...); 248| 48.9k| } _ZNK5ofats10any_detail18any_invocable_implIvLb0EJNSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEbEEcvbEv: 228| 84.1k| explicit operator bool() const noexcept { return handle_ != nullptr; } _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_PN3uWS11HttpRequestEEED2Ev: 206| 612k| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_PN3uWS11HttpRequestEEE7destroyEv: 239| 612k| void destroy() noexcept { 240| 612k| if (handle_) { ------------------ | Branch (240:9): [True: 612k, False: 0] ------------------ 241| 612k| handle_(action::destroy, &storage_, nullptr); 242| 612k| handle_ = nullptr; 243| 612k| } 244| 612k| } _ZN5ofats13any_invocableIFPvS1_NSt3__117basic_string_viewIcNS2_11char_traitsIcEEEEbEEC2IZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESE_SF_iEUlS1_S6_bE_vEEOT_: 303| 612k| any_invocable(F&& f) { \ 304| 612k| base_type::template create>(std::forward(f)); \ 305| 612k| } \ _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEEC2Ev: 186| 612k| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE6createIZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESE_SF_iEUlS2_S7_bE_JSH_EEEvDpOT0_: 232| 612k| void create(Args&&... args) { 233| 612k| using hdl = handler; 234| 612k| hdl::create(storage_, std::forward(args)...); 235| 612k| handle_ = &hdl::handle; 236| 612k| call_ = &hdl::call; 237| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE13small_handlerIZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESE_SF_iEUlS2_S7_bE_E6createIJSH_EEEvRNS0_7storageEDpOT_: 118| 612k| static void create(storage& s, Args&&... args) { 119| 612k| new (static_cast(&s.buf_)) T(std::forward(args)...); 120| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE12handler_baseINS8_13small_handlerIZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESF_SG_iEUlS2_S7_bE_EEE6handleENS0_6actionEPNS0_7storageESN_: 103| 612k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 612k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 612k] ------------------ 105| 612k| case (action::destroy): ------------------ | Branch (105:9): [True: 612k, False: 0] ------------------ 106| 612k| Derived::destroy(*current); 107| 612k| break; 108| 0| case (action::move): ------------------ | Branch (108:9): [True: 0, False: 612k] ------------------ 109| 0| Derived::move(*current, *other); 110| 0| break; 111| 612k| } 112| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE13small_handlerIZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESE_SF_iEUlS2_S7_bE_E7destroyERNS0_7storageE: 122| 612k| static void destroy(storage& s) noexcept { 123| 612k| T& value = *static_cast(static_cast(&s.buf_)); 124| 612k| value.~T(); 125| 612k| } _ZN5ofats10any_detail14handler_traitsIPvJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE13small_handlerIZZN3uWS11HttpContextILb0EE4initEvENKUlP11us_socket_tPciE_clESE_SF_iEUlS2_S7_bE_E4callERNS0_7storageES2_S7_b: 132| 49.9k| static R call(storage& s, ArgTypes... args) { 133| 49.9k| return std::invoke(*static_cast(static_cast(&s.buf_)), 134| 49.9k| std::forward(args)...); 135| 49.9k| } _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEED2Ev: 206| 612k| ~any_invocable_impl() { destroy(); } _ZN5ofats10any_detail18any_invocable_implIPvLb0EJS2_NSt3__117basic_string_viewIcNS3_11char_traitsIcEEEEbEE7destroyEv: 239| 612k| void destroy() noexcept { 240| 612k| if (handle_) { ------------------ | Branch (240:9): [True: 612k, False: 0] ------------------ 241| 612k| handle_(action::destroy, &storage_, nullptr); 242| 612k| handle_ = nullptr; 243| 612k| } 244| 612k| } _ZNK5ofats10any_detail18any_invocable_implIbLb0EJmEEcvbEv: 228| 9.03k| explicit operator bool() const noexcept { return handle_ != nullptr; } _ZN5ofats10any_detail18any_invocable_implIbLb0EJmEE4swapERS2_: 208| 32.1k| void swap(any_invocable_impl& rhs) noexcept { 209| 32.1k| if (handle_) { ------------------ | Branch (209:9): [True: 0, False: 32.1k] ------------------ 210| 0| if (rhs.handle_) { ------------------ | Branch (210:11): [True: 0, False: 0] ------------------ 211| 0| storage tmp; 212| 0| handle_(action::move, &tmp, &storage_); 213| 0| rhs.handle_(action::move, &storage_, &rhs.storage_); 214| 0| handle_(action::move, &rhs.storage_, &tmp); 215| 0| std::swap(handle_, rhs.handle_); 216| 0| std::swap(call_, rhs.call_); 217| 0| } else { 218| 0| rhs.swap(*this); 219| 0| } 220| 32.1k| } else if (rhs.handle_) { ------------------ | Branch (220:16): [True: 0, False: 32.1k] ------------------ 221| 0| rhs.handle_(action::move, &storage_, &rhs.storage_); 222| 0| handle_ = rhs.handle_; 223| 0| call_ = rhs.call_; 224| 0| rhs.handle_ = nullptr; 225| 0| } 226| 32.1k| } _ZNK5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEEcvbEv: 228| 4.50k| explicit operator bool() const noexcept { return handle_ != nullptr; } _ZN5ofats10any_detail18any_invocable_implIbLb0EJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEEC2EOS9_: 188| 6.76k| any_invocable_impl(any_invocable_impl&& rhs) noexcept { 189| 6.76k| if (rhs.handle_) { ------------------ | Branch (189:9): [True: 6.76k, False: 0] ------------------ 190| 6.76k| handle_ = rhs.handle_; 191| 6.76k| handle_(action::move, &storage_, &rhs.storage_); 192| 6.76k| call_ = rhs.call_; 193| 6.76k| rhs.handle_ = nullptr; 194| 6.76k| } 195| 6.76k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEEC2EOS8_: 188| 9.01k| any_invocable_impl(any_invocable_impl&& rhs) noexcept { 189| 9.01k| if (rhs.handle_) { ------------------ | Branch (189:9): [True: 9.01k, False: 0] ------------------ 190| 9.01k| handle_ = rhs.handle_; 191| 9.01k| handle_(action::move, &storage_, &rhs.storage_); 192| 9.01k| call_ = rhs.call_; 193| 9.01k| rhs.handle_ = nullptr; 194| 9.01k| } 195| 9.01k| } _ZN5ofats13any_invocableIFbPN3uWS10HttpRouterINS1_15HttpContextDataILb0EE10RouterDataEEEEEC2IZNS1_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSD_11char_traitsIcEENSD_9allocatorIcEEEESJ_ONS0_IFvPNS1_12HttpResponseILb0EEEPNS1_11HttpRequestEEEEbEUlPT_E_vEEOSS_: 303| 4.50k| any_invocable(F&& f) { \ 304| 4.50k| base_type::template create>(std::forward(f)); \ 305| 4.50k| } \ _ZN5ofats10any_detail18any_invocable_implIbLb0EJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEEC2Ev: 186| 4.50k| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIbLb0EJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE6createIZNS2_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSD_11char_traitsIcEENSD_9allocatorIcEEEESJ_ONS_13any_invocableIFvPNS2_12HttpResponseILb0EEEPNS2_11HttpRequestEEEEbEUlPT_E_JSV_EEEvDpOT0_: 232| 4.50k| void create(Args&&... args) { 233| 4.50k| using hdl = handler; 234| 4.50k| hdl::create(storage_, std::forward(args)...); 235| 4.50k| handle_ = &hdl::handle; 236| 4.50k| call_ = &hdl::call; 237| 4.50k| } _ZN5ofats10any_detail14handler_traitsIbJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE13large_handlerIZNS2_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSD_11char_traitsIcEENSD_9allocatorIcEEEESJ_ONS_13any_invocableIFvPNS2_12HttpResponseILb0EEEPNS2_11HttpRequestEEEEbEUlPT_E_E6createIJSV_EEEvRNS0_7storageEDpOT_: 141| 4.50k| static void create(storage& s, Args&&... args) { 142| 4.50k| s.ptr_ = new T(std::forward(args)...); 143| 4.50k| } _ZN5ofats10any_detail14handler_traitsIbJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE12handler_baseINS9_13large_handlerIZNS2_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSE_11char_traitsIcEENSE_9allocatorIcEEEESK_ONS_13any_invocableIFvPNS2_12HttpResponseILb0EEEPNS2_11HttpRequestEEEEbEUlPT_E_EEE6handleENS0_6actionEPNS0_7storageES11_: 103| 11.2k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 11.2k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 11.2k] ------------------ 105| 4.50k| case (action::destroy): ------------------ | Branch (105:9): [True: 4.50k, False: 6.76k] ------------------ 106| 4.50k| Derived::destroy(*current); 107| 4.50k| break; 108| 6.76k| case (action::move): ------------------ | Branch (108:9): [True: 6.76k, False: 4.50k] ------------------ 109| 6.76k| Derived::move(*current, *other); 110| 6.76k| break; 111| 11.2k| } 112| 11.2k| } _ZN5ofats10any_detail14handler_traitsIbJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE13large_handlerIZNS2_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSD_11char_traitsIcEENSD_9allocatorIcEEEESJ_ONS_13any_invocableIFvPNS2_12HttpResponseILb0EEEPNS2_11HttpRequestEEEEbEUlPT_E_E7destroyERNS0_7storageE: 145| 4.50k| static void destroy(storage& s) noexcept { delete static_cast(s.ptr_); } _ZN5ofats10any_detail14handler_traitsIbJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE13large_handlerIZNS2_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSD_11char_traitsIcEENSD_9allocatorIcEEEESJ_ONS_13any_invocableIFvPNS2_12HttpResponseILb0EEEPNS2_11HttpRequestEEEEbEUlPT_E_E4moveERNS0_7storageESY_: 147| 6.76k| static void move(storage& dst, storage& src) noexcept { 148| 6.76k| dst.ptr_ = src.ptr_; 149| 6.76k| } _ZN5ofats10any_detail14handler_traitsIbJPN3uWS10HttpRouterINS2_15HttpContextDataILb0EE10RouterDataEEEEE13large_handlerIZNS2_11HttpContextILb0EE6onHttpENSt3__112basic_stringIcNSD_11char_traitsIcEENSD_9allocatorIcEEEESJ_ONS_13any_invocableIFvPNS2_12HttpResponseILb0EEEPNS2_11HttpRequestEEEEbEUlPT_E_E4callERNS0_7storageES8_: 151| 48.9k| static R call(storage& s, ArgTypes... args) { 152| 48.9k| return std::invoke(*static_cast(s.ptr_), 153| 48.9k| std::forward(args)...); 154| 48.9k| } _ZN5ofats13any_invocableIFvPN3uWS12HttpResponseILb0EEEPNS1_11HttpRequestEEEclES4_S6_: 345| 48.9k| R operator()(ArgTypes... args) cv ref noexcept(noex) { \ 346| 48.9k| return base_type::call(std::forward(args)...); \ 347| 48.9k| } \ _ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE4callES5_S7_: 246| 48.9k| R call(ArgTypes... args) noexcept(is_noexcept) { 247| 48.9k| return call_(storage_, std::forward(args)...); 248| 48.9k| } _ZN5ofats13any_invocableIFvPN3uWS12HttpResponseILb0EEEPNS1_11HttpRequestEEEC2IZNS1_12TemplatedAppILb0EEC1ENS1_20SocketContextOptionsEEUlPT_PT0_E_vEEOSD_: 303| 2.25k| any_invocable(F&& f) { \ 304| 2.25k| base_type::template create>(std::forward(f)); \ 305| 2.25k| } \ _ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEEC2Ev: 186| 4.50k| any_invocable_impl() noexcept = default; _ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE6createIZNS2_12TemplatedAppILb0EEC1ENS2_20SocketContextOptionsEEUlPT_PT0_E_JSH_EEEvDpOT0_: 232| 2.25k| void create(Args&&... args) { 233| 2.25k| using hdl = handler; 234| 2.25k| hdl::create(storage_, std::forward(args)...); 235| 2.25k| handle_ = &hdl::handle; 236| 2.25k| call_ = &hdl::call; 237| 2.25k| } _ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZNS2_12TemplatedAppILb0EEC1ENS2_20SocketContextOptionsEEUlPT_PT0_E_E6createIJSH_EEEvRNS0_7storageEDpOT_: 118| 6.76k| static void create(storage& s, Args&&... args) { 119| 6.76k| new (static_cast(&s.buf_)) T(std::forward(args)...); 120| 6.76k| } _ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE12handler_baseINS8_13small_handlerIZNS2_12TemplatedAppILb0EEC1ENS2_20SocketContextOptionsEEUlPT_PT0_E_EEE6handleENS0_6actionEPNS0_7storageESN_: 103| 6.76k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 6.76k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 6.76k] ------------------ 105| 2.25k| case (action::destroy): ------------------ | Branch (105:9): [True: 2.25k, False: 4.50k] ------------------ 106| 2.25k| Derived::destroy(*current); 107| 2.25k| break; 108| 4.50k| case (action::move): ------------------ | Branch (108:9): [True: 4.50k, False: 2.25k] ------------------ 109| 4.50k| Derived::move(*current, *other); 110| 4.50k| break; 111| 6.76k| } 112| 6.76k| } _ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZNS2_12TemplatedAppILb0EEC1ENS2_20SocketContextOptionsEEUlPT_PT0_E_E7destroyERNS0_7storageE: 122| 6.76k| static void destroy(storage& s) noexcept { 123| 6.76k| T& value = *static_cast(static_cast(&s.buf_)); 124| 6.76k| value.~T(); 125| 6.76k| } _ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZNS2_12TemplatedAppILb0EEC1ENS2_20SocketContextOptionsEEUlPT_PT0_E_E4moveERNS0_7storageESK_: 127| 4.50k| static void move(storage& dst, storage& src) noexcept { 128| 4.50k| create(dst, std::move(*static_cast(static_cast(&src.buf_)))); 129| 4.50k| destroy(src); 130| 4.50k| } _ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZNS2_12TemplatedAppILb0EEC1ENS2_20SocketContextOptionsEEUlPT_PT0_E_E4callERNS0_7storageES5_S7_: 132| 14.7k| static R call(storage& s, ArgTypes... args) { 133| 14.7k| return std::invoke(*static_cast(static_cast(&s.buf_)), 134| 14.7k| std::forward(args)...); 135| 14.7k| } _ZN5ofats13any_invocableIFvvEEaSIDnDnEENSt3__19enable_ifIXaantsr3stdE9is_same_vIT0_S2_Esr3stdE23is_move_constructible_vIS6_EERS2_E4typeEOT_: 335| 32.1k| operator=(F&& f) { \ 336| 32.1k| any_invocable{std::forward(f)}.swap(*this); \ 337| 32.1k| return *this; \ 338| 32.1k| } \ _ZN5ofats10any_detail18any_invocable_implIvLb0EJEEC2EDn: 187| 32.1k| any_invocable_impl(std::nullptr_t) noexcept {} _ZN5ofats10any_detail18any_invocable_implIvLb0EJEE4swapERS2_: 208| 100k| void swap(any_invocable_impl& rhs) noexcept { 209| 100k| if (handle_) { ------------------ | Branch (209:9): [True: 34.2k, False: 66.3k] ------------------ 210| 34.2k| if (rhs.handle_) { ------------------ | Branch (210:11): [True: 0, False: 34.2k] ------------------ 211| 0| storage tmp; 212| 0| handle_(action::move, &tmp, &storage_); 213| 0| rhs.handle_(action::move, &storage_, &rhs.storage_); 214| 0| handle_(action::move, &rhs.storage_, &tmp); 215| 0| std::swap(handle_, rhs.handle_); 216| 0| std::swap(call_, rhs.call_); 217| 34.2k| } else { 218| 34.2k| rhs.swap(*this); 219| 34.2k| } 220| 66.3k| } else if (rhs.handle_) { ------------------ | Branch (220:16): [True: 51.6k, False: 14.7k] ------------------ 221| 51.6k| rhs.handle_(action::move, &storage_, &rhs.storage_); 222| 51.6k| handle_ = rhs.handle_; 223| 51.6k| call_ = rhs.call_; 224| 51.6k| rhs.handle_ = nullptr; 225| 51.6k| } 226| 100k| } _ZN5ofats13any_invocableIFbmEEaSIDnDnEENSt3__19enable_ifIXaantsr3stdE9is_same_vIT0_S2_Esr3stdE23is_move_constructible_vIS6_EERS2_E4typeEOT_: 335| 32.1k| operator=(F&& f) { \ 336| 32.1k| any_invocable{std::forward(f)}.swap(*this); \ 337| 32.1k| return *this; \ 338| 32.1k| } \ _ZN5ofats10any_detail18any_invocable_implIbLb0EJmEEC2EDn: 187| 32.1k| any_invocable_impl(std::nullptr_t) noexcept {} AsyncEpollHelloWorld.cpp:_ZN5ofats13any_invocableIFvPN3uWS12HttpResponseILb0EEEPNS1_11HttpRequestEEEC2IZ4testvE3$_0vEEOT_: 303| 2.25k| any_invocable(F&& f) { \ 304| 2.25k| base_type::template create>(std::forward(f)); \ 305| 2.25k| } \ AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail18any_invocable_implIvLb0EJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE6createIZ4testvE3$_0JSA_EEEvDpOT0_: 232| 2.25k| void create(Args&&... args) { 233| 2.25k| using hdl = handler; 234| 2.25k| hdl::create(storage_, std::forward(args)...); 235| 2.25k| handle_ = &hdl::handle; 236| 2.25k| call_ = &hdl::call; 237| 2.25k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZ4testvE3$_0E6createIJSA_EEEvRNS0_7storageEDpOT_: 118| 6.76k| static void create(storage& s, Args&&... args) { 119| 6.76k| new (static_cast(&s.buf_)) T(std::forward(args)...); 120| 6.76k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE12handler_baseINS8_13small_handlerIZ4testvE3$_0EEE6handleENS0_6actionEPNS0_7storageESG_: 103| 6.76k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 6.76k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 6.76k] ------------------ 105| 2.25k| case (action::destroy): ------------------ | Branch (105:9): [True: 2.25k, False: 4.50k] ------------------ 106| 2.25k| Derived::destroy(*current); 107| 2.25k| break; 108| 4.50k| case (action::move): ------------------ | Branch (108:9): [True: 4.50k, False: 2.25k] ------------------ 109| 4.50k| Derived::move(*current, *other); 110| 4.50k| break; 111| 6.76k| } 112| 6.76k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZ4testvE3$_0E7destroyERNS0_7storageE: 122| 6.76k| static void destroy(storage& s) noexcept { 123| 6.76k| T& value = *static_cast(static_cast(&s.buf_)); 124| 6.76k| value.~T(); 125| 6.76k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZ4testvE3$_0E4moveERNS0_7storageESD_: 127| 4.50k| static void move(storage& dst, storage& src) noexcept { 128| 4.50k| create(dst, std::move(*static_cast(static_cast(&src.buf_)))); 129| 4.50k| destroy(src); 130| 4.50k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJPN3uWS12HttpResponseILb0EEEPNS2_11HttpRequestEEE13small_handlerIZ4testvE3$_0E4callERNS0_7storageES5_S7_: 132| 34.2k| static R call(storage& s, ArgTypes... args) { 133| 34.2k| return std::invoke(*static_cast(static_cast(&s.buf_)), 134| 34.2k| std::forward(args)...); 135| 34.2k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJEEaSEOS2_: 197| 34.2k| any_invocable_impl& operator=(any_invocable_impl&& rhs) noexcept { 198| 34.2k| any_invocable_impl{std::move(rhs)}.swap(*this); 199| 34.2k| return *this; 200| 34.2k| } _ZN5ofats10any_detail18any_invocable_implIvLb0EJEEC2EOS2_: 188| 87.8k| any_invocable_impl(any_invocable_impl&& rhs) noexcept { 189| 87.8k| if (rhs.handle_) { ------------------ | Branch (189:9): [True: 87.8k, False: 0] ------------------ 190| 87.8k| handle_ = rhs.handle_; 191| 87.8k| handle_(action::move, &storage_, &rhs.storage_); 192| 87.8k| call_ = rhs.call_; 193| 87.8k| rhs.handle_ = nullptr; 194| 87.8k| } 195| 87.8k| } AsyncEpollHelloWorld.cpp:_ZN5ofats13any_invocableIFvvEEC2IZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE_vEEOSA_: 303| 34.2k| any_invocable(F&& f) { \ 304| 34.2k| base_type::template create>(std::forward(f)); \ 305| 34.2k| } \ AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail18any_invocable_implIvLb0EJEE6createIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE_JSE_EEEvDpOT0_: 232| 34.2k| void create(Args&&... args) { 233| 34.2k| using hdl = handler; 234| 34.2k| hdl::create(storage_, std::forward(args)...); 235| 34.2k| handle_ = &hdl::handle; 236| 34.2k| call_ = &hdl::call; 237| 34.2k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13small_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE_E6createIJSE_EEEvRNS0_7storageEDpOT_: 118| 120k| static void create(storage& s, Args&&... args) { 119| 120k| new (static_cast(&s.buf_)) T(std::forward(args)...); 120| 120k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE12handler_baseINS2_13small_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS7_11HttpRequestEEEDaPT_PT0_EUlvE_EEE6handleENS0_6actionEPNS0_7storageESK_: 103| 120k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 120k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 120k] ------------------ 105| 34.2k| case (action::destroy): ------------------ | Branch (105:9): [True: 34.2k, False: 85.8k] ------------------ 106| 34.2k| Derived::destroy(*current); 107| 34.2k| break; 108| 85.8k| case (action::move): ------------------ | Branch (108:9): [True: 85.8k, False: 34.2k] ------------------ 109| 85.8k| Derived::move(*current, *other); 110| 85.8k| break; 111| 120k| } 112| 120k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13small_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE_E7destroyERNS0_7storageE: 122| 120k| static void destroy(storage& s) noexcept { 123| 120k| T& value = *static_cast(static_cast(&s.buf_)); 124| 120k| value.~T(); 125| 120k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13small_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE_E4moveERNS0_7storageESH_: 127| 85.8k| static void move(storage& dst, storage& src) noexcept { 128| 85.8k| create(dst, std::move(*static_cast(static_cast(&src.buf_)))); 129| 85.8k| destroy(src); 130| 85.8k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13small_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE_E4callERNS0_7storageE: 132| 16.8k| static R call(storage& s, ArgTypes... args) { 133| 16.8k| return std::invoke(*static_cast(static_cast(&s.buf_)), 134| 16.8k| std::forward(args)...); 135| 16.8k| } AsyncEpollHelloWorld.cpp:_ZN5ofats13any_invocableIFvvEEC2IZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE0_vEEOSA_: 303| 34.2k| any_invocable(F&& f) { \ 304| 34.2k| base_type::template create>(std::forward(f)); \ 305| 34.2k| } \ AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail18any_invocable_implIvLb0EJEE6createIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE0_JSE_EEEvDpOT0_: 232| 34.2k| void create(Args&&... args) { 233| 34.2k| using hdl = handler; 234| 34.2k| hdl::create(storage_, std::forward(args)...); 235| 34.2k| handle_ = &hdl::handle; 236| 34.2k| call_ = &hdl::call; 237| 34.2k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE0_E6createIJSE_EEEvRNS0_7storageEDpOT_: 141| 34.2k| static void create(storage& s, Args&&... args) { 142| 34.2k| s.ptr_ = new T(std::forward(args)...); 143| 34.2k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE12handler_baseINS2_13large_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS7_11HttpRequestEEEDaPT_PT0_EUlvE0_EEE6handleENS0_6actionEPNS0_7storageESK_: 103| 87.8k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 87.8k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 87.8k] ------------------ 105| 34.2k| case (action::destroy): ------------------ | Branch (105:9): [True: 34.2k, False: 53.5k] ------------------ 106| 34.2k| Derived::destroy(*current); 107| 34.2k| break; 108| 53.5k| case (action::move): ------------------ | Branch (108:9): [True: 53.5k, False: 34.2k] ------------------ 109| 53.5k| Derived::move(*current, *other); 110| 53.5k| break; 111| 87.8k| } 112| 87.8k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE0_E7destroyERNS0_7storageE: 145| 34.2k| static void destroy(storage& s) noexcept { delete static_cast(s.ptr_); } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE0_E4moveERNS0_7storageESH_: 147| 53.5k| static void move(storage& dst, storage& src) noexcept { 148| 53.5k| dst.ptr_ = src.ptr_; 149| 53.5k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_EUlvE0_E4callERNS0_7storageE: 151| 33.5k| static R call(storage& s, ArgTypes... args) { 152| 33.5k| return std::invoke(*static_cast(s.ptr_), 153| 33.5k| std::forward(args)...); 154| 33.5k| } AsyncEpollHelloWorld.cpp:_ZN5ofats13any_invocableIFvvEEC2IZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvEUlvE_vEEOSA_: 303| 17.3k| any_invocable(F&& f) { \ 304| 17.3k| base_type::template create>(std::forward(f)); \ 305| 17.3k| } \ AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail18any_invocable_implIvLb0EJEE6createIZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvEUlvE_JSF_EEEvDpOT0_: 232| 17.3k| void create(Args&&... args) { 233| 17.3k| using hdl = handler; 234| 17.3k| hdl::create(storage_, std::forward(args)...); 235| 17.3k| handle_ = &hdl::handle; 236| 17.3k| call_ = &hdl::call; 237| 17.3k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvEUlvE_E6createIJSF_EEEvRNS0_7storageEDpOT_: 141| 17.3k| static void create(storage& s, Args&&... args) { 142| 17.3k| s.ptr_ = new T(std::forward(args)...); 143| 17.3k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE12handler_baseINS2_13large_handlerIZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS7_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvEUlvE_EEE6handleENS0_6actionEPNS0_7storageESL_: 103| 17.3k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 17.3k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 17.3k] ------------------ 105| 17.3k| case (action::destroy): ------------------ | Branch (105:9): [True: 17.3k, False: 0] ------------------ 106| 17.3k| Derived::destroy(*current); 107| 17.3k| break; 108| 0| case (action::move): ------------------ | Branch (108:9): [True: 0, False: 17.3k] ------------------ 109| 0| Derived::move(*current, *other); 110| 0| break; 111| 17.3k| } 112| 17.3k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvEUlvE_E7destroyERNS0_7storageE: 145| 17.3k| static void destroy(storage& s) noexcept { delete static_cast(s.ptr_); } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJEE13large_handlerIZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS6_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvEUlvE_E4callERNS0_7storageE: 151| 17.3k| static R call(storage& s, ArgTypes... args) { 152| 17.3k| return std::invoke(*static_cast(s.ptr_), 153| 17.3k| std::forward(args)...); 154| 17.3k| } _ZN5ofats13any_invocableIFvP18us_listen_socket_tEEclES2_: 345| 2.25k| R operator()(ArgTypes... args) cv ref noexcept(noex) { \ 346| 2.25k| return base_type::call(std::forward(args)...); \ 347| 2.25k| } \ _ZN5ofats10any_detail18any_invocable_implIvLb0EJP18us_listen_socket_tEE4callES3_: 246| 2.25k| R call(ArgTypes... args) noexcept(is_noexcept) { 247| 2.25k| return call_(storage_, std::forward(args)...); 248| 2.25k| } AsyncEpollHelloWorld.cpp:_ZN5ofats13any_invocableIFvP18us_listen_socket_tEEC2IZ4testvE3$_1vEEOT_: 303| 2.25k| any_invocable(F&& f) { \ 304| 2.25k| base_type::template create>(std::forward(f)); \ 305| 2.25k| } \ _ZN5ofats10any_detail18any_invocable_implIvLb0EJP18us_listen_socket_tEEC2Ev: 186| 2.25k| any_invocable_impl() noexcept = default; AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail18any_invocable_implIvLb0EJP18us_listen_socket_tEE6createIZ4testvE3$_1JS6_EEEvDpOT0_: 232| 2.25k| void create(Args&&... args) { 233| 2.25k| using hdl = handler; 234| 2.25k| hdl::create(storage_, std::forward(args)...); 235| 2.25k| handle_ = &hdl::handle; 236| 2.25k| call_ = &hdl::call; 237| 2.25k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJP18us_listen_socket_tEE13small_handlerIZ4testvE3$_1E6createIJS6_EEEvRNS0_7storageEDpOT_: 118| 2.25k| static void create(storage& s, Args&&... args) { 119| 2.25k| new (static_cast(&s.buf_)) T(std::forward(args)...); 120| 2.25k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJP18us_listen_socket_tEE12handler_baseINS4_13small_handlerIZ4testvE3$_1EEE6handleENS0_6actionEPNS0_7storageESC_: 103| 2.25k| static void handle(action act, storage* current, storage* other = nullptr) { 104| 2.25k| switch (act) { ------------------ | Branch (104:15): [True: 0, False: 2.25k] ------------------ 105| 2.25k| case (action::destroy): ------------------ | Branch (105:9): [True: 2.25k, False: 0] ------------------ 106| 2.25k| Derived::destroy(*current); 107| 2.25k| break; 108| 0| case (action::move): ------------------ | Branch (108:9): [True: 0, False: 2.25k] ------------------ 109| 0| Derived::move(*current, *other); 110| 0| break; 111| 2.25k| } 112| 2.25k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJP18us_listen_socket_tEE13small_handlerIZ4testvE3$_1E7destroyERNS0_7storageE: 122| 2.25k| static void destroy(storage& s) noexcept { 123| 2.25k| T& value = *static_cast(static_cast(&s.buf_)); 124| 2.25k| value.~T(); 125| 2.25k| } AsyncEpollHelloWorld.cpp:_ZN5ofats10any_detail14handler_traitsIvJP18us_listen_socket_tEE13small_handlerIZ4testvE3$_1E4callERNS0_7storageES3_: 132| 2.25k| static R call(storage& s, ArgTypes... args) { 133| 2.25k| return std::invoke(*static_cast(static_cast(&s.buf_)), 134| 2.25k| std::forward(args)...); 135| 2.25k| } _ZN3uWS5utils6u64toaEmPc: 46| 32.1k|inline int u64toa(uint64_t value, char *dst) { 47| 32.1k| char temp[20]; 48| 32.1k| char *p = temp; 49| 64.3k| do { 50| 64.3k| *p++ = (char) ((value % 10) + '0'); 51| 64.3k| value /= 10; 52| 64.3k| } while (value > 0); ------------------ | Branch (52:14): [True: 32.1k, False: 32.1k] ------------------ 53| | 54| 32.1k| int ret = (int) (p - temp); 55| | 56| 64.3k| do { 57| 64.3k| *dst++ = *--p; 58| 64.3k| } while (p != temp); ------------------ | Branch (58:14): [True: 32.1k, False: 32.1k] ------------------ 59| | 60| 32.1k| return ret; 61| 32.1k|} apple_no_sigpipe: 269| 1.44M|LIBUS_SOCKET_DESCRIPTOR apple_no_sigpipe(LIBUS_SOCKET_DESCRIPTOR fd) { 270| |#ifdef __APPLE__ 271| | if (fd != LIBUS_SOCKET_ERROR) { 272| | int no_sigpipe = 1; 273| | setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &no_sigpipe, sizeof(int)); 274| | } 275| |#endif 276| 1.44M| return fd; 277| 1.44M|} bsd_set_nonblocking: 279| 1.44M|LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd) { 280| |#ifdef _WIN32 281| | /* Libuv will set windows sockets as non-blocking */ 282| |#else 283| 1.44M| fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); 284| 1.44M|#endif 285| 1.44M| return fd; 286| 1.44M|} bsd_socket_nodelay: 288| 1.44M|void bsd_socket_nodelay(LIBUS_SOCKET_DESCRIPTOR fd, int enabled) { 289| 1.44M| setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *) &enabled, sizeof(enabled)); 290| 1.44M|} bsd_create_socket: 300| 2.25k|LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol) { 301| | // returns INVALID_SOCKET on error 302| 2.25k| int flags = 0; 303| 2.25k|#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) 304| 2.25k| flags = SOCK_CLOEXEC | SOCK_NONBLOCK; 305| 2.25k|#endif 306| | 307| 2.25k| LIBUS_SOCKET_DESCRIPTOR created_fd = socket(domain, type | flags, protocol); ------------------ | | 38| 2.25k|#define LIBUS_SOCKET_DESCRIPTOR int ------------------ 308| | 309| 2.25k| return bsd_set_nonblocking(apple_no_sigpipe(created_fd)); 310| 2.25k|} bsd_close_socket: 312| 1.44M|void bsd_close_socket(LIBUS_SOCKET_DESCRIPTOR fd) { 313| |#ifdef _WIN32 314| | closesocket(fd); 315| |#else 316| 1.44M| close(fd); 317| 1.44M|#endif 318| 1.44M|} bsd_shutdown_socket: 320| 564k|void bsd_shutdown_socket(LIBUS_SOCKET_DESCRIPTOR fd) { 321| |#ifdef _WIN32 322| | shutdown(fd, SD_SEND); 323| |#else 324| 564k| shutdown(fd, SHUT_WR); 325| 564k|#endif 326| 564k|} internal_finalize_bsd_addr: 336| 1.44M|void internal_finalize_bsd_addr(struct bsd_addr_t *addr) { 337| | // parse, so to speak, the address 338| 1.44M| if (addr->mem.ss_family == AF_INET6) { ------------------ | Branch (338:9): [True: 461k, False: 979k] ------------------ 339| 461k| addr->ip = (char *) &((struct sockaddr_in6 *) addr)->sin6_addr; 340| 461k| addr->ip_length = sizeof(struct in6_addr); 341| 461k| addr->port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port); 342| 979k| } else if (addr->mem.ss_family == AF_INET) { ------------------ | Branch (342:16): [True: 979k, False: 0] ------------------ 343| 979k| addr->ip = (char *) &((struct sockaddr_in *) addr)->sin_addr; 344| 979k| addr->ip_length = sizeof(struct in_addr); 345| 979k| addr->port = ntohs(((struct sockaddr_in *) addr)->sin_port); 346| 979k| } else { 347| 0| addr->ip_length = 0; 348| 0| addr->port = -1; 349| 0| } 350| 1.44M|} bsd_addr_get_ip: 370| 1.44M|char *bsd_addr_get_ip(struct bsd_addr_t *addr) { 371| 1.44M| return addr->ip; 372| 1.44M|} bsd_addr_get_ip_length: 374| 1.44M|int bsd_addr_get_ip_length(struct bsd_addr_t *addr) { 375| 1.44M| return addr->ip_length; 376| 1.44M|} bsd_accept_socket: 383| 3.29M|LIBUS_SOCKET_DESCRIPTOR bsd_accept_socket(LIBUS_SOCKET_DESCRIPTOR fd, struct bsd_addr_t *addr) { 384| 3.29M| LIBUS_SOCKET_DESCRIPTOR accepted_fd; ------------------ | | 38| 3.29M|#define LIBUS_SOCKET_DESCRIPTOR int ------------------ 385| 3.29M| addr->len = sizeof(addr->mem); 386| | 387| 3.29M|#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) 388| | // Linux, FreeBSD 389| 3.29M| accepted_fd = accept4(fd, (struct sockaddr *) addr, &addr->len, SOCK_CLOEXEC | SOCK_NONBLOCK); 390| |#else 391| | // Windows, OS X 392| | accepted_fd = accept(fd, (struct sockaddr *) addr, &addr->len); 393| | 394| |#endif 395| | 396| | /* We cannot rely on addr since it is not initialized if failed */ 397| 3.29M| if (accepted_fd == LIBUS_SOCKET_ERROR) { ------------------ | | 44| 3.29M|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (397:9): [True: 1.85M, False: 1.44M] ------------------ 398| 1.85M| return LIBUS_SOCKET_ERROR; ------------------ | | 44| 1.85M|#define LIBUS_SOCKET_ERROR -1 ------------------ 399| 1.85M| } 400| | 401| 1.44M| internal_finalize_bsd_addr(addr); 402| | 403| 1.44M| return bsd_set_nonblocking(apple_no_sigpipe(accepted_fd)); 404| 3.29M|} bsd_recv: 406| 648k|int bsd_recv(LIBUS_SOCKET_DESCRIPTOR fd, void *buf, int length, int flags) { 407| 648k| return recv(fd, buf, length, flags); 408| 648k|} bsd_send: 436| 633k|int bsd_send(LIBUS_SOCKET_DESCRIPTOR fd, const char *buf, int length, int msg_more) { 437| | 438| | // MSG_MORE (Linux), MSG_PARTIAL (Windows), TCP_NOPUSH (BSD) 439| | 440| |#ifndef MSG_NOSIGNAL 441| |#define MSG_NOSIGNAL 0 442| |#endif 443| | 444| 633k|#ifdef MSG_MORE 445| | 446| | // for Linux we do not want signals 447| 633k| return send(fd, buf, length, ((msg_more != 0) * MSG_MORE) | MSG_NOSIGNAL); 448| | 449| |#else 450| | 451| | // use TCP_NOPUSH 452| | 453| | return send(fd, buf, length, MSG_NOSIGNAL); 454| | 455| |#endif 456| 633k|} bsd_would_block: 458| 3.54k|int bsd_would_block() { 459| |#ifdef _WIN32 460| | return WSAGetLastError() == WSAEWOULDBLOCK; 461| |#else 462| 3.54k| return errno == EWOULDBLOCK;// || errno == EAGAIN; 463| 3.54k|#endif 464| 3.54k|} bsd_create_listen_socket: 468| 2.25k|LIBUS_SOCKET_DESCRIPTOR bsd_create_listen_socket(const char *host, int port, int options) { 469| 2.25k| struct addrinfo hints, *result; 470| 2.25k| memset(&hints, 0, sizeof(struct addrinfo)); 471| | 472| 2.25k| hints.ai_flags = AI_PASSIVE; 473| 2.25k| hints.ai_family = AF_UNSPEC; 474| 2.25k| hints.ai_socktype = SOCK_STREAM; 475| | 476| 2.25k| char port_string[16]; 477| 2.25k| snprintf(port_string, 16, "%d", port); 478| | 479| 2.25k| if (getaddrinfo(host, port_string, &hints, &result)) { ------------------ | Branch (479:9): [True: 0, False: 2.25k] ------------------ 480| 0| return LIBUS_SOCKET_ERROR; ------------------ | | 44| 0|#define LIBUS_SOCKET_ERROR -1 ------------------ 481| 0| } 482| | 483| 2.25k| LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; ------------------ | | 38| 2.25k|#define LIBUS_SOCKET_DESCRIPTOR int ------------------ LIBUS_SOCKET_DESCRIPTOR listenFd = LIBUS_SOCKET_ERROR; ------------------ | | 44| 2.25k|#define LIBUS_SOCKET_ERROR -1 ------------------ 484| 2.25k| struct addrinfo *listenAddr; 485| 4.50k| for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { ------------------ | | 44| 2.25k|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (485:39): [True: 2.25k, False: 2.25k] | Branch (485:44): [True: 2.25k, False: 0] ------------------ 486| 2.25k| if (a->ai_family == AF_INET6) { ------------------ | Branch (486:13): [True: 1.14k, False: 1.11k] ------------------ 487| 1.14k| listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); 488| 1.14k| listenAddr = a; 489| 1.14k| } 490| 2.25k| } 491| | 492| 3.36k| for (struct addrinfo *a = result; a && listenFd == LIBUS_SOCKET_ERROR; a = a->ai_next) { ------------------ | | 44| 2.25k|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (492:39): [True: 2.25k, False: 1.11k] | Branch (492:44): [True: 1.11k, False: 1.14k] ------------------ 493| 1.11k| if (a->ai_family == AF_INET) { ------------------ | Branch (493:13): [True: 1.11k, False: 1] ------------------ 494| 1.11k| listenFd = bsd_create_socket(a->ai_family, a->ai_socktype, a->ai_protocol); 495| 1.11k| listenAddr = a; 496| 1.11k| } 497| 1.11k| } 498| | 499| 2.25k| if (listenFd == LIBUS_SOCKET_ERROR) { ------------------ | | 44| 2.25k|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (499:9): [True: 1, False: 2.25k] ------------------ 500| 1| freeaddrinfo(result); 501| 1| return LIBUS_SOCKET_ERROR; ------------------ | | 44| 1|#define LIBUS_SOCKET_ERROR -1 ------------------ 502| 1| } 503| | 504| 2.25k| if (port != 0) { ------------------ | Branch (504:9): [True: 2.25k, False: 0] ------------------ 505| | /* Otherwise, always enable SO_REUSEPORT and SO_REUSEADDR _unless_ options specify otherwise */ 506| |#ifdef _WIN32 507| | if (options & LIBUS_LISTEN_EXCLUSIVE_PORT) { 508| | int optval2 = 1; 509| | setsockopt(listenFd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (void *) &optval2, sizeof(optval2)); 510| | } else { 511| | int optval3 = 1; 512| | setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &optval3, sizeof(optval3)); 513| | } 514| |#else 515| 2.25k| #if /*defined(__linux) &&*/ defined(SO_REUSEPORT) 516| 2.25k| if (!(options & LIBUS_LISTEN_EXCLUSIVE_PORT)) { ------------------ | Branch (516:13): [True: 2.25k, False: 0] ------------------ 517| 2.25k| int optval = 1; 518| 2.25k| setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, (void *) &optval, sizeof(optval)); 519| 2.25k| } 520| 2.25k| #endif 521| 2.25k| int enabled = 1; 522| 2.25k| setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, (void *) &enabled, sizeof(enabled)); 523| 2.25k|#endif 524| | 525| 2.25k| } 526| | 527| 2.25k|#ifdef IPV6_V6ONLY 528| 2.25k| int disabled = 0; 529| 2.25k| setsockopt(listenFd, IPPROTO_IPV6, IPV6_V6ONLY, (void *) &disabled, sizeof(disabled)); 530| 2.25k|#endif 531| | 532| 2.25k| if (bind(listenFd, listenAddr->ai_addr, (socklen_t) listenAddr->ai_addrlen) || listen(listenFd, 512)) { ------------------ | Branch (532:9): [True: 0, False: 2.25k] | Branch (532:84): [True: 11, False: 2.24k] ------------------ 533| 11| bsd_close_socket(listenFd); 534| 11| freeaddrinfo(result); 535| 11| return LIBUS_SOCKET_ERROR; ------------------ | | 44| 11|#define LIBUS_SOCKET_ERROR -1 ------------------ 536| 11| } 537| | 538| 2.24k| freeaddrinfo(result); 539| 2.24k| return listenFd; 540| 2.25k|} default_is_low_prio_handler: 25| 648k|int default_is_low_prio_handler(struct us_socket_t *s) { 26| 648k| return 0; 27| 648k|} us_listen_socket_close: 35| 2.24k|void us_listen_socket_close(int ssl, struct us_listen_socket_t *ls) { 36| | /* us_listen_socket_t extends us_socket_t so we close in similar ways */ 37| 2.24k| if (!us_socket_is_closed(0, &ls->s)) { ------------------ | Branch (37:9): [True: 2.24k, False: 0] ------------------ 38| 2.24k| us_internal_socket_context_unlink_listen_socket(ls->s.context, ls); 39| 2.24k| us_poll_stop((struct us_poll_t *) &ls->s, ls->s.context->loop); 40| 2.24k| bsd_close_socket(us_poll_fd((struct us_poll_t *) &ls->s)); 41| | 42| | /* Link this socket to the close-list and let it be deleted after this iteration */ 43| 2.24k| ls->s.next = ls->s.context->loop->data.closed_head; 44| 2.24k| ls->s.context->loop->data.closed_head = &ls->s; 45| | 46| | /* Any socket with prev = context is marked as closed */ 47| 2.24k| ls->s.prev = (struct us_socket_t *) ls->s.context; 48| 2.24k| } 49| | 50| | /* We cannot immediately free a listen socket as we can be inside an accept loop */ 51| 2.24k|} us_internal_socket_context_unlink_listen_socket: 71| 2.24k|void us_internal_socket_context_unlink_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { 72| | /* We have to properly update the iterator used to sweep sockets for timeouts */ 73| 2.24k| if (ls == (struct us_listen_socket_t *) context->iterator) { ------------------ | Branch (73:9): [True: 0, False: 2.24k] ------------------ 74| 0| context->iterator = ls->s.next; 75| 0| } 76| | 77| 2.24k| if (ls->s.prev == ls->s.next) { ------------------ | Branch (77:9): [True: 2.24k, False: 0] ------------------ 78| 2.24k| context->head_listen_sockets = 0; 79| 2.24k| } else { 80| 0| if (ls->s.prev) { ------------------ | Branch (80:13): [True: 0, False: 0] ------------------ 81| 0| ls->s.prev->next = ls->s.next; 82| 0| } else { 83| 0| context->head_listen_sockets = (struct us_listen_socket_t *) ls->s.next; 84| 0| } 85| 0| if (ls->s.next) { ------------------ | Branch (85:13): [True: 0, False: 0] ------------------ 86| 0| ls->s.next->prev = ls->s.prev; 87| 0| } 88| 0| } 89| 2.24k|} us_internal_socket_context_unlink_socket: 91| 1.44M|void us_internal_socket_context_unlink_socket(struct us_socket_context_t *context, struct us_socket_t *s) { 92| | /* We have to properly update the iterator used to sweep sockets for timeouts */ 93| 1.44M| if (s == context->iterator) { ------------------ | Branch (93:9): [True: 5.64k, False: 1.43M] ------------------ 94| 5.64k| context->iterator = s->next; 95| 5.64k| } 96| | 97| 1.44M| if (s->prev == s->next) { ------------------ | Branch (97:9): [True: 158k, False: 1.28M] ------------------ 98| 158k| context->head_sockets = 0; 99| 1.28M| } else { 100| 1.28M| if (s->prev) { ------------------ | Branch (100:13): [True: 426k, False: 856k] ------------------ 101| 426k| s->prev->next = s->next; 102| 856k| } else { 103| 856k| context->head_sockets = s->next; 104| 856k| } 105| 1.28M| if (s->next) { ------------------ | Branch (105:13): [True: 1.26M, False: 18.4k] ------------------ 106| 1.26M| s->next->prev = s->prev; 107| 1.26M| } 108| 1.28M| } 109| 1.44M|} us_internal_socket_context_link_listen_socket: 112| 2.24k|void us_internal_socket_context_link_listen_socket(struct us_socket_context_t *context, struct us_listen_socket_t *ls) { 113| 2.24k| ls->s.context = context; 114| 2.24k| ls->s.next = (struct us_socket_t *) context->head_listen_sockets; 115| 2.24k| ls->s.prev = 0; 116| 2.24k| if (context->head_listen_sockets) { ------------------ | Branch (116:9): [True: 0, False: 2.24k] ------------------ 117| 0| context->head_listen_sockets->s.prev = &ls->s; 118| 0| } 119| 2.24k| context->head_listen_sockets = ls; 120| 2.24k|} us_internal_socket_context_link_socket: 123| 1.44M|void us_internal_socket_context_link_socket(struct us_socket_context_t *context, struct us_socket_t *s) { 124| 1.44M| s->context = context; 125| 1.44M| s->next = context->head_sockets; 126| 1.44M| s->prev = 0; 127| 1.44M| if (context->head_sockets) { ------------------ | Branch (127:9): [True: 1.28M, False: 158k] ------------------ 128| 1.28M| context->head_sockets->prev = s; 129| 1.28M| } 130| 1.44M| context->head_sockets = s; 131| 1.44M|} us_socket_context_loop: 133| 2.60M|struct us_loop_t *us_socket_context_loop(int ssl, struct us_socket_context_t *context) { 134| 2.60M| return context->loop; 135| 2.60M|} us_create_socket_context: 204| 2.25k|struct us_socket_context_t *us_create_socket_context(int ssl, struct us_loop_t *loop, int context_ext_size, struct us_socket_context_options_t options) { 205| |#ifndef LIBUS_NO_SSL 206| | if (ssl) { 207| | /* This function will call us, again, with SSL = false and a bigger ext_size */ 208| | return (struct us_socket_context_t *) us_internal_create_ssl_socket_context(loop, context_ext_size, options); 209| | } 210| |#endif 211| | 212| | /* This path is taken once either way - always BEFORE whatever SSL may do LATER. 213| | * context_ext_size will however be modified larger in case of SSL, to hold SSL extensions */ 214| | 215| 2.25k| struct us_socket_context_t *context = malloc(sizeof(struct us_socket_context_t) + context_ext_size); 216| 2.25k| context->loop = loop; 217| 2.25k| context->head_sockets = 0; 218| 2.25k| context->head_listen_sockets = 0; 219| 2.25k| context->iterator = 0; 220| 2.25k| context->next = 0; 221| 2.25k| context->is_low_prio = default_is_low_prio_handler; 222| | 223| | /* Begin at 0 */ 224| 2.25k| context->timestamp = 0; 225| 2.25k| context->long_timestamp = 0; 226| 2.25k| context->global_tick = 0; 227| | 228| | /* Some new events must be set to null for backwards compatibility */ 229| 2.25k| context->on_pre_open = 0; 230| | 231| 2.25k| us_internal_loop_link(loop, context); 232| | 233| | /* If we are called from within SSL code, SSL code will make further changes to us */ 234| 2.25k| return context; 235| 2.25k|} us_socket_context_free: 237| 2.25k|void us_socket_context_free(int ssl, struct us_socket_context_t *context) { 238| |#ifndef LIBUS_NO_SSL 239| | if (ssl) { 240| | /* This function will call us again with SSL=false */ 241| | us_internal_ssl_socket_context_free((struct us_internal_ssl_socket_context_t *) context); 242| | return; 243| | } 244| |#endif 245| | 246| | /* This path is taken once either way - always AFTER whatever SSL may do BEFORE. 247| | * This is the opposite order compared to when creating the context - SSL code is cleaning up before non-SSL */ 248| | 249| 2.25k| us_internal_loop_unlink(context->loop, context); 250| 2.25k| free(context); 251| 2.25k|} us_socket_context_listen: 253| 2.25k|struct us_listen_socket_t *us_socket_context_listen(int ssl, struct us_socket_context_t *context, const char *host, int port, int options, int socket_ext_size) { 254| |#ifndef LIBUS_NO_SSL 255| | if (ssl) { 256| | return us_internal_ssl_socket_context_listen((struct us_internal_ssl_socket_context_t *) context, host, port, options, socket_ext_size); 257| | } 258| |#endif 259| | 260| 2.25k| LIBUS_SOCKET_DESCRIPTOR listen_socket_fd = bsd_create_listen_socket(host, port, options); ------------------ | | 38| 2.25k|#define LIBUS_SOCKET_DESCRIPTOR int ------------------ 261| | 262| 2.25k| if (listen_socket_fd == LIBUS_SOCKET_ERROR) { ------------------ | | 44| 2.25k|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (262:9): [True: 12, False: 2.24k] ------------------ 263| 12| return 0; 264| 12| } 265| | 266| 2.24k| struct us_poll_t *p = us_create_poll(context->loop, 0, sizeof(struct us_listen_socket_t)); 267| 2.24k| us_poll_init(p, listen_socket_fd, POLL_TYPE_SEMI_SOCKET); 268| 2.24k| us_poll_start(p, context->loop, LIBUS_SOCKET_READABLE); ------------------ | | 27| 2.24k|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 269| | 270| 2.24k| struct us_listen_socket_t *ls = (struct us_listen_socket_t *) p; 271| | 272| 2.24k| ls->s.context = context; 273| 2.24k| ls->s.timeout = 255; 274| 2.24k| ls->s.long_timeout = 255; 275| 2.24k| ls->s.low_prio_state = 0; 276| 2.24k| ls->s.next = 0; 277| 2.24k| us_internal_socket_context_link_listen_socket(context, ls); 278| | 279| 2.24k| ls->socket_ext_size = socket_ext_size; 280| | 281| 2.24k| return ls; 282| 2.25k|} us_socket_context_on_open: 426| 2.25k|void us_socket_context_on_open(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_open)(struct us_socket_t *s, int is_client, char *ip, int ip_length)) { 427| |#ifndef LIBUS_NO_SSL 428| | if (ssl) { 429| | us_internal_ssl_socket_context_on_open((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int, char *, int)) on_open); 430| | return; 431| | } 432| |#endif 433| | 434| 2.25k| context->on_open = on_open; 435| 2.25k|} us_socket_context_on_close: 437| 2.25k|void us_socket_context_on_close(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_close)(struct us_socket_t *s, int code, void *reason)) { 438| |#ifndef LIBUS_NO_SSL 439| | if (ssl) { 440| | us_internal_ssl_socket_context_on_close((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, int code, void *reason)) on_close); 441| | return; 442| | } 443| |#endif 444| | 445| 2.25k| context->on_close = on_close; 446| 2.25k|} us_socket_context_on_data: 448| 2.25k|void us_socket_context_on_data(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_data)(struct us_socket_t *s, char *data, int length)) { 449| |#ifndef LIBUS_NO_SSL 450| | if (ssl) { 451| | us_internal_ssl_socket_context_on_data((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *, char *, int)) on_data); 452| | return; 453| | } 454| |#endif 455| | 456| 2.25k| context->on_data = on_data; 457| 2.25k|} us_socket_context_on_writable: 459| 2.25k|void us_socket_context_on_writable(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_writable)(struct us_socket_t *s)) { 460| |#ifndef LIBUS_NO_SSL 461| | if (ssl) { 462| | us_internal_ssl_socket_context_on_writable((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_writable); 463| | return; 464| | } 465| |#endif 466| | 467| 2.25k| context->on_writable = on_writable; 468| 2.25k|} us_socket_context_on_timeout: 481| 2.25k|void us_socket_context_on_timeout(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_timeout)(struct us_socket_t *)) { 482| |#ifndef LIBUS_NO_SSL 483| | if (ssl) { 484| | us_internal_ssl_socket_context_on_timeout((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_timeout); 485| | return; 486| | } 487| |#endif 488| | 489| 2.25k| context->on_socket_timeout = on_timeout; 490| 2.25k|} us_socket_context_on_end: 492| 2.25k|void us_socket_context_on_end(int ssl, struct us_socket_context_t *context, struct us_socket_t *(*on_end)(struct us_socket_t *)) { 493| |#ifndef LIBUS_NO_SSL 494| | if (ssl) { 495| | us_internal_ssl_socket_context_on_end((struct us_internal_ssl_socket_context_t *) context, (struct us_internal_ssl_socket_t * (*)(struct us_internal_ssl_socket_t *)) on_end); 496| | return; 497| | } 498| |#endif 499| | 500| 2.25k| context->on_end = on_end; 501| 2.25k|} us_socket_context_ext: 514| 3.50M|void *us_socket_context_ext(int ssl, struct us_socket_context_t *context) { 515| |#ifndef LIBUS_NO_SSL 516| | if (ssl) { 517| | return us_internal_ssl_socket_context_ext((struct us_internal_ssl_socket_context_t *) context); 518| | } 519| |#endif 520| | 521| 3.50M| return context + 1; 522| 3.50M|} us_loop_free: 36| 2.25k|void us_loop_free(struct us_loop_t *loop) { 37| 2.25k| us_internal_loop_data_free(loop); 38| 2.25k| close(loop->fd); 39| 2.25k| free(loop); 40| 2.25k|} us_create_poll: 43| 1.44M|struct us_poll_t *us_create_poll(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { 44| 1.44M| if (!fallthrough) { ------------------ | Branch (44:9): [True: 1.44M, False: 6.76k] ------------------ 45| 1.44M| loop->num_polls++; 46| 1.44M| } 47| 1.44M| return malloc(sizeof(struct us_poll_t) + ext_size); 48| 1.44M|} us_poll_free: 51| 1.44M|void us_poll_free(struct us_poll_t *p, struct us_loop_t *loop) { 52| 1.44M| loop->num_polls--; 53| 1.44M| free(p); 54| 1.44M|} us_poll_init: 61| 1.44M|void us_poll_init(struct us_poll_t *p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type) { 62| 1.44M| p->state.fd = fd; 63| 1.44M| p->state.poll_type = poll_type; 64| 1.44M|} us_poll_events: 66| 14.1M|int us_poll_events(struct us_poll_t *p) { 67| 14.1M| return ((p->state.poll_type & POLL_TYPE_POLLING_IN) ? LIBUS_SOCKET_READABLE : 0) | ((p->state.poll_type & POLL_TYPE_POLLING_OUT) ? LIBUS_SOCKET_WRITABLE : 0); ------------------ | | 27| 14.1M|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ return ((p->state.poll_type & POLL_TYPE_POLLING_IN) ? LIBUS_SOCKET_READABLE : 0) | ((p->state.poll_type & POLL_TYPE_POLLING_OUT) ? LIBUS_SOCKET_WRITABLE : 0); ------------------ | | 28| 14.1M|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ | Branch (67:13): [True: 14.1M, False: 32.4k] | Branch (67:89): [True: 1.19M, False: 12.9M] ------------------ 68| 14.1M|} us_poll_fd: 70| 12.3M|LIBUS_SOCKET_DESCRIPTOR us_poll_fd(struct us_poll_t *p) { 71| 12.3M| return p->state.fd; 72| 12.3M|} us_internal_poll_type: 75| 13.5M|int us_internal_poll_type(struct us_poll_t *p) { 76| 13.5M| return p->state.poll_type & 3; 77| 13.5M|} us_internal_poll_set_type: 80| 564k|void us_internal_poll_set_type(struct us_poll_t *p, int poll_type) { 81| 564k| p->state.poll_type = poll_type | (p->state.poll_type & 12); 82| 564k|} us_timer_ext: 85| 1.86M|void *us_timer_ext(struct us_timer_t *timer) { 86| 1.86M| return ((struct us_internal_callback_t *) timer) + 1; 87| 1.86M|} us_create_loop: 96| 2.25k|struct us_loop_t *us_create_loop(void *hint, void (*wakeup_cb)(struct us_loop_t *loop), void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop), unsigned int ext_size) { 97| 2.25k| struct us_loop_t *loop = (struct us_loop_t *) malloc(sizeof(struct us_loop_t) + ext_size); 98| 2.25k| loop->num_polls = 0; 99| | /* These could be accessed if we close a poll before starting the loop */ 100| 2.25k| loop->num_ready_polls = 0; 101| 2.25k| loop->current_ready_poll = 0; 102| | 103| 2.25k|#ifdef LIBUS_USE_EPOLL 104| 2.25k| loop->fd = epoll_create1(EPOLL_CLOEXEC); 105| |#else 106| | loop->fd = kqueue(); 107| |#endif 108| | 109| 2.25k| us_internal_loop_data_init(loop, wakeup_cb, pre_cb, post_cb); 110| 2.25k| return loop; 111| 2.25k|} us_loop_run: 113| 2.25k|void us_loop_run(struct us_loop_t *loop) { 114| 2.25k| us_loop_integrate(loop); 115| | 116| | /* While we have non-fallthrough polls we shouldn't fall through */ 117| 2.42M| while (loop->num_polls) { ------------------ | Branch (117:12): [True: 2.41M, False: 2.25k] ------------------ 118| | /* Emit pre callback */ 119| 2.41M| us_internal_loop_pre(loop); 120| | 121| | /* Fetch ready polls */ 122| 2.41M|#ifdef LIBUS_USE_EPOLL 123| 2.41M| loop->num_ready_polls = epoll_wait(loop->fd, loop->ready_polls, 1024, -1); 124| |#else 125| | loop->num_ready_polls = kevent(loop->fd, NULL, 0, loop->ready_polls, 1024, NULL); 126| |#endif 127| | 128| | /* Iterate ready polls, dispatching them by type */ 129| 11.4M| for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) { ------------------ | Branch (129:44): [True: 9.04M, False: 2.41M] ------------------ 130| 9.04M| struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll); ------------------ | | 28| 9.04M|#define GET_READY_POLL(loop, index) (struct us_poll_t *) loop->ready_polls[index].data.ptr ------------------ 131| | /* Any ready poll marked with nullptr will be ignored */ 132| 9.04M| if (poll) { ------------------ | Branch (132:17): [True: 9.04M, False: 0] ------------------ 133| 9.04M|#ifdef LIBUS_USE_EPOLL 134| 9.04M| int events = loop->ready_polls[loop->current_ready_poll].events; 135| 9.04M| int error = loop->ready_polls[loop->current_ready_poll].events & (EPOLLERR | EPOLLHUP); 136| |#else 137| | /* EVFILT_READ, EVFILT_TIME, EVFILT_USER are all mapped to LIBUS_SOCKET_READABLE */ 138| | int events = LIBUS_SOCKET_READABLE; 139| | if (loop->ready_polls[loop->current_ready_poll].filter == EVFILT_WRITE) { 140| | events = LIBUS_SOCKET_WRITABLE; 141| | } 142| | int error = loop->ready_polls[loop->current_ready_poll].flags & (EV_ERROR | EV_EOF); 143| |#endif 144| | /* Always filter all polls by what they actually poll for (callback polls always poll for readable) */ 145| 9.04M| events &= us_poll_events(poll); 146| 9.04M| if (events || error) { ------------------ | Branch (146:21): [True: 6.25M, False: 2.78M] | Branch (146:31): [True: 2.78M, False: 0] ------------------ 147| 9.04M| us_internal_dispatch_ready_poll(poll, error, events); 148| 9.04M| } 149| 9.04M| } 150| 9.04M| } 151| | /* Emit post callback */ 152| 2.41M| us_internal_loop_post(loop); 153| 2.41M| } 154| 2.25k|} us_internal_loop_update_pending_ready_polls: 156| 1.44M|void us_internal_loop_update_pending_ready_polls(struct us_loop_t *loop, struct us_poll_t *old_poll, struct us_poll_t *new_poll, int old_events, int new_events) { 157| 1.44M|#ifdef LIBUS_USE_EPOLL 158| | /* Epoll only has one ready poll per poll */ 159| 1.44M| int num_entries_possibly_remaining = 1; 160| |#else 161| | /* Ready polls may contain same poll twice under kqueue, as one poll may hold two filters */ 162| | int num_entries_possibly_remaining = 2;//((old_events & LIBUS_SOCKET_READABLE) ? 1 : 0) + ((old_events & LIBUS_SOCKET_WRITABLE) ? 1 : 0); 163| |#endif 164| | 165| | /* Todo: for kqueue if we track things in us_change_poll it is possible to have a fast path with no seeking in cases of: 166| | * current poll being us AND we only poll for one thing */ 167| | 168| 2.90M| for (int i = loop->current_ready_poll; i < loop->num_ready_polls && num_entries_possibly_remaining; i++) { ------------------ | Branch (168:44): [True: 2.87M, False: 27.7k] | Branch (168:73): [True: 1.45M, False: 1.42M] ------------------ 169| 1.45M| if (GET_READY_POLL(loop, i) == old_poll) { ------------------ | | 28| 1.45M|#define GET_READY_POLL(loop, index) (struct us_poll_t *) loop->ready_polls[index].data.ptr ------------------ | Branch (169:13): [True: 1.43M, False: 19.9k] ------------------ 170| | 171| | // if new events does not contain the ready events of this poll then remove (no we filter that out later on) 172| 1.43M| SET_READY_POLL(loop, i, new_poll); ------------------ | | 29| 1.43M|#define SET_READY_POLL(loop, index, poll) loop->ready_polls[index].data.ptr = poll ------------------ 173| | 174| 1.43M| num_entries_possibly_remaining--; 175| 1.43M| } 176| 1.45M| } 177| 1.44M|} us_poll_start: 226| 1.44M|void us_poll_start(struct us_poll_t *p, struct us_loop_t *loop, int events) { 227| 1.44M| p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0); ------------------ | | 27| 1.44M|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0); ------------------ | | 28| 1.44M|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ | Branch (227:54): [True: 1.44M, False: 0] | Branch (227:118): [True: 0, False: 1.44M] ------------------ 228| | 229| 1.44M|#ifdef LIBUS_USE_EPOLL 230| 1.44M| struct epoll_event event; 231| 1.44M| event.events = events; 232| 1.44M| event.data.ptr = p; 233| 1.44M| epoll_ctl(loop->fd, EPOLL_CTL_ADD, p->state.fd, &event); 234| |#else 235| | kqueue_change(loop->fd, p->state.fd, 0, events, p); 236| |#endif 237| 1.44M|} us_poll_change: 239| 1.20M|void us_poll_change(struct us_poll_t *p, struct us_loop_t *loop, int events) { 240| 1.20M| int old_events = us_poll_events(p); 241| 1.20M| if (old_events != events) { ------------------ | Branch (241:9): [True: 1.14M, False: 65.6k] ------------------ 242| | 243| 1.14M| p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0); ------------------ | | 27| 1.14M|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ p->state.poll_type = us_internal_poll_type(p) | ((events & LIBUS_SOCKET_READABLE) ? POLL_TYPE_POLLING_IN : 0) | ((events & LIBUS_SOCKET_WRITABLE) ? POLL_TYPE_POLLING_OUT : 0); ------------------ | | 28| 1.14M|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ | Branch (243:58): [True: 1.11M, False: 32.4k] | Branch (243:122): [True: 559k, False: 583k] ------------------ 244| | 245| 1.14M|#ifdef LIBUS_USE_EPOLL 246| 1.14M| struct epoll_event event; 247| 1.14M| event.events = events; 248| 1.14M| event.data.ptr = p; 249| 1.14M| epoll_ctl(loop->fd, EPOLL_CTL_MOD, p->state.fd, &event); 250| |#else 251| | kqueue_change(loop->fd, p->state.fd, old_events, events, p); 252| |#endif 253| | /* Set all removed events to null-polls in pending ready poll list */ 254| | //us_internal_loop_update_pending_ready_polls(loop, p, p, old_events, events); 255| 1.14M| } 256| 1.20M|} us_poll_stop: 258| 1.44M|void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) { 259| 1.44M| int old_events = us_poll_events(p); 260| 1.44M| int new_events = 0; 261| 1.44M|#ifdef LIBUS_USE_EPOLL 262| 1.44M| struct epoll_event event; 263| 1.44M| epoll_ctl(loop->fd, EPOLL_CTL_DEL, p->state.fd, &event); 264| |#else 265| | if (old_events) { 266| | kqueue_change(loop->fd, p->state.fd, old_events, new_events, NULL); 267| | } 268| |#endif 269| | 270| | /* Disable any instance of us in the pending ready poll list */ 271| 1.44M| us_internal_loop_update_pending_ready_polls(loop, p, 0, old_events, new_events); 272| 1.44M|} us_internal_accept_poll_event: 274| 5.69M|unsigned int us_internal_accept_poll_event(struct us_poll_t *p) { 275| 5.69M|#ifdef LIBUS_USE_EPOLL 276| 5.69M| int fd = us_poll_fd(p); 277| 5.69M| uint64_t buf; 278| 5.69M| int read_length = read(fd, &buf, 8); 279| 5.69M| (void)read_length; 280| 5.69M| return buf; 281| |#else 282| | /* Kqueue has no underlying FD for timers or user events */ 283| | return 0; 284| |#endif 285| 5.69M|} us_create_timer: 289| 4.50k|struct us_timer_t *us_create_timer(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { 290| 4.50k| struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); 291| 4.50k| int timerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); 292| 4.50k| if (timerfd == -1) { ------------------ | Branch (292:9): [True: 0, False: 4.50k] ------------------ 293| 0| return NULL; 294| 0| } 295| 4.50k| us_poll_init(p, timerfd, POLL_TYPE_CALLBACK); 296| | 297| 4.50k| struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; 298| 4.50k| cb->loop = loop; 299| 4.50k| cb->cb_expects_the_loop = 0; 300| 4.50k| cb->leave_poll_ready = 0; 301| | 302| 4.50k| return (struct us_timer_t *) cb; 303| 4.50k|} us_timer_close: 325| 4.50k|void us_timer_close(struct us_timer_t *timer) { 326| 4.50k| struct us_internal_callback_t *cb = (struct us_internal_callback_t *) timer; 327| | 328| 4.50k| us_poll_stop(&cb->p, cb->loop); 329| 4.50k| close(us_poll_fd(&cb->p)); 330| | 331| | /* (regular) sockets are the only polls which are not freed immediately */ 332| 4.50k| us_poll_free((struct us_poll_t *) timer, cb->loop); 333| 4.50k|} us_timer_set: 335| 4.50k|void us_timer_set(struct us_timer_t *t, void (*cb)(struct us_timer_t *t), int ms, int repeat_ms) { 336| 4.50k| struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) t; 337| | 338| 4.50k| internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; 339| | 340| 4.50k| struct itimerspec timer_spec = { 341| 4.50k| {repeat_ms / 1000, (long) (repeat_ms % 1000) * (long) 1000000}, 342| 4.50k| {ms / 1000, (long) (ms % 1000) * (long) 1000000} 343| 4.50k| }; 344| | 345| 4.50k| timerfd_settime(us_poll_fd((struct us_poll_t *) t), 0, &timer_spec, NULL); 346| 4.50k| us_poll_start((struct us_poll_t *) t, internal_cb->loop, LIBUS_SOCKET_READABLE); ------------------ | | 27| 4.50k|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 347| 4.50k|} us_internal_create_async: 374| 2.25k|struct us_internal_async *us_internal_create_async(struct us_loop_t *loop, int fallthrough, unsigned int ext_size) { 375| 2.25k| struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_internal_callback_t) + ext_size); 376| 2.25k| us_poll_init(p, eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC), POLL_TYPE_CALLBACK); 377| | 378| 2.25k| struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; 379| 2.25k| cb->loop = loop; 380| 2.25k| cb->cb_expects_the_loop = 1; 381| 2.25k| cb->leave_poll_ready = 0; 382| | 383| 2.25k| return (struct us_internal_async *) cb; 384| 2.25k|} us_internal_async_close: 387| 2.25k|void us_internal_async_close(struct us_internal_async *a) { 388| 2.25k| struct us_internal_callback_t *cb = (struct us_internal_callback_t *) a; 389| | 390| 2.25k| us_poll_stop(&cb->p, cb->loop); 391| 2.25k| close(us_poll_fd(&cb->p)); 392| | 393| | /* (regular) sockets are the only polls which are not freed immediately */ 394| 2.25k| us_poll_free((struct us_poll_t *) a, cb->loop); 395| 2.25k|} us_internal_async_set: 397| 2.25k|void us_internal_async_set(struct us_internal_async *a, void (*cb)(struct us_internal_async *)) { 398| 2.25k| struct us_internal_callback_t *internal_cb = (struct us_internal_callback_t *) a; 399| | 400| 2.25k| internal_cb->cb = (void (*)(struct us_internal_callback_t *)) cb; 401| | 402| 2.25k| us_poll_start((struct us_poll_t *) a, internal_cb->loop, LIBUS_SOCKET_READABLE); ------------------ | | 27| 2.25k|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 403| 2.25k|} us_internal_async_wakeup: 405| 34.2k|void us_internal_async_wakeup(struct us_internal_async *a) { 406| 34.2k| uint64_t one = 1; 407| 34.2k| int written = write(us_poll_fd((struct us_poll_t *) a), &one, 8); 408| 34.2k| (void)written; 409| 34.2k|} us_internal_loop_data_init: 26| 2.25k| void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)) { 27| 2.25k| loop->data.sweep_timer = us_create_timer(loop, 1, 0); 28| 2.25k| loop->data.recv_buf = malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2); ------------------ | | 22| 2.25k|#define LIBUS_RECV_BUFFER_LENGTH 524288 ------------------ loop->data.recv_buf = malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2); ------------------ | | 26| 2.25k|#define LIBUS_RECV_BUFFER_PADDING 32 ------------------ 29| 2.25k| loop->data.ssl_data = 0; 30| 2.25k| loop->data.head = 0; 31| 2.25k| loop->data.iterator = 0; 32| 2.25k| loop->data.closed_head = 0; 33| 2.25k| loop->data.low_prio_head = 0; 34| 2.25k| loop->data.low_prio_budget = 0; 35| | 36| 2.25k| loop->data.pre_cb = pre_cb; 37| 2.25k| loop->data.post_cb = post_cb; 38| 2.25k| loop->data.iteration_nr = 0; 39| | 40| 2.25k| loop->data.wakeup_async = us_internal_create_async(loop, 1, 0); 41| 2.25k| us_internal_async_set(loop->data.wakeup_async, (void (*)(struct us_internal_async *)) wakeup_cb); 42| 2.25k|} us_internal_loop_data_free: 44| 2.25k|void us_internal_loop_data_free(struct us_loop_t *loop) { 45| |#ifndef LIBUS_NO_SSL 46| | us_internal_free_loop_ssl_data(loop); 47| |#endif 48| | 49| 2.25k| free(loop->data.recv_buf); 50| | 51| 2.25k| us_timer_close(loop->data.sweep_timer); 52| 2.25k| us_internal_async_close(loop->data.wakeup_async); 53| 2.25k|} us_wakeup_loop: 55| 34.2k|void us_wakeup_loop(struct us_loop_t *loop) { 56| 34.2k| us_internal_async_wakeup(loop->data.wakeup_async); 57| 34.2k|} us_internal_loop_link: 59| 2.25k|void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) { 60| | /* Insert this context as the head of loop */ 61| 2.25k| context->next = loop->data.head; 62| 2.25k| context->prev = 0; 63| 2.25k| if (loop->data.head) { ------------------ | Branch (63:9): [True: 0, False: 2.25k] ------------------ 64| 0| loop->data.head->prev = context; 65| 0| } 66| 2.25k| loop->data.head = context; 67| 2.25k|} us_internal_loop_unlink: 70| 2.25k|void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context) { 71| 2.25k| if (loop->data.head == context) { ------------------ | Branch (71:9): [True: 2.25k, False: 0] ------------------ 72| 2.25k| loop->data.head = context->next; 73| 2.25k| if (loop->data.head) { ------------------ | Branch (73:13): [True: 0, False: 2.25k] ------------------ 74| 0| loop->data.head->prev = 0; 75| 0| } 76| 2.25k| } else { 77| 0| context->prev->next = context->next; 78| 0| if (context->next) { ------------------ | Branch (78:13): [True: 0, False: 0] ------------------ 79| 0| context->next->prev = context->prev; 80| 0| } 81| 0| } 82| 2.25k|} us_internal_timer_sweep: 85| 1.94M|void us_internal_timer_sweep(struct us_loop_t *loop) { 86| 1.94M| struct us_internal_loop_data_t *loop_data = &loop->data; 87| | /* For all socket contexts in this loop */ 88| 3.89M| for (loop_data->iterator = loop_data->head; loop_data->iterator; loop_data->iterator = loop_data->iterator->next) { ------------------ | Branch (88:49): [True: 1.94M, False: 1.94M] ------------------ 89| | 90| 1.94M| struct us_socket_context_t *context = loop_data->iterator; 91| | 92| | /* Update this context's timestamps (this could be moved to loop and done once) */ 93| 1.94M| context->global_tick++; 94| 1.94M| unsigned char short_ticks = context->timestamp = context->global_tick % 240; 95| 1.94M| unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240; 96| | 97| | /* Begin at head */ 98| 1.94M| struct us_socket_t *s = context->head_sockets; 99| 1.95M| while (s) { ------------------ | Branch (99:16): [True: 58.0k, False: 1.89M] ------------------ 100| | /* Seek until end or timeout found (tightest loop) */ 101| 142k| while (1) { ------------------ | Branch (101:20): [Folded - Ignored] ------------------ 102| | /* We only read from 1 random cache line here */ 103| 142k| if (short_ticks == s->timeout || long_ticks == s->long_timeout) { ------------------ | Branch (103:21): [True: 5.64k, False: 137k] | Branch (103:50): [True: 0, False: 137k] ------------------ 104| 5.64k| break; 105| 5.64k| } 106| | 107| | /* Did we reach the end without a find? */ 108| 137k| if ((s = s->next) == 0) { ------------------ | Branch (108:21): [True: 52.4k, False: 84.9k] ------------------ 109| 52.4k| goto next_context; 110| 52.4k| } 111| 137k| } 112| | 113| | /* Here we have a timeout to emit (slow path) */ 114| 5.64k| context->iterator = s; 115| | 116| 5.64k| if (short_ticks == s->timeout) { ------------------ | Branch (116:17): [True: 5.64k, False: 0] ------------------ 117| 5.64k| s->timeout = 255; 118| 5.64k| context->on_socket_timeout(s); 119| 5.64k| } 120| | 121| 5.64k| if (context->iterator == s && long_ticks == s->long_timeout) { ------------------ | Branch (121:17): [True: 0, False: 5.64k] | Branch (121:43): [True: 0, False: 0] ------------------ 122| 0| s->long_timeout = 255; 123| 0| context->on_socket_long_timeout(s); 124| 0| } 125| | 126| | /* Check for unlink / link (if the event handler did not modify the chain, we step 1) */ 127| 5.64k| if (s == context->iterator) { ------------------ | Branch (127:17): [True: 0, False: 5.64k] ------------------ 128| 0| s = s->next; 129| 5.64k| } else { 130| | /* The iterator was changed by event handler */ 131| 5.64k| s = context->iterator; 132| 5.64k| } 133| 5.64k| } 134| | /* We always store a 0 to context->iterator here since we are no longer iterating this context */ 135| 1.94M| next_context: 136| 1.94M| context->iterator = 0; 137| 1.94M| } 138| 1.94M|} us_internal_handle_low_priority_sockets: 145| 2.41M|void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) { 146| 2.41M| struct us_internal_loop_data_t *loop_data = &loop->data; 147| 2.41M| struct us_socket_t *s; 148| | 149| 2.41M| loop_data->low_prio_budget = MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION; 150| | 151| 2.41M| for (s = loop_data->low_prio_head; s && loop_data->low_prio_budget > 0; s = loop_data->low_prio_head, loop_data->low_prio_budget--) { ------------------ | Branch (151:40): [True: 0, False: 2.41M] | Branch (151:45): [True: 0, False: 0] ------------------ 152| | /* Unlink this socket from the low-priority queue */ 153| 0| loop_data->low_prio_head = s->next; 154| 0| if (s->next) s->next->prev = 0; ------------------ | Branch (154:13): [True: 0, False: 0] ------------------ 155| 0| s->next = 0; 156| | 157| 0| us_internal_socket_context_link_socket(s->context, s); 158| 0| us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE); ------------------ | | 27| 0|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 159| | 160| 0| s->low_prio_state = 2; 161| 0| } 162| 2.41M|} us_internal_free_closed_sockets: 165| 2.41M|void us_internal_free_closed_sockets(struct us_loop_t *loop) { 166| | /* Free all closed sockets (maybe it is better to reverse order?) */ 167| 2.41M| if (loop->data.closed_head) { ------------------ | Branch (167:9): [True: 199k, False: 2.21M] ------------------ 168| 1.64M| for (struct us_socket_t *s = loop->data.closed_head; s; ) { ------------------ | Branch (168:62): [True: 1.44M, False: 199k] ------------------ 169| 1.44M| struct us_socket_t *next = s->next; 170| 1.44M| us_poll_free((struct us_poll_t *) s, loop); 171| 1.44M| s = next; 172| 1.44M| } 173| 199k| loop->data.closed_head = 0; 174| 199k| } 175| 2.41M|} sweep_timer_cb: 177| 1.94M|void sweep_timer_cb(struct us_internal_callback_t *cb) { 178| 1.94M| us_internal_timer_sweep(cb->loop); 179| 1.94M|} us_internal_loop_pre: 186| 2.41M|void us_internal_loop_pre(struct us_loop_t *loop) { 187| 2.41M| loop->data.iteration_nr++; 188| 2.41M| us_internal_handle_low_priority_sockets(loop); 189| 2.41M| loop->data.pre_cb(loop); 190| 2.41M|} us_internal_loop_post: 192| 2.41M|void us_internal_loop_post(struct us_loop_t *loop) { 193| 2.41M| us_internal_free_closed_sockets(loop); 194| 2.41M| loop->data.post_cb(loop); 195| 2.41M|} us_adopt_accepted_socket: 198| 1.44M| unsigned int socket_ext_size, char *addr_ip, int addr_ip_length) { 199| |#ifndef LIBUS_NO_SSL 200| | if (ssl) { 201| | return (struct us_socket_t *)us_internal_ssl_adopt_accepted_socket((struct us_internal_ssl_socket_context_t *)context, accepted_fd, 202| | socket_ext_size, addr_ip, addr_ip_length); 203| | } 204| |#endif 205| 1.44M| struct us_poll_t *accepted_p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size); 206| 1.44M| us_poll_init(accepted_p, accepted_fd, POLL_TYPE_SOCKET); 207| 1.44M| us_poll_start(accepted_p, context->loop, LIBUS_SOCKET_READABLE); ------------------ | | 27| 1.44M|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 208| | 209| 1.44M| struct us_socket_t *s = (struct us_socket_t *) accepted_p; 210| | 211| 1.44M| s->context = context; 212| 1.44M| s->timeout = 255; 213| 1.44M| s->long_timeout = 255; 214| 1.44M| s->low_prio_state = 0; 215| | 216| | /* We always use nodelay */ 217| 1.44M| bsd_socket_nodelay(accepted_fd, 1); 218| | 219| 1.44M| us_internal_socket_context_link_socket(context, s); 220| | 221| 1.44M| context->on_open(s, 0, addr_ip, addr_ip_length); 222| 1.44M| return s; 223| 1.44M|} us_internal_dispatch_ready_poll: 225| 9.04M|void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) { 226| 9.04M| switch (us_internal_poll_type(p)) { ------------------ | Branch (226:13): [True: 0, False: 9.04M] ------------------ 227| 5.69M| case POLL_TYPE_CALLBACK: { ------------------ | Branch (227:5): [True: 5.69M, False: 3.34M] ------------------ 228| 5.69M| struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p; 229| | /* Timers, asyncs should accept (read), while UDP sockets should obviously not */ 230| 5.69M| if (!cb->leave_poll_ready) { ------------------ | Branch (230:17): [True: 5.69M, False: 0] ------------------ 231| | /* Let's just have this macro to silence the CodeQL alert regarding empty function when using libuv */ 232| 5.69M| #ifndef LIBUS_USE_LIBUV 233| 5.69M| us_internal_accept_poll_event(p); 234| 5.69M| #endif 235| 5.69M| } 236| 5.69M| cb->cb(cb->cb_expects_the_loop ? (struct us_internal_callback_t *) cb->loop : (struct us_internal_callback_t *) &cb->p); ------------------ | Branch (236:20): [True: 1.88M, False: 3.81M] ------------------ 237| 5.69M| } 238| 5.69M| break; 239| 1.85M| case POLL_TYPE_SEMI_SOCKET: { ------------------ | Branch (239:5): [True: 1.85M, False: 7.18M] ------------------ 240| | /* Both connect and listen sockets are semi-sockets 241| | * but they poll for different events */ 242| 1.85M| if (us_poll_events(p) == LIBUS_SOCKET_WRITABLE) { ------------------ | | 28| 1.85M|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ | Branch (242:17): [True: 0, False: 1.85M] ------------------ 243| 0| struct us_socket_t *s = (struct us_socket_t *) p; 244| | 245| | /* It is perfectly possible to come here with an error */ 246| 0| if (error) { ------------------ | Branch (246:21): [True: 0, False: 0] ------------------ 247| | /* Emit error, close without emitting on_close */ 248| 0| s->context->on_connect_error(s, 0); 249| 0| us_socket_close_connecting(0, s); 250| 0| } else { 251| | /* All sockets poll for readable */ 252| 0| us_poll_change(p, s->context->loop, LIBUS_SOCKET_READABLE); ------------------ | | 27| 0|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 253| | 254| | /* We always use nodelay */ 255| 0| bsd_socket_nodelay(us_poll_fd(p), 1); 256| | 257| | /* We are now a proper socket */ 258| 0| us_internal_poll_set_type(p, POLL_TYPE_SOCKET); 259| | 260| | /* If we used a connection timeout we have to reset it here */ 261| 0| us_socket_timeout(0, s, 0); 262| | 263| 0| s->context->on_open(s, 1, 0, 0); 264| 0| } 265| 1.85M| } else { 266| 1.85M| struct us_listen_socket_t *listen_socket = (struct us_listen_socket_t *) p; 267| 1.85M| struct bsd_addr_t addr; 268| | 269| 1.85M| LIBUS_SOCKET_DESCRIPTOR client_fd = bsd_accept_socket(us_poll_fd(p), &addr); ------------------ | | 38| 1.85M|#define LIBUS_SOCKET_DESCRIPTOR int ------------------ 270| 1.85M| if (client_fd == LIBUS_SOCKET_ERROR) { ------------------ | | 44| 1.85M|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (270:21): [True: 1.69M, False: 165k] ------------------ 271| | /* Todo: start timer here */ 272| | 273| 1.69M| } else { 274| | 275| | /* Todo: stop timer if any */ 276| | 277| 1.44M| do { 278| 1.44M| struct us_socket_context_t *context = us_socket_context(0, &listen_socket->s); 279| | /* See if we want to export the FD or keep it here (this event can be unset) */ 280| 1.44M| if (context->on_pre_open == 0 || context->on_pre_open(client_fd) == client_fd) { ------------------ | Branch (280:29): [True: 1.44M, False: 0] | Branch (280:58): [True: 0, False: 0] ------------------ 281| | 282| | /* Adopt the newly accepted socket */ 283| 1.44M| us_adopt_accepted_socket(0, context, 284| 1.44M| client_fd, listen_socket->socket_ext_size, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr)); 285| | 286| | /* Exit accept loop if listen socket was closed in on_open handler */ 287| 1.44M| if (us_socket_is_closed(0, &listen_socket->s)) { ------------------ | Branch (287:33): [True: 0, False: 1.44M] ------------------ 288| 0| break; 289| 0| } 290| | 291| 1.44M| } 292| | 293| 1.44M| } while ((client_fd = bsd_accept_socket(us_poll_fd(p), &addr)) != LIBUS_SOCKET_ERROR); ------------------ | | 44| 1.44M|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (293:30): [True: 1.27M, False: 165k] ------------------ 294| 165k| } 295| 1.85M| } 296| 1.85M| } 297| 0| break; 298| 0| case POLL_TYPE_SOCKET_SHUT_DOWN: ------------------ | Branch (298:5): [True: 0, False: 9.04M] ------------------ 299| 1.48M| case POLL_TYPE_SOCKET: { ------------------ | Branch (299:5): [True: 1.48M, False: 7.55M] ------------------ 300| | /* We should only use s, no p after this point */ 301| 1.48M| struct us_socket_t *s = (struct us_socket_t *) p; 302| | 303| | /* Such as epollerr epollhup */ 304| 1.48M| if (error) { ------------------ | Branch (304:17): [True: 837k, False: 651k] ------------------ 305| | /* Todo: decide what code we give here */ 306| 837k| s = us_socket_close(0, s, 0, NULL); 307| 837k| return; 308| 837k| } 309| | 310| 651k| if (events & LIBUS_SOCKET_WRITABLE) { ------------------ | | 28| 651k|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ | Branch (310:17): [True: 9.03k, False: 642k] ------------------ 311| | /* Note: if we failed a write as a socket of one loop then adopted 312| | * to another loop, this will be wrong. Absurd case though */ 313| 9.03k| s->context->loop->data.last_write_failed = 0; 314| | 315| 9.03k| s = s->context->on_writable(s); 316| | 317| 9.03k| if (us_socket_is_closed(0, s)) { ------------------ | Branch (317:21): [True: 1.86k, False: 7.16k] ------------------ 318| 1.86k| return; 319| 1.86k| } 320| | 321| | /* If we have no failed write or if we shut down, then stop polling for more writable */ 322| 7.16k| if (!s->context->loop->data.last_write_failed || us_socket_is_shut_down(0, s)) { ------------------ | Branch (322:21): [True: 463, False: 6.70k] | Branch (322:66): [True: 0, False: 6.70k] ------------------ 323| 463| us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE); ------------------ | | 27| 463|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 324| 463| } 325| 7.16k| } 326| | 327| 649k| if (events & LIBUS_SOCKET_READABLE) { ------------------ | | 27| 649k|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ | Branch (327:17): [True: 648k, False: 1.53k] ------------------ 328| | /* Contexts may prioritize down sockets that are currently readable, e.g. when SSL handshake has to be done. 329| | * SSL handshakes are CPU intensive, so we limit the number of handshakes per loop iteration, and move the rest 330| | * to the low-priority queue */ 331| 648k| if (s->context->is_low_prio(s)) { ------------------ | Branch (331:21): [True: 0, False: 648k] ------------------ 332| 0| if (s->low_prio_state == 2) { ------------------ | Branch (332:25): [True: 0, False: 0] ------------------ 333| 0| s->low_prio_state = 0; /* Socket has been delayed and now it's time to process incoming data for one iteration */ 334| 0| } else if (s->context->loop->data.low_prio_budget > 0) { ------------------ | Branch (334:32): [True: 0, False: 0] ------------------ 335| 0| s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */ 336| 0| } else { 337| 0| us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE); ------------------ | | 28| 0|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ 338| 0| us_internal_socket_context_unlink_socket(s->context, s); 339| | 340| | /* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are 341| | * maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts 342| | * under high load */ 343| 0| s->prev = 0; 344| 0| s->next = s->context->loop->data.low_prio_head; 345| 0| if (s->next) s->next->prev = s; ------------------ | Branch (345:29): [True: 0, False: 0] ------------------ 346| 0| s->context->loop->data.low_prio_head = s; 347| | 348| 0| s->low_prio_state = 1; 349| | 350| 0| break; 351| 0| } 352| 0| } 353| | 354| 648k| int length; 355| 648k| read_more: 356| 648k| length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0); ------------------ | | 26| 648k|#define LIBUS_RECV_BUFFER_PADDING 32 ------------------ length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0); ------------------ | | 22| 648k|#define LIBUS_RECV_BUFFER_LENGTH 524288 ------------------ 357| 648k| if (length > 0) { ------------------ | Branch (357:21): [True: 612k, False: 35.9k] ------------------ 358| 612k| s = s->context->on_data(s, s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length); ------------------ | | 26| 612k|#define LIBUS_RECV_BUFFER_PADDING 32 ------------------ 359| | 360| | /* If we filled the entire recv buffer, we need to immediately read again since otherwise a 361| | * pending hangup event in the same even loop iteration can close the socket before we get 362| | * the chance to read again next iteration */ 363| 612k| if (length == LIBUS_RECV_BUFFER_LENGTH && s && !us_socket_is_closed(0, s)) { ------------------ | | 22| 1.22M|#define LIBUS_RECV_BUFFER_LENGTH 524288 ------------------ | Branch (363:25): [True: 0, False: 612k] | Branch (363:63): [True: 0, False: 0] | Branch (363:68): [True: 0, False: 0] ------------------ 364| 0| goto read_more; 365| 0| } 366| | 367| 612k| } else if (!length) { ------------------ | Branch (367:28): [True: 32.4k, False: 3.54k] ------------------ 368| 32.4k| if (us_socket_is_shut_down(0, s)) { ------------------ | Branch (368:25): [True: 0, False: 32.4k] ------------------ 369| | /* We got FIN back after sending it */ 370| | /* Todo: We should give "CLEAN SHUTDOWN" as reason here */ 371| 0| s = us_socket_close(0, s, 0, NULL); 372| 32.4k| } else { 373| | /* We got FIN, so stop polling for readable */ 374| 32.4k| us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE); ------------------ | | 28| 32.4k|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ 375| 32.4k| s = s->context->on_end(s); 376| 32.4k| } 377| 32.4k| } else if (length == LIBUS_SOCKET_ERROR && !bsd_would_block()) { ------------------ | | 44| 7.08k|#define LIBUS_SOCKET_ERROR -1 ------------------ | Branch (377:28): [True: 3.54k, False: 0] | Branch (377:60): [True: 0, False: 3.54k] ------------------ 378| | /* Todo: decide also here what kind of reason we should give */ 379| 0| s = us_socket_close(0, s, 0, NULL); 380| 0| } 381| 648k| } 382| 649k| } 383| 649k| break; 384| 9.04M| } 385| 9.04M|} us_loop_integrate: 388| 2.25k|void us_loop_integrate(struct us_loop_t *loop) { 389| 2.25k| us_timer_set(loop->data.sweep_timer, (void (*)(struct us_timer_t *)) sweep_timer_cb, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000); ------------------ | | 24| 2.25k|#define LIBUS_TIMEOUT_GRANULARITY 4 ------------------ us_timer_set(loop->data.sweep_timer, (void (*)(struct us_timer_t *)) sweep_timer_cb, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000); ------------------ | | 24| 2.25k|#define LIBUS_TIMEOUT_GRANULARITY 4 ------------------ 390| 2.25k|} us_loop_ext: 392| 9.36M|void *us_loop_ext(struct us_loop_t *loop) { 393| 9.36M| return loop + 1; 394| 9.36M|} us_socket_context: 61| 7.57M|struct us_socket_context_t *us_socket_context(int ssl, struct us_socket_t *s) { 62| 7.57M| return s->context; 63| 7.57M|} us_socket_timeout: 65| 1.56M|void us_socket_timeout(int ssl, struct us_socket_t *s, unsigned int seconds) { 66| 1.56M| if (seconds) { ------------------ | Branch (66:9): [True: 1.51M, False: 50.5k] ------------------ 67| 1.51M| s->timeout = ((unsigned int)s->context->timestamp + ((seconds + 3) >> 2)) % 240; 68| 1.51M| } else { 69| 50.5k| s->timeout = 255; 70| 50.5k| } 71| 1.56M|} us_socket_is_closed: 87| 5.27M|int us_socket_is_closed(int ssl, struct us_socket_t *s) { 88| 5.27M| return s->prev == (struct us_socket_t *) s->context; 89| 5.27M|} us_socket_close: 116| 1.44M|struct us_socket_t *us_socket_close(int ssl, struct us_socket_t *s, int code, void *reason) { 117| 1.44M| if (!us_socket_is_closed(0, s)) { ------------------ | Branch (117:9): [True: 1.44M, False: 0] ------------------ 118| 1.44M| if (s->low_prio_state == 1) { ------------------ | Branch (118:13): [True: 0, False: 1.44M] ------------------ 119| | /* Unlink this socket from the low-priority queue */ 120| 0| if (!s->prev) s->context->loop->data.low_prio_head = s->next; ------------------ | Branch (120:17): [True: 0, False: 0] ------------------ 121| 0| else s->prev->next = s->next; 122| | 123| 0| if (s->next) s->next->prev = s->prev; ------------------ | Branch (123:17): [True: 0, False: 0] ------------------ 124| | 125| 0| s->prev = 0; 126| 0| s->next = 0; 127| 0| s->low_prio_state = 0; 128| 1.44M| } else { 129| 1.44M| us_internal_socket_context_unlink_socket(s->context, s); 130| 1.44M| } 131| 1.44M| us_poll_stop((struct us_poll_t *) s, s->context->loop); 132| 1.44M| bsd_close_socket(us_poll_fd((struct us_poll_t *) s)); 133| | 134| | /* Link this socket to the close-list and let it be deleted after this iteration */ 135| 1.44M| s->next = s->context->loop->data.closed_head; 136| 1.44M| s->context->loop->data.closed_head = s; 137| | 138| | /* Any socket with prev = context is marked as closed */ 139| 1.44M| s->prev = (struct us_socket_t *) s->context; 140| | 141| 1.44M| return s->context->on_close(s, code, reason); 142| 1.44M| } 143| 0| return s; 144| 1.44M|} us_socket_write: 173| 633k|int us_socket_write(int ssl, struct us_socket_t *s, const char *data, int length, int msg_more) { 174| |#ifndef LIBUS_NO_SSL 175| | if (ssl) { 176| | return us_internal_ssl_socket_write((struct us_internal_ssl_socket_t *) s, data, length, msg_more); 177| | } 178| |#endif 179| | 180| 633k| if (us_socket_is_closed(ssl, s) || us_socket_is_shut_down(ssl, s)) { ------------------ | Branch (180:9): [True: 0, False: 633k] | Branch (180:40): [True: 0, False: 633k] ------------------ 181| 0| return 0; 182| 0| } 183| | 184| 633k| int written = bsd_send(us_poll_fd(&s->p), data, length, msg_more); 185| 633k| if (written != length) { ------------------ | Branch (185:9): [True: 611k, False: 22.0k] ------------------ 186| 611k| s->context->loop->data.last_write_failed = 1; 187| 611k| us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); ------------------ | | 27| 611k|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ us_poll_change(&s->p, s->context->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE); ------------------ | | 28| 611k|#define LIBUS_SOCKET_WRITABLE EPOLLOUT ------------------ 188| 611k| } 189| | 190| 633k| return written < 0 ? 0 : written; ------------------ | Branch (190:12): [True: 2.97k, False: 631k] ------------------ 191| 633k|} us_socket_ext: 193| 4.41M|void *us_socket_ext(int ssl, struct us_socket_t *s) { 194| |#ifndef LIBUS_NO_SSL 195| | if (ssl) { 196| | return us_internal_ssl_socket_ext((struct us_internal_ssl_socket_t *) s); 197| | } 198| |#endif 199| | 200| 4.41M| return s + 1; 201| 4.41M|} us_socket_is_shut_down: 203| 1.89M|int us_socket_is_shut_down(int ssl, struct us_socket_t *s) { 204| |#ifndef LIBUS_NO_SSL 205| | if (ssl) { 206| | return us_internal_ssl_socket_is_shut_down((struct us_internal_ssl_socket_t *) s); 207| | } 208| |#endif 209| | 210| 1.89M| return us_internal_poll_type(&s->p) == POLL_TYPE_SOCKET_SHUT_DOWN; 211| 1.89M|} us_socket_shutdown: 213| 564k|void us_socket_shutdown(int ssl, struct us_socket_t *s) { 214| |#ifndef LIBUS_NO_SSL 215| | if (ssl) { 216| | us_internal_ssl_socket_shutdown((struct us_internal_ssl_socket_t *) s); 217| | return; 218| | } 219| |#endif 220| | 221| | /* Todo: should we emit on_close if calling shutdown on an already half-closed socket? 222| | * We need more states in that case, we need to track RECEIVED_FIN 223| | * so far, the app has to track this and call close as needed */ 224| 564k| if (!us_socket_is_closed(ssl, s) && !us_socket_is_shut_down(ssl, s)) { ------------------ | Branch (224:9): [True: 564k, False: 0] | Branch (224:41): [True: 564k, False: 0] ------------------ 225| 564k| us_internal_poll_set_type(&s->p, POLL_TYPE_SOCKET_SHUT_DOWN); 226| 564k| us_poll_change(&s->p, s->context->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE); ------------------ | | 27| 564k|#define LIBUS_SOCKET_READABLE EPOLLIN ------------------ 227| 564k| bsd_shutdown_socket(us_poll_fd((struct us_poll_t *) s)); 228| 564k| } 229| 564k|} _Z4testv: 10| 2.25k|void test() { 11| | 12| 2.25k| { 13| | /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. 14| | * You may swap to using uWS:App() if you don't need SSL */ 15| 2.25k| auto app = uWS::App({ 16| | /* There are example certificates in uWebSockets.js repo */ 17| 2.25k| .key_file_name = "../misc/key.pem", 18| 2.25k| .cert_file_name = "../misc/cert.pem", 19| 2.25k| .passphrase = "1234" 20| 2.25k| }).get("/*", [](auto *res, auto *req) { 21| 2.25k| auto aborted = std::make_shared(); 22| 2.25k| *aborted = false; 23| 2.25k| res->onAborted([aborted]() { 24| 2.25k| *aborted = true; 25| 2.25k| }); 26| | 27| 2.25k| uWS::Loop::get()->defer([res, aborted]() { 28| 2.25k| if (!*aborted) { 29| 2.25k| res->cork([res, aborted]() { 30| | // Todo: also test upgrade to websocket here 31| 2.25k| res->end("Hello async!"); 32| 2.25k| }); 33| 2.25k| } 34| 2.25k| }); 35| 2.25k| }).listen(9001, [](auto *listenSocket) { 36| 2.25k| listen_socket = listenSocket; 37| 2.25k| }); 38| | 39| 2.25k| app.run(); 40| 2.25k| } 41| 2.25k| uWS::Loop::get()->free(); 42| 2.25k|} _Z8teardownv: 45| 2.24k|void teardown() { 46| | /* If we are called twice there's a bug (it potentially could if 47| | * all open sockets cannot be error-closed in one epoll_wait call). 48| | * But we only allow 1k FDs and we have a buffer of 1024 from epoll_wait */ 49| 2.24k| if (!listen_socket) { ------------------ | Branch (49:6): [True: 0, False: 2.24k] ------------------ 50| 0| exit(-1); 51| 0| } 52| | 53| | /* We might have open sockets still, and these will be error-closed by epoll_wait */ 54| | // us_socket_context_close - close all open sockets created with this socket context 55| 2.24k| if (listen_socket) { ------------------ | Branch (55:9): [True: 2.24k, False: 0] ------------------ 56| 2.24k| us_listen_socket_close(0, listen_socket); 57| 2.24k| listen_socket = NULL; 58| 2.24k| } 59| 2.24k|} AsyncEpollHelloWorld.cpp:_ZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS1_11HttpRequestEEEDaPT_PT0_: 20| 34.2k| }).get("/*", [](auto *res, auto *req) { 21| 34.2k| auto aborted = std::make_shared(); 22| 34.2k| *aborted = false; 23| 34.2k| res->onAborted([aborted]() { 24| 34.2k| *aborted = true; 25| 34.2k| }); 26| | 27| 34.2k| uWS::Loop::get()->defer([res, aborted]() { 28| 34.2k| if (!*aborted) { 29| 34.2k| res->cork([res, aborted]() { 30| | // Todo: also test upgrade to websocket here 31| 34.2k| res->end("Hello async!"); 32| 34.2k| }); 33| 34.2k| } 34| 34.2k| }); 35| 34.2k| }).listen(9001, [](auto *listenSocket) { AsyncEpollHelloWorld.cpp:_ZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS1_11HttpRequestEEEDaPT_PT0_ENKUlvE_clEv: 23| 16.8k| res->onAborted([aborted]() { 24| 16.8k| *aborted = true; 25| 16.8k| }); AsyncEpollHelloWorld.cpp:_ZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS1_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEv: 27| 33.5k| uWS::Loop::get()->defer([res, aborted]() { 28| 33.5k| if (!*aborted) { ------------------ | Branch (28:21): [True: 17.3k, False: 16.1k] ------------------ 29| 17.3k| res->cork([res, aborted]() { 30| | // Todo: also test upgrade to websocket here 31| 17.3k| res->end("Hello async!"); 32| 17.3k| }); 33| 17.3k| } 34| 33.5k| }); AsyncEpollHelloWorld.cpp:_ZZZZ4testvENK3$_0clIN3uWS12HttpResponseILb0EEENS1_11HttpRequestEEEDaPT_PT0_ENKUlvE0_clEvENKUlvE_clEv: 29| 17.3k| res->cork([res, aborted]() { 30| | // Todo: also test upgrade to websocket here 31| 17.3k| res->end("Hello async!"); 32| 17.3k| }); AsyncEpollHelloWorld.cpp:_ZZ4testvENK3$_1clI18us_listen_socket_tEEDaPT_: 35| 2.25k| }).listen(9001, [](auto *listenSocket) { 36| 2.25k| listen_socket = listenSocket; 37| 2.25k| }); set_consumable_data: 71| 2.25k|void set_consumable_data(const unsigned char *new_data, int new_length) { 72| 2.25k| consumable_data = (unsigned char *) new_data; 73| 2.25k| consumable_data_length = new_length; 74| 2.25k|} consume_byte: 77| 3.30M|int consume_byte(unsigned char *b) { 78| 3.30M| if (consumable_data_length) { ------------------ | Branch (78:6): [True: 3.30M, False: 1.35k] ------------------ 79| 3.30M| *b = consumable_data[0]; 80| 3.30M| consumable_data++; 81| 3.30M| consumable_data_length--; 82| 3.30M| return 0; 83| 3.30M| } 84| 1.35k| return -1; 85| 3.30M|} allocate_fd: 90| 1.45M|int allocate_fd() { 91| | // this can be massively optimized by having a list of free blocks or the like 92| 452M| for (int fd = 0; fd < MAX_FDS; fd++) { ------------------ | Branch (92:19): [True: 452M, False: 839] ------------------ 93| 452M| if (!fd_to_file[fd]) { ------------------ | Branch (93:7): [True: 1.45M, False: 451M] ------------------ 94| 1.45M| num_fds++; 95| 1.45M| return fd + RESERVED_SYSTEM_FDS; 96| 1.45M| } 97| 452M| } 98| 839| return -1; 99| 1.45M|} init_fd: 102| 1.45M|void init_fd(int fd, int type, struct file *f) { 103| 1.45M| if (fd >= RESERVED_SYSTEM_FDS) { ------------------ | Branch (103:6): [True: 1.45M, False: 0] ------------------ 104| 1.45M| fd_to_file[fd - RESERVED_SYSTEM_FDS] = f; 105| 1.45M| fd_to_file[fd - RESERVED_SYSTEM_FDS]->type = type; 106| 1.45M| fd_to_file[fd - RESERVED_SYSTEM_FDS]->next = NULL; 107| 1.45M| fd_to_file[fd - RESERVED_SYSTEM_FDS]->prev = NULL; 108| 1.45M| } 109| 1.45M|} map_fd: 111| 18.3M|struct file *map_fd(int fd) { 112| 18.3M| if (fd >= RESERVED_SYSTEM_FDS && fd < MAX_FDS + RESERVED_SYSTEM_FDS) { ------------------ | Branch (112:6): [True: 18.3M, False: 0] | Branch (112:35): [True: 18.3M, False: 0] ------------------ 113| 18.3M| return fd_to_file[fd - RESERVED_SYSTEM_FDS]; 114| 18.3M| } 115| 0| return NULL; 116| 18.3M|} free_fd: 119| 1.45M|int free_fd(int fd) { 120| 1.45M| if (fd >= RESERVED_SYSTEM_FDS && fd < MAX_FDS + RESERVED_SYSTEM_FDS) { ------------------ | Branch (120:6): [True: 1.45M, False: 0] | Branch (120:35): [True: 1.45M, False: 0] ------------------ 121| 1.45M| if (fd_to_file[fd - RESERVED_SYSTEM_FDS]) { ------------------ | Branch (121:7): [True: 1.45M, False: 0] ------------------ 122| 1.45M| fd_to_file[fd - RESERVED_SYSTEM_FDS] = 0; 123| 1.45M| num_fds--; 124| 1.45M| return 0; 125| 1.45M| } 126| 1.45M| } 127| | 128| 0| return -1; 129| 1.45M|} __wrap_epoll_create1: 141| 2.25k|int __wrap_epoll_create1(int flags) { 142| | 143| | /* Todo: check that we do not allocate more than one epoll FD */ 144| 2.25k| int fd = allocate_fd(); 145| | 146| 2.25k| if (fd != -1) { ------------------ | Branch (146:6): [True: 2.25k, False: 0] ------------------ 147| 2.25k| struct epoll_file *ef = (struct epoll_file *)malloc(sizeof(struct epoll_file)); 148| | 149| | /* Init the epoll_file */ 150| 2.25k| ef->poll_set_head = NULL; 151| 2.25k| ef->poll_set_tail = NULL; 152| | 153| 2.25k| init_fd(fd, FD_TYPE_EPOLL, (struct file *)ef); 154| 2.25k| } 155| | 156| |#ifdef PRINTF_DEBUG 157| | printf("epoll_create1 returning epfd: %d\n", fd); 158| |#endif 159| | 160| 2.25k| return fd; 161| 2.25k|} __wrap_epoll_ctl: 165| 4.04M|int __wrap_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) { 166| | 167| 4.04M| struct epoll_file *ef = (struct epoll_file *)map_fd(epfd); 168| 4.04M| if (!ef) { ------------------ | Branch (168:6): [True: 0, False: 4.04M] ------------------ 169| 0| return -1; 170| 0| } 171| | 172| 4.04M| struct file *f = (struct file *)map_fd(fd); 173| 4.04M| if (!f) { ------------------ | Branch (173:6): [True: 0, False: 4.04M] ------------------ 174| 0| return -1; 175| 0| } 176| | 177| | /* We add new polls in the head */ 178| 4.04M| if (op == EPOLL_CTL_ADD) { ------------------ | Branch (178:6): [True: 1.44M, False: 2.59M] ------------------ 179| | // if there is a head already 180| 1.44M| if (ef->poll_set_head) { ------------------ | Branch (180:7): [True: 1.44M, False: 2.25k] ------------------ 181| 1.44M| ef->poll_set_head->prev = f; 182| | 183| | // then it will be our next 184| 1.44M| f->next = ef->poll_set_head; 185| 1.44M| } else { 186| | // if there was no head then we became the tail also 187| 2.25k| ef->poll_set_tail = f; 188| 2.25k| } 189| | 190| | // we are now the head in any case 191| 1.44M| ef->poll_set_head = f; 192| | 193| 1.44M| f->epev = *event; 194| | 195| 2.59M| } else if (op == EPOLL_CTL_MOD) { ------------------ | Branch (195:13): [True: 1.14M, False: 1.44M] ------------------ 196| | /* Modifying is simply changing the file itself */ 197| 1.14M| f->epev = *event; 198| 1.44M| } else if (op == EPOLL_CTL_DEL) { ------------------ | Branch (198:13): [True: 1.44M, False: 0] ------------------ 199| | 200| 1.44M| if (f->prev) { ------------------ | Branch (200:7): [True: 430k, False: 1.01M] ------------------ 201| 430k| f->prev->next = f->next; 202| 1.01M| } else { 203| 1.01M| ef->poll_set_head = f->next; 204| 1.01M| } 205| | 206| 1.44M| if (f->next) { ------------------ | Branch (206:7): [True: 1.44M, False: 2.25k] ------------------ 207| 1.44M| f->next->prev = f->prev; 208| 1.44M| } else { 209| | // tail ska vara vår.prev 210| 2.25k| ef->poll_set_tail = f->prev; 211| 2.25k| } 212| | 213| | // a file that is not in the list should be reset to NULL 214| 1.44M| f->prev = NULL; 215| 1.44M| f->next = NULL; 216| 1.44M| } 217| | 218| | /* You have to poll for errors and hangups */ 219| 4.04M| f->epev.events |= EPOLLERR | EPOLLHUP; 220| | 221| 4.04M| return 0; 222| 4.04M|} __wrap_epoll_wait: 226| 2.41M| int maxevents, int timeout) { 227| | //printf("epoll_wait: %d\n", 0); 228| | 229| |#ifdef PRINTF_DEBUG 230| | printf("Calling epoll_wait\n"); 231| |#endif 232| | 233| 2.41M| struct epoll_file *ef = (struct epoll_file *)map_fd(epfd); 234| 2.41M| if (!ef) { ------------------ | Branch (234:6): [True: 0, False: 2.41M] ------------------ 235| 0| return -1; 236| 0| } 237| | 238| 2.41M| if (consumable_data_length) { ------------------ | Branch (238:6): [True: 2.41M, False: 2.24k] ------------------ 239| | 240| 2.41M| int ready_events = 0; 241| | 242| 14.6M| for (struct file *f = ef->poll_set_head; f; f = f->next) { ------------------ | Branch (242:44): [True: 12.2M, False: 2.41M] ------------------ 243| | 244| | 245| | /* Consume one fuzz byte, AND it with the event */ 246| 12.2M| if (!consumable_data_length) { ------------------ | Branch (246:8): [True: 301, False: 12.2M] ------------------ 247| | // break if we have no data 248| 301| break; 249| 301| } 250| | 251| | // here we have the main condition that drives everything 252| 12.2M| int ready_event = consumable_data[0] & f->epev.events; 253| | 254| | // consume the byte 255| 12.2M| consumable_data_length--; 256| 12.2M| consumable_data++; 257| | 258| 12.2M| if (ready_event) { ------------------ | Branch (258:8): [True: 8.99M, False: 3.27M] ------------------ 259| 8.99M| if (ready_events < maxevents) { ------------------ | Branch (259:9): [True: 8.99M, False: 0] ------------------ 260| 8.99M| events[ready_events] = f->epev; 261| | 262| | // todo: the event should be masked by the byte, not everything it wants shold be given all the time! 263| 8.99M| events[ready_events++].events = ready_event; 264| 8.99M| } else { 265| | // we are full, break 266| 0| break; 267| 0| } 268| 8.99M| } 269| | 270| 12.2M| } 271| | 272| 2.41M| return ready_events; 273| | 274| 2.41M| } else { 275| | 276| |#ifdef PRINTF_DEBUG 277| | printf("Calling teardown\n"); 278| |#endif 279| 2.24k| teardown(); 280| | 281| | // after shutting down the listen socket we clear the whole list (the bug in epoll_ctl remove) 282| | // so the below loop doesn't work - we never close anything more than the listen socket! 283| | 284| | /* You don't really need to emit teardown, you could simply emit error on every poll */ 285| | 286| 2.24k| int ready_events = 0; 287| | 288| |#ifdef PRINTF_DEBUG 289| | printf("Emitting error on every remaining FD\n"); 290| |#endif 291| 54.5k| for (struct file *f = ef->poll_set_head; f; f = f->next) { ------------------ | Branch (291:44): [True: 52.2k, False: 2.24k] ------------------ 292| | 293| 52.2k| if (f->type == FD_TYPE_SOCKET) { ------------------ | Branch (293:8): [True: 45.5k, False: 6.72k] ------------------ 294| | 295| 45.5k| if (ready_events < maxevents) { ------------------ | Branch (295:9): [True: 45.5k, False: 0] ------------------ 296| 45.5k| events[ready_events] = f->epev; 297| | 298| | // todo: the event should be masked by the byte, not everything it wants shold be given all the time! 299| 45.5k| events[ready_events++].events = EPOLLERR | EPOLLHUP; 300| 45.5k| } else { 301| | // we are full, break 302| 0| break; 303| 0| } 304| | 305| 45.5k| } 306| 52.2k| } 307| | 308| |#ifdef PRINTF_DEBUG 309| | printf("Ready events: %d\n", ready_events); 310| |#endif 311| | 312| 2.24k| return ready_events; 313| 2.24k| } 314| 2.41M|} __wrap_read: 332| 6.34M|int __wrap_read(int fd, void *buf, size_t count) { 333| | 334| 6.34M| if (fd < RESERVED_SYSTEM_FDS) { ------------------ | Branch (334:6): [True: 0, False: 6.34M] ------------------ 335| 0| return __real_read(fd, buf, count); 336| 0| } 337| | 338| |#ifdef PRINTF_DEBUG 339| | printf("Wrapped read\n"); 340| |#endif 341| | 342| | /* Let's try and clear the buffer first */ 343| | //memset(buf, 0, count); 344| | 345| 6.34M| struct file *f = map_fd(fd); 346| 6.34M| if (!f) { ------------------ | Branch (346:6): [True: 0, False: 6.34M] ------------------ 347| 0| return -1; 348| 0| } 349| | 350| 6.34M| errno = 0; 351| | 352| 6.34M| if (f->type == FD_TYPE_SOCKET) { ------------------ | Branch (352:6): [True: 648k, False: 5.69M] ------------------ 353| | 354| 648k| if (!consumable_data_length) { ------------------ | Branch (354:7): [True: 3.54k, False: 644k] ------------------ 355| 3.54k| errno = EWOULDBLOCK; 356| 3.54k| return -1; 357| 644k| } else { 358| 644k| int data_available = (unsigned char) consumable_data[0]; 359| 644k| consumable_data_length--; 360| 644k| consumable_data++; 361| | 362| 644k| if (consumable_data_length < data_available) { ------------------ | Branch (362:8): [True: 1.46k, False: 643k] ------------------ 363| 1.46k| data_available = consumable_data_length; 364| 1.46k| } 365| | 366| 644k| if (count < data_available) { ------------------ | Branch (366:8): [True: 0, False: 644k] ------------------ 367| 0| data_available = count; 368| 0| } 369| | 370| 644k| memcpy(buf, consumable_data, data_available); 371| | 372| 644k| consumable_data_length -= data_available; 373| 644k| consumable_data += data_available; 374| | 375| 644k| return data_available; 376| 644k| } 377| 648k| } 378| | 379| 5.69M| if (f->type == FD_TYPE_EVENT) { ------------------ | Branch (379:6): [True: 1.88M, False: 3.81M] ------------------ 380| 1.88M| memset(buf, 1, 8); 381| 1.88M| return 8; 382| 1.88M| } 383| | 384| 3.81M| if (f->type == FD_TYPE_TIMER) { ------------------ | Branch (384:6): [True: 3.81M, False: 0] ------------------ 385| 3.81M| memset(buf, 1, 8); 386| 3.81M| return 8; 387| 3.81M| } 388| | 389| 0| return -1; 390| 3.81M|} __wrap_recv: 393| 648k|int __wrap_recv(int sockfd, void *buf, size_t len, int flags) { 394| 648k| return __wrap_read(sockfd, buf, len); 395| 648k|} __wrap_send: 397| 633k|int __wrap_send(int sockfd, const void *buf, size_t len, int flags) { 398| | 399| 633k| if (consumable_data_length) { ------------------ | Branch (399:6): [True: 631k, False: 2.97k] ------------------ 400| | /* We can send len scaled by the 1 byte */ 401| 631k| unsigned char scale = consumable_data[0]; 402| 631k| consumable_data++; 403| 631k| consumable_data_length--; 404| | 405| 631k| int written = float(scale) / 255.0f * len; 406| | 407| 631k| if (written == 0) { ------------------ | Branch (407:7): [True: 485k, False: 145k] ------------------ 408| 485k| errno = EWOULDBLOCK; 409| 485k| } else { 410| 145k| errno = 0; 411| 145k| } 412| | 413| 631k| return written; 414| 631k| } else { 415| 2.97k| return -1; 416| 2.97k| } 417| 633k|} __wrap_bind: 424| 2.25k|int __wrap_bind() { 425| 2.25k| return 0; 426| 2.25k|} __wrap_setsockopt: 428| 1.44M|int __wrap_setsockopt() { 429| 1.44M| return 0; 430| 1.44M|} __wrap_fcntl: 433| 2.88M|int __wrap_fcntl(int fd, int cmd, ... /* arg */) { 434| 2.88M| if (fd < RESERVED_SYSTEM_FDS) { ------------------ | Branch (434:6): [True: 2, False: 2.88M] ------------------ 435| 2| va_list args; 436| 2| va_start(args, cmd); 437| 2| int ret = __real_fcntl(fd, cmd, args); 438| 2| va_end(args); 439| 2| return ret; 440| 2| } 441| | 442| 2.88M| return 0; 443| 2.88M|} __wrap_getaddrinfo: 448| 2.25k| struct addrinfo **res) { 449| | //printf("Wrapped getaddrinfo\n"); 450| | 451| 2.25k| struct addrinfo default_hints = {}; 452| | 453| 2.25k| if (!hints) { ------------------ | Branch (453:6): [True: 0, False: 2.25k] ------------------ 454| 0| hints = &default_hints; 455| 0| } 456| | 457| 2.25k| unsigned char b; 458| 2.25k| if (consume_byte(&b)) { ------------------ | Branch (458:6): [True: 0, False: 2.25k] ------------------ 459| 0| return -1; 460| 0| } 461| | 462| | /* This one should be thread_local */ 463| 2.25k| static /*thread_local*/ struct addrinfo ai; 464| 2.25k| ai.ai_flags = hints->ai_flags; 465| 2.25k| ai.ai_socktype = hints->ai_socktype; 466| 2.25k| ai.ai_protocol = hints->ai_protocol; 467| | 468| 2.25k| if (b > 127) { ------------------ | Branch (468:6): [True: 1.11k, False: 1.14k] ------------------ 469| 1.11k| ai.ai_family = AF_INET;//hints->ai_family; 470| 1.14k| } else { 471| 1.14k| ai.ai_family = AF_INET6;//hints->ai_family; 472| 1.14k| } 473| | 474| | /* This one is for generating the wrong family (maybe invalid?) */ 475| 2.25k| if (b == 0) { ------------------ | Branch (475:6): [True: 1, False: 2.25k] ------------------ 476| 1| ai.ai_family = hints->ai_family; 477| 1| } 478| | 479| 2.25k| ai.ai_next = NULL; 480| 2.25k| ai.ai_canonname = NULL; // fel 481| | 482| | // these should depend on inet6 or inet */ 483| 2.25k| ai.ai_addrlen = 4; // fel 484| 2.25k| ai.ai_addr = NULL; // ska peka på en sockaddr! 485| | 486| | // we need to return an addrinfo with family AF_INET6 487| | 488| 2.25k| *res = &ai; 489| 2.25k| return 0; 490| 2.25k|} __wrap_freeaddrinfo: 492| 2.25k|int __wrap_freeaddrinfo() { 493| 2.25k| return 0; 494| 2.25k|} __wrap_accept4: 521| 3.29M|int __wrap_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { 522| | /* We must end with -1 since we are called in a loop */ 523| | 524| 3.29M| unsigned char b; 525| 3.29M| if (consume_byte(&b)) { ------------------ | Branch (525:6): [True: 1.34k, False: 3.29M] ------------------ 526| 1.34k| return -1; 527| 1.34k| } 528| | 529| | /* This rule might change, anything below 10 is accepted */ 530| 3.29M| if (b < 10) { ------------------ | Branch (530:6): [True: 1.44M, False: 1.85M] ------------------ 531| | 532| 1.44M| int fd = allocate_fd(); 533| 1.44M| if (fd != -1) { ------------------ | Branch (533:7): [True: 1.44M, False: 839] ------------------ 534| | 535| | /* Allocate the file */ 536| 1.44M| struct socket_file *sf = (struct socket_file *) malloc(sizeof(struct socket_file)); 537| | 538| | /* Init the file */ 539| | 540| | /* Here we need to create a socket FD and return */ 541| 1.44M| init_fd(fd, FD_TYPE_SOCKET, (struct file *)sf); 542| | 543| | /* We need to provide an addr */ 544| | 545| | /* Begin by setting it to an empty in6 address */ 546| 1.44M| memset(&sf->addr, 0, sizeof(struct sockaddr_in6)); 547| 1.44M| sf->len = sizeof(struct sockaddr_in6); 548| 1.44M| sf->addr.in6.sin6_family = AF_INET6; 549| | 550| | /* Opt-in to ipv4 */ 551| 1.44M| if (b < 5) { ------------------ | Branch (551:8): [True: 979k, False: 461k] ------------------ 552| 979k| memset(&sf->addr, 0, sizeof(struct sockaddr_in6)); 553| 979k| sf->len = sizeof(struct sockaddr_in); 554| 979k| sf->addr.in.sin_family = AF_INET; 555| 979k| } 556| | 557| 1.44M| if (addr) { ------------------ | Branch (557:8): [True: 1.44M, False: 0] ------------------ 558| | /* Copy from socket to addr */ 559| 1.44M| memcpy(addr, &sf->addr, sf->len); 560| 1.44M| } 561| 1.44M| } 562| | 563| 1.44M| return fd; 564| 1.44M| } 565| | 566| 1.85M| return -1; 567| 3.29M|} __wrap_listen: 569| 2.25k|int __wrap_listen() { 570| | /* Listen consumes one byte and fails on -1 */ 571| 2.25k| unsigned char b; 572| 2.25k| if (consume_byte(&b)) { ------------------ | Branch (572:6): [True: 10, False: 2.24k] ------------------ 573| 10| return -1; 574| 10| } 575| | 576| 2.24k| if (b) { ------------------ | Branch (576:6): [True: 2.24k, False: 1] ------------------ 577| 2.24k| return 0; 578| 2.24k| } 579| | 580| 1| return -1; 581| 2.24k|} __wrap_socket: 584| 2.25k|int __wrap_socket(int domain, int type, int protocol) { 585| | 586| | /* Only accept valid families */ 587| 2.25k| if (domain != AF_INET && domain != AF_INET6) { ------------------ | Branch (587:6): [True: 1.14k, False: 1.11k] | Branch (587:27): [True: 0, False: 1.14k] ------------------ 588| 0| return -1; 589| 0| } 590| | 591| 2.25k| int fd = allocate_fd(); 592| | 593| 2.25k| if (fd != -1) { ------------------ | Branch (593:6): [True: 2.25k, False: 0] ------------------ 594| 2.25k| struct socket_file *sf = (struct socket_file *)malloc(sizeof(struct socket_file)); 595| | 596| | /* Init the file */ 597| | 598| 2.25k| init_fd(fd, FD_TYPE_SOCKET, (struct file *)sf); 599| 2.25k| } 600| | 601| |#ifdef PRINTF_DEBUG 602| | printf("socket returning fd: %d\n", fd); 603| |#endif 604| | 605| 2.25k| return fd; 606| 2.25k|} __wrap_shutdown: 608| 564k|int __wrap_shutdown() { 609| | //printf("Wrapped shutdown\n"); 610| 564k| return 0; 611| 564k|} __wrap_timerfd_create: 619| 4.50k|int __wrap_timerfd_create(int clockid, int flags) { 620| | 621| 4.50k| int fd = allocate_fd(); 622| | 623| 4.50k| if (fd != -1) { ------------------ | Branch (623:6): [True: 4.50k, False: 0] ------------------ 624| 4.50k| struct timer_file *tf = (struct timer_file *)malloc(sizeof(struct timer_file)); 625| | 626| | /* Init the file */ 627| | 628| | 629| 4.50k| init_fd(fd, FD_TYPE_TIMER, (struct file *)tf); 630| | 631| 4.50k| } 632| | 633| |#ifdef PRINTF_DEBUG 634| | printf("timerfd_create returning fd: %d\n", fd); 635| |#endif 636| | 637| 4.50k| return fd; 638| 4.50k|} __wrap_timerfd_settime: 642| 4.50k| struct itimerspec *old_value) { 643| | //printf("timerfd_settime: %d\n", fd); 644| 4.50k| return 0; 645| 4.50k|} __wrap_eventfd: 653| 2.25k|int __wrap_eventfd() { 654| | 655| 2.25k| int fd = allocate_fd(); 656| | 657| 2.25k| if (fd != -1) { ------------------ | Branch (657:6): [True: 2.25k, False: 0] ------------------ 658| 2.25k| struct event_file *ef = (struct event_file *)malloc(sizeof(struct event_file)); 659| | 660| | /* Init the file */ 661| | 662| 2.25k| init_fd(fd, FD_TYPE_EVENT, (struct file *)ef); 663| | 664| | //printf("eventfd: %d\n", fd); 665| 2.25k| } 666| | 667| |#ifdef PRINTF_DEBUG 668| | printf("eventfd returning fd: %d\n", fd); 669| |#endif 670| | 671| 2.25k| return fd; 672| 2.25k|} __wrap_close: 678| 1.45M|int __wrap_close(int fd) { 679| | 680| 1.45M| if (fd < RESERVED_SYSTEM_FDS) { ------------------ | Branch (680:6): [True: 0, False: 1.45M] ------------------ 681| 0| return __real_close(fd); 682| 0| } 683| | 684| 1.45M| struct file *f = map_fd(fd); 685| | 686| 1.45M| if (!f) { ------------------ | Branch (686:6): [True: 0, False: 1.45M] ------------------ 687| 0| return -1; 688| 0| } 689| | 690| 1.45M| if (f->type == FD_TYPE_EPOLL) { ------------------ | Branch (690:6): [True: 2.25k, False: 1.44M] ------------------ 691| |#ifdef PRINTF_DEBUG 692| | printf("Closing epoll FD: %d\n", fd); 693| |#endif 694| | 695| 2.25k| free(f); 696| | 697| 2.25k| return free_fd(fd); 698| | 699| 1.44M| } else if (f->type == FD_TYPE_TIMER) { ------------------ | Branch (699:13): [True: 4.50k, False: 1.44M] ------------------ 700| |#ifdef PRINTF_DEBUG 701| | printf("Closing timer fd: %d\n", fd); 702| |#endif 703| | 704| 4.50k| free(f); 705| | 706| 4.50k| return free_fd(fd); 707| 1.44M| } else if (f->type == FD_TYPE_EVENT) { ------------------ | Branch (707:13): [True: 2.25k, False: 1.44M] ------------------ 708| |#ifdef PRINTF_DEBUG 709| | printf("Closing event fd: %d\n", fd); 710| |#endif 711| | 712| 2.25k| free(f); 713| | 714| 2.25k| return free_fd(fd); 715| 1.44M| } else if (f->type == FD_TYPE_SOCKET) { ------------------ | Branch (715:13): [True: 1.44M, False: 0] ------------------ 716| |#ifdef PRINTF_DEBUG 717| | printf("Closing socket fd: %d\n", fd); 718| |#endif 719| | 720| | // we should call epoll_ctl remove here 721| | 722| 1.44M| free(f); 723| | 724| 1.44M| int ret = free_fd(fd); 725| | 726| |#ifdef PRINTF_DEBUG 727| | printf("Ret: %d\n", ret); 728| |#endif 729| | 730| | //free(-1); 731| 1.44M| return ret; 732| 1.44M| } 733| | 734| 0| return -1; 735| 1.45M|} LLVMFuzzerTestOneInput: 737| 2.25k|int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 738| 2.25k| set_consumable_data(data, size); 739| | 740| 2.25k| test(); 741| | 742| 2.25k| if (num_fds) { ------------------ | Branch (742:6): [True: 0, False: 2.25k] ------------------ 743| 0| printf("ERROR! Cannot leave open FDs after test!\n"); 744| 0| } 745| | 746| 2.25k| return 0; 747| 2.25k|}