From 7ed3145ccf27b35a9b345f84d3b008dc4409eddc Mon Sep 17 00:00:00 2001 From: Chris Wall Date: Fri, 11 Sep 2026 14:54:20 -0400 Subject: [PATCH] uWS Update - 20.80 --- .../Public/FicsitRemoteMonitoring.h | 13 + Source/ThirdParty/uWebSockets/App.h | 170 +- Source/ThirdParty/uWebSockets/AsyncSocket.h | 27 +- .../ThirdParty/uWebSockets/AsyncSocketData.h | 17 +- Source/ThirdParty/uWebSockets/BloomFilter.h | 3 +- Source/ThirdParty/uWebSockets/CachingApp.h | 115 + .../ThirdParty/uWebSockets/ChunkedEncoding.h | 311 ++- Source/ThirdParty/uWebSockets/HttpContext.h | 41 +- .../ThirdParty/uWebSockets/HttpContextData.h | 4 + Source/ThirdParty/uWebSockets/HttpErrors.h | 4 +- Source/ThirdParty/uWebSockets/HttpParser.h | 145 +- Source/ThirdParty/uWebSockets/HttpResponse.h | 107 +- .../ThirdParty/uWebSockets/HttpResponseData.h | 56 +- Source/ThirdParty/uWebSockets/HttpRouter.h | 24 +- Source/ThirdParty/uWebSockets/LocalCluster.h | 8 +- Source/ThirdParty/uWebSockets/Loop.h | 34 + Source/ThirdParty/uWebSockets/LoopData.h | 7 +- .../ThirdParty/uWebSockets/MoveOnlyFunction.h | 12 + .../uWebSockets/PerMessageDeflate.h | 46 +- Source/ThirdParty/uWebSockets/ProxyParser.h | 38 +- Source/ThirdParty/uWebSockets/TopicTree.h | 5 +- Source/ThirdParty/uWebSockets/WebSocket.h | 42 +- .../ThirdParty/uWebSockets/WebSocketContext.h | 8 +- .../uWebSockets/WebSocketExtensions.h | 8 +- .../uWebSockets/WebSocketProtocol.h | 65 +- Source/ThirdParty/uWebSockets/quic.h | 68 - Source/ThirdParty/uWebSockets/uv.h | 1912 ----------------- Source/ThirdParty/uWebSockets/zconf.h | 64 +- Source/ThirdParty/uWebSockets/zlib.h | 309 ++- 29 files changed, 1214 insertions(+), 2449 deletions(-) create mode 100644 Source/ThirdParty/uWebSockets/CachingApp.h delete mode 100644 Source/ThirdParty/uWebSockets/quic.h delete mode 100644 Source/ThirdParty/uWebSockets/uv.h diff --git a/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h b/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h index dc86ff60..380fcf64 100644 --- a/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h +++ b/Source/FicsitRemoteMonitoring/Public/FicsitRemoteMonitoring.h @@ -12,7 +12,20 @@ #include "RemoteMonitoringLibrary.h" THIRD_PARTY_INCLUDES_START + +// uWebSockets/Unreal warning-policy collision +#if PLATFORM_WINDOWS + #pragma warning(push) + #pragma warning(disable : 4706) +#endif + #include "ThirdParty/uWebSockets/App.h" + +// uWebSockets/Unreal warning-policy collision +#if PLATFORM_WINDOWS + #pragma warning(pop) +#endif + THIRD_PARTY_INCLUDES_END #include "FicsitRemoteMonitoring.generated.h" diff --git a/Source/ThirdParty/uWebSockets/App.h b/Source/ThirdParty/uWebSockets/App.h index f0e2a4dc..4192f084 100644 --- a/Source/ThirdParty/uWebSockets/App.h +++ b/Source/ThirdParty/uWebSockets/App.h @@ -18,6 +18,8 @@ #ifndef UWS_APP_H #define UWS_APP_H +#define _CRT_SECURE_NO_WARNINGS + #include #include #include @@ -102,7 +104,7 @@ struct TemplatedApp { us_socket_context_add_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str(), options, domainRouter); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&removeServerName(std::string hostname_pattern) { @@ -114,7 +116,7 @@ struct TemplatedApp { } us_socket_context_remove_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str()); - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&missingServerName(MoveOnlyFunction handler) { @@ -130,7 +132,7 @@ struct TemplatedApp { }); } - return std::move(*this); + return std::move(static_cast(*this)); } /* Returns the SSL_CTX of this app, or nullptr. */ @@ -142,13 +144,34 @@ struct TemplatedApp { TemplatedApp &&filter(MoveOnlyFunction *, int)> &&filterHandler) { httpContext->filter(std::move(filterHandler)); - return std::move(*this); + return std::move(static_cast(*this)); + } + + /* Same as publish, but takes a prepared message */ + bool publishPrepared(std::string_view topic, PreparedMessage &preparedMessage) { + if (!topicTree) { + return false; + } + + /* It is assumed by heuristics that a prepared message ought to be big, + * and so there is no fast path for small messages (yet?) as preparing a small message is unlikely */ + + return reinterpret_cast *>(topicTree)->publishBig(nullptr, topic, &preparedMessage, [](Subscriber *s, PreparedMessage *preparedMessage) { + auto *ws = (WebSocket *) s->user; + + /* Send will drain if needed */ + ws->sendPrepared(*preparedMessage); + }); } /* Publishes a message to all websocket contexts - conceptually as if publishing to the one single * TopicTree of this app (technically there are many TopicTrees, however the concept is that one * app has one conceptual Topic tree) */ bool publish(std::string_view topic, std::string_view message, OpCode opCode, bool compress = false) { + if (!topicTree) { + return false; + } + /* Anything big bypasses corking efforts */ if (message.length() >= LoopData::CORK_BUFFER_SIZE) { return topicTree->publishBig(nullptr, topic, {message, opCode, compress}, [](Subscriber *s, TopicTreeBigMessage &message) { @@ -166,6 +189,10 @@ struct TemplatedApp { * This function should probably be optimized a lot in future releases, * it could be O(1) with a hash map of fullnames and their counts. */ unsigned int numSubscribers(std::string_view topic) { + if (!topicTree) { + return 0; + } + Topic *t = topicTree->lookupTopic(topic); if (t) { return (unsigned int) t->size(); @@ -187,12 +214,12 @@ struct TemplatedApp { /* Delete TopicTree */ if (topicTree) { - delete topicTree; - /* And unregister loop callbacks */ /* We must unregister any loop post handler here */ Loop::get()->removePostHandler(topicTree); Loop::get()->removePreHandler(topicTree); + + delete topicTree; } } @@ -219,11 +246,20 @@ struct TemplatedApp { /* Register default handler for 404 (can be overridden by user) */ this->any("/*", [](auto *res, auto */*req*/) { - res->writeStatus("404 File Not Found"); - res->end("

File Not Found


uWebSockets/20 Server"); + res->writeStatus("404 File Not Found"); + res->end("

File Not Found


uWebSockets/20 Server"); }); } + TemplatedApp& operator=(const TemplatedApp&) = delete; + + TemplatedApp& operator=(TemplatedApp&& other) { + std::swap(this->httpContext, other.httpContext); + std::swap(this->topicTree, other.topicTree); + std::swap(this->webSocketContextDeleters, other.webSocketContextDeleters); + std::swap(this->webSocketContexts, other.webSocketContexts); + } + bool constructorFailed() { return !httpContext; } @@ -263,7 +299,7 @@ struct TemplatedApp { us_socket_context_close(SSL, (struct us_socket_context_t *) webSocketContext); } - return std::move(*this); + return std::move(static_cast(*this)); } template @@ -273,7 +309,7 @@ struct TemplatedApp { "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly."); if (!httpContext) { - return std::move(*this); + return std::move(static_cast(*this)); } /* Terminate on misleading idleTimeout values */ @@ -382,14 +418,7 @@ struct TemplatedApp { webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped); webSocketContext->getExt()->drainHandler = std::move(behavior.drain); webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription); - webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket *ws, int code, std::string_view message) mutable { - if (closeHandler) { - closeHandler(ws, code, message); - } - - /* Destruct user data after returning from close handler */ - ((UserData *) ws->getUserData())->~UserData(); - }); + webSocketContext->getExt()->closeHandler = std::move(behavior.close); webSocketContext->getExt()->pingHandler = std::move(behavior.ping); webSocketContext->getExt()->pongHandler = std::move(behavior.pong); @@ -443,7 +472,7 @@ struct TemplatedApp { req->setYield(true); } }, true); - return std::move(*this); + return std::move(static_cast(*this)); } /* Browse to a server name, changing the router to this domain */ @@ -459,70 +488,70 @@ struct TemplatedApp { httpContextData->currentRouter = &httpContextData->router; } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&get(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("GET", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&post(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("POST", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&options(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("OPTIONS", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&del(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("DELETE", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&patch(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("PATCH", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&put(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("PUT", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&head(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("HEAD", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&connect(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("CONNECT", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } TemplatedApp &&trace(std::string pattern, MoveOnlyFunction *, HttpRequest *)> &&handler) { if (httpContext) { httpContext->onHttp("TRACE", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } /* This one catches any method */ @@ -530,7 +559,7 @@ struct TemplatedApp { if (httpContext) { httpContext->onHttp("*", pattern, std::move(handler)); } - return std::move(*this); + return std::move(static_cast(*this)); } /* Host, port, callback */ @@ -539,7 +568,7 @@ struct TemplatedApp { return listen(port, std::move(handler)); } handler(httpContext ? httpContext->listen(host.c_str(), port, 0) : nullptr); - return std::move(*this); + return std::move(static_cast(*this)); } /* Host, port, options, callback */ @@ -548,48 +577,99 @@ struct TemplatedApp { return listen(port, options, std::move(handler)); } handler(httpContext ? httpContext->listen(host.c_str(), port, options) : nullptr); - return std::move(*this); + return std::move(static_cast(*this)); } /* Port, callback */ TemplatedApp &&listen(int port, MoveOnlyFunction &&handler) { handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr); - return std::move(*this); + return std::move(static_cast(*this)); } /* Port, options, callback */ TemplatedApp &&listen(int port, int options, MoveOnlyFunction &&handler) { handler(httpContext ? httpContext->listen(nullptr, port, options) : nullptr); - return std::move(*this); + return std::move(static_cast(*this)); } /* options, callback, path to unix domain socket */ TemplatedApp &&listen(int options, MoveOnlyFunction &&handler, std::string path) { handler(httpContext ? httpContext->listen(path.c_str(), options) : nullptr); - return std::move(*this); + return std::move(static_cast(*this)); } /* callback, path to unix domain socket */ TemplatedApp &&listen(MoveOnlyFunction &&handler, std::string path) { handler(httpContext ? httpContext->listen(path.c_str(), 0) : nullptr); - return std::move(*this); + return std::move(static_cast(*this)); } /* Register event handler for accepted FD. Can be used together with adoptSocket. */ - TemplatedApp &&preOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) { + TemplatedApp &&preOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(struct us_socket_context_t *, LIBUS_SOCKET_DESCRIPTOR, char *, int)) { httpContext->onPreOpen(handler); - return std::move(*this); + return std::move(static_cast(*this)); + } + + TemplatedApp &&removeChildApp(TemplatedApp *app) { + /* Remove this app from httpContextData list over child apps and reset round robin */ + auto &childApps = httpContext->getSocketContextData()->childApps; + childApps.erase( + std::remove(childApps.begin(), childApps.end(), (void *) app), + childApps.end() + ); + httpContext->getSocketContextData()->roundRobin = 0; + + return std::move(static_cast(*this)); + } + + TemplatedApp &&addChildApp(TemplatedApp *app) { + /* Add this app to httpContextData list over child apps and set onPreOpen */ + httpContext->getSocketContextData()->childApps.push_back((void *) app); + + httpContext->onPreOpen([](struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd, char *ip, int ip_length) -> LIBUS_SOCKET_DESCRIPTOR { + + HttpContext *httpContext = (HttpContext *) context; + + if (httpContext->getSocketContextData()->childApps.empty()) { + return fd; + } + + //std::cout << "Distributing fd: " << fd << " from context: " << context << std::endl; + + unsigned int *roundRobin = &httpContext->getSocketContextData()->roundRobin; + + //std::cout << "Round robin is: " << *roundRobin << " and size of apps is: " << httpContext->getSocketContextData()->childApps.size() << std::endl; + + TemplatedApp *receivingApp = (TemplatedApp *) httpContext->getSocketContextData()->childApps[*roundRobin]; + + + //std::cout << "Loop is " << receivingApp->getLoop() << std::endl; + + + receivingApp->getLoop()->defer([fd, ipStore = std::string(ip, ip + ip_length), receivingApp]() { + //std::cout << "About to adopt socket " << fd << " on receivingApp " << receivingApp << std::endl; + receivingApp->adoptSocket(fd, std::string_view(ipStore)); + //std::cout << "Done " << std::endl; + }); + + if (++(*roundRobin) == httpContext->getSocketContextData()->childApps.size()) { + *roundRobin = 0; + } + + return fd + 1; + }); + return std::move(static_cast(*this)); } /* adopt an externally accepted socket */ - TemplatedApp &&adoptSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) { - httpContext->adoptAcceptedSocket(accepted_fd); - return std::move(*this); + TemplatedApp &&adoptSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd, std::string_view ip = std::string_view()) { + httpContext->adoptAcceptedSocket(accepted_fd, (char *) ip.data(), (int) ip.length()); + return std::move(static_cast(*this)); } TemplatedApp &&run() { uWS::run(); - return std::move(*this); + return std::move(static_cast(*this)); } Loop *getLoop() { @@ -598,9 +678,11 @@ struct TemplatedApp { }; -typedef TemplatedApp App; -typedef TemplatedApp SSLApp; +} +namespace uWS { + typedef uWS::TemplatedApp App; + typedef uWS::TemplatedApp SSLApp; } #endif // UWS_APP_H diff --git a/Source/ThirdParty/uWebSockets/AsyncSocket.h b/Source/ThirdParty/uWebSockets/AsyncSocket.h index 2406d640..4231f26f 100644 --- a/Source/ThirdParty/uWebSockets/AsyncSocket.h +++ b/Source/ThirdParty/uWebSockets/AsyncSocket.h @@ -141,7 +141,7 @@ struct AsyncSocket { getLoopData()->corkedSocket = this; } - /* Returns wheter we are corked or not */ + /* Returns whether we are corked or not */ bool isCorked() { return getLoopData()->corkedSocket == this; } @@ -219,10 +219,15 @@ struct AsyncSocket { /* Returns the remote IP address or empty string on failure */ std::string_view getRemoteAddress() { +#ifdef UWS_REMOTE_ADDRESS_USERSPACE + AsyncSocketData *data = getAsyncSocketData(); + return std::string_view(data->remoteAddress, (unsigned int) data->remoteAddressLength); +#else static thread_local char buf[16]; int ipLength = 16; us_socket_remote_address(SSL, (us_socket_t *) this, buf, &ipLength); return std::string_view(buf, (unsigned int) ipLength); +#endif } /* Returns the text representation of IP */ @@ -230,8 +235,14 @@ struct AsyncSocket { return addressAsText(getRemoteAddress()); } + /* Returns the remote port number or -1 on failure */ + unsigned int getRemotePort() { + int port = us_socket_remote_port(SSL, (us_socket_t *) this); + return (unsigned int) port; + } + /* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible. - * Returns pair of bytes written (anywhere) and wheter or not this call resulted in the polling for + * Returns pair of bytes written (anywhere) and whether or not this call resulted in the polling for * writable (or we are in a state that implies polling for writable). */ std::pair write(const char *src, int length, bool optionally = false, int nextLength = 0) { /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ @@ -336,7 +347,17 @@ struct AsyncSocket { loopData->corkOffset = 0; if (failed) { - /* We do not need to care for buffering here, write does that */ + /* If corked data fails to flush, and we have more data to write, immediately buffer it here + * since the above call to write excludes src */ + if (!optionally && src && length) { + AsyncSocketData *asyncSocketData = getAsyncSocketData(); + asyncSocketData->buffer.append(src, (size_t) length); + + /* We wrote to per socket buffer, so report success */ + return {length, true}; + } + + /* We do not need to care for buffering (of the corked data) here, write did that */ return {0, true}; } } diff --git a/Source/ThirdParty/uWebSockets/AsyncSocketData.h b/Source/ThirdParty/uWebSockets/AsyncSocketData.h index b72b2c10..1259ea68 100644 --- a/Source/ThirdParty/uWebSockets/AsyncSocketData.h +++ b/Source/ThirdParty/uWebSockets/AsyncSocketData.h @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2021. + * Authored by Alex Hultman, 2018-2025. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,29 +37,30 @@ struct BackPressure { pendingRemoval += length; /* Always erase a minimum of 1/32th the current backpressure */ if (pendingRemoval > (buffer.length() >> 5)) { - buffer.erase(0, pendingRemoval); + std::string(buffer.begin() + pendingRemoval, buffer.end()).swap(buffer); pendingRemoval = 0; } } size_t length() { return buffer.length() - pendingRemoval; } + /* Only used in AsyncSocket::write - what about replacing it with the other functions like erase(length())? */ void clear() { pendingRemoval = 0; buffer.clear(); + buffer.shrink_to_fit(); } + /* Only used by AsyncSocket::write (optionally) before append */ void reserve(size_t length) { buffer.reserve(length + pendingRemoval); } + /* Only used by getSendBuffer as last resort */ void resize(size_t length) { buffer.resize(length + pendingRemoval); } const char *data() { return buffer.data() + pendingRemoval; } - size_t size() { - return length(); - } /* The total length, incuding pending removal */ size_t totalLength() { return buffer.length(); @@ -73,6 +74,12 @@ struct AsyncSocketData { /* This will do for now */ BackPressure buffer; +#ifdef UWS_REMOTE_ADDRESS_USERSPACE + /* Cache for remote address, populated on socket open */ + char remoteAddress[16]; + int remoteAddressLength = 0; +#endif + /* Allow move constructing us */ AsyncSocketData(BackPressure &&backpressure) : buffer(std::move(backpressure)) { diff --git a/Source/ThirdParty/uWebSockets/BloomFilter.h b/Source/ThirdParty/uWebSockets/BloomFilter.h index 5d2398eb..12d29e61 100644 --- a/Source/ThirdParty/uWebSockets/BloomFilter.h +++ b/Source/ThirdParty/uWebSockets/BloomFilter.h @@ -21,6 +21,7 @@ /* This filter has no false positives or collisions for the standard * and non-standard common request headers */ +#include #include #include @@ -30,7 +31,7 @@ struct BloomFilter { private: std::bitset<256> filter; static inline uint32_t perfectHash(uint32_t features) { - return features *= 1843993368; + return features * 1843993368; } union ScrambleArea { diff --git a/Source/ThirdParty/uWebSockets/CachingApp.h b/Source/ThirdParty/uWebSockets/CachingApp.h new file mode 100644 index 00000000..3aad3a75 --- /dev/null +++ b/Source/ThirdParty/uWebSockets/CachingApp.h @@ -0,0 +1,115 @@ +#ifndef UWS_CACHINGAPP_H +#define UWS_CACHINGAPP_H + +#include "App.h" +#include +#include +#include +#include + +namespace uWS { + +struct StringViewHash { + size_t operator()(std::string_view sv) const { + return std::hash{}(sv); + } +}; + +struct StringViewEqual { + bool operator()(std::string_view sv1, std::string_view sv2) const { + return sv1 == sv2; + } +}; + + + +class CachingHttpResponse { +public: + CachingHttpResponse(uWS::HttpResponse *res) + : res(res) {} + + void write(std::string_view data) { + buffer.append(data); + } + + void end(std::string_view data = "", bool closeConnection = false) { + buffer.append(data); + + // end for all queued up sockets also + res->end(buffer); + + created = time(0); + + std::ignore = closeConnection; + } + +public: + uWS::HttpResponse* res; // should be a vector of waiting sockets + + + std::string buffer; // body + time_t created; +}; + +typedef std::unordered_map CacheType; + +// we can also derive from H3app later on +template +struct CachingApp : public uWS::TemplatedAppBase> { +public: + CachingApp(SocketContextOptions options = {}) : uWS::TemplatedAppBase>(options) {} + + using uWS::TemplatedAppBase>::get; + + CachingApp(const CachingApp &other) = delete; + CachingApp(CachingApp &&other) : uWS::TemplatedAppBase>(std::move(other)) { + // also move the cache + } + + ~CachingApp() { + + } + + // variant 1: only taking URL into account + CachingApp &&get(const std::string& url, uWS::MoveOnlyFunction &&handler, unsigned int secondsToExpiry) { + ((uWS::TemplatedAppBase> *)this)->get(url, [this, handler = std::move(handler), secondsToExpiry](auto* res, auto* req) mutable { + /* We need to know the cache key and the time of now */ + std::string_view cache_key = req->getFullUrl(); + time_t now = static_cast(us_loop_ext((us_loop_t *)uWS::Loop::get()))->cacheTimepoint; + + auto it = cache.find(cache_key); + if (it != cache.end()) { + + if (it->second->created + secondsToExpiry > now) { + res->end(it->second->buffer); // tryEnd! + return; + } + + /* We are no longer valid, delete old cache and fall through to create a new entry */ + delete it->second; + + // is the cache completed? if not, add yourself to the waiting list of sockets to that cache + + // if the cache completed? ok, is it still valid? use it + } + + // immediately take the place in the cache + CachingHttpResponse *cachingRes; + cache[cache_key] = (cachingRes = new CachingHttpResponse(res)); + + handler(cachingRes, req); + }); + return std::move(*this); + } + + // variant 2: taking URL and a list of headers into account + // todo + +private: + CacheType cache; +}; + +} +#endif \ No newline at end of file diff --git a/Source/ThirdParty/uWebSockets/ChunkedEncoding.h b/Source/ThirdParty/uWebSockets/ChunkedEncoding.h index 9ecffd6e..0efeb270 100644 --- a/Source/ThirdParty/uWebSockets/ChunkedEncoding.h +++ b/Source/ThirdParty/uWebSockets/ChunkedEncoding.h @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2022. + * Authored by Alex Hultman, 2018-2026. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ #include #include +#include #include #include #include "MoveOnlyFunction.h" @@ -29,69 +30,47 @@ namespace uWS { - constexpr uint64_t STATE_HAS_SIZE = 1ull << (sizeof(uint64_t) * 8 - 1);//0x80000000; - constexpr uint64_t STATE_IS_CHUNKED = 1ull << (sizeof(uint64_t) * 8 - 2);//0x40000000; - constexpr uint64_t STATE_SIZE_MASK = ~(3ull << (sizeof(uint64_t) * 8 - 2));//0x3FFFFFFF; - constexpr uint64_t STATE_IS_ERROR = ~0ull;//0xFFFFFFFF; - constexpr uint64_t STATE_SIZE_OVERFLOW = 0x0Full << (sizeof(uint64_t) * 8 - 8);//0x0F000000; + constexpr uint64_t STATE_HAS_SIZE = 1ull << (sizeof(uint64_t) * 8 - 1); + constexpr uint64_t STATE_IS_CHUNKED = 1ull << (sizeof(uint64_t) * 8 - 2); + + // Internal sub-states encoded in top bits reserved for control state + constexpr uint64_t STATE_EXTENSION_MODE = 1ull << (sizeof(uint64_t) * 8 - 3); + constexpr uint64_t STATE_TRAILER_MODE = 1ull << (sizeof(uint64_t) * 8 - 4); + constexpr uint64_t STATE_EXTENSION_EXPECTS_NAME = 1ull << (sizeof(uint64_t) * 8 - 5); + constexpr uint64_t STATE_EXTENSION_QUOTED = 1ull << (sizeof(uint64_t) * 8 - 6); + constexpr uint64_t STATE_EXTENSION_EXPECTS_LF = 1ull << (sizeof(uint64_t) * 8 - 7); + constexpr uint64_t STATE_EXTENSION_IN_NAME = 1ull << (sizeof(uint64_t) * 8 - 8); + + constexpr uint64_t STATE_SIZE_MASK = ~(0xFFull << (sizeof(uint64_t) * 8 - 8)); + constexpr uint64_t STATE_IS_ERROR = ~0ull; + constexpr uint64_t STATE_SIZE_OVERFLOW = 0x0Full << (sizeof(uint64_t) * 8 - 12); + + /* Helper: RFC 9110 Section 5.6.2 Token character check */ + inline bool isValidTokenChar(unsigned char c) { + if (c < 0x20 || c >= 0x7F) return false; + switch (c) { + case '(': case ')': case '<': case '>': case '@': + case ',': case ';': case ':': case '\\': case '"': + case '/': case '[': case ']': case '?': case '=': + case '{': case '}': case ' ': case '\t': + return false; + default: + return true; + } + } inline uint64_t chunkSize(uint64_t state) { return state & STATE_SIZE_MASK; } - /* Reads hex number until CR or out of data to consume. Updates state. Returns bytes consumed. */ - inline void consumeHexNumber(std::string_view &data, uint64_t &state) { - /* Consume everything higher than 32 */ - while (data.length() && data.data()[0] > 32) { - - unsigned char digit = (unsigned char)data.data()[0]; - if (digit >= 'a') { - digit = (unsigned char) (digit - ('a' - ':')); - } else if (digit >= 'A') { - digit = (unsigned char) (digit - ('A' - ':')); - } - - unsigned int number = ((unsigned int) digit - (unsigned int) '0'); - - if (number > 16 || (chunkSize(state) & STATE_SIZE_OVERFLOW)) { - state = STATE_IS_ERROR; - return; - } - - // extract state bits - uint64_t bits = /*state &*/ STATE_IS_CHUNKED; - - state = (state & STATE_SIZE_MASK) * 16ull + number; - - state |= bits; - data.remove_prefix(1); - } - /* Consume everything not /n */ - while (data.length() && data.data()[0] != '\n') { - data.remove_prefix(1); - } - /* Now we stand on \n so consume it and enable size */ - if (data.length()) { - state += 2; // include the two last /r/n - state |= STATE_HAS_SIZE | STATE_IS_CHUNKED; - data.remove_prefix(1); - } - } - inline void decChunkSize(uint64_t &state, unsigned int by) { - - //unsigned int bits = state & STATE_IS_CHUNKED; - state = (state & ~STATE_SIZE_MASK) | (chunkSize(state) - by); - - //state |= bits; } inline bool hasChunkSize(uint64_t state) { return state & STATE_HAS_SIZE; } - /* Are we in the middle of parsing chunked encoding? */ inline bool isParsingChunkedEncoding(uint64_t state) { return state & ~STATE_SIZE_MASK; } @@ -100,26 +79,158 @@ namespace uWS { return state == STATE_IS_ERROR; } - /* Returns next chunk (empty or not), or if all data was consumed, nullopt is returned. */ - static std::optional getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) { - + inline void consumeHexNumber(std::string_view &data, uint64_t &state) { while (data.length()) { + unsigned char c = (unsigned char)data.data()[0]; - // if in "drop trailer mode", just drop up to what we have as size - if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) { + if (!(state & STATE_EXTENSION_MODE)) { + if (c == ';') { + if (!hasChunkSize(state) && (state & STATE_SIZE_MASK) == 0 && !(state & STATE_IS_CHUNKED)) { + state = STATE_IS_ERROR; + return; + } + state |= STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME; + data.remove_prefix(1); + continue; + } - //printf("Parsing trailer now\n"); + if (c == '\r') { + state |= STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_LF; + data.remove_prefix(1); + continue; + } - while(data.length() && chunkSize(state)) { + if (c == '\n') { + data.remove_prefix(1); + state += 2; + state |= STATE_HAS_SIZE | STATE_IS_CHUNKED; + state &= ~(STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME | STATE_EXTENSION_IN_NAME | STATE_EXTENSION_QUOTED | STATE_EXTENSION_EXPECTS_LF); + return; + } + + unsigned int number = 0; + if (c >= '0' && c <= '9') number = c - '0'; + else if (c >= 'a' && c <= 'f') number = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') number = c - 'A' + 10; + else { + state = STATE_IS_ERROR; + return; + } + + if (chunkSize(state) & STATE_SIZE_OVERFLOW) { + state = STATE_IS_ERROR; + return; + } + + uint64_t bits = state & STATE_IS_CHUNKED; + state = (state & STATE_SIZE_MASK) * 16ull + number; + state |= bits; + data.remove_prefix(1); + } else { + if (state & STATE_EXTENSION_EXPECTS_LF) { + if (c != '\n') { + state = STATE_IS_ERROR; + return; + } + data.remove_prefix(1); + state += 2; + state |= STATE_HAS_SIZE | STATE_IS_CHUNKED; + state &= ~(STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME | STATE_EXTENSION_IN_NAME | STATE_EXTENSION_QUOTED | STATE_EXTENSION_EXPECTS_LF); + return; + } + + if (c == 0x00 || (c < 0x20 && c != '\r' && c != '\n' && c != '\t')) { + state = STATE_IS_ERROR; + return; + } + + if (c == '\r') { + if (state & STATE_EXTENSION_EXPECTS_NAME) { + state = STATE_IS_ERROR; + return; + } + state |= STATE_EXTENSION_EXPECTS_LF; + data.remove_prefix(1); + continue; + } + + if (state & STATE_EXTENSION_EXPECTS_NAME) { + if (!isValidTokenChar(c)) { + state = STATE_IS_ERROR; + return; + } + state &= ~STATE_EXTENSION_EXPECTS_NAME; + state |= STATE_EXTENSION_IN_NAME; + } else if (state & STATE_EXTENSION_IN_NAME) { + if (c == '=') state &= ~STATE_EXTENSION_IN_NAME; + else if (c == ';') { + state &= ~STATE_EXTENSION_IN_NAME; + state |= STATE_EXTENSION_EXPECTS_NAME; + } else if (!isValidTokenChar(c)) { + state = STATE_IS_ERROR; + return; + } + } else { + if (c == '"') state ^= STATE_EXTENSION_QUOTED; + if (c == ';' && !(state & STATE_EXTENSION_QUOTED)) state |= STATE_EXTENSION_EXPECTS_NAME; + } + + data.remove_prefix(1); + if (c == '\n' && !(state & STATE_EXTENSION_QUOTED)) { + if (state & STATE_EXTENSION_EXPECTS_NAME) { + state = STATE_IS_ERROR; + return; + } + state += 2; + state |= STATE_HAS_SIZE | STATE_IS_CHUNKED; + state &= ~(STATE_EXTENSION_MODE | STATE_EXTENSION_EXPECTS_NAME | STATE_EXTENSION_IN_NAME | STATE_EXTENSION_QUOTED | STATE_EXTENSION_EXPECTS_LF); + return; + } + } + } + } + + static std::optional getNextChunk(std::string_view &data, uint64_t &state, bool trailer = false) { + while (data.length()) { + + // Standard-compliant Trailer parsing state machine + if (state & STATE_TRAILER_MODE) { + while (data.length()) { + char c = data.data()[0]; data.remove_prefix(1); - decChunkSize(state, 1); if (chunkSize(state) == 0) { + // Start of line: \r means final empty line, else it's a trailer header + if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 1; + else state = (state & ~STATE_SIZE_MASK) | 2; + } else if (chunkSize(state) == 1) { + // Expecting \n of final empty line + if (c == '\n') { + state = 0; + return std::nullopt; + } + state = STATE_IS_ERROR; + return std::nullopt; + } else if (chunkSize(state) == 2) { + // Inside trailer header, wait for \r + if (c == '\r') state = (state & ~STATE_SIZE_MASK) | 3; + } else if (chunkSize(state) == 3) { + // Expecting \n to terminate the header line + if (c == '\n') state = (state & ~STATE_SIZE_MASK) | 0; + else if (c != '\r') state = (state & ~STATE_SIZE_MASK) | 2; + } + } + return std::nullopt; + } - /* This is an actual place where we need 0 as state */ - state = 0; + // Drop Trailer Mode (Legacy fallback) + if (((state & STATE_IS_CHUNKED) == 0) && hasChunkSize(state) && chunkSize(state)) { + while (data.length() && chunkSize(state)) { + data.remove_prefix(1); + decChunkSize(state, 1); - /* The parser MUST stop consuming here */ + if (chunkSize(state) == 0) { + state = 0; return std::nullopt; } } @@ -132,67 +243,77 @@ namespace uWS { return std::nullopt; } if (hasChunkSize(state) && chunkSize(state) == 2) { - - //printf("Setting state to trailer-parsing and emitting empty chunk\n"); - - // set trailer state and increase size to 4 if (trailer) { - state = 4 /*| STATE_IS_CHUNKED*/ | STATE_HAS_SIZE; + state = STATE_TRAILER_MODE | 0; } else { - state = 2 /*| STATE_IS_CHUNKED*/ | STATE_HAS_SIZE; + state = 2 | STATE_HAS_SIZE; } - return std::string_view(nullptr, 0); } continue; } - // do we have data to emit all? + // Emit Body Payload Data with strict CRLF enforcement if (data.length() >= chunkSize(state)) { - // emit all but 2 bytes then reset state to 0 and goto beginning - // not fin std::string_view emitSoon; bool shouldEmit = false; + if (chunkSize(state) > 2) { + if (data[chunkSize(state) - 2] != '\r' || data[chunkSize(state) - 1] != '\n') { + state = STATE_IS_ERROR; + return std::nullopt; + } emitSoon = std::string_view(data.data(), chunkSize(state) - 2); shouldEmit = true; + } else if (chunkSize(state) == 2) { + if (data[0] != '\r' || data[1] != '\n') { + state = STATE_IS_ERROR; + return std::nullopt; + } + } else if (chunkSize(state) == 1) { + if (data[0] != '\n') { + state = STATE_IS_ERROR; + return std::nullopt; + } } + data.remove_prefix(chunkSize(state)); state = STATE_IS_CHUNKED; - if (shouldEmit) { - return emitSoon; - } + if (shouldEmit) return emitSoon; continue; } else { - /* We will consume all our input data */ std::string_view emitSoon; if (chunkSize(state) > 2) { uint64_t maximalAppEmit = chunkSize(state) - 2; if (data.length() > maximalAppEmit) { + // Enforce partial CRLF boundary safety limit + if (data[maximalAppEmit] != '\r') { + state = STATE_IS_ERROR; + return std::nullopt; + } emitSoon = data.substr(0, maximalAppEmit); } else { - //cb(data); emitSoon = data; } + } else if (chunkSize(state) == 2) { + if (data[0] != '\r') { + state = STATE_IS_ERROR; + return std::nullopt; + } } + decChunkSize(state, (unsigned int) data.length()); state |= STATE_IS_CHUNKED; - // new: decrease data by its size (bug) - data.remove_prefix(data.length()); // ny bug fix för getNextChunk - if (emitSoon.length()) { - return emitSoon; - } else { - return std::nullopt; - } + data.remove_prefix(data.length()); + if (emitSoon.length()) return emitSoon; + else return std::nullopt; } } return std::nullopt; } - /* This is really just a wrapper for convenience */ struct ChunkIterator { - std::string_view *data; std::optional chunk; uint64_t *state; @@ -202,22 +323,13 @@ namespace uWS { chunk = uWS::getNextChunk(*data, *state, trailer); } - ChunkIterator() { + ChunkIterator() {} - } - - ChunkIterator begin() { - return *this; - } - - ChunkIterator end() { - return ChunkIterator(); - } + ChunkIterator begin() { return *this; } + ChunkIterator end() { return ChunkIterator(); } std::string_view operator*() { - if (!chunk.has_value()) { - std::abort(); - } + if (!chunk.has_value()) std::abort(); return chunk.value(); } @@ -229,7 +341,6 @@ namespace uWS { chunk = uWS::getNextChunk(*data, *state, trailer); return *this; } - }; } diff --git a/Source/ThirdParty/uWebSockets/HttpContext.h b/Source/ThirdParty/uWebSockets/HttpContext.h index 1f47b8bc..79de776b 100644 --- a/Source/ThirdParty/uWebSockets/HttpContext.h +++ b/Source/ThirdParty/uWebSockets/HttpContext.h @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2020. + * Authored by Alex Hultman, 2018-2026. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -69,13 +69,27 @@ struct HttpContext { /* Init the HttpContext by registering libusockets event handlers */ HttpContext *init() { /* Handle socket connections */ - us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) { + us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char *ip, int ip_length) { /* Any connected socket should timeout until it has a request */ us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); /* Init socket ext */ new (us_socket_ext(SSL, s)) HttpResponseData; +#ifdef UWS_REMOTE_ADDRESS_USERSPACE + /* Copy remote address into per-socket cache for later retrieval */ + AsyncSocketData *asyncSocketData = (AsyncSocketData *) us_socket_ext(SSL, s); + if (ip_length > 0 && ip_length <= 16) { + memcpy(asyncSocketData->remoteAddress, ip, (size_t) ip_length); + asyncSocketData->remoteAddressLength = ip_length; + } else { + asyncSocketData->remoteAddressLength = 0; + } +#else + (void) ip; + (void) ip_length; +#endif + /* Call filter */ HttpContextData *httpContextData = getSocketContextDataS(s); for (auto &f : httpContextData->filterHandlers) { @@ -149,7 +163,9 @@ struct HttpContext { HttpResponseData *httpResponseData = (HttpResponseData *) us_socket_ext(SSL, (us_socket_t *) s); httpResponseData->offset = 0; - /* Are we not ready for another request yet? Terminate the connection. */ + /* Are we not ready for another request yet? Terminate the connection. + * Important for denying async pipelining until, if ever, we want to suppot it. + * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */ if (httpResponseData->state & HttpResponseData::HTTP_RESPONSE_PENDING) { us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); return nullptr; @@ -199,7 +215,10 @@ struct HttpContext { /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ if (!((HttpResponse *) s)->hasResponded() && !httpResponseData->onAborted) { /* Throw exception here? */ - std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; + std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" + << std::endl + << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl + << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl; std::terminate(); } @@ -211,12 +230,12 @@ struct HttpContext { /* Continue parsing */ return s; - }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { + }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * { /* We always get an empty chunk even if there is no data */ if (httpResponseData->inStream) { /* Todo: can this handle timeout for non-post as well? */ - if (fin) { + if (maxRemainingBodyLength == 0) { /* If we just got the last chunk (or empty chunk), disable timeout */ us_socket_timeout(SSL, (struct us_socket_t *) user, 0); } else { @@ -230,7 +249,7 @@ struct HttpContext { } /* We might respond in the handler, so do not change timeout after this */ - httpResponseData->inStream(data, fin); + httpResponseData->inStream(data, maxRemainingBodyLength); /* Was the socket closed? */ if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { @@ -244,7 +263,7 @@ struct HttpContext { /* If we were given the last data chunk, reset data handler to ensure following * requests on the same socket won't trigger any previously registered behavior */ - if (fin) { + if (maxRemainingBodyLength == 0) { httpResponseData->inStream = nullptr; } } @@ -487,13 +506,13 @@ struct HttpContext { return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData)); } - void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) { + void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(struct us_socket_context_t *, LIBUS_SOCKET_DESCRIPTOR, char *, int)) { us_socket_context_on_pre_open(SSL, getSocketContext(), handler); } /* Adopt an externally accepted socket into this HttpContext */ - us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) { - return us_adopt_accepted_socket(SSL, getSocketContext(), accepted_fd, sizeof(HttpResponseData), 0, 0); + us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd, char *ip, int ip_length) { + return us_adopt_accepted_socket(SSL, getSocketContext(), accepted_fd, sizeof(HttpResponseData), ip, ip_length); } }; diff --git a/Source/ThirdParty/uWebSockets/HttpContextData.h b/Source/ThirdParty/uWebSockets/HttpContextData.h index 157af353..8337a3a9 100644 --- a/Source/ThirdParty/uWebSockets/HttpContextData.h +++ b/Source/ThirdParty/uWebSockets/HttpContextData.h @@ -49,6 +49,10 @@ struct alignas(16) HttpContextData { HttpRouter router; void *upgradedWebSocket = nullptr; bool isParsingHttp = false; + + /* If we are main acceptor, distribute to these apps */ + std::vector childApps; + unsigned int roundRobin = 0; }; } diff --git a/Source/ThirdParty/uWebSockets/HttpErrors.h b/Source/ThirdParty/uWebSockets/HttpErrors.h index a17a1c73..28a1354a 100644 --- a/Source/ThirdParty/uWebSockets/HttpErrors.h +++ b/Source/ThirdParty/uWebSockets/HttpErrors.h @@ -31,7 +31,7 @@ enum HttpError { #ifndef UWS_HTTPRESPONSE_NO_WRITEMARK /* Returned parser errors match this LUT. */ -static const std::string_view httpErrorResponses[] = { +static constexpr std::string_view httpErrorResponses[] = { "", /* Zeroth place is no error so don't use it */ "HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n

HTTP Version Not Supported

This server does not support HTTP/1.0.


uWebSockets/20 Server", "HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n

Request Header Fields Too Large


uWebSockets/20 Server", @@ -40,7 +40,7 @@ static const std::string_view httpErrorResponses[] = { #else /* Anonymized pages */ -static const std::string_view httpErrorResponses[] = { +static constexpr std::string_view httpErrorResponses[] = { "", /* Zeroth place is no error so don't use it */ "HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n", "HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n", diff --git a/Source/ThirdParty/uWebSockets/HttpParser.h b/Source/ThirdParty/uWebSockets/HttpParser.h index 1bf3b570..cd49ec84 100644 --- a/Source/ThirdParty/uWebSockets/HttpParser.h +++ b/Source/ThirdParty/uWebSockets/HttpParser.h @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2024. + * Authored by Alex Hultman, 2018-2026. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -112,6 +112,17 @@ struct HttpRequest { didYield = yield; } + bool areIdentical(std::string_view lowerCasedHeader, std::string_view expectedValue) { + for (Header *h = headers; (++h)->key.length(); ) { + if (h->key.length() == lowerCasedHeader.length() && !strncmp(h->key.data(), lowerCasedHeader.data(), lowerCasedHeader.length())) { + if (expectedValue != h->value) { + return false; + } + } + } + return true; + } + std::string_view getHeader(std::string_view lowerCasedHeader) { if (bf.mightHave(lowerCasedHeader)) { for (Header *h = headers; (++h)->key.length(); ) { @@ -290,12 +301,15 @@ struct HttpParser { } /* Puts method as key, target as value and returns non-null (or nullptr on error). */ - static inline char *consumeRequestLine(char *data, HttpRequest::Header &header) { + static inline char *consumeRequestLine(char *data, char *end, HttpRequest::Header &header) { /* Scan until single SP, assume next is / (origin request) */ char *start = data; /* This catches the post padded CR and fails */ while (data[0] > 32) data++; - if (data[0] == 32 && data[1] == '/') { + if (&data[1] == end) [[unlikely]] { + return nullptr; + } + if (data[0] == 32 && data[1] == '/') [[likely]] { header.key = {start, (size_t) (data - start)}; data++; /* Scan for less than 33 (catches post padded CR and fails) */ @@ -308,14 +322,30 @@ struct HttpParser { /* Now we stand on space */ header.value = {start, (size_t) (data - start)}; /* Check that the following is http 1.1 */ + if (data + 11 >= end) { + /* Whatever we have must be part of the version string */ + if (memcmp(" HTTP/1.1\r\n", data, std::min(11, (unsigned int) (end - data))) == 0) { + return nullptr; + } + return (char *) 0x1; + } if (memcmp(" HTTP/1.1\r\n", data, 11) == 0) { return data + 11; } - return nullptr; + /* If we stand at the post padded CR, we have fragmented input so try again later */ + if (data[0] == '\r') { + return nullptr; + } + /* This is an error */ + return (char *) 0x1; } } } - return nullptr; + /* If we stand at the post padded CR, we have fragmented input so try again later */ + if (data[0] == '\r') { + return nullptr; + } + return (char *) 0x1; } /* RFC 9110: 5.5 Field Values (TLDR; anything above 31 is allowed; htab (9) is also allowed) @@ -364,14 +394,12 @@ struct HttpParser { * which is then removed, and our counters to flip due to overflow and we end up with a crash */ /* The request line is different from the field names / field values */ - postPaddedBuffer = consumeRequestLine(postPaddedBuffer, headers[0]); - if (!postPaddedBuffer) { + if ((char *) 2 > (postPaddedBuffer = consumeRequestLine(postPaddedBuffer, end, headers[0]))) { /* Error - invalid request line */ - /* Assuming it is 505 HTTP Version Not Supported */ - err = HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED; + /* Assuming it is 400 Bad Request */ + err = postPaddedBuffer ? HTTP_ERROR_400_BAD_REQUEST : 0; return 0; } - headers++; for (unsigned int i = 1; i < UWS_HTTP_MAX_HEADERS_COUNT - 1; i++) { @@ -381,8 +409,14 @@ struct HttpParser { headers->key = std::string_view(preliminaryKey, (size_t) (postPaddedBuffer - preliminaryKey)); /* We should not accept whitespace between key and colon, so colon must foloow immediately */ - if (postPaddedBuffer[0] != ':') { + /* We also cannot accept empty strings as keys */ + if (postPaddedBuffer[0] != ':' || preliminaryKey == postPaddedBuffer) { + /* If we stand at the end, we are fragmented */ + if (postPaddedBuffer == end) { + return 0; + } /* Error: invalid chars in field name */ + err = HTTP_ERROR_400_BAD_REQUEST; return 0; } postPaddedBuffer++; @@ -399,6 +433,7 @@ struct HttpParser { continue; } /* Error - invalid chars in field value */ + err = HTTP_ERROR_400_BAD_REQUEST; return 0; } break; @@ -430,6 +465,9 @@ struct HttpParser { return (unsigned int) ((postPaddedBuffer + 2) - start); } else { /* \r\n\r plus non-\n letter is malformed request, or simply out of search space */ + if (postPaddedBuffer + 1 < end) { + err = HTTP_ERROR_400_BAD_REQUEST; + } return 0; } } @@ -439,15 +477,26 @@ struct HttpParser { } } /* We ran out of header space, too large request */ + err = HTTP_ERROR_400_BAD_REQUEST;//HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE; return 0; } + bool isInvalidHost(std::string_view host) { + for (char c : host) { + unsigned char uc = static_cast(c); + if (uc < 33 || c == ',' || c == '@') { + return true; // Invalid character found + } + } + return false; + } + /* This is the only caller of getHeaders and is thus the deepest part of the parser. * From here we return either [consumed, user] for "keep going", * or [consumed, nullptr] for "break; I am closed or upgraded to websocket" * or [whatever, fullptr] for "break and close me, I am a parser error!" */ template - std::pair fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction &requestHandler, MoveOnlyFunction &dataHandler) { + std::pair fenceAndConsumePostPadded(char *data, unsigned int length, void *user, void *reserved, HttpRequest *req, MoveOnlyFunction &requestHandler, MoveOnlyFunction &dataHandler) { /* How much data we CONSUMED (to throw away) */ unsigned int consumedTotal = 0; @@ -458,21 +507,14 @@ struct HttpParser { data[length] = '\r'; data[length + 1] = 'a'; /* Anything that is not \n, to trigger "invalid request" */ - //for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved, err)); ) { - unsigned int consumed; - while (length) { - consumed = getHeaders(data, data + length, req->headers, reserved, err); - if (!consumed) { - break; - } - + for (unsigned int consumed; length && (consumed = getHeaders(data, data + length, req->headers, reserved, err)); ) { data += consumed; length -= consumed; consumedTotal += consumed; /* Even if we could parse it, check for length here as well */ if (consumed > MAX_FALLBACK_SIZE) { - return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; + return {HTTP_ERROR_400_BAD_REQUEST /*HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE*/, FULLPTR}; } /* Store HTTP version (ancient 1.0 or 1.1) */ @@ -481,11 +523,22 @@ struct HttpParser { /* Add all headers to bloom filter */ req->bf.reset(); for (HttpRequest::Header *h = req->headers; (++h)->key.length(); ) { + if (req->bf.mightHave(h->key)) [[unlikely]] { + /* Host header is not allowed twice */ + if (h->key == "host" && req->getHeader("host").data()) { + return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; + } + } req->bf.add(h->key); } - /* Break if no host header (but we can have empty string which is different from nullptr) */ - if (!req->getHeader("host").data()) { + /* Break if no host header or empty string */ + if (!req->getHeader("host").length()) { + return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; + } + + /* Break if invalid host */ + if (isInvalidHost(req->getHeader("host"))) { return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; } @@ -496,7 +549,7 @@ struct HttpParser { * ought to be handled as an error. */ std::string_view transferEncodingString = req->getHeader("transfer-encoding"); std::string_view contentLengthString = req->getHeader("content-length"); - if (transferEncodingString.length() && contentLengthString.length()) { + if (transferEncodingString.data() != nullptr && contentLengthString.data() != nullptr) { /* Returning fullptr is the same as calling the errorHandler */ /* We could be smart and set an error in the context along with this, to indicate what * http error response we might want to return */ @@ -525,7 +578,12 @@ struct HttpParser { /* RFC 9112 6.3 * If a message is received with both a Transfer-Encoding and a Content-Length header field, * the Transfer-Encoding overrides the Content-Length. */ - if (transferEncodingString.length()) { + if (transferEncodingString.data() != nullptr) { + + /* We only support chunked */ + if (transferEncodingString != "chunked") { + return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; + } /* If a proxy sent us the transfer-encoding header that 100% means it must be chunked or else the proxy is * not RFC 9112 compliant. Therefore it is always better to assume this is the case, since that entirely eliminates @@ -545,26 +603,32 @@ struct HttpParser { /* Go ahead and parse it (todo: better heuristics for emitting FIN to the app level) */ std::string_view dataToConsume(data, length); for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { - dataHandler(user, chunk, chunk.length() == 0); + dataHandler(user, chunk, chunk.length() ? UINT64_MAX : 0); } if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; } - consumed = (length - (unsigned int) dataToConsume.length()); + unsigned int consumed = (length - (unsigned int) dataToConsume.length()); data = (char *) dataToConsume.data(); length = (unsigned int) dataToConsume.length(); consumedTotal += consumed; } - } else if (contentLengthString.length()) { + } else if (contentLengthString.data() != nullptr) { + + /* Content-Length must be the same */ + if (!req->areIdentical("content-length", contentLengthString)) { + return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; + } + remainingStreamingBytes = toUnsignedInteger(contentLengthString); - if (remainingStreamingBytes == UINT64_MAX) { + if (remainingStreamingBytes == UINT64_MAX || contentLengthString.length() == 0) { /* Parser error */ return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; } if (!CONSUME_MINIMALLY) { unsigned int emittable = (unsigned int) std::min(remainingStreamingBytes, length); - dataHandler(user, std::string_view(data, emittable), emittable == remainingStreamingBytes); + dataHandler(user, std::string_view(data, emittable), remainingStreamingBytes - emittable); remainingStreamingBytes -= emittable; data += emittable; @@ -573,7 +637,7 @@ struct HttpParser { } } else { /* If we came here without a body; emit an empty data chunk to signal no data */ - dataHandler(user, {}, true); + dataHandler(user, {}, 0); } /* Consume minimally should break as easrly as possible */ @@ -589,7 +653,7 @@ struct HttpParser { } public: - std::pair consumePostPadded(char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction &&requestHandler, MoveOnlyFunction &&dataHandler) { + std::pair consumePostPadded(char *data, unsigned int length, void *user, void *reserved, MoveOnlyFunction &&requestHandler, MoveOnlyFunction &&dataHandler) { /* This resets BloomFilter by construction, but later we also reset it again. * Optimize this to skip resetting twice (req could be made global) */ @@ -601,7 +665,8 @@ struct HttpParser { if (isParsingChunkedEncoding(remainingStreamingBytes)) { std::string_view dataToConsume(data, length); for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { - dataHandler(user, chunk, chunk.length() == 0); + /* If we got the zero size chunk, maxRemainingBodyLength is 0, else it is practically infinity */ + dataHandler(user, chunk, chunk.length() ? UINT64_MAX : 0); } if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; @@ -612,11 +677,11 @@ struct HttpParser { // this is exactly the same as below! // todo: refactor this if (remainingStreamingBytes >= length) { - void *returnedUser = dataHandler(user, std::string_view(data, length), remainingStreamingBytes == length); + void *returnedUser = dataHandler(user, std::string_view(data, length), remainingStreamingBytes - length); remainingStreamingBytes -= length; return {0, returnedUser}; } else { - void *returnedUser = dataHandler(user, std::string_view(data, remainingStreamingBytes), true); + void *returnedUser = dataHandler(user, std::string_view(data, remainingStreamingBytes), 0); data += (unsigned int) remainingStreamingBytes; length -= (unsigned int) remainingStreamingBytes; @@ -658,7 +723,7 @@ struct HttpParser { if (isParsingChunkedEncoding(remainingStreamingBytes)) { std::string_view dataToConsume(data, length); for (auto chunk : uWS::ChunkIterator(&dataToConsume, &remainingStreamingBytes)) { - dataHandler(user, chunk, chunk.length() == 0); + dataHandler(user, chunk, chunk.length() ? UINT64_MAX : 0); } if (isParsingInvalidChunkedEncoding(remainingStreamingBytes)) { return {HTTP_ERROR_400_BAD_REQUEST, FULLPTR}; @@ -668,11 +733,11 @@ struct HttpParser { } else { // this is exactly the same as above! if (remainingStreamingBytes >= (unsigned int) length) { - void *returnedUser = dataHandler(user, std::string_view(data, length), remainingStreamingBytes == (unsigned int) length); + void *returnedUser = dataHandler(user, std::string_view(data, length), remainingStreamingBytes - (unsigned int) length); remainingStreamingBytes -= length; return {0, returnedUser}; } else { - void *returnedUser = dataHandler(user, std::string_view(data, remainingStreamingBytes), true); + void *returnedUser = dataHandler(user, std::string_view(data, remainingStreamingBytes), 0); data += (unsigned int) remainingStreamingBytes; length -= (unsigned int) remainingStreamingBytes; @@ -688,7 +753,7 @@ struct HttpParser { } else { if (fallback.length() == MAX_FALLBACK_SIZE) { - return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; + return {HTTP_ERROR_400_BAD_REQUEST /*HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE*/, FULLPTR}; } return {0, user}; } @@ -706,7 +771,7 @@ struct HttpParser { if (length < MAX_FALLBACK_SIZE) { fallback.append(data, length); } else { - return {HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE, FULLPTR}; + return {HTTP_ERROR_400_BAD_REQUEST /*HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE*/, FULLPTR}; } } diff --git a/Source/ThirdParty/uWebSockets/HttpResponse.h b/Source/ThirdParty/uWebSockets/HttpResponse.h index bf623f41..9c4b3708 100644 --- a/Source/ThirdParty/uWebSockets/HttpResponse.h +++ b/Source/ThirdParty/uWebSockets/HttpResponse.h @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2020. + * Authored by Alex Hultman, 2018-2026. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -73,6 +73,32 @@ struct HttpResponse : public AsyncSocket { Super::write(buf, length); } + /* Switch to chunked encoding and terminate headers if we have not already. + * The header/body separator is written here, once. Chunks themselves always + * include their own trailing CRLF (RFC 9112). */ + void ensureChunkedBodyStarted() { + HttpResponseData *httpResponseData = getHttpResponseData(); + + if (!(httpResponseData->state & HttpResponseData::HTTP_WRITE_CALLED)) { + writeMark(); + writeHeader("Transfer-Encoding", "chunked"); + httpResponseData->state |= HttpResponseData::HTTP_WRITE_CALLED; + + /* Start of the body */ + Super::write("\r\n", 2); + } + } + + /* Emit one complete chunk: chunk-size CRLF chunk-data CRLF. + * Super::write reports failed=true for backpressure even when all bytes + * were queued, so the trailer must still be written. */ + bool writeChunk(std::string_view data) { + writeUnsignedHex((unsigned int) data.length()); + Super::write("\r\n", 2); + Super::write(data.data(), (int) data.length()); + return !Super::write("\r\n", 2).second; + } + /* Called only once per request */ void writeMark() { /* Date is always written */ @@ -121,16 +147,12 @@ struct HttpResponse : public AsyncSocket { /* Do not allow sending 0 chunk here */ if (data.length()) { - Super::write("\r\n", 2); - writeUnsignedHex((unsigned int) data.length()); - Super::write("\r\n", 2); - /* Ignoring optional for now */ - Super::write(data.data(), (int) data.length()); + writeChunk(data); } /* Terminating 0 chunk */ - Super::write("\r\n0\r\n\r\n", 7); + Super::write("0\r\n\r\n", 5); httpResponseData->markDone(); @@ -196,8 +218,11 @@ struct HttpResponse : public AsyncSocket { Super::timeout(HTTP_TIMEOUT_S); } - /* Remove onAborted function if we reach the end */ - if (httpResponseData->offset == totalSize) { + /* Remove onAborted, onWritable function and mark done if we reach the end, or if we were given no data (faked size like in HEAD response) */ + /* I need to figure out if this line should rather be simply httpResponseData->offset == data.length() */ + /* No that can't be right, tryEnd with fake length should not complete the response even if the smaller chunk wrote in one go */ + /* Possibly need to separate endWithoutBody and tryEnd with fake length into two separate calls with a boolean that explicitly marks isHeadOnly */ + if (httpResponseData->offset == totalSize || !data.length()) { httpResponseData->markDone(); /* We need to check if we should close this socket here now */ @@ -229,6 +254,10 @@ struct HttpResponse : public AsyncSocket { std::string_view getProxiedRemoteAddressAsText() { return Super::addressAsText(getProxiedRemoteAddress()); } + + unsigned int getProxiedRemotePort() { + return getHttpResponseData()->proxyParser.getSourcePort(); + } #endif /* Manually upgrade to WebSocket. Typically called in upgrade handler. Immediately calls open handler. @@ -355,6 +384,7 @@ struct HttpResponse : public AsyncSocket { /* See AsyncSocket */ using Super::getRemoteAddress; using Super::getRemoteAddressAsText; + using Super::getRemotePort; using Super::getNativeHandle; /* Throttle reads and writes */ @@ -419,6 +449,15 @@ struct HttpResponse : public AsyncSocket { return this; } + /* Begin writing the response body. Useful for chunked encodings whose first chunk is not yet known */ + void beginWrite() { + /* Write status if not already done */ + writeStatus(HTTP_200_OK); + + /* Terminate headers now; later write()/end() emit complete chunks only */ + ensureChunkedBodyStarted(); + } + /* End without a body (no content-length) or end with a spoofed content-length. */ void endWithoutBody(std::optional reportedContentLength = std::nullopt, bool closeConnection = false) { if (reportedContentLength.has_value()) { @@ -436,7 +475,8 @@ struct HttpResponse : public AsyncSocket { /* Try and end the response. Returns [true, true] on success. * Starts a timeout in some cases. Returns [ok, hasResponded] */ std::pair tryEnd(std::string_view data, uintmax_t totalSize = 0, bool closeConnection = false) { - return {internalEnd(data, totalSize, true, true, closeConnection), hasResponded()}; + bool ok = internalEnd(data, totalSize, true, true, closeConnection); + return {ok, hasResponded()}; } /* Write parts of the response in chunking fashion. Starts timeout if failed. */ @@ -449,27 +489,15 @@ struct HttpResponse : public AsyncSocket { return true; } - HttpResponseData *httpResponseData = getHttpResponseData(); - - if (!(httpResponseData->state & HttpResponseData::HTTP_WRITE_CALLED)) { - /* Write mark on first call to write */ - writeMark(); + ensureChunkedBodyStarted(); - writeHeader("Transfer-Encoding", "chunked"); - httpResponseData->state |= HttpResponseData::HTTP_WRITE_CALLED; - } - - Super::write("\r\n", 2); - writeUnsignedHex((unsigned int) data.length()); - Super::write("\r\n", 2); - - auto [written, failed] = Super::write(data.data(), (int) data.length()); - if (failed) { + bool ok = writeChunk(data); + if (!ok) { Super::timeout(HTTP_TIMEOUT_S); } /* If we did not fail the write, accept more */ - return !failed; + return ok; } /* Get the current byte write offset for this Http response */ @@ -479,6 +507,13 @@ struct HttpResponse : public AsyncSocket { return httpResponseData->offset; } + /* Get the remaining body length if set via content-length, UINT64_MAX if transfer-encoding is chunked, or 0 if no body */ + uint64_t maxRemainingBodyLength() { + HttpResponseData *httpResponseData = getHttpResponseData(); + + return httpResponseData->maxRemainingBodyLength(); + } + /* If you are messing around with sendfile you might want to override the offset. */ void overrideWriteOffset(uintmax_t offset) { HttpResponseData *httpResponseData = getHttpResponseData(); @@ -497,6 +532,9 @@ struct HttpResponse : public AsyncSocket { HttpResponse *cork(MoveOnlyFunction &&handler) { if (!Super::isCorked() && Super::canCork()) { LoopData *loopData = Super::getLoopData(); + /* Remember our socket context so we can detect a WebSocket upgrade in the + * handler even when the poll realloc kept our address (see below). */ + struct us_socket_context_t *preCorkContext = us_socket_context(SSL, (struct us_socket_t *) this); Super::cork(); handler(); @@ -517,7 +555,11 @@ struct HttpResponse : public AsyncSocket { /* If we are no longer an HTTP socket then early return the new "this". * We don't want to even overwrite timeout as it is set in upgrade already. */ - if (this != newCorkedSocket) { + /* The pointer check alone is not enough: us_socket_context_adopt_socket() can + * realloc the poll in place, leaving the upgraded WebSocket at our old address + * (this == newCorkedSocket). The socket context always changes on upgrade. */ + if (this != newCorkedSocket || + us_socket_context(SSL, (struct us_socket_t *) newCorkedSocket) != preCorkContext) { return static_cast(newCorkedSocket); } @@ -565,6 +607,17 @@ struct HttpResponse : public AsyncSocket { /* Attach a read handler for data sent. Will be called with FIN set true if last segment. */ void onData(MoveOnlyFunction &&handler) { + if (handler) { + onDataV2([handler = std::move(handler)](std::string_view chunk, uint64_t maxRemainingBodyLength) mutable { + handler(chunk, maxRemainingBodyLength == 0); + }); + } else { + onDataV2(nullptr); + } + } + + /* Attach a read handler for data sent. Will be called with maxRemainingBodyLength. maxRemainingBodyLength == 0 is the same as isLast. */ + void onDataV2(MoveOnlyFunction &&handler) { HttpResponseData *data = getHttpResponseData(); data->inStream = std::move(handler); diff --git a/Source/ThirdParty/uWebSockets/HttpResponseData.h b/Source/ThirdParty/uWebSockets/HttpResponseData.h index 49600810..f17690de 100644 --- a/Source/ThirdParty/uWebSockets/HttpResponseData.h +++ b/Source/ThirdParty/uWebSockets/HttpResponseData.h @@ -43,23 +43,51 @@ struct HttpResponseData : AsyncSocketData, HttpParser { state &= ~HttpResponseData::HTTP_RESPONSE_PENDING; } - /* Caller of onWritable. It is possible onWritable calls markDone so we need to borrow it. */ - bool callOnWritable(uintmax_t newOffset) { - /* Borrow real onWritable */ + /* Caller of onWritable. It is possible onWritable calls markDone so we need to borrow it. + * It is also possible user code sets a new onWritable while running user registered onWritable. */ + bool callOnWritable(uintmax_t offset) { + /* 1. Borrow the real callback */ MoveOnlyFunction borrowedOnWritable = std::move(onWritable); - - /* Set onWritable to placeholder */ - onWritable = [](uintmax_t) {return true;}; - - /* Run borrowed onWritable */ + + /* 2. Setup the stack-based detection flag */ + bool placeholderReplaced = false; + + struct Sentinel { + bool *replacedFlag; + Sentinel(bool *f) : replacedFlag(f) {} + + Sentinel(Sentinel &&other) noexcept : replacedFlag(other.replacedFlag) { + other.replacedFlag = nullptr; + } + + ~Sentinel() { + if (replacedFlag) { + *replacedFlag = true; + } + } + + /* Delete copy to ensure move-only semantics */ + Sentinel(const Sentinel&) = delete; + Sentinel& operator=(const Sentinel&) = delete; + }; + + /* 3. Set placeholder with the captured Sentinel */ + onWritable = [tracker = Sentinel(&placeholderReplaced)](uintmax_t) { + return true; + }; + + /* 4. Run the borrowed callback */ bool ret = borrowedOnWritable(offset); - - /* If we still have onWritable (the placeholder) then move back the real one */ - if (onWritable) { - /* We haven't reset onWritable, so give it back */ + + /* + 5. If placeholderReplaced is STILL false, it means the lambda (and its Sentinel) + is still sitting inside 'onWritable'. If it's true, the lambda was destroyed + to make room for a new one. + */ + if (!placeholderReplaced) { onWritable = std::move(borrowedOnWritable); } - + return ret; } private: @@ -75,7 +103,7 @@ struct HttpResponseData : AsyncSocketData, HttpParser { /* Per socket event handlers */ MoveOnlyFunction onWritable; MoveOnlyFunction onAborted; - MoveOnlyFunction inStream; // onData + MoveOnlyFunction inStream; // onData /* Outgoing offset */ uintmax_t offset = 0; diff --git a/Source/ThirdParty/uWebSockets/HttpRouter.h b/Source/ThirdParty/uWebSockets/HttpRouter.h index 4fcfc229..0d6f5f9e 100644 --- a/Source/ThirdParty/uWebSockets/HttpRouter.h +++ b/Source/ThirdParty/uWebSockets/HttpRouter.h @@ -165,7 +165,7 @@ struct HttpRouter { } /* Executes as many handlers it can */ - bool executeHandlers(Node *parent, int urlSegment, USERDATA &newUserData) { + bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) { auto [segment, isStop] = getUrlSegment(urlSegment); @@ -217,7 +217,7 @@ struct HttpRouter { std::string segment = std::string(getUrlSegment(i).first); Node *next = nullptr; for (std::unique_ptr &child : n->children) { - if (child->name == segment && child->isHighPriority == (priority == HIGH_PRIORITY)) { + if (((segment.length() && child->name.length() && segment[0] == ':' && child->name[0] == ':') || child->name == segment) && child->isHighPriority == (priority == HIGH_PRIORITY)) { next = child.get(); break; } @@ -271,7 +271,10 @@ struct HttpRouter { } } - /* Always test any route last */ + /* Always test any route last (this check should not be necessary if we always have at least one handler) */ + if (root.children.empty()) [[unlikely]] { + return false; + } return executeHandlers(root.children.back().get(), 0, userData); } @@ -302,14 +305,13 @@ struct HttpRouter { /* ANY method must be last, GET must be first */ std::sort(root.children.begin(), root.children.end(), [](const auto &a, const auto &b) { - /* Assuming the list of methods is unique, non-repeating */ - if (a->name == "GET") { + if (a->name == "GET" && b->name != "GET") { return true; - } else if (b->name == "GET") { + } else if (b->name == "GET" && a->name != "GET") { return false; - } else if (a->name == ANY_METHOD_TOKEN) { + } else if (a->name == ANY_METHOD_TOKEN && b->name != ANY_METHOD_TOKEN) { return false; - } else if (b->name == ANY_METHOD_TOKEN) { + } else if (b->name == ANY_METHOD_TOKEN && a->name != ANY_METHOD_TOKEN) { return true; } else { return a->name < b->name; @@ -357,11 +359,11 @@ struct HttpRouter { /* Removes ALL routes with the same handler as can be found with the given parameters. * Removing a wildcard is done by removing ONE OF the methods the wildcard would match with. * Example: If wildcard includes POST, GET, PUT, you can remove ALL THREE by removing GET. */ - void remove(std::string method, std::string pattern, uint32_t priority) { + bool remove(std::string method, std::string pattern, uint32_t priority) { uint32_t handler = findHandler(method, pattern, priority); if (handler == UINT32_MAX) { /* Not found or already removed, do nothing */ - return; + return false; } /* Cull the entire tree */ @@ -372,6 +374,8 @@ struct HttpRouter { /* Now remove the actual handler */ handlers.erase(handlers.begin() + (handler & HANDLER_MASK)); + + return true; } }; diff --git a/Source/ThirdParty/uWebSockets/LocalCluster.h b/Source/ThirdParty/uWebSockets/LocalCluster.h index 81feffc9..520f5b80 100644 --- a/Source/ThirdParty/uWebSockets/LocalCluster.h +++ b/Source/ThirdParty/uWebSockets/LocalCluster.h @@ -34,14 +34,16 @@ struct LocalCluster { cb(*app); - app->preOpen([](LIBUS_SOCKET_DESCRIPTOR fd) -> LIBUS_SOCKET_DESCRIPTOR { + app->preOpen([](struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR fd, char *ip, int ip_length) -> LIBUS_SOCKET_DESCRIPTOR { + + std::ignore = context; /* Distribute this socket in round robin fashion */ //std::cout << "About to load balance " << fd << " to " << roundRobin << std::endl; auto receivingApp = apps[roundRobin]; - apps[roundRobin]->getLoop()->defer([fd, receivingApp]() { - receivingApp->adoptSocket(fd); + apps[roundRobin]->getLoop()->defer([fd, ipStore = std::string(ip, ip + ip_length), receivingApp]() { + receivingApp->adoptSocket(fd, std::string_view(ipStore)); }); roundRobin = (roundRobin + 1) % hardwareConcurrency; diff --git a/Source/ThirdParty/uWebSockets/Loop.h b/Source/ThirdParty/uWebSockets/Loop.h index ce8d3781..61c2e089 100644 --- a/Source/ThirdParty/uWebSockets/Loop.h +++ b/Source/ThirdParty/uWebSockets/Loop.h @@ -25,6 +25,15 @@ #include namespace uWS { + +/* A prepared message is dependent on the Loop, so it belongs here */ +struct PreparedMessage { + /* These should be a single alloation along with the PreparedMessage itself (they are static) */ + std::string originalMessage, compressedMessage; + bool compressed; + int opCode; +}; + struct Loop { private: static void wakeupCb(us_loop_t *loop) { @@ -106,6 +115,31 @@ struct Loop { } public: + + /* Preformatted messages need the Loop */ + PreparedMessage prepareMessage(std::string_view message, int opCode, bool compress = true) { + /* The message could be formatted right here, but this optimization is not done yet */ + PreparedMessage preparedMessage; + preparedMessage.compressed = compress; + preparedMessage.opCode = opCode; + preparedMessage.originalMessage = message; + + LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this); + + if (compress) { + /* Initialize loop's deflate inflate streams */ + if (!loopData->zlibContext) { + loopData->zlibContext = new ZlibContext; + loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR); + loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR); + } + + preparedMessage.compressedMessage = loopData->deflationStream->deflate(loopData->zlibContext, {preparedMessage.originalMessage.data(), preparedMessage.originalMessage.length()}, true); + } + + return preparedMessage; + } + /* Lazily initializes a per-thread loop and returns it. * Will automatically free all initialized loops at exit. */ static Loop *get(void *existingNativeLoop = nullptr) { diff --git a/Source/ThirdParty/uWebSockets/LoopData.h b/Source/ThirdParty/uWebSockets/LoopData.h index 986bf0cb..1b79659c 100644 --- a/Source/ThirdParty/uWebSockets/LoopData.h +++ b/Source/ThirdParty/uWebSockets/LoopData.h @@ -61,13 +61,13 @@ struct alignas(16) LoopData { } void updateDate() { - time_t now = time(0); + cacheTimepoint = time(0); struct tm tstruct = {}; #ifdef _WIN32 /* Micro, fucking soft never follows spec. */ - gmtime_s(&tstruct, &now); + gmtime_s(&tstruct, &cacheTimepoint); #else - gmtime_r(&now, &tstruct); + gmtime_r(&cacheTimepoint, &tstruct); #endif static const char wday_name[][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" @@ -87,6 +87,7 @@ struct alignas(16) LoopData { } char date[32]; + time_t cacheTimepoint = 0; /* Be silent */ bool noMark = false; diff --git a/Source/ThirdParty/uWebSockets/MoveOnlyFunction.h b/Source/ThirdParty/uWebSockets/MoveOnlyFunction.h index b1ae785e..c767aaf4 100644 --- a/Source/ThirdParty/uWebSockets/MoveOnlyFunction.h +++ b/Source/ThirdParty/uWebSockets/MoveOnlyFunction.h @@ -28,6 +28,9 @@ SOFTWARE. #define _ANY_INVOKABLE_H_ #include + +#if !defined(__cpp_lib_move_only_function) || __cpp_lib_move_only_function < 202110L + #include #include @@ -374,4 +377,13 @@ namespace uWS { using MoveOnlyFunction = ofats::any_invocable; } +#else // !defined(__cpp_lib_move_only_function) || __cpp_lib_move_only_function < 202110L + +namespace uWS { + template + using MoveOnlyFunction = std::move_only_function; +} + +#endif + #endif // _ANY_INVOKABLE_H_ diff --git a/Source/ThirdParty/uWebSockets/PerMessageDeflate.h b/Source/ThirdParty/uWebSockets/PerMessageDeflate.h index 17832c71..dec13f16 100644 --- a/Source/ThirdParty/uWebSockets/PerMessageDeflate.h +++ b/Source/ThirdParty/uWebSockets/PerMessageDeflate.h @@ -150,21 +150,6 @@ struct DeflationStream { /* Deflate and optionally reset. You must not deflate an empty string. */ std::string_view deflate(ZlibContext *zlibContext, std::string_view raw, bool reset) { -#ifdef UWS_USE_LIBDEFLATE - /* Run a fast path in case of shared_compressor */ - if (reset) { - size_t written = 0; - static unsigned char buf[1024 + 1]; - - written = libdeflate_deflate_compress(zlibContext->compressor, raw.data(), raw.length(), buf, 1024); - - if (written) { - memcpy(&buf[written], "\x00", 1); - return std::string_view((char *) buf, written + 1); - } - } -#endif - /* Odd place to clear this one, fix */ zlibContext->dynamicDeflationBuffer.clear(); @@ -228,20 +213,23 @@ struct InflationStream { std::optional inflate(ZlibContext *zlibContext, std::string_view compressed, size_t maxPayloadLength, bool reset) { #ifdef UWS_USE_LIBDEFLATE - /* Try fast path first */ - size_t written = 0; - static char buf[1024]; - - /* We have to pad 9 bytes and restore those bytes when done since 9 is more than 6 of next WebSocket message */ - char tmp[9]; - memcpy(tmp, (char *) compressed.data() + compressed.length(), 9); - memcpy((char *) compressed.data() + compressed.length(), "\x00\x00\xff\xff\x01\x00\x00\xff\xff", 9); - libdeflate_result res = libdeflate_deflate_decompress(zlibContext->decompressor, compressed.data(), compressed.length() + 9, buf, 1024, &written); - memcpy((char *) compressed.data() + compressed.length(), tmp, 9); - - if (res == 0) { - /* Fast path wins */ - return std::string_view(buf, written); + if (reset) { + /* Try fast path first (assuming single DEFLATE block and shared compressor aka reset = true) */ + size_t written = 0, consumed; + zlibContext->dynamicInflationBuffer.clear(); + zlibContext->dynamicInflationBuffer.reserve(maxPayloadLength); + + ((char *)compressed.data())[0] |= 0x1; // BFINAL = 1 + libdeflate_result res = libdeflate_deflate_decompress_ex(zlibContext->decompressor, compressed.data(), compressed.length(), zlibContext->dynamicInflationBuffer.data(), maxPayloadLength, &consumed, &written); + + /* Still not entirely sure why 1 extra zero byte is optional and ignored by both zlib and libdeflate in some cases */ + /* Minimal reproducer is load_test.c with 102 byte message size. It should be tested with Chrome at various message sizes as well. */ + if (res == 0 && (consumed == compressed.length() || (consumed + 1 == compressed.length() && compressed[consumed] == '\0'))) { + return std::string_view(zlibContext->dynamicInflationBuffer.data(), written); + } else { + /* We can only end up here if the first DEFLATE block was not the last, so mark it as such */ + ((char *)compressed.data())[0] &= ~0x1; // BFINAL = 0 + } } #endif diff --git a/Source/ThirdParty/uWebSockets/ProxyParser.h b/Source/ThirdParty/uWebSockets/ProxyParser.h index 95ee3d11..ffe4bf67 100644 --- a/Source/ThirdParty/uWebSockets/ProxyParser.h +++ b/Source/ThirdParty/uWebSockets/ProxyParser.h @@ -1,5 +1,5 @@ /* - * Authored by Alex Hultman, 2018-2020. + * Authored by Alex Hultman, 2018-2026. * Intellectual property of third-party. * Licensed under the Apache License, Version 2.0 (the "License"); @@ -68,7 +68,11 @@ T _cond_byte_swap(T value) { struct ProxyParser { private: - union proxy_addr addr; + union proxy_addr addr = {}; + /* We must always consume all PROXY v2 data, even if done, but we may not overwrite our + * parsed-out data one read for the first time. This property mainly fixes L4 TCP-only + * proxying where no HTTP-level cleaning is applied. */ + bool done = false; /* Default family of 0 signals no proxy address */ uint8_t family = 0; @@ -91,6 +95,22 @@ struct ProxyParser { } } + unsigned int getSourcePort() { + + // UNSPEC family and protocol + if (family == 0) { + return {}; + } + + if ((family & 0xf0) >> 4 == 1) { + /* Family 1 is INET4 */ + return addr.ipv4_addr.src_port; + } else { + /* Family 2 is INET6 */ + return addr.ipv6_addr.src_port; + } + } + /* Returns [done, consumed] where done = false on failure */ std::pair parse(std::string_view data) { @@ -145,11 +165,15 @@ struct ProxyParser { //printf("Family: %d\n", (header.fam & 0xf0) >> 4); //printf("Transport: %d\n", (header.fam & 0x0f)); - /* We have 0 family by default, and UNSPEC is 0 as well */ - family = header.fam; + /* Copy payload (only if not already done so before) */ + if (!done) { + /* We have 0 family by default, and UNSPEC is 0 as well */ + family = header.fam; + + memcpy(&addr, data.data() + 16, hostLength); - /* Copy payload */ - memcpy(&addr, data.data() + 16, hostLength); + done = true; + } /* We consumed everything */ return {true, 16 + hostLength}; @@ -160,4 +184,4 @@ struct ProxyParser { #endif -#endif // UWS_PROXY_PARSER_H \ No newline at end of file +#endif // UWS_PROXY_PARSER_H diff --git a/Source/ThirdParty/uWebSockets/TopicTree.h b/Source/ThirdParty/uWebSockets/TopicTree.h index 4ce5adc5..16b3f716 100644 --- a/Source/ThirdParty/uWebSockets/TopicTree.h +++ b/Source/ThirdParty/uWebSockets/TopicTree.h @@ -30,6 +30,7 @@ #include #include #include +#include namespace uWS { @@ -71,7 +72,7 @@ struct Subscriber { void *user; bool needsDrainage() { - return numMessageIndices != 0; + return numMessageIndices; } }; @@ -201,7 +202,7 @@ struct TopicTree { /* Remove us from topic */ topicPtr->erase(s); - int newCount = topicPtr->size(); + int newCount = (int) topicPtr->size(); /* If there is no subscriber to this topic, remove it */ if (!topicPtr->size()) { diff --git a/Source/ThirdParty/uWebSockets/WebSocket.h b/Source/ThirdParty/uWebSockets/WebSocket.h index f2663e58..00da7812 100644 --- a/Source/ThirdParty/uWebSockets/WebSocket.h +++ b/Source/ThirdParty/uWebSockets/WebSocket.h @@ -27,6 +27,13 @@ namespace uWS { +/* Experimental */ +enum CompressFlags : int { + NO_ACTION, + COMPRESS, + ALREADY_COMPRESSED +}; + template struct WebSocket : AsyncSocket { template friend struct TemplatedApp; @@ -51,6 +58,7 @@ struct WebSocket : AsyncSocket { using Super::getBufferedAmount; using Super::getRemoteAddress; using Super::getRemoteAddressAsText; + using Super::getRemotePort; using Super::getNativeHandle; /* WebSocket close cannot be an alias to AsyncSocket::close since @@ -87,9 +95,23 @@ struct WebSocket : AsyncSocket { return send(message, CONTINUATION, compress, true); } + /* Experimental */ + bool hasNegotiatedCompression() { + WebSocketData *webSocketData = (WebSocketData *) Super::getAsyncSocketData(); + return webSocketData->compressionStatus == WebSocketData::ENABLED; + } + + /* Experimental */ + SendStatus sendPrepared(PreparedMessage &preparedMessage) { + if (preparedMessage.compressed && hasNegotiatedCompression() && preparedMessage.compressedMessage.length() < preparedMessage.originalMessage.length()) { + return send({preparedMessage.compressedMessage.data(), preparedMessage.compressedMessage.length()}, (OpCode) preparedMessage.opCode, uWS::CompressFlags::ALREADY_COMPRESSED); + } + return send({preparedMessage.originalMessage.data(), preparedMessage.originalMessage.length()}, (OpCode) preparedMessage.opCode); + } + /* Send or buffer a WebSocket frame, compressed or not. Returns BACKPRESSURE on increased user space backpressure, * DROPPED on dropped message (due to backpressure) or SUCCCESS if you are free to send even more now. */ - SendStatus send(std::string_view message, OpCode opCode = OpCode::BINARY, bool compress = false, bool fin = true) { + SendStatus send(std::string_view message, OpCode opCode = OpCode::BINARY, int compress = false, bool fin = true) { WebSocketContextData *webSocketContextData = (WebSocketContextData *) us_socket_context_ext(SSL, (us_socket_context_t *) us_socket_context(SSL, (us_socket_t *) this) ); @@ -115,7 +137,7 @@ struct WebSocket : AsyncSocket { /* Special path for long sends of non-compressed, non-SSL messages */ if (message.length() >= 16 * 1024 && !compress && !SSL && !webSocketData->subscriber && getBufferedAmount() == 0 && Super::getLoopData()->corkOffset == 0) { char header[10]; - int header_length = (int) protocol::formatMessage(header, nullptr, 0, opCode, message.length(), compress, fin); + int header_length = (int) protocol::formatMessage(header, "", 0, opCode, message.length(), compress, fin); int written = us_socket_write2(0, (struct us_socket_t *)this, header, header_length, message.data(), (int) message.length()); if (written != header_length + (int) message.length()) { @@ -145,12 +167,15 @@ struct WebSocket : AsyncSocket { /* Check and correct the compress hint. It is never valid to compress 0 bytes */ if (message.length() && opCode < 3 && webSocketData->compressionStatus == WebSocketData::ENABLED) { - LoopData *loopData = Super::getLoopData(); - /* Compress using either shared or dedicated deflationStream */ - if (webSocketData->deflationStream) { - message = webSocketData->deflationStream->deflate(loopData->zlibContext, message, false); - } else { - message = loopData->deflationStream->deflate(loopData->zlibContext, message, true); + /* If compress is 2 (IS_PRE_COMPRESSED), skip this step (experimental) */ + if (compress != CompressFlags::ALREADY_COMPRESSED) { + LoopData *loopData = Super::getLoopData(); + /* Compress using either shared or dedicated deflationStream */ + if (webSocketData->deflationStream) { + message = webSocketData->deflationStream->deflate(loopData->zlibContext, message, false); + } else { + message = loopData->deflationStream->deflate(loopData->zlibContext, message, true); + } } } else { compress = false; @@ -241,6 +266,7 @@ struct WebSocket : AsyncSocket { if (webSocketContextData->closeHandler) { webSocketContextData->closeHandler(this, code, message); } + ((USERDATA *) this->getUserData())->~USERDATA(); } /* Corks the response if possible. Leaves already corked socket be. */ diff --git a/Source/ThirdParty/uWebSockets/WebSocketContext.h b/Source/ThirdParty/uWebSockets/WebSocketContext.h index c49a6ac9..d8b3fd03 100644 --- a/Source/ThirdParty/uWebSockets/WebSocketContext.h +++ b/Source/ThirdParty/uWebSockets/WebSocketContext.h @@ -269,9 +269,11 @@ struct WebSocketContext { webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); webSocketData->subscriber = nullptr; + auto *ws = (WebSocket *) s; if (webSocketContextData->closeHandler) { - webSocketContextData->closeHandler((WebSocket *) s, 1006, {(char *) reason, (size_t) code}); + webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); } + ((USERDATA *) ws->getUserData())->~USERDATA(); } /* Destruct in-placed data struct */ @@ -369,11 +371,11 @@ struct WebSocketContext { return s; }); - /* Handle FIN, HTTP does not support half-closed sockets, so simply close */ + /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { /* If we get a fin, we just close I guess */ - us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); + us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); return s; }); diff --git a/Source/ThirdParty/uWebSockets/WebSocketExtensions.h b/Source/ThirdParty/uWebSockets/WebSocketExtensions.h index 63e81951..93fd5df7 100644 --- a/Source/ThirdParty/uWebSockets/WebSocketExtensions.h +++ b/Source/ThirdParty/uWebSockets/WebSocketExtensions.h @@ -91,7 +91,7 @@ struct ExtensionsParser { } ExtensionsParser(const char *data, size_t length) { - const char* stop = data + length; + const char *stop = data + length; int token = 1; /* Ignore anything before permessage-deflate or x-webkit-deflate-frame */ @@ -101,11 +101,7 @@ struct ExtensionsParser { perMessageDeflate = (token == TOK_PERMESSAGE_DEFLATE); xWebKitDeflateFrame = (token == TOK_X_WEBKIT_DEFLATE_FRAME); - /* Main loop */ - while (true) { - token = getToken(data, stop); - if (!token) break; - + while ((token = getToken(data, stop))) { switch (token) { case TOK_X_WEBKIT_DEFLATE_FRAME: /* Duplicates not allowed/supported */ diff --git a/Source/ThirdParty/uWebSockets/WebSocketProtocol.h b/Source/ThirdParty/uWebSockets/WebSocketProtocol.h index 7fd00ffc..a6c17300 100644 --- a/Source/ThirdParty/uWebSockets/WebSocketProtocol.h +++ b/Source/ThirdParty/uWebSockets/WebSocketProtocol.h @@ -25,14 +25,20 @@ #include #include +#ifdef UWS_USE_SIMDUTF + #include +#endif + namespace uWS { /* We should not overcomplicate these */ -const std::string_view ERR_TOO_BIG_MESSAGE("Received too big message"); -const std::string_view ERR_WEBSOCKET_TIMEOUT("WebSocket timed out from inactivity"); -const std::string_view ERR_INVALID_TEXT("Received invalid UTF-8"); -const std::string_view ERR_TOO_BIG_MESSAGE_INFLATION("Received too big message, or other inflation error"); -const std::string_view ERR_INVALID_CLOSE_PAYLOAD("Received invalid close payload"); +constexpr std::string_view ERR_TOO_BIG_MESSAGE("Received too big message"); +constexpr std::string_view ERR_WEBSOCKET_TIMEOUT("WebSocket timed out from inactivity"); +constexpr std::string_view ERR_INVALID_TEXT("Received invalid UTF-8"); +constexpr std::string_view ERR_TOO_BIG_MESSAGE_INFLATION("Received too big message, or other inflation error"); +constexpr std::string_view ERR_INVALID_CLOSE_PAYLOAD("Received invalid close payload"); +constexpr std::string_view ERR_PROTOCOL("Received invalid WebSocket frame"); +constexpr std::string_view ERR_TCP_FIN("Received TCP FIN before WebSocket close frame"); enum OpCode : unsigned char { CONTINUATION = 0, @@ -93,34 +99,45 @@ T bit_cast(char *c) { /* Byte swap for little-endian systems */ template T cond_byte_swap(T value) { + static_assert(std::is_trivially_copyable::value, "T must be trivially copyable"); uint32_t endian_test = 1; - if (*((char *)&endian_test)) { - union { - T i; - uint8_t b[sizeof(T)]; - } src = { value }, dst{}; - - for (unsigned int i = 0; i < sizeof(value); i++) { - dst.b[i] = src.b[sizeof(value) - 1 - i]; + if (*reinterpret_cast(&endian_test)) { + uint8_t src[sizeof(T)]; + uint8_t dst[sizeof(T)]; + + std::memcpy(src, &value, sizeof(T)); + for (size_t i = 0; i < sizeof(T); ++i) { + dst[i] = src[sizeof(T) - 1 - i]; } - return dst.i; + T result; + std::memcpy(&result, dst, sizeof(T)); + return result; } return value; } +#ifdef UWS_USE_SIMDUTF + +static bool isValidUtf8(unsigned char *s, size_t length) +{ + return simdutf::validate_utf8((const char *)s, length); +} + +#else // Based on utf8_check.c by Markus Kuhn, 2005 // https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c // Optimized for predominantly 7-bit content by Alex Hultman, 2016 // Licensed as Zlib, like the rest of this project +// This runs about 40% faster than simdutf with g++ -mavx static bool isValidUtf8(unsigned char *s, size_t length) { for (unsigned char *e = s + length; s != e; ) { - if (s + 4 <= e) { - uint32_t tmp; - memcpy(&tmp, s, 4); - if ((tmp & 0x80808080) == 0) { - s += 4; + if (s + 16 <= e) { + uint64_t tmp[2]; + memcpy(tmp, s, 16); + if (((tmp[0] & 0x8080808080808080) | (tmp[1] & 0x8080808080808080)) == 0) { + s += 16; continue; } } @@ -155,6 +172,8 @@ static bool isValidUtf8(unsigned char *s, size_t length) return true; } +#endif + struct CloseFrame { uint16_t code; char *message; @@ -170,7 +189,7 @@ static inline CloseFrame parseClosePayload(char *src, size_t length) { if (cf.code < 1000 || cf.code > 4999 || (cf.code > 1011 && cf.code < 4000) || (cf.code >= 1004 && cf.code <= 1006) || !isValidUtf8((unsigned char *) cf.message, cf.length)) { /* Even though we got a WebSocket close frame, it in itself is abnormal */ - return {1006, nullptr, 0}; + return {1006, (char *) ERR_INVALID_CLOSE_PAYLOAD.data(), ERR_INVALID_CLOSE_PAYLOAD.length()}; } } return cf; @@ -340,12 +359,12 @@ struct WebSocketProtocol { static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState *wState, void *user) { if (getOpCode(src)) { if (wState->state.opStack == 1 || (!wState->state.lastFin && getOpCode(src) < 2)) { - Impl::forceClose(wState, user); + Impl::forceClose(wState, user, ERR_PROTOCOL); return true; } wState->state.opCode[++wState->state.opStack] = (OpCode) getOpCode(src); } else if (wState->state.opStack == -1) { - Impl::forceClose(wState, user); + Impl::forceClose(wState, user, ERR_PROTOCOL); return true; } wState->state.lastFin = isFin(src); @@ -468,7 +487,7 @@ struct WebSocketProtocol { // invalid reserved bits / invalid opcodes / invalid control frames / set compressed frame if ((rsv1(src) && !Impl::setCompressed(wState, user)) || rsv23(src) || (getOpCode(src) > 2 && getOpCode(src) < 8) || getOpCode(src) > 10 || (getOpCode(src) > 2 && (!isFin(src) || payloadLength(src) > 125))) { - Impl::forceClose(wState, user); + Impl::forceClose(wState, user, ERR_PROTOCOL); return; } diff --git a/Source/ThirdParty/uWebSockets/quic.h b/Source/ThirdParty/uWebSockets/quic.h deleted file mode 100644 index 6d33d27b..00000000 --- a/Source/ThirdParty/uWebSockets/quic.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifdef LIBUS_USE_QUIC - -#ifndef LIBUS_QUIC_H -#define LIBUS_QUIC_H - -/* Experimental QUIC functions */ - -#include "libusockets.h" - -typedef struct { - const char *cert_file_name; - const char *key_file_name; - const char *passphrase; -} us_quic_socket_context_options_t; - - -typedef struct { - /* Refers to either the shared listen socket or the client UDP socket */ - void *udp_socket; -} us_quic_socket_t; - -struct us_quic_socket_context_s; -struct us_quic_listen_socket_s; -struct us_quic_stream_s; - -typedef struct us_quic_socket_context_s us_quic_socket_context_t; -typedef struct us_quic_listen_socket_s us_quic_listen_socket_t; -typedef struct us_quic_stream_s us_quic_stream_t; - - -void *us_quic_stream_ext(us_quic_stream_t *s); -int us_quic_stream_write(us_quic_stream_t *s, char *data, int length); -int us_quic_stream_shutdown(us_quic_stream_t *s); -int us_quic_stream_shutdown_read(us_quic_stream_t *s); -void us_quic_stream_close(us_quic_stream_t *s); - -int us_quic_socket_context_get_header(us_quic_socket_context_t *context, int index, char **name, int *name_length, char **value, int *value_length); - - -void us_quic_socket_context_set_header(us_quic_socket_context_t *context, int index, const char *key, int key_length, const char *value, int value_length); -void us_quic_socket_context_send_headers(us_quic_socket_context_t *context, us_quic_stream_t *s, int num, int has_body); - -us_quic_socket_context_t *us_create_quic_socket_context(struct us_loop_t *loop, us_quic_socket_context_options_t options, int ext_size); -us_quic_listen_socket_t *us_quic_socket_context_listen(us_quic_socket_context_t *context, const char *host, int port, int ext_size); -us_quic_socket_t *us_quic_socket_context_connect(us_quic_socket_context_t *context, const char *host, int port, int ext_size); - -void us_quic_socket_create_stream(us_quic_socket_t *s, int ext_size); -us_quic_socket_t *us_quic_stream_socket(us_quic_stream_t *s); - -/* This one is ugly and is only used to make clean examples */ -int us_quic_stream_is_client(us_quic_stream_t *s); - -void us_quic_socket_context_on_stream_data(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s, char *data, int length)); -void us_quic_socket_context_on_stream_end(us_quic_socket_context_t *context, void(*on_stream_data)(us_quic_stream_t *s)); -void us_quic_socket_context_on_stream_headers(us_quic_socket_context_t *context, void(*on_stream_headers)(us_quic_stream_t *s)); -void us_quic_socket_context_on_stream_open(us_quic_socket_context_t *context, void(*on_stream_open)(us_quic_stream_t *s, int is_client)); -void us_quic_socket_context_on_stream_close(us_quic_socket_context_t *context, void(*on_stream_close)(us_quic_stream_t *s)); -void us_quic_socket_context_on_open(us_quic_socket_context_t *context, void(*on_open)(us_quic_socket_t *s, int is_client)); -void us_quic_socket_context_on_close(us_quic_socket_context_t *context, void(*on_close)(us_quic_socket_t *s)); -void us_quic_socket_context_on_stream_writable(us_quic_socket_context_t *context, void(*on_stream_writable)(us_quic_stream_t *s)); - - - -void *us_quic_socket_context_ext(us_quic_socket_context_t *context); -us_quic_socket_context_t *us_quic_socket_context(us_quic_socket_t *s); - -#endif -#endif \ No newline at end of file diff --git a/Source/ThirdParty/uWebSockets/uv.h b/Source/ThirdParty/uWebSockets/uv.h deleted file mode 100644 index a2b93d8e..00000000 --- a/Source/ThirdParty/uWebSockets/uv.h +++ /dev/null @@ -1,1912 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -/* See https://github.com/libuv/libuv#documentation for documentation. */ - -#ifndef UV_H -#define UV_H -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(BUILDING_UV_SHARED) && 1 -#error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both." -#endif - -#ifndef UV_EXTERN -#ifdef _WIN32 - /* Windows - set up dll import/export decorators. */ -# if defined(BUILDING_UV_SHARED) - /* Building shared library. */ -# define UV_EXTERN __declspec(dllexport) -# elif 1 - /* Using shared library. */ -# define UV_EXTERN __declspec(dllimport) -# else - /* Building static library. */ -# define UV_EXTERN /* nothing */ -# endif -#elif __GNUC__ >= 4 -# define UV_EXTERN __attribute__((visibility("default"))) -#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) /* Sun Studio >= 8 */ -# define UV_EXTERN __global -#else -# define UV_EXTERN /* nothing */ -#endif -#endif /* UV_EXTERN */ - -#include "uv/errno.h" -#include "uv/version.h" -#include -#include -#include - -/* Internal type, do not use. */ -struct uv__queue { - struct uv__queue* next; - struct uv__queue* prev; -}; - -#if defined(_WIN32) -# include "uv/win.h" -#else -# include "uv/unix.h" -#endif - -/* Expand this list if necessary. */ -#define UV_ERRNO_MAP(XX) \ - XX(E2BIG, "argument list too long") \ - XX(EACCES, "permission denied") \ - XX(EADDRINUSE, "address already in use") \ - XX(EADDRNOTAVAIL, "address not available") \ - XX(EAFNOSUPPORT, "address family not supported") \ - XX(EAGAIN, "resource temporarily unavailable") \ - XX(EAI_ADDRFAMILY, "address family not supported") \ - XX(EAI_AGAIN, "temporary failure") \ - XX(EAI_BADFLAGS, "bad ai_flags value") \ - XX(EAI_BADHINTS, "invalid value for hints") \ - XX(EAI_CANCELED, "request canceled") \ - XX(EAI_FAIL, "permanent failure") \ - XX(EAI_FAMILY, "ai_family not supported") \ - XX(EAI_MEMORY, "out of memory") \ - XX(EAI_NODATA, "no address") \ - XX(EAI_NONAME, "unknown node or service") \ - XX(EAI_OVERFLOW, "argument buffer overflow") \ - XX(EAI_PROTOCOL, "resolved protocol is unknown") \ - XX(EAI_SERVICE, "service not available for socket type") \ - XX(EAI_SOCKTYPE, "socket type not supported") \ - XX(EALREADY, "connection already in progress") \ - XX(EBADF, "bad file descriptor") \ - XX(EBUSY, "resource busy or locked") \ - XX(ECANCELED, "operation canceled") \ - XX(ECHARSET, "invalid Unicode character") \ - XX(ECONNABORTED, "software caused connection abort") \ - XX(ECONNREFUSED, "connection refused") \ - XX(ECONNRESET, "connection reset by peer") \ - XX(EDESTADDRREQ, "destination address required") \ - XX(EEXIST, "file already exists") \ - XX(EFAULT, "bad address in system call argument") \ - XX(EFBIG, "file too large") \ - XX(EHOSTUNREACH, "host is unreachable") \ - XX(EINTR, "interrupted system call") \ - XX(EINVAL, "invalid argument") \ - XX(EIO, "i/o error") \ - XX(EISCONN, "socket is already connected") \ - XX(EISDIR, "illegal operation on a directory") \ - XX(ELOOP, "too many symbolic links encountered") \ - XX(EMFILE, "too many open files") \ - XX(EMSGSIZE, "message too long") \ - XX(ENAMETOOLONG, "name too long") \ - XX(ENETDOWN, "network is down") \ - XX(ENETUNREACH, "network is unreachable") \ - XX(ENFILE, "file table overflow") \ - XX(ENOBUFS, "no buffer space available") \ - XX(ENODEV, "no such device") \ - XX(ENOENT, "no such file or directory") \ - XX(ENOMEM, "not enough memory") \ - XX(ENONET, "machine is not on the network") \ - XX(ENOPROTOOPT, "protocol not available") \ - XX(ENOSPC, "no space left on device") \ - XX(ENOSYS, "function not implemented") \ - XX(ENOTCONN, "socket is not connected") \ - XX(ENOTDIR, "not a directory") \ - XX(ENOTEMPTY, "directory not empty") \ - XX(ENOTSOCK, "socket operation on non-socket") \ - XX(ENOTSUP, "operation not supported on socket") \ - XX(EOVERFLOW, "value too large for defined data type") \ - XX(EPERM, "operation not permitted") \ - XX(EPIPE, "broken pipe") \ - XX(EPROTO, "protocol error") \ - XX(EPROTONOSUPPORT, "protocol not supported") \ - XX(EPROTOTYPE, "protocol wrong type for socket") \ - XX(ERANGE, "result too large") \ - XX(EROFS, "read-only file system") \ - XX(ESHUTDOWN, "cannot send after transport endpoint shutdown") \ - XX(ESPIPE, "invalid seek") \ - XX(ESRCH, "no such process") \ - XX(ETIMEDOUT, "connection timed out") \ - XX(ETXTBSY, "text file is busy") \ - XX(EXDEV, "cross-device link not permitted") \ - XX(UNKNOWN, "unknown error") \ - XX(EOF, "end of file") \ - XX(ENXIO, "no such device or address") \ - XX(EMLINK, "too many links") \ - XX(EHOSTDOWN, "host is down") \ - XX(EREMOTEIO, "remote I/O error") \ - XX(ENOTTY, "inappropriate ioctl for device") \ - XX(EFTYPE, "inappropriate file type or format") \ - XX(EILSEQ, "illegal byte sequence") \ - XX(ESOCKTNOSUPPORT, "socket type not supported") \ - XX(ENODATA, "no data available") \ - XX(EUNATCH, "protocol driver not attached") \ - -#define UV_HANDLE_TYPE_MAP(XX) \ - XX(ASYNC, async) \ - XX(CHECK, check) \ - XX(FS_EVENT, fs_event) \ - XX(FS_POLL, fs_poll) \ - XX(HANDLE, handle) \ - XX(IDLE, idle) \ - XX(NAMED_PIPE, pipe) \ - XX(POLL, poll) \ - XX(PREPARE, prepare) \ - XX(PROCESS, process) \ - XX(STREAM, stream) \ - XX(TCP, tcp) \ - XX(TIMER, timer) \ - XX(TTY, tty) \ - XX(UDP, udp) \ - XX(SIGNAL, signal) \ - -#define UV_REQ_TYPE_MAP(XX) \ - XX(REQ, req) \ - XX(CONNECT, connect) \ - XX(WRITE, write) \ - XX(SHUTDOWN, shutdown) \ - XX(UDP_SEND, udp_send) \ - XX(FS, fs) \ - XX(WORK, work) \ - XX(GETADDRINFO, getaddrinfo) \ - XX(GETNAMEINFO, getnameinfo) \ - XX(RANDOM, random) \ - -typedef enum { -#define XX(code, _) UV_ ## code = UV__ ## code, - UV_ERRNO_MAP(XX) -#undef XX - UV_ERRNO_MAX = UV__EOF - 1 -} uv_errno_t; - -typedef enum { - UV_UNKNOWN_HANDLE = 0, -#define XX(uc, lc) UV_##uc, - UV_HANDLE_TYPE_MAP(XX) -#undef XX - UV_FILE, - UV_HANDLE_TYPE_MAX -} uv_handle_type; - -typedef enum { - UV_UNKNOWN_REQ = 0, -#define XX(uc, lc) UV_##uc, - UV_REQ_TYPE_MAP(XX) -#undef XX - UV_REQ_TYPE_PRIVATE - UV_REQ_TYPE_MAX -} uv_req_type; - - -/* Handle types. */ -typedef struct uv_loop_s uv_loop_t; -typedef struct uv_handle_s uv_handle_t; -typedef struct uv_dir_s uv_dir_t; -typedef struct uv_stream_s uv_stream_t; -typedef struct uv_tcp_s uv_tcp_t; -typedef struct uv_udp_s uv_udp_t; -typedef struct uv_pipe_s uv_pipe_t; -typedef struct uv_tty_s uv_tty_t; -typedef struct uv_poll_s uv_poll_t; -typedef struct uv_timer_s uv_timer_t; -typedef struct uv_prepare_s uv_prepare_t; -typedef struct uv_check_s uv_check_t; -typedef struct uv_idle_s uv_idle_t; -typedef struct uv_async_s uv_async_t; -typedef struct uv_process_s uv_process_t; -typedef struct uv_fs_event_s uv_fs_event_t; -typedef struct uv_fs_poll_s uv_fs_poll_t; -typedef struct uv_signal_s uv_signal_t; - -/* Request types. */ -typedef struct uv_req_s uv_req_t; -typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; -typedef struct uv_getnameinfo_s uv_getnameinfo_t; -typedef struct uv_shutdown_s uv_shutdown_t; -typedef struct uv_write_s uv_write_t; -typedef struct uv_connect_s uv_connect_t; -typedef struct uv_udp_send_s uv_udp_send_t; -typedef struct uv_fs_s uv_fs_t; -typedef struct uv_work_s uv_work_t; -typedef struct uv_random_s uv_random_t; - -/* None of the above. */ -typedef struct uv_env_item_s uv_env_item_t; -typedef struct uv_cpu_info_s uv_cpu_info_t; -typedef struct uv_interface_address_s uv_interface_address_t; -typedef struct uv_dirent_s uv_dirent_t; -typedef struct uv_passwd_s uv_passwd_t; -typedef struct uv_group_s uv_group_t; -typedef struct uv_utsname_s uv_utsname_t; -typedef struct uv_statfs_s uv_statfs_t; - -typedef struct uv_metrics_s uv_metrics_t; - -typedef enum { - UV_LOOP_BLOCK_SIGNAL = 0, - UV_METRICS_IDLE_TIME -} uv_loop_option; - -typedef enum { - UV_RUN_DEFAULT = 0, - UV_RUN_ONCE, - UV_RUN_NOWAIT -} uv_run_mode; - - -UV_EXTERN unsigned int uv_version(void); -UV_EXTERN const char* uv_version_string(void); - -typedef void* (*uv_malloc_func)(size_t size); -typedef void* (*uv_realloc_func)(void* ptr, size_t size); -typedef void* (*uv_calloc_func)(size_t count, size_t size); -typedef void (*uv_free_func)(void* ptr); - -UV_EXTERN void uv_library_shutdown(void); - -UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func, - uv_realloc_func realloc_func, - uv_calloc_func calloc_func, - uv_free_func free_func); - -UV_EXTERN uv_loop_t* uv_default_loop(void); -UV_EXTERN int uv_loop_init(uv_loop_t* loop); -UV_EXTERN int uv_loop_close(uv_loop_t* loop); -/* - * NOTE: - * This function is DEPRECATED, users should - * allocate the loop manually and use uv_loop_init instead. - */ -UV_EXTERN uv_loop_t* uv_loop_new(void); -/* - * NOTE: - * This function is DEPRECATED. Users should use - * uv_loop_close and free the memory manually instead. - */ -UV_EXTERN void uv_loop_delete(uv_loop_t*); -UV_EXTERN size_t uv_loop_size(void); -UV_EXTERN int uv_loop_alive(const uv_loop_t* loop); -UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...); -UV_EXTERN int uv_loop_fork(uv_loop_t* loop); - -UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode); -UV_EXTERN void uv_stop(uv_loop_t*); - -UV_EXTERN void uv_ref(uv_handle_t*); -UV_EXTERN void uv_unref(uv_handle_t*); -UV_EXTERN int uv_has_ref(const uv_handle_t*); - -UV_EXTERN void uv_update_time(uv_loop_t*); -UV_EXTERN uint64_t uv_now(const uv_loop_t*); - -UV_EXTERN int uv_backend_fd(const uv_loop_t*); -UV_EXTERN int uv_backend_timeout(const uv_loop_t*); - -typedef void (*uv_alloc_cb)(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf); -typedef void (*uv_read_cb)(uv_stream_t* stream, - ssize_t nread, - const uv_buf_t* buf); -typedef void (*uv_write_cb)(uv_write_t* req, int status); -typedef void (*uv_connect_cb)(uv_connect_t* req, int status); -typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status); -typedef void (*uv_connection_cb)(uv_stream_t* server, int status); -typedef void (*uv_close_cb)(uv_handle_t* handle); -typedef void (*uv_poll_cb)(uv_poll_t* handle, int status, int events); -typedef void (*uv_timer_cb)(uv_timer_t* handle); -typedef void (*uv_async_cb)(uv_async_t* handle); -typedef void (*uv_prepare_cb)(uv_prepare_t* handle); -typedef void (*uv_check_cb)(uv_check_t* handle); -typedef void (*uv_idle_cb)(uv_idle_t* handle); -typedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal); -typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg); -typedef void (*uv_fs_cb)(uv_fs_t* req); -typedef void (*uv_work_cb)(uv_work_t* req); -typedef void (*uv_after_work_cb)(uv_work_t* req, int status); -typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, - int status, - struct addrinfo* res); -typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, - int status, - const char* hostname, - const char* service); -typedef void (*uv_random_cb)(uv_random_t* req, - int status, - void* buf, - size_t buflen); - -typedef enum { - UV_CLOCK_MONOTONIC, - UV_CLOCK_REALTIME -} uv_clock_id; - -/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */ -typedef struct { - long tv_sec; - long tv_nsec; -} uv_timespec_t; - -typedef struct { - int64_t tv_sec; - int32_t tv_nsec; -} uv_timespec64_t; - -/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */ -typedef struct { - long tv_sec; - long tv_usec; -} uv_timeval_t; - -typedef struct { - int64_t tv_sec; - int32_t tv_usec; -} uv_timeval64_t; - -typedef struct { - uint64_t st_dev; - uint64_t st_mode; - uint64_t st_nlink; - uint64_t st_uid; - uint64_t st_gid; - uint64_t st_rdev; - uint64_t st_ino; - uint64_t st_size; - uint64_t st_blksize; - uint64_t st_blocks; - uint64_t st_flags; - uint64_t st_gen; - uv_timespec_t st_atim; - uv_timespec_t st_mtim; - uv_timespec_t st_ctim; - uv_timespec_t st_birthtim; -} uv_stat_t; - - -typedef void (*uv_fs_event_cb)(uv_fs_event_t* handle, - const char* filename, - int events, - int status); - -typedef void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, - int status, - const uv_stat_t* prev, - const uv_stat_t* curr); - -typedef void (*uv_signal_cb)(uv_signal_t* handle, int signum); - - -typedef enum { - UV_LEAVE_GROUP = 0, - UV_JOIN_GROUP -} uv_membership; - - -UV_EXTERN int uv_translate_sys_error(int sys_errno); - -UV_EXTERN const char* uv_strerror(int err); -UV_EXTERN char* uv_strerror_r(int err, char* buf, size_t buflen); - -UV_EXTERN const char* uv_err_name(int err); -UV_EXTERN char* uv_err_name_r(int err, char* buf, size_t buflen); - - -#define UV_REQ_FIELDS \ - /* public */ \ - void* data; \ - /* read-only */ \ - uv_req_type type; \ - /* private */ \ - void* reserved[6]; \ - UV_REQ_PRIVATE_FIELDS \ - -/* Abstract base class of all requests. */ -struct uv_req_s { - UV_REQ_FIELDS -}; - - -/* Platform-specific request types. */ -UV_PRIVATE_REQ_TYPES - - -UV_EXTERN int uv_shutdown(uv_shutdown_t* req, - uv_stream_t* handle, - uv_shutdown_cb cb); - -struct uv_shutdown_s { - UV_REQ_FIELDS - uv_stream_t* handle; - uv_shutdown_cb cb; - UV_SHUTDOWN_PRIVATE_FIELDS -}; - - -#define UV_HANDLE_FIELDS \ - /* public */ \ - void* data; \ - /* read-only */ \ - uv_loop_t* loop; \ - uv_handle_type type; \ - /* private */ \ - uv_close_cb close_cb; \ - struct uv__queue handle_queue; \ - union { \ - int fd; \ - void* reserved[4]; \ - } u; \ - UV_HANDLE_PRIVATE_FIELDS \ - -/* The abstract base class of all handles. */ -struct uv_handle_s { - UV_HANDLE_FIELDS -}; - -UV_EXTERN size_t uv_handle_size(uv_handle_type type); -UV_EXTERN uv_handle_type uv_handle_get_type(const uv_handle_t* handle); -UV_EXTERN const char* uv_handle_type_name(uv_handle_type type); -UV_EXTERN void* uv_handle_get_data(const uv_handle_t* handle); -UV_EXTERN uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle); -UV_EXTERN void uv_handle_set_data(uv_handle_t* handle, void* data); - -UV_EXTERN size_t uv_req_size(uv_req_type type); -UV_EXTERN void* uv_req_get_data(const uv_req_t* req); -UV_EXTERN void uv_req_set_data(uv_req_t* req, void* data); -UV_EXTERN uv_req_type uv_req_get_type(const uv_req_t* req); -UV_EXTERN const char* uv_req_type_name(uv_req_type type); - -UV_EXTERN int uv_is_active(const uv_handle_t* handle); - -UV_EXTERN void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg); - -/* Helpers for ad hoc debugging, no API/ABI stability guaranteed. */ -UV_EXTERN void uv_print_all_handles(uv_loop_t* loop, FILE* stream); -UV_EXTERN void uv_print_active_handles(uv_loop_t* loop, FILE* stream); - -UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb); - -UV_EXTERN int uv_send_buffer_size(uv_handle_t* handle, int* value); -UV_EXTERN int uv_recv_buffer_size(uv_handle_t* handle, int* value); - -UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); - -UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); - -UV_EXTERN int uv_pipe(uv_file fds[2], int read_flags, int write_flags); -UV_EXTERN int uv_socketpair(int type, - int protocol, - uv_os_sock_t socket_vector[2], - int flags0, - int flags1); - -#define UV_STREAM_FIELDS \ - /* number of bytes queued for writing */ \ - size_t write_queue_size; \ - uv_alloc_cb alloc_cb; \ - uv_read_cb read_cb; \ - /* private */ \ - UV_STREAM_PRIVATE_FIELDS - -/* - * uv_stream_t is a subclass of uv_handle_t. - * - * uv_stream is an abstract class. - * - * uv_stream_t is the parent class of uv_tcp_t, uv_pipe_t and uv_tty_t. - */ -struct uv_stream_s { - UV_HANDLE_FIELDS - UV_STREAM_FIELDS -}; - -UV_EXTERN size_t uv_stream_get_write_queue_size(const uv_stream_t* stream); - -UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); -UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client); - -UV_EXTERN int uv_read_start(uv_stream_t*, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb); -UV_EXTERN int uv_read_stop(uv_stream_t*); - -UV_EXTERN int uv_write(uv_write_t* req, - uv_stream_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_write_cb cb); -UV_EXTERN int uv_write2(uv_write_t* req, - uv_stream_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_stream_t* send_handle, - uv_write_cb cb); -UV_EXTERN int uv_try_write(uv_stream_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs); -UV_EXTERN int uv_try_write2(uv_stream_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_stream_t* send_handle); - -/* uv_write_t is a subclass of uv_req_t. */ -struct uv_write_s { - UV_REQ_FIELDS - uv_write_cb cb; - uv_stream_t* send_handle; /* TODO: make private and unix-only in v2.x. */ - uv_stream_t* handle; - UV_WRITE_PRIVATE_FIELDS -}; - - -UV_EXTERN int uv_is_readable(const uv_stream_t* handle); -UV_EXTERN int uv_is_writable(const uv_stream_t* handle); - -UV_EXTERN int uv_stream_set_blocking(uv_stream_t* handle, int blocking); - -UV_EXTERN int uv_is_closing(const uv_handle_t* handle); - - -/* - * uv_tcp_t is a subclass of uv_stream_t. - * - * Represents a TCP stream or TCP server. - */ -struct uv_tcp_s { - UV_HANDLE_FIELDS - UV_STREAM_FIELDS - UV_TCP_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_tcp_init(uv_loop_t*, uv_tcp_t* handle); -UV_EXTERN int uv_tcp_init_ex(uv_loop_t*, uv_tcp_t* handle, unsigned int flags); -UV_EXTERN int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock); -UV_EXTERN int uv_tcp_nodelay(uv_tcp_t* handle, int enable); -UV_EXTERN int uv_tcp_keepalive(uv_tcp_t* handle, - int enable, - unsigned int delay); -UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable); - -enum uv_tcp_flags { - /* Used with uv_tcp_bind, when an IPv6 address is used. */ - UV_TCP_IPV6ONLY = 1 -}; - -UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle, - const struct sockaddr* addr, - unsigned int flags); -UV_EXTERN int uv_tcp_getsockname(const uv_tcp_t* handle, - struct sockaddr* name, - int* namelen); -UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle, - struct sockaddr* name, - int* namelen); -UV_EXTERN int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb); -UV_EXTERN int uv_tcp_connect(uv_connect_t* req, - uv_tcp_t* handle, - const struct sockaddr* addr, - uv_connect_cb cb); - -/* uv_connect_t is a subclass of uv_req_t. */ -struct uv_connect_s { - UV_REQ_FIELDS - uv_connect_cb cb; - uv_stream_t* handle; - UV_CONNECT_PRIVATE_FIELDS -}; - - -/* - * UDP support. - */ - -enum uv_udp_flags { - /* Disables dual stack mode. */ - UV_UDP_IPV6ONLY = 1, - /* - * Indicates message was truncated because read buffer was too small. The - * remainder was discarded by the OS. Used in uv_udp_recv_cb. - */ - UV_UDP_PARTIAL = 2, - /* - * Indicates if SO_REUSEADDR will be set when binding the handle. - * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other - * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that - * multiple threads or processes can bind to the same address without error - * (provided they all set the flag) but only the last one to bind will receive - * any traffic, in effect "stealing" the port from the previous listener. - */ - UV_UDP_REUSEADDR = 4, - /* - * Indicates that the message was received by recvmmsg, so the buffer provided - * must not be freed by the recv_cb callback. - */ - UV_UDP_MMSG_CHUNK = 8, - /* - * Indicates that the buffer provided has been fully utilized by recvmmsg and - * that it should now be freed by the recv_cb callback. When this flag is set - * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. - */ - UV_UDP_MMSG_FREE = 16, - /* - * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. - * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on - * Linux. This stops the Linux kernel from suppressing some ICMP error - * messages and enables full ICMP error reporting for faster failover. - * This flag is no-op on platforms other than Linux. - */ - UV_UDP_LINUX_RECVERR = 32, - /* - * Indicates that recvmmsg should be used, if available. - */ - UV_UDP_RECVMMSG = 256 -}; - -typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); -typedef void (*uv_udp_recv_cb)(uv_udp_t* handle, - ssize_t nread, - const uv_buf_t* buf, - const struct sockaddr* addr, - unsigned flags); - -/* uv_udp_t is a subclass of uv_handle_t. */ -struct uv_udp_s { - UV_HANDLE_FIELDS - /* read-only */ - /* - * Number of bytes queued for sending. This field strictly shows how much - * information is currently queued. - */ - size_t send_queue_size; - /* - * Number of send requests currently in the queue awaiting to be processed. - */ - size_t send_queue_count; - UV_UDP_PRIVATE_FIELDS -}; - -/* uv_udp_send_t is a subclass of uv_req_t. */ -struct uv_udp_send_s { - UV_REQ_FIELDS - uv_udp_t* handle; - uv_udp_send_cb cb; - UV_UDP_SEND_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_udp_init(uv_loop_t*, uv_udp_t* handle); -UV_EXTERN int uv_udp_init_ex(uv_loop_t*, uv_udp_t* handle, unsigned int flags); -UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); -UV_EXTERN int uv_udp_bind(uv_udp_t* handle, - const struct sockaddr* addr, - unsigned int flags); -UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr); - -UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle, - struct sockaddr* name, - int* namelen); -UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle, - struct sockaddr* name, - int* namelen); -UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, - const char* multicast_addr, - const char* interface_addr, - uv_membership membership); -UV_EXTERN int uv_udp_set_source_membership(uv_udp_t* handle, - const char* multicast_addr, - const char* interface_addr, - const char* source_addr, - uv_membership membership); -UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on); -UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); -UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, - const char* interface_addr); -UV_EXTERN int uv_udp_set_broadcast(uv_udp_t* handle, int on); -UV_EXTERN int uv_udp_set_ttl(uv_udp_t* handle, int ttl); -UV_EXTERN int uv_udp_send(uv_udp_send_t* req, - uv_udp_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - const struct sockaddr* addr, - uv_udp_send_cb send_cb); -UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - const struct sockaddr* addr); -UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, - uv_alloc_cb alloc_cb, - uv_udp_recv_cb recv_cb); -UV_EXTERN int uv_udp_using_recvmmsg(const uv_udp_t* handle); -UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); -UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle); -UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle); - - -/* - * uv_tty_t is a subclass of uv_stream_t. - * - * Representing a stream for the console. - */ -struct uv_tty_s { - UV_HANDLE_FIELDS - UV_STREAM_FIELDS - UV_TTY_PRIVATE_FIELDS -}; - -typedef enum { - /* Initial/normal terminal mode */ - UV_TTY_MODE_NORMAL, - /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */ - UV_TTY_MODE_RAW, - /* Binary-safe I/O mode for IPC (Unix-only) */ - UV_TTY_MODE_IO -} uv_tty_mode_t; - -typedef enum { - /* - * The console supports handling of virtual terminal sequences - * (Windows10 new console, ConEmu) - */ - UV_TTY_SUPPORTED, - /* The console cannot process the virtual terminal sequence. (Legacy - * console) - */ - UV_TTY_UNSUPPORTED -} uv_tty_vtermstate_t; - - -UV_EXTERN int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable); -UV_EXTERN int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode); -UV_EXTERN int uv_tty_reset_mode(void); -UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); -UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state); -UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state); - -#ifdef __cplusplus -extern "C++" { - -inline int uv_tty_set_mode(uv_tty_t* handle, int mode) { - return uv_tty_set_mode(handle, static_cast(mode)); -} - -} -#endif - -UV_EXTERN uv_handle_type uv_guess_handle(uv_file file); - -enum { - UV_PIPE_NO_TRUNCATE = 1u << 0 -}; - -/* - * uv_pipe_t is a subclass of uv_stream_t. - * - * Representing a pipe stream or pipe server. On Windows this is a Named - * Pipe. On Unix this is a Unix domain socket. - */ -struct uv_pipe_s { - UV_HANDLE_FIELDS - UV_STREAM_FIELDS - int ipc; /* non-zero if this pipe is used for passing handles */ - UV_PIPE_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); -UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file); -UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); -UV_EXTERN int uv_pipe_bind2(uv_pipe_t* handle, - const char* name, - size_t namelen, - unsigned int flags); -UV_EXTERN void uv_pipe_connect(uv_connect_t* req, - uv_pipe_t* handle, - const char* name, - uv_connect_cb cb); -UV_EXTERN int uv_pipe_connect2(uv_connect_t* req, - uv_pipe_t* handle, - const char* name, - size_t namelen, - unsigned int flags, - uv_connect_cb cb); -UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, - char* buffer, - size_t* size); -UV_EXTERN int uv_pipe_getpeername(const uv_pipe_t* handle, - char* buffer, - size_t* size); -UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count); -UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle); -UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); -UV_EXTERN int uv_pipe_chmod(uv_pipe_t* handle, int flags); - - -struct uv_poll_s { - UV_HANDLE_FIELDS - uv_poll_cb poll_cb; - UV_POLL_PRIVATE_FIELDS -}; - -enum uv_poll_event { - UV_READABLE = 1, - UV_WRITABLE = 2, - UV_DISCONNECT = 4, - UV_PRIORITIZED = 8 -}; - -UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd); -UV_EXTERN int uv_poll_init_socket(uv_loop_t* loop, - uv_poll_t* handle, - uv_os_sock_t socket); -UV_EXTERN int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb); -UV_EXTERN int uv_poll_stop(uv_poll_t* handle); - - -struct uv_prepare_s { - UV_HANDLE_FIELDS - UV_PREPARE_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_prepare_init(uv_loop_t*, uv_prepare_t* prepare); -UV_EXTERN int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb); -UV_EXTERN int uv_prepare_stop(uv_prepare_t* prepare); - - -struct uv_check_s { - UV_HANDLE_FIELDS - UV_CHECK_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_check_init(uv_loop_t*, uv_check_t* check); -UV_EXTERN int uv_check_start(uv_check_t* check, uv_check_cb cb); -UV_EXTERN int uv_check_stop(uv_check_t* check); - - -struct uv_idle_s { - UV_HANDLE_FIELDS - UV_IDLE_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_idle_init(uv_loop_t*, uv_idle_t* idle); -UV_EXTERN int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb); -UV_EXTERN int uv_idle_stop(uv_idle_t* idle); - - -struct uv_async_s { - UV_HANDLE_FIELDS - UV_ASYNC_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_async_init(uv_loop_t*, - uv_async_t* async, - uv_async_cb async_cb); -UV_EXTERN int uv_async_send(uv_async_t* async); - - -/* - * uv_timer_t is a subclass of uv_handle_t. - * - * Used to get woken up at a specified time in the future. - */ -struct uv_timer_s { - UV_HANDLE_FIELDS - UV_TIMER_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_timer_init(uv_loop_t*, uv_timer_t* handle); -UV_EXTERN int uv_timer_start(uv_timer_t* handle, - uv_timer_cb cb, - uint64_t timeout, - uint64_t repeat); -UV_EXTERN int uv_timer_stop(uv_timer_t* handle); -UV_EXTERN int uv_timer_again(uv_timer_t* handle); -UV_EXTERN void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat); -UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle); -UV_EXTERN uint64_t uv_timer_get_due_in(const uv_timer_t* handle); - - -/* - * uv_getaddrinfo_t is a subclass of uv_req_t. - * - * Request object for uv_getaddrinfo. - */ -struct uv_getaddrinfo_s { - UV_REQ_FIELDS - /* read-only */ - uv_loop_t* loop; - /* struct addrinfo* addrinfo is marked as private, but it really isn't. */ - UV_GETADDRINFO_PRIVATE_FIELDS -}; - - -UV_EXTERN int uv_getaddrinfo(uv_loop_t* loop, - uv_getaddrinfo_t* req, - uv_getaddrinfo_cb getaddrinfo_cb, - const char* node, - const char* service, - const struct addrinfo* hints); -UV_EXTERN void uv_freeaddrinfo(struct addrinfo* ai); - - -/* -* uv_getnameinfo_t is a subclass of uv_req_t. -* -* Request object for uv_getnameinfo. -*/ -struct uv_getnameinfo_s { - UV_REQ_FIELDS - /* read-only */ - uv_loop_t* loop; - /* host and service are marked as private, but they really aren't. */ - UV_GETNAMEINFO_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_getnameinfo(uv_loop_t* loop, - uv_getnameinfo_t* req, - uv_getnameinfo_cb getnameinfo_cb, - const struct sockaddr* addr, - int flags); - - -/* uv_spawn() options. */ -typedef enum { - UV_IGNORE = 0x00, - UV_CREATE_PIPE = 0x01, - UV_INHERIT_FD = 0x02, - UV_INHERIT_STREAM = 0x04, - - /* - * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE - * determine the direction of flow, from the child process' perspective. Both - * flags may be specified to create a duplex data stream. - */ - UV_READABLE_PIPE = 0x10, - UV_WRITABLE_PIPE = 0x20, - - /* - * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the - * handle in non-blocking mode in the child. This may cause loss of data, - * if the child is not designed to handle to encounter this mode, - * but can also be significantly more efficient. - */ - UV_NONBLOCK_PIPE = 0x40, - UV_OVERLAPPED_PIPE = 0x40 /* old name, for compatibility */ -} uv_stdio_flags; - -typedef struct uv_stdio_container_s { - uv_stdio_flags flags; - - union { - uv_stream_t* stream; - int fd; - } data; -} uv_stdio_container_t; - -typedef struct uv_process_options_s { - uv_exit_cb exit_cb; /* Called after the process exits. */ - const char* file; /* Path to program to execute. */ - /* - * Command line arguments. args[0] should be the path to the program. On - * Windows this uses CreateProcess which concatenates the arguments into a - * string this can cause some strange errors. See the note at - * windows_verbatim_arguments. - */ - char** args; - /* - * This will be set as the environ variable in the subprocess. If this is - * NULL then the parents environ will be used. - */ - char** env; - /* - * If non-null this represents a directory the subprocess should execute - * in. Stands for current working directory. - */ - const char* cwd; - /* - * Various flags that control how uv_spawn() behaves. See the definition of - * `enum uv_process_flags` below. - */ - unsigned int flags; - /* - * The `stdio` field points to an array of uv_stdio_container_t structs that - * describe the file descriptors that will be made available to the child - * process. The convention is that stdio[0] points to stdin, fd 1 is used for - * stdout, and fd 2 is stderr. - * - * Note that on windows file descriptors greater than 2 are available to the - * child process only if the child processes uses the MSVCRT runtime. - */ - int stdio_count; - uv_stdio_container_t* stdio; - /* - * Libuv can change the child process' user/group id. This happens only when - * the appropriate bits are set in the flags fields. This is not supported on - * windows; uv_spawn() will fail and set the error to UV_ENOTSUP. - */ - uv_uid_t uid; - uv_gid_t gid; -} uv_process_options_t; - -/* - * These are the flags that can be used for the uv_process_options.flags field. - */ -enum uv_process_flags { - /* - * Set the child process' user id. The user id is supplied in the `uid` field - * of the options struct. This does not work on windows; setting this flag - * will cause uv_spawn() to fail. - */ - UV_PROCESS_SETUID = (1 << 0), - /* - * Set the child process' group id. The user id is supplied in the `gid` - * field of the options struct. This does not work on windows; setting this - * flag will cause uv_spawn() to fail. - */ - UV_PROCESS_SETGID = (1 << 1), - /* - * Do not wrap any arguments in quotes, or perform any other escaping, when - * converting the argument list into a command line string. This option is - * only meaningful on Windows systems. On Unix it is silently ignored. - */ - UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2), - /* - * Spawn the child process in a detached state - this will make it a process - * group leader, and will effectively enable the child to keep running after - * the parent exits. Note that the child process will still keep the - * parent's event loop alive unless the parent process calls uv_unref() on - * the child's process handle. - */ - UV_PROCESS_DETACHED = (1 << 3), - /* - * Hide the subprocess window that would normally be created. This option is - * only meaningful on Windows systems. On Unix it is silently ignored. - */ - UV_PROCESS_WINDOWS_HIDE = (1 << 4), - /* - * Hide the subprocess console window that would normally be created. This - * option is only meaningful on Windows systems. On Unix it is silently - * ignored. - */ - UV_PROCESS_WINDOWS_HIDE_CONSOLE = (1 << 5), - /* - * Hide the subprocess GUI window that would normally be created. This - * option is only meaningful on Windows systems. On Unix it is silently - * ignored. - */ - UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6) -}; - -/* - * uv_process_t is a subclass of uv_handle_t. - */ -struct uv_process_s { - UV_HANDLE_FIELDS - uv_exit_cb exit_cb; - int pid; - UV_PROCESS_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_spawn(uv_loop_t* loop, - uv_process_t* handle, - const uv_process_options_t* options); -UV_EXTERN int uv_process_kill(uv_process_t*, int signum); -UV_EXTERN int uv_kill(int pid, int signum); -UV_EXTERN uv_pid_t uv_process_get_pid(const uv_process_t*); - - -/* - * uv_work_t is a subclass of uv_req_t. - */ -struct uv_work_s { - UV_REQ_FIELDS - uv_loop_t* loop; - uv_work_cb work_cb; - uv_after_work_cb after_work_cb; - UV_WORK_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_queue_work(uv_loop_t* loop, - uv_work_t* req, - uv_work_cb work_cb, - uv_after_work_cb after_work_cb); - -UV_EXTERN int uv_cancel(uv_req_t* req); - - -struct uv_cpu_times_s { - uint64_t user; /* milliseconds */ - uint64_t nice; /* milliseconds */ - uint64_t sys; /* milliseconds */ - uint64_t idle; /* milliseconds */ - uint64_t irq; /* milliseconds */ -}; - -struct uv_cpu_info_s { - char* model; - int speed; - struct uv_cpu_times_s cpu_times; -}; - -struct uv_interface_address_s { - char* name; - char phys_addr[6]; - int is_internal; - union { - struct sockaddr_in address4; - struct sockaddr_in6 address6; - } address; - union { - struct sockaddr_in netmask4; - struct sockaddr_in6 netmask6; - } netmask; -}; - -struct uv_passwd_s { - char* username; - unsigned long uid; - unsigned long gid; - char* shell; - char* homedir; -}; - -struct uv_group_s { - char* groupname; - unsigned long gid; - char** members; -}; - -struct uv_utsname_s { - char sysname[256]; - char release[256]; - char version[256]; - char machine[256]; - /* This struct does not contain the nodename and domainname fields present in - the utsname type. domainname is a GNU extension. Both fields are referred - to as meaningless in the docs. */ -}; - -struct uv_statfs_s { - uint64_t f_type; - uint64_t f_bsize; - uint64_t f_blocks; - uint64_t f_bfree; - uint64_t f_bavail; - uint64_t f_files; - uint64_t f_ffree; - uint64_t f_spare[4]; -}; - -typedef enum { - UV_DIRENT_UNKNOWN, - UV_DIRENT_FILE, - UV_DIRENT_DIR, - UV_DIRENT_LINK, - UV_DIRENT_FIFO, - UV_DIRENT_SOCKET, - UV_DIRENT_CHAR, - UV_DIRENT_BLOCK -} uv_dirent_type_t; - -struct uv_dirent_s { - const char* name; - uv_dirent_type_t type; -}; - -UV_EXTERN char** uv_setup_args(int argc, char** argv); -UV_EXTERN int uv_get_process_title(char* buffer, size_t size); -UV_EXTERN int uv_set_process_title(const char* title); -UV_EXTERN int uv_resident_set_memory(size_t* rss); -UV_EXTERN int uv_uptime(double* uptime); -UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd); -UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd); - -typedef struct { - uv_timeval_t ru_utime; /* user CPU time used */ - uv_timeval_t ru_stime; /* system CPU time used */ - uint64_t ru_maxrss; /* maximum resident set size */ - uint64_t ru_ixrss; /* integral shared memory size */ - uint64_t ru_idrss; /* integral unshared data size */ - uint64_t ru_isrss; /* integral unshared stack size */ - uint64_t ru_minflt; /* page reclaims (soft page faults) */ - uint64_t ru_majflt; /* page faults (hard page faults) */ - uint64_t ru_nswap; /* swaps */ - uint64_t ru_inblock; /* block input operations */ - uint64_t ru_oublock; /* block output operations */ - uint64_t ru_msgsnd; /* IPC messages sent */ - uint64_t ru_msgrcv; /* IPC messages received */ - uint64_t ru_nsignals; /* signals received */ - uint64_t ru_nvcsw; /* voluntary context switches */ - uint64_t ru_nivcsw; /* involuntary context switches */ -} uv_rusage_t; - -UV_EXTERN int uv_getrusage(uv_rusage_t* rusage); - -UV_EXTERN int uv_os_homedir(char* buffer, size_t* size); -UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size); -UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd); -UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); -UV_EXTERN int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid); -UV_EXTERN int uv_os_get_group(uv_group_t* grp, uv_uid_t gid); -UV_EXTERN void uv_os_free_group(uv_group_t* grp); -UV_EXTERN uv_pid_t uv_os_getpid(void); -UV_EXTERN uv_pid_t uv_os_getppid(void); - -#if defined(__PASE__) -/* On IBM i PASE, the highest process priority is -10 */ -# define UV_PRIORITY_LOW 39 /* RUNPTY(99) */ -# define UV_PRIORITY_BELOW_NORMAL 15 /* RUNPTY(50) */ -# define UV_PRIORITY_NORMAL 0 /* RUNPTY(20) */ -# define UV_PRIORITY_ABOVE_NORMAL -4 /* RUNTY(12) */ -# define UV_PRIORITY_HIGH -7 /* RUNPTY(6) */ -# define UV_PRIORITY_HIGHEST -10 /* RUNPTY(1) */ -#else -# define UV_PRIORITY_LOW 19 -# define UV_PRIORITY_BELOW_NORMAL 10 -# define UV_PRIORITY_NORMAL 0 -# define UV_PRIORITY_ABOVE_NORMAL -7 -# define UV_PRIORITY_HIGH -14 -# define UV_PRIORITY_HIGHEST -20 -#endif - -UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority); -UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority); - -UV_EXTERN unsigned int uv_available_parallelism(void); -UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); -UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); -UV_EXTERN int uv_cpumask_size(void); - -UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, - int* count); -UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, - int count); - -struct uv_env_item_s { - char* name; - char* value; -}; - -UV_EXTERN int uv_os_environ(uv_env_item_t** envitems, int* count); -UV_EXTERN void uv_os_free_environ(uv_env_item_t* envitems, int count); -UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size); -UV_EXTERN int uv_os_setenv(const char* name, const char* value); -UV_EXTERN int uv_os_unsetenv(const char* name); - -#ifdef MAXHOSTNAMELEN -# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1) -#else - /* - Fallback for the maximum hostname size, including the null terminator. The - Windows gethostname() documentation states that 256 bytes will always be - large enough to hold the null-terminated hostname. - */ -# define UV_MAXHOSTNAMESIZE 256 -#endif - -UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); - -UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); - -struct uv_metrics_s { - uint64_t loop_count; - uint64_t events; - uint64_t events_waiting; - /* private */ - uint64_t* reserved[13]; -}; - -UV_EXTERN int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics); -UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop); - -typedef enum { - UV_FS_UNKNOWN = -1, - UV_FS_CUSTOM, - UV_FS_OPEN, - UV_FS_CLOSE, - UV_FS_READ, - UV_FS_WRITE, - UV_FS_SENDFILE, - UV_FS_STAT, - UV_FS_LSTAT, - UV_FS_FSTAT, - UV_FS_FTRUNCATE, - UV_FS_UTIME, - UV_FS_FUTIME, - UV_FS_ACCESS, - UV_FS_CHMOD, - UV_FS_FCHMOD, - UV_FS_FSYNC, - UV_FS_FDATASYNC, - UV_FS_UNLINK, - UV_FS_RMDIR, - UV_FS_MKDIR, - UV_FS_MKDTEMP, - UV_FS_RENAME, - UV_FS_SCANDIR, - UV_FS_LINK, - UV_FS_SYMLINK, - UV_FS_READLINK, - UV_FS_CHOWN, - UV_FS_FCHOWN, - UV_FS_REALPATH, - UV_FS_COPYFILE, - UV_FS_LCHOWN, - UV_FS_OPENDIR, - UV_FS_READDIR, - UV_FS_CLOSEDIR, - UV_FS_STATFS, - UV_FS_MKSTEMP, - UV_FS_LUTIME -} uv_fs_type; - -struct uv_dir_s { - uv_dirent_t* dirents; - size_t nentries; - void* reserved[4]; - UV_DIR_PRIVATE_FIELDS -}; - -/* uv_fs_t is a subclass of uv_req_t. */ -struct uv_fs_s { - UV_REQ_FIELDS - uv_fs_type fs_type; - uv_loop_t* loop; - uv_fs_cb cb; - ssize_t result; - void* ptr; - const char* path; - uv_stat_t statbuf; /* Stores the result of uv_fs_stat() and uv_fs_fstat(). */ - UV_FS_PRIVATE_FIELDS -}; - -UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*); -UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*); -UV_EXTERN int uv_fs_get_system_error(const uv_fs_t*); -UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*); -UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*); -UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*); - -UV_EXTERN void uv_fs_req_cleanup(uv_fs_t* req); -UV_EXTERN int uv_fs_close(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - uv_fs_cb cb); -UV_EXTERN int uv_fs_open(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - int flags, - int mode, - uv_fs_cb cb); -UV_EXTERN int uv_fs_read(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - const uv_buf_t bufs[], - unsigned int nbufs, - int64_t offset, - uv_fs_cb cb); -UV_EXTERN int uv_fs_unlink(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_write(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - const uv_buf_t bufs[], - unsigned int nbufs, - int64_t offset, - uv_fs_cb cb); -/* - * This flag can be used with uv_fs_copyfile() to return an error if the - * destination already exists. - */ -#define UV_FS_COPYFILE_EXCL 0x0001 - -/* - * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. - * If copy-on-write is not supported, a fallback copy mechanism is used. - */ -#define UV_FS_COPYFILE_FICLONE 0x0002 - -/* - * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. - * If copy-on-write is not supported, an error is returned. - */ -#define UV_FS_COPYFILE_FICLONE_FORCE 0x0004 - -UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - const char* new_path, - int flags, - uv_fs_cb cb); -UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - int mode, - uv_fs_cb cb); -UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, - uv_fs_t* req, - const char* tpl, - uv_fs_cb cb); -UV_EXTERN int uv_fs_mkstemp(uv_loop_t* loop, - uv_fs_t* req, - const char* tpl, - uv_fs_cb cb); -UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_scandir(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - int flags, - uv_fs_cb cb); -UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req, - uv_dirent_t* ent); -UV_EXTERN int uv_fs_opendir(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_readdir(uv_loop_t* loop, - uv_fs_t* req, - uv_dir_t* dir, - uv_fs_cb cb); -UV_EXTERN int uv_fs_closedir(uv_loop_t* loop, - uv_fs_t* req, - uv_dir_t* dir, - uv_fs_cb cb); -UV_EXTERN int uv_fs_stat(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_fstat(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - uv_fs_cb cb); -UV_EXTERN int uv_fs_rename(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - const char* new_path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_fsync(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - uv_fs_cb cb); -UV_EXTERN int uv_fs_fdatasync(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - uv_fs_cb cb); -UV_EXTERN int uv_fs_ftruncate(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - int64_t offset, - uv_fs_cb cb); -UV_EXTERN int uv_fs_sendfile(uv_loop_t* loop, - uv_fs_t* req, - uv_file out_fd, - uv_file in_fd, - int64_t in_offset, - size_t length, - uv_fs_cb cb); -UV_EXTERN int uv_fs_access(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - int mode, - uv_fs_cb cb); -UV_EXTERN int uv_fs_chmod(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - int mode, - uv_fs_cb cb); -UV_EXTERN int uv_fs_utime(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - double atime, - double mtime, - uv_fs_cb cb); -UV_EXTERN int uv_fs_futime(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - double atime, - double mtime, - uv_fs_cb cb); -UV_EXTERN int uv_fs_lutime(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - double atime, - double mtime, - uv_fs_cb cb); -UV_EXTERN int uv_fs_lstat(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_link(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - const char* new_path, - uv_fs_cb cb); - -/* - * This flag can be used with uv_fs_symlink() on Windows to specify whether - * path argument points to a directory. - */ -#define UV_FS_SYMLINK_DIR 0x0001 - -/* - * This flag can be used with uv_fs_symlink() on Windows to specify whether - * the symlink is to be created using junction points. - */ -#define UV_FS_SYMLINK_JUNCTION 0x0002 - -UV_EXTERN int uv_fs_symlink(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - const char* new_path, - int flags, - uv_fs_cb cb); -UV_EXTERN int uv_fs_readlink(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_realpath(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); -UV_EXTERN int uv_fs_fchmod(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - int mode, - uv_fs_cb cb); -UV_EXTERN int uv_fs_chown(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_uid_t uid, - uv_gid_t gid, - uv_fs_cb cb); -UV_EXTERN int uv_fs_fchown(uv_loop_t* loop, - uv_fs_t* req, - uv_file file, - uv_uid_t uid, - uv_gid_t gid, - uv_fs_cb cb); -UV_EXTERN int uv_fs_lchown(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_uid_t uid, - uv_gid_t gid, - uv_fs_cb cb); -UV_EXTERN int uv_fs_statfs(uv_loop_t* loop, - uv_fs_t* req, - const char* path, - uv_fs_cb cb); - - -enum uv_fs_event { - UV_RENAME = 1, - UV_CHANGE = 2 -}; - - -struct uv_fs_event_s { - UV_HANDLE_FIELDS - /* private */ - char* path; - UV_FS_EVENT_PRIVATE_FIELDS -}; - - -/* - * uv_fs_stat() based polling file watcher. - */ -struct uv_fs_poll_s { - UV_HANDLE_FIELDS - /* Private, don't touch. */ - void* poll_ctx; -}; - -UV_EXTERN int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle); -UV_EXTERN int uv_fs_poll_start(uv_fs_poll_t* handle, - uv_fs_poll_cb poll_cb, - const char* path, - unsigned int interval); -UV_EXTERN int uv_fs_poll_stop(uv_fs_poll_t* handle); -UV_EXTERN int uv_fs_poll_getpath(uv_fs_poll_t* handle, - char* buffer, - size_t* size); - - -struct uv_signal_s { - UV_HANDLE_FIELDS - uv_signal_cb signal_cb; - int signum; - UV_SIGNAL_PRIVATE_FIELDS -}; - -UV_EXTERN int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle); -UV_EXTERN int uv_signal_start(uv_signal_t* handle, - uv_signal_cb signal_cb, - int signum); -UV_EXTERN int uv_signal_start_oneshot(uv_signal_t* handle, - uv_signal_cb signal_cb, - int signum); -UV_EXTERN int uv_signal_stop(uv_signal_t* handle); - -UV_EXTERN void uv_loadavg(double avg[3]); - - -/* - * Flags to be passed to uv_fs_event_start(). - */ -enum uv_fs_event_flags { - /* - * By default, if the fs event watcher is given a directory name, we will - * watch for all events in that directory. This flags overrides this behavior - * and makes fs_event report only changes to the directory entry itself. This - * flag does not affect individual files watched. - * This flag is currently not implemented yet on any backend. - */ - UV_FS_EVENT_WATCH_ENTRY = 1, - - /* - * By default uv_fs_event will try to use a kernel interface such as inotify - * or kqueue to detect events. This may not work on remote filesystems such - * as NFS mounts. This flag makes fs_event fall back to calling stat() on a - * regular interval. - * This flag is currently not implemented yet on any backend. - */ - UV_FS_EVENT_STAT = 2, - - /* - * By default, event watcher, when watching directory, is not registering - * (is ignoring) changes in it's subdirectories. - * This flag will override this behaviour on platforms that support it. - */ - UV_FS_EVENT_RECURSIVE = 4 -}; - - -UV_EXTERN int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle); -UV_EXTERN int uv_fs_event_start(uv_fs_event_t* handle, - uv_fs_event_cb cb, - const char* path, - unsigned int flags); -UV_EXTERN int uv_fs_event_stop(uv_fs_event_t* handle); -UV_EXTERN int uv_fs_event_getpath(uv_fs_event_t* handle, - char* buffer, - size_t* size); - -UV_EXTERN int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr); -UV_EXTERN int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr); - -UV_EXTERN int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); -UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size); -UV_EXTERN int uv_ip_name(const struct sockaddr* src, char* dst, size_t size); - -UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); -UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); - - -struct uv_random_s { - UV_REQ_FIELDS - /* read-only */ - uv_loop_t* loop; - /* private */ - int status; - void* buf; - size_t buflen; - uv_random_cb cb; - struct uv__work work_req; -}; - -UV_EXTERN int uv_random(uv_loop_t* loop, - uv_random_t* req, - void *buf, - size_t buflen, - unsigned flags, /* For future extension; must be 0. */ - uv_random_cb cb); - -#if defined(IF_NAMESIZE) -# define UV_IF_NAMESIZE (IF_NAMESIZE + 1) -#elif defined(IFNAMSIZ) -# define UV_IF_NAMESIZE (IFNAMSIZ + 1) -#else -# define UV_IF_NAMESIZE (16 + 1) -#endif - -UV_EXTERN int uv_if_indextoname(unsigned int ifindex, - char* buffer, - size_t* size); -UV_EXTERN int uv_if_indextoiid(unsigned int ifindex, - char* buffer, - size_t* size); - -UV_EXTERN int uv_exepath(char* buffer, size_t* size); - -UV_EXTERN int uv_cwd(char* buffer, size_t* size); - -UV_EXTERN int uv_chdir(const char* dir); - -UV_EXTERN uint64_t uv_get_free_memory(void); -UV_EXTERN uint64_t uv_get_total_memory(void); -UV_EXTERN uint64_t uv_get_constrained_memory(void); -UV_EXTERN uint64_t uv_get_available_memory(void); - -UV_EXTERN int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts); -UV_EXTERN uint64_t uv_hrtime(void); -UV_EXTERN void uv_sleep(unsigned int msec); - -UV_EXTERN void uv_disable_stdio_inheritance(void); - -UV_EXTERN int uv_dlopen(const char* filename, uv_lib_t* lib); -UV_EXTERN void uv_dlclose(uv_lib_t* lib); -UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); -UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); - -UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); -UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle); -UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); -UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); -UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); -UV_EXTERN void uv_mutex_unlock(uv_mutex_t* handle); - -UV_EXTERN int uv_rwlock_init(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_destroy(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_rdlock(uv_rwlock_t* rwlock); -UV_EXTERN int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_rdunlock(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_wrlock(uv_rwlock_t* rwlock); -UV_EXTERN int uv_rwlock_trywrlock(uv_rwlock_t* rwlock); -UV_EXTERN void uv_rwlock_wrunlock(uv_rwlock_t* rwlock); - -UV_EXTERN int uv_sem_init(uv_sem_t* sem, unsigned int value); -UV_EXTERN void uv_sem_destroy(uv_sem_t* sem); -UV_EXTERN void uv_sem_post(uv_sem_t* sem); -UV_EXTERN void uv_sem_wait(uv_sem_t* sem); -UV_EXTERN int uv_sem_trywait(uv_sem_t* sem); - -UV_EXTERN int uv_cond_init(uv_cond_t* cond); -UV_EXTERN void uv_cond_destroy(uv_cond_t* cond); -UV_EXTERN void uv_cond_signal(uv_cond_t* cond); -UV_EXTERN void uv_cond_broadcast(uv_cond_t* cond); - -UV_EXTERN int uv_barrier_init(uv_barrier_t* barrier, unsigned int count); -UV_EXTERN void uv_barrier_destroy(uv_barrier_t* barrier); -UV_EXTERN int uv_barrier_wait(uv_barrier_t* barrier); - -UV_EXTERN void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex); -UV_EXTERN int uv_cond_timedwait(uv_cond_t* cond, - uv_mutex_t* mutex, - uint64_t timeout); - -UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void)); - -UV_EXTERN int uv_key_create(uv_key_t* key); -UV_EXTERN void uv_key_delete(uv_key_t* key); -UV_EXTERN void* uv_key_get(uv_key_t* key); -UV_EXTERN void uv_key_set(uv_key_t* key, void* value); - -UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv); - -typedef void (*uv_thread_cb)(void* arg); - -UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg); - -typedef enum { - UV_THREAD_NO_FLAGS = 0x00, - UV_THREAD_HAS_STACK_SIZE = 0x01 -} uv_thread_create_flags; - -struct uv_thread_options_s { - unsigned int flags; - size_t stack_size; - /* More fields may be added at any time. */ -}; - -typedef struct uv_thread_options_s uv_thread_options_t; - -UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid, - const uv_thread_options_t* params, - uv_thread_cb entry, - void* arg); -UV_EXTERN int uv_thread_setaffinity(uv_thread_t* tid, - char* cpumask, - char* oldmask, - size_t mask_size); -UV_EXTERN int uv_thread_getaffinity(uv_thread_t* tid, - char* cpumask, - size_t mask_size); -UV_EXTERN int uv_thread_getcpu(void); -UV_EXTERN uv_thread_t uv_thread_self(void); -UV_EXTERN int uv_thread_join(uv_thread_t *tid); -UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2); - -/* The presence of these unions force similar struct layout. */ -#define XX(_, name) uv_ ## name ## _t name; -union uv_any_handle { - UV_HANDLE_TYPE_MAP(XX) -}; - -union uv_any_req { - UV_REQ_TYPE_MAP(XX) -}; -#undef XX - - -struct uv_loop_s { - /* User data - use this for whatever. */ - void* data; - /* Loop reference counting. */ - unsigned int active_handles; - struct uv__queue handle_queue; - union { - void* unused; - unsigned int count; - } active_reqs; - /* Internal storage for future extensions. */ - void* internal_fields; - /* Internal flag to signal loop stop. */ - unsigned int stop_flag; - UV_LOOP_PRIVATE_FIELDS -}; - -UV_EXTERN void* uv_loop_get_data(const uv_loop_t*); -UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data); - -/* Don't export the private CPP symbols. */ -#undef UV_HANDLE_TYPE_PRIVATE -#undef UV_REQ_TYPE_PRIVATE -#undef UV_REQ_PRIVATE_FIELDS -#undef UV_STREAM_PRIVATE_FIELDS -#undef UV_TCP_PRIVATE_FIELDS -#undef UV_PREPARE_PRIVATE_FIELDS -#undef UV_CHECK_PRIVATE_FIELDS -#undef UV_IDLE_PRIVATE_FIELDS -#undef UV_ASYNC_PRIVATE_FIELDS -#undef UV_TIMER_PRIVATE_FIELDS -#undef UV_GETADDRINFO_PRIVATE_FIELDS -#undef UV_GETNAMEINFO_PRIVATE_FIELDS -#undef UV_FS_REQ_PRIVATE_FIELDS -#undef UV_WORK_PRIVATE_FIELDS -#undef UV_FS_EVENT_PRIVATE_FIELDS -#undef UV_SIGNAL_PRIVATE_FIELDS -#undef UV_LOOP_PRIVATE_FIELDS -#undef UV_LOOP_PRIVATE_PLATFORM_FIELDS -#undef UV__ERR - -#ifdef __cplusplus -} -#endif -#endif /* UV_H */ diff --git a/Source/ThirdParty/uWebSockets/zconf.h b/Source/ThirdParty/uWebSockets/zconf.h index e614f9d3..828ca617 100644 --- a/Source/ThirdParty/uWebSockets/zconf.h +++ b/Source/ThirdParty/uWebSockets/zconf.h @@ -1,5 +1,5 @@ /* zconf.h -- configuration of the zlib compression library - * Copyright (C) 1995-2024 Jean-loup Gailly, Mark Adler + * Copyright (C) 1995-2026 Jean-loup Gailly, Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -7,8 +7,6 @@ #ifndef ZCONF_H #define ZCONF_H -/* #undef Z_PREFIX */ -/* #undef Z_HAVE_UNISTD_H */ /* * If you *really* need a unique prefix for all types and library functions, @@ -35,7 +33,10 @@ # ifndef Z_SOLO # define compress z_compress # define compress2 z_compress2 +# define compress_z z_compress_z +# define compress2_z z_compress2_z # define compressBound z_compressBound +# define compressBound_z z_compressBound_z # endif # define crc32 z_crc32 # define crc32_combine z_crc32_combine @@ -46,6 +47,7 @@ # define crc32_z z_crc32_z # define deflate z_deflate # define deflateBound z_deflateBound +# define deflateBound_z z_deflateBound_z # define deflateCopy z_deflateCopy # define deflateEnd z_deflateEnd # define deflateGetDictionary z_deflateGetDictionary @@ -61,6 +63,7 @@ # define deflateSetDictionary z_deflateSetDictionary # define deflateSetHeader z_deflateSetHeader # define deflateTune z_deflateTune +# define deflateUsed z_deflateUsed # define deflate_copyright z_deflate_copyright # define get_crc_table z_get_crc_table # ifndef Z_SOLO @@ -130,9 +133,12 @@ # define inflate_copyright z_inflate_copyright # define inflate_fast z_inflate_fast # define inflate_table z_inflate_table +# define inflate_fixed z_inflate_fixed # ifndef Z_SOLO # define uncompress z_uncompress # define uncompress2 z_uncompress2 +# define uncompress_z z_uncompress_z +# define uncompress2_z z_uncompress2_z # endif # define zError z_zError # ifndef Z_SOLO @@ -236,10 +242,12 @@ # endif #endif -#if defined(ZLIB_CONST) && !defined(z_const) -# define z_const const -#else -# define z_const +#ifndef z_const +# ifdef ZLIB_CONST +# define z_const const +# else +# define z_const +# endif #endif #ifdef Z_SOLO @@ -333,7 +341,7 @@ /* If building or using zlib as a DLL, define ZLIB_DLL. * This is not mandatory, but it offers a little performance increase. */ -# if 1 +# ifdef ZLIB_DLL # if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) # ifdef ZLIB_INTERNAL # define ZEXTERN extern __declspec(dllexport) @@ -366,7 +374,7 @@ #endif #if defined (__BEOS__) -# if 1 +# ifdef ZLIB_DLL # ifdef ZLIB_INTERNAL # define ZEXPORT __declspec(dllexport) # define ZEXPORTVA __declspec(dllexport) @@ -435,20 +443,12 @@ typedef uLong FAR uLongf; typedef unsigned long z_crc_t; #endif -#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ -# if ~(~HAVE_UNISTD_H + 0) == 0 && ~(~HAVE_UNISTD_H + 1) == 1 -# define Z_HAVE_UNISTD_H -# elif HAVE_UNISTD_H != 0 -# define Z_HAVE_UNISTD_H -# endif +#if HAVE_UNISTD_H-0 /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H #endif -#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ -# if ~(~HAVE_STDARG_H + 0) == 0 && ~(~HAVE_STDARG_H + 1) == 1 -# define Z_HAVE_STDARG_H -# elif HAVE_STDARG_H != 0 -# define Z_HAVE_STDARG_H -# endif +#if HAVE_STDARG_H-0 /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H #endif #ifdef STDC @@ -480,12 +480,8 @@ typedef uLong FAR uLongf; #endif #ifndef Z_HAVE_UNISTD_H -# ifdef __WATCOMC__ -# define Z_HAVE_UNISTD_H -# endif -#endif -#ifndef Z_HAVE_UNISTD_H -# if defined(_LARGEFILE64_SOURCE) && !defined(_WIN32) +# if defined(__WATCOMC__) || defined(__GO32__) || \ + (defined(_LARGEFILE64_SOURCE) && !defined(_WIN32)) # define Z_HAVE_UNISTD_H # endif #endif @@ -520,17 +516,19 @@ typedef uLong FAR uLongf; #endif #ifndef z_off_t -# define z_off_t long +# define z_off_t long long #endif #if !defined(_WIN32) && defined(Z_LARGE64) # define z_off64_t off64_t +#elif defined(__MINGW32__) +# define z_off64_t long long +#elif defined(_WIN32) && !defined(__GNUC__) +# define z_off64_t __int64 +#elif defined(__GO32__) +# define z_off64_t offset_t #else -# if defined(_WIN32) && !defined(__GNUC__) -# define z_off64_t __int64 -# else -# define z_off64_t z_off_t -# endif +# define z_off64_t z_off_t #endif /* MVS linker does not support external names larger than 8 bytes */ diff --git a/Source/ThirdParty/uWebSockets/zlib.h b/Source/ThirdParty/uWebSockets/zlib.h index 8d4b932e..a57d3361 100644 --- a/Source/ThirdParty/uWebSockets/zlib.h +++ b/Source/ThirdParty/uWebSockets/zlib.h @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.3.1, January 22nd, 2024 + version 1.3.2, February 17th, 2026 - Copyright (C) 1995-2024 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2026 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -24,24 +24,28 @@ The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + Comments) 1950 to 1952 at https://datatracker.ietf.org/doc/html/rfc1950 (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H #define ZLIB_H -#include "zconf.h" +#ifdef ZLIB_BUILD +# include +#else +# include "zconf.h" +#endif #ifdef __cplusplus extern "C" { #endif -#define ZLIB_VERSION "1.3.1" -#define ZLIB_VERNUM 0x1310 +#define ZLIB_VERSION "1.3.2" +#define ZLIB_VERNUM 0x1320 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 3 -#define ZLIB_VER_REVISION 1 +#define ZLIB_VER_REVISION 2 #define ZLIB_VER_SUBREVISION 0 /* @@ -441,7 +445,7 @@ ZEXTERN int ZEXPORT inflate(z_streamp strm, int flush); The Z_BLOCK option assists in appending to or combining deflate streams. To assist in this, on return inflate() always sets strm->data_type to the - number of unused bits in the last byte taken from strm->next_in, plus 64 if + number of unused bits in the input taken from strm->next_in, plus 64 if inflate() is currently decoding the last block in the deflate stream, plus 128 if inflate() returned immediately after decoding an end-of-block code or decoding the complete header up to just before the first byte of the deflate @@ -587,18 +591,21 @@ ZEXTERN int ZEXPORT deflateInit2(z_streamp strm, The strategy parameter is used to tune the compression algorithm. Use the value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a - filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no - string match), or Z_RLE to limit match distances to one (run-length - encoding). Filtered data consists mostly of small values with a somewhat - random distribution. In this case, the compression algorithm is tuned to - compress them better. The effect of Z_FILTERED is to force more Huffman - coding and less string matching; it is somewhat intermediate between - Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as - fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The - strategy parameter only affects the compression ratio but not the - correctness of the compressed output even if it is not set appropriately. - Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler - decoder for special applications. + filter (or predictor), Z_RLE to limit match distances to one (run-length + encoding), or Z_HUFFMAN_ONLY to force Huffman encoding only (no string + matching). Filtered data consists mostly of small values with a somewhat + random distribution, as produced by the PNG filters. In this case, the + compression algorithm is tuned to compress them better. The effect of + Z_FILTERED is to force more Huffman coding and less string matching than the + default; it is intermediate between Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. + Z_RLE is almost as fast as Z_HUFFMAN_ONLY, but should give better + compression for PNG image data than Huffman only. The degree of string + matching from most to none is: Z_DEFAULT_STRATEGY, Z_FILTERED, Z_RLE, then + Z_HUFFMAN_ONLY. The strategy parameter affects the compression ratio but + never the correctness of the compressed output, even if it is not set + optimally for the given data. Z_FIXED uses the default string matching, but + prevents the use of dynamic Huffman codes, allowing for a simpler decoder + for special applications. deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid @@ -758,8 +765,8 @@ ZEXTERN int ZEXPORT deflateTune(z_streamp strm, returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. */ -ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, - uLong sourceLen); +ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, uLong sourceLen); +ZEXTERN z_size_t ZEXPORT deflateBound_z(z_streamp strm, z_size_t sourceLen); /* deflateBound() returns an upper bound on the compressed size after deflation of sourceLen bytes. It must be called after deflateInit() or @@ -771,6 +778,9 @@ ZEXTERN uLong ZEXPORT deflateBound(z_streamp strm, to return Z_STREAM_END. Note that it is possible for the compressed size to be larger than the value returned by deflateBound() if flush options other than Z_FINISH or Z_NO_FLUSH are used. + + delfateBound_z() is the same, but takes and returns a size_t length. Note + that a long is 32 bits on Windows. */ ZEXTERN int ZEXPORT deflatePending(z_streamp strm, @@ -785,6 +795,21 @@ ZEXTERN int ZEXPORT deflatePending(z_streamp strm, or bits are Z_NULL, then those values are not set. deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. If an int is 16 bits and memLevel is 9, then + it is possible for the number of pending bytes to not fit in an unsigned. In + that case Z_BUF_ERROR is returned and *pending is set to the maximum value + of an unsigned. + */ + +ZEXTERN int ZEXPORT deflateUsed(z_streamp strm, + int *bits); +/* + deflateUsed() returns in *bits the most recent number of deflate bits used + in the last byte when flushing to a byte boundary. The result is in 1..8, or + 0 if there has not yet been a flush. This helps determine the location of + the last bit of a deflate stream. + + deflateUsed returns Z_OK if success, or Z_STREAM_ERROR if the source stream state was inconsistent. */ @@ -987,13 +1012,15 @@ ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, int bits, int value); /* - This function inserts bits in the inflate input stream. The intent is - that this function is used to start inflating at a bit position in the - middle of a byte. The provided bits will be used before any bytes are used - from next_in. This function should only be used with raw inflate, and - should be used before the first inflate() call after inflateInit2() or - inflateReset(). bits must be less than or equal to 16, and that many of the - least significant bits of value will be inserted in the input. + This function inserts bits in the inflate input stream. The intent is to + use inflatePrime() to start inflating at a bit position in the middle of a + byte. The provided bits will be used before any bytes are used from + next_in. This function should be used with raw inflate, before the first + inflate() call, after inflateInit2() or inflateReset(). It can also be used + after an inflate() return indicates the end of a deflate block or header + when using Z_BLOCK. bits must be less than or equal to 16, and that many of + the least significant bits of value will be inserted in the input. The + other bits in value can be non-zero, and will be ignored. If bits is negative, then the input stream bit buffer is emptied. Then inflatePrime() can be called again to put bits in the buffer. This is used @@ -1001,7 +1028,15 @@ ZEXTERN int ZEXPORT inflatePrime(z_streamp strm, to feeding inflate codes. inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. + stream state was inconsistent, or if bits is out of range. If inflate was + in the middle of processing a header, trailer, or stored block lengths, then + it is possible for there to be only eight bits available in the bit buffer. + In that case, bits > 8 is considered out of range. However, when used as + outlined above, there will always be 16 bits available in the buffer for + insertion. As noted in its documentation above, inflate records the number + of bits in the bit buffer on return in data_type. 32 minus that is the + number of bits available for insertion. inflatePrime does not update + data_type with the new number of bits in buffer. */ ZEXTERN long ZEXPORT inflateMark(z_streamp strm); @@ -1047,20 +1082,22 @@ ZEXTERN int ZEXPORT inflateGetHeader(z_streamp strm, The text, time, xflags, and os fields are filled in with the gzip header contents. hcrc is set to true if there is a header CRC. (The header CRC - was valid if done is set to one.) If extra is not Z_NULL, then extra_max - contains the maximum number of bytes to write to extra. Once done is true, - extra_len contains the actual extra field length, and extra contains the - extra field, or that field truncated if extra_max is less than extra_len. - If name is not Z_NULL, then up to name_max characters are written there, - terminated with a zero unless the length is greater than name_max. If - comment is not Z_NULL, then up to comm_max characters are written there, - terminated with a zero unless the length is greater than comm_max. When any - of extra, name, or comment are not Z_NULL and the respective field is not - present in the header, then that field is set to Z_NULL to signal its - absence. This allows the use of deflateSetHeader() with the returned - structure to duplicate the header. However if those fields are set to - allocated memory, then the application will need to save those pointers - elsewhere so that they can be eventually freed. + was valid if done is set to one.) The extra, name, and comment pointers + much each be either Z_NULL or point to space to store that information from + the header. If extra is not Z_NULL, then extra_max contains the maximum + number of bytes that can be written to extra. Once done is true, extra_len + contains the actual extra field length, and extra contains the extra field, + or that field truncated if extra_max is less than extra_len. If name is not + Z_NULL, then up to name_max characters, including the terminating zero, are + written there. If comment is not Z_NULL, then up to comm_max characters, + including the terminating zero, are written there. The application can tell + that the name or comment did not fit in the provided space by the absence of + a terminating zero. If any of extra, name, or comment are not present in + the header, then that field's pointer is set to Z_NULL. This allows the use + of deflateSetHeader() with the returned structure to duplicate the header. + Note that if those fields initially pointed to allocated memory, then the + application will need to save them elsewhere so that they can be eventually + freed. If inflateGetHeader is not used, then the header information is simply discarded. The header is always checked for validity, including the header @@ -1208,13 +1245,14 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags(void); 21: FASTEST -- deflate algorithm with only one, lowest compression level 22,23: 0 (reserved) - The sprintf variant used by gzprintf (zero is best): + The sprintf variant used by gzprintf (all zeros is best): 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format - 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() is not secure! 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + 27: 0 = gzprintf() present, 1 = not -- 1 means gzprintf() returns an error Remainder: - 27-31: 0 (reserved) + 28-31: 0 (reserved) */ #ifndef Z_SOLO @@ -1226,11 +1264,14 @@ ZEXTERN uLong ZEXPORT zlibCompileFlags(void); stream-oriented functions. To simplify the interface, some default options are assumed (compression level and memory usage, standard memory allocation functions). The source code of these utility functions can be modified if - you need special options. + you need special options. The _z versions of the functions use the size_t + type for lengths. Note that a long is 32 bits on Windows. */ -ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, +ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen); +ZEXTERN int ZEXPORT compress_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is the byte length of the source buffer. Upon entry, destLen is the total size @@ -1244,9 +1285,12 @@ ZEXTERN int ZEXPORT compress(Bytef *dest, uLongf *destLen, buffer. */ -ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, +ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level); +ZEXTERN int ZEXPORT compress2_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t sourceLen, + int level); /* Compresses the source buffer into the destination buffer. The level parameter has the same meaning as in deflateInit. sourceLen is the byte @@ -1261,21 +1305,24 @@ ZEXTERN int ZEXPORT compress2(Bytef *dest, uLongf *destLen, */ ZEXTERN uLong ZEXPORT compressBound(uLong sourceLen); +ZEXTERN z_size_t ZEXPORT compressBound_z(z_size_t sourceLen); /* compressBound() returns an upper bound on the compressed size after compress() or compress2() on sourceLen bytes. It would be used before a compress() or compress2() call to allocate the destination buffer. */ -ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, +ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen); +ZEXTERN int ZEXPORT uncompress_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is - the byte length of the source buffer. Upon entry, destLen is the total size + the byte length of the source buffer. On entry, *destLen is the total size of the destination buffer, which must be large enough to hold the entire uncompressed data. (The size of the uncompressed data must have been saved previously by the compressor and transmitted to the decompressor by some - mechanism outside the scope of this compression library.) Upon exit, destLen + mechanism outside the scope of this compression library.) On exit, *destLen is the actual size of the uncompressed data. uncompress returns Z_OK if success, Z_MEM_ERROR if there was not @@ -1285,8 +1332,10 @@ ZEXTERN int ZEXPORT uncompress(Bytef *dest, uLongf *destLen, buffer with the uncompressed data up to that point. */ -ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, +ZEXTERN int ZEXPORT uncompress2(Bytef *dest, uLongf *destLen, const Bytef *source, uLong *sourceLen); +ZEXTERN int ZEXPORT uncompress2_z(Bytef *dest, z_size_t *destLen, + const Bytef *source, z_size_t *sourceLen); /* Same as uncompress, except that sourceLen is a pointer, where the length of the source is *sourceLen. On return, *sourceLen is the number of @@ -1314,13 +1363,17 @@ ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of deflateInit2 for more information about the strategy parameter.) 'T' will request transparent writing or - appending with no compression and not using the gzip format. - - "a" can be used instead of "w" to request that the gzip stream that will - be written be appended to the file. "+" will result in an error, since + appending with no compression and not using the gzip format. 'T' cannot be + used to force transparent reading. Transparent reading is automatically + performed if there is no gzip header at the start. Transparent reading can + be disabled with the 'G' option, which will instead return an error if there + is no gzip header. 'N' will open the file in non-blocking mode. + + 'a' can be used instead of 'w' to request that the gzip stream that will + be written be appended to the file. '+' will result in an error, since reading and writing to the same gzip file is not supported. The addition of - "x" when writing will create the file exclusively, which fails if the file - already exists. On systems that support it, the addition of "e" when + 'x' when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of 'e' when reading or writing will set the flag to close the file on an execve() call. These functions, as well as gzip, will read and decode a sequence of gzip @@ -1339,14 +1392,22 @@ ZEXTERN gzFile ZEXPORT gzopen(const char *path, const char *mode); insufficient memory to allocate the gzFile state, or if an invalid mode was specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). errno can be checked to determine if the reason gzopen failed was that the - file could not be opened. + file could not be opened. Note that if 'N' is in mode for non-blocking, the + open() itself can fail in order to not block. In that case gzopen() will + return NULL and errno will be EAGAIN or ENONBLOCK. The call to gzopen() can + then be re-tried. If the application would like to block on opening the + file, then it can use open() without O_NONBLOCK, and then gzdopen() with the + resulting file descriptor and 'N' in the mode, which will set it to non- + blocking. */ ZEXTERN gzFile ZEXPORT gzdopen(int fd, const char *mode); /* Associate a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or fileno (if the file has - been previously opened with fopen). The mode parameter is as in gzopen. + been previously opened with fopen). The mode parameter is as in gzopen. An + 'e' in mode will set fd's flag to close the file on an execve() call. An 'N' + in mode will set fd's non-blocking flag. The next call of gzclose on the returned gzFile will also close the file descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor @@ -1416,10 +1477,16 @@ ZEXTERN int ZEXPORT gzread(gzFile file, voidp buf, unsigned len); stream. Alternatively, gzerror can be used before gzclose to detect this case. + gzread can be used to read a gzip file on a non-blocking device. If the + input stalls and there is no uncompressed data to return, then gzread() will + return -1, and errno will be EAGAIN or EWOULDBLOCK. gzread() can then be + called again. + gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. If len is too large to fit in an int, then nothing is read, -1 is returned, and the error state is set to - Z_STREAM_ERROR. + Z_STREAM_ERROR. If some data was read before an error, then that data is + returned until exhausted, after which the next call will signal the error. */ ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, @@ -1443,15 +1510,20 @@ ZEXTERN z_size_t ZEXPORT gzfread(voidp buf, z_size_t size, z_size_t nitems, multiple of size, then the final partial item is nevertheless read into buf and the end-of-file flag is set. The length of the partial item read is not provided, but could be inferred from the result of gztell(). This behavior - is the same as the behavior of fread() implementations in common libraries, - but it prevents the direct use of gzfread() to read a concurrently written - file, resetting and retrying on end-of-file, when size is not 1. + is the same as that of fread() implementations in common libraries. This + could result in data loss if used with size != 1 when reading a concurrently + written file or a non-blocking file. In that case, use size == 1 or gzread() + instead. */ ZEXTERN int ZEXPORT gzwrite(gzFile file, voidpc buf, unsigned len); /* Compress and write the len uncompressed bytes at buf to file. gzwrite - returns the number of uncompressed bytes written or 0 in case of error. + returns the number of uncompressed bytes written, or 0 in case of error or + if len is 0. If the write destination is non-blocking, then gzwrite() may + return a number of bytes written that is not 0 and less than len. + + If len does not fit in an int, then 0 is returned and nothing is written. */ ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, @@ -1466,9 +1538,18 @@ ZEXTERN z_size_t ZEXPORT gzfwrite(voidpc buf, z_size_t size, if there was an error. If the multiplication of size and nitems overflows, i.e. the product does not fit in a z_size_t, then nothing is written, zero is returned, and the error state is set to Z_STREAM_ERROR. + + If writing a concurrently read file or a non-blocking file with size != 1, + a partial item could be written, with no way of knowing how much of it was + not written, resulting in data loss. In that case, use size == 1 or + gzwrite() instead. */ +#if defined(STDC) || defined(Z_HAVE_STDARG_H) ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); +#else +ZEXTERN int ZEXPORTVA gzprintf(); +#endif /* Convert, format, compress, and write the arguments (...) to file under control of the string format, as in fprintf. gzprintf returns the number of @@ -1476,11 +1557,19 @@ ZEXTERN int ZEXPORTVA gzprintf(gzFile file, const char *format, ...); of error. The number of uncompressed bytes written is limited to 8191, or one less than the buffer size given to gzbuffer(). The caller should assure that this limit is not exceeded. If it is exceeded, then gzprintf() will - return an error (0) with nothing written. In this case, there may also be a - buffer overflow with unpredictable consequences, which is possible only if - zlib was compiled with the insecure functions sprintf() or vsprintf(), - because the secure snprintf() or vsnprintf() functions were not available. - This can be determined using zlibCompileFlags(). + return an error (0) with nothing written. + + In that last case, there may also be a buffer overflow with unpredictable + consequences, which is possible only if zlib was compiled with the insecure + functions sprintf() or vsprintf(), because the secure snprintf() and + vsnprintf() functions were not available. That would only be the case for + a non-ANSI C compiler. zlib may have been built without gzprintf() because + secure functions were not available and having gzprintf() be insecure was + not an option, in which case, gzprintf() returns Z_STREAM_ERROR. All of + these possibilities can be determined using zlibCompileFlags(). + + If a Z_BUF_ERROR is returned, then nothing was written due to a stall on + the non-blocking write destination. */ ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); @@ -1489,6 +1578,11 @@ ZEXTERN int ZEXPORT gzputs(gzFile file, const char *s); the terminating null character. gzputs returns the number of characters written, or -1 in case of error. + The number of characters written may be less than the length of the string + if the write destination is non-blocking. + + If the length of the string does not fit in an int, then -1 is returned + and nothing is written. */ ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); @@ -1501,8 +1595,13 @@ ZEXTERN char * ZEXPORT gzgets(gzFile file, char *buf, int len); left untouched. gzgets returns buf which is a null-terminated string, or it returns NULL - for end-of-file or in case of error. If there was an error, the contents at - buf are indeterminate. + for end-of-file or in case of error. If some data was read before an error, + then that data is returned until exhausted, after which the next call will + return NULL to signal the error. + + gzgets can be used on a file being concurrently written, and on a non- + blocking device, both as for gzread(). However lines may be broken in the + middle, leaving it up to the application to reassemble them as needed. */ ZEXTERN int ZEXPORT gzputc(gzFile file, int c); @@ -1513,11 +1612,19 @@ ZEXTERN int ZEXPORT gzputc(gzFile file, int c); ZEXTERN int ZEXPORT gzgetc(gzFile file); /* - Read and decompress one byte from file. gzgetc returns this byte or -1 - in case of end of file or error. This is implemented as a macro for speed. - As such, it does not do all of the checking the other functions do. I.e. - it does not check to see if file is NULL, nor whether the structure file - points to has been clobbered or not. + Read and decompress one byte from file. gzgetc returns this byte or -1 in + case of end of file or error. If some data was read before an error, then + that data is returned until exhausted, after which the next call will return + -1 to signal the error. + + This is implemented as a macro for speed. As such, it does not do all of + the checking the other functions do. I.e. it does not check to see if file + is NULL, nor whether the structure file points to has been clobbered or not. + + gzgetc can be used to read a gzip file on a non-blocking device. If the + input stalls and there is no uncompressed data to return, then gzgetc() will + return -1, and errno will be EAGAIN or EWOULDBLOCK. gzread() can then be + called again. */ ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); @@ -1530,6 +1637,11 @@ ZEXTERN int ZEXPORT gzungetc(int c, gzFile file); output buffer size of pushed characters is allowed. (See gzbuffer above.) The pushed character will be discarded if the stream is repositioned with gzseek() or gzrewind(). + + gzungetc(-1, file) will force any pending seek to execute. Then gztell() + will report the position, even if the requested seek reached end of file. + This can be used to determine the number of uncompressed bytes in a gzip + file without having to read it into a buffer. */ ZEXTERN int ZEXPORT gzflush(gzFile file, int flush); @@ -1559,7 +1671,8 @@ ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, If the file is opened for reading, this function is emulated but can be extremely slow. If the file is opened for writing, only forward seeks are supported; gzseek then compresses a sequence of zeroes up to the new - starting position. + starting position. For reading or writing, any actual seeking is deferred + until the next read or write operation, or close operation when writing. gzseek returns the resulting offset location as measured in bytes from the beginning of the uncompressed stream, or -1 in case of error, in @@ -1567,7 +1680,7 @@ ZEXTERN z_off_t ZEXPORT gzseek(gzFile file, would be before the current position. */ -ZEXTERN int ZEXPORT gzrewind(gzFile file); +ZEXTERN int ZEXPORT gzrewind(gzFile file); /* Rewind file. This function is supported only for reading. @@ -1575,7 +1688,7 @@ ZEXTERN int ZEXPORT gzrewind(gzFile file); */ /* -ZEXTERN z_off_t ZEXPORT gztell(gzFile file); +ZEXTERN z_off_t ZEXPORT gztell(gzFile file); Return the starting position for the next gzread or gzwrite on file. This position represents a number of bytes in the uncompressed data stream, @@ -1620,8 +1733,11 @@ ZEXTERN int ZEXPORT gzdirect(gzFile file); If gzdirect() is used immediately after gzopen() or gzdopen() it will cause buffers to be allocated to allow reading the file to determine if it - is a gzip file. Therefore if gzbuffer() is used, it should be called before - gzdirect(). + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). If the input is being written concurrently or the device is non- + blocking, then gzdirect() may give a different answer once four bytes of + input have been accumulated, which is what is needed to confirm or deny a + gzip header. Before this, gzdirect() will return true (1). When writing, gzdirect() returns true (1) if transparent writing was requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: @@ -1631,7 +1747,7 @@ ZEXTERN int ZEXPORT gzdirect(gzFile file); gzip file reading and decompression, which may not be desired.) */ -ZEXTERN int ZEXPORT gzclose(gzFile file); +ZEXTERN int ZEXPORT gzclose(gzFile file); /* Flush all pending output for file, if necessary, close file and deallocate the (de)compression state. Note that once file is closed, you @@ -1659,9 +1775,10 @@ ZEXTERN int ZEXPORT gzclose_w(gzFile file); ZEXTERN const char * ZEXPORT gzerror(gzFile file, int *errnum); /* Return the error message for the last error which occurred on file. - errnum is set to zlib error number. If an error occurred in the file system - and not in the compression library, errnum is set to Z_ERRNO and the - application may consult errno to get the exact error code. + If errnum is not NULL, *errnum is set to zlib error number. If an error + occurred in the file system and not in the compression library, *errnum is + set to Z_ERRNO and the application may consult errno to get the exact error + code. The application must not modify the returned string. Future calls to this function may invalidate the previously returned string. If file is @@ -1712,7 +1829,8 @@ ZEXTERN uLong ZEXPORT adler32(uLong adler, const Bytef *buf, uInt len); ZEXTERN uLong ZEXPORT adler32_z(uLong adler, const Bytef *buf, z_size_t len); /* - Same as adler32(), but with a size_t length. + Same as adler32(), but with a size_t length. Note that a long is 32 bits + on Windows. */ /* @@ -1748,7 +1866,8 @@ ZEXTERN uLong ZEXPORT crc32(uLong crc, const Bytef *buf, uInt len); ZEXTERN uLong ZEXPORT crc32_z(uLong crc, const Bytef *buf, z_size_t len); /* - Same as crc32(), but with a size_t length. + Same as crc32(), but with a size_t length. Note that a long is 32 bits on + Windows. */ /* @@ -1758,14 +1877,14 @@ ZEXTERN uLong ZEXPORT crc32_combine(uLong crc1, uLong crc2, z_off_t len2); seq1 and seq2 with lengths len1 and len2, CRC-32 check values were calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and - len2. len2 must be non-negative. + len2. len2 must be non-negative, otherwise zero is returned. */ /* ZEXTERN uLong ZEXPORT crc32_combine_gen(z_off_t len2); Return the operator corresponding to length len2, to be used with - crc32_combine_op(). len2 must be non-negative. + crc32_combine_op(). len2 must be non-negative, otherwise zero is returned. */ ZEXTERN uLong ZEXPORT crc32_combine_op(uLong crc1, uLong crc2, uLong op); @@ -1888,9 +2007,9 @@ ZEXTERN int ZEXPORT gzgetc_(gzFile file); /* backward compatibility */ ZEXTERN z_off_t ZEXPORT gzseek64(gzFile, z_off_t, int); ZEXTERN z_off_t ZEXPORT gztell64(gzFile); ZEXTERN z_off_t ZEXPORT gzoffset64(gzFile); - ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off_t); - ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off_t); + ZEXTERN uLong ZEXPORT adler32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine64(uLong, uLong, z_off64_t); + ZEXTERN uLong ZEXPORT crc32_combine_gen64(z_off64_t); # endif #else ZEXTERN gzFile ZEXPORT gzopen(const char *, const char *);