From 67fdec20726e48ba3a934cb25bb30d47ec4a4f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yaroslav=20De=20La=20Pe=C3=B1a=20Smirnov?= Date: Wed, 29 Nov 2017 11:44:34 +0300 Subject: Initial commit, version 0.5.3 --- node_modules/uws/src/HTTPSocket.h | 285 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 node_modules/uws/src/HTTPSocket.h (limited to 'node_modules/uws/src/HTTPSocket.h') diff --git a/node_modules/uws/src/HTTPSocket.h b/node_modules/uws/src/HTTPSocket.h new file mode 100644 index 0000000..5cc7a7f --- /dev/null +++ b/node_modules/uws/src/HTTPSocket.h @@ -0,0 +1,285 @@ +#ifndef HTTPSOCKET_UWS_H +#define HTTPSOCKET_UWS_H + +#include "Socket.h" +#include +// #include + +namespace uWS { + +struct Header { + char *key, *value; + unsigned int keyLength, valueLength; + + operator bool() { + return key; + } + + // slow without string_view! + std::string toString() { + return std::string(value, valueLength); + } +}; + +enum HttpMethod { + METHOD_GET, + METHOD_POST, + METHOD_PUT, + METHOD_DELETE, + METHOD_PATCH, + METHOD_OPTIONS, + METHOD_HEAD, + METHOD_TRACE, + METHOD_CONNECT, + METHOD_INVALID +}; + +struct HttpRequest { + Header *headers; + Header getHeader(const char *key) { + return getHeader(key, strlen(key)); + } + + HttpRequest(Header *headers = nullptr) : headers(headers) {} + + Header getHeader(const char *key, size_t length) { + if (headers) { + for (Header *h = headers; *++h; ) { + if (h->keyLength == length && !strncmp(h->key, key, length)) { + return *h; + } + } + } + return {nullptr, nullptr, 0, 0}; + } + + Header getUrl() { + if (headers->key) { + return *headers; + } + return {nullptr, nullptr, 0, 0}; + } + + HttpMethod getMethod() { + if (!headers->key) { + return METHOD_INVALID; + } + switch (headers->keyLength) { + case 3: + if (!strncmp(headers->key, "get", 3)) { + return METHOD_GET; + } else if (!strncmp(headers->key, "put", 3)) { + return METHOD_PUT; + } + break; + case 4: + if (!strncmp(headers->key, "post", 4)) { + return METHOD_POST; + } else if (!strncmp(headers->key, "head", 4)) { + return METHOD_HEAD; + } + break; + case 5: + if (!strncmp(headers->key, "patch", 5)) { + return METHOD_PATCH; + } else if (!strncmp(headers->key, "trace", 5)) { + return METHOD_TRACE; + } + break; + case 6: + if (!strncmp(headers->key, "delete", 6)) { + return METHOD_DELETE; + } + break; + case 7: + if (!strncmp(headers->key, "options", 7)) { + return METHOD_OPTIONS; + } else if (!strncmp(headers->key, "connect", 7)) { + return METHOD_CONNECT; + } + break; + } + return METHOD_INVALID; + } +}; + +struct HttpResponse; + +template +struct WIN32_EXPORT HttpSocket : uS::Socket { + void *httpUser; // remove this later, setTimeout occupies user for now + HttpResponse *outstandingResponsesHead = nullptr; + HttpResponse *outstandingResponsesTail = nullptr; + HttpResponse *preAllocatedResponse = nullptr; + + std::string httpBuffer; + size_t contentLength = 0; + bool missedDeadline = false; + + HttpSocket(uS::Socket *socket) : uS::Socket(std::move(*socket)) {} + + void terminate() { + onEnd(this); + } + + void upgrade(const char *secKey, const char *extensions, + size_t extensionsLength, const char *subprotocol, + size_t subprotocolLength, bool *perMessageDeflate); + +private: + friend struct uS::Socket; + friend struct HttpResponse; + friend struct Hub; + static uS::Socket *onData(uS::Socket *s, char *data, size_t length); + static void onEnd(uS::Socket *s); +}; + +struct HttpResponse { + HttpSocket *httpSocket; + HttpResponse *next = nullptr; + void *userData = nullptr; + void *extraUserData = nullptr; + HttpSocket::Queue::Message *messageQueue = nullptr; + bool hasEnded = false; + bool hasHead = false; + + HttpResponse(HttpSocket *httpSocket) : httpSocket(httpSocket) { + + } + + template + static HttpResponse *allocateResponse(HttpSocket *httpSocket) { + if (httpSocket->preAllocatedResponse) { + HttpResponse *ret = httpSocket->preAllocatedResponse; + httpSocket->preAllocatedResponse = nullptr; + return ret; + } else { + return new HttpResponse((HttpSocket *) httpSocket); + } + } + + //template + void freeResponse(HttpSocket *httpData) { + if (httpData->preAllocatedResponse) { + delete this; + } else { + httpData->preAllocatedResponse = this; + } + } + + void write(const char *message, size_t length = 0, + void(*callback)(void *httpSocket, void *data, bool cancelled, void *reserved) = nullptr, + void *callbackData = nullptr) { + + struct NoopTransformer { + static size_t estimate(const char *data, size_t length) { + return length; + } + + static size_t transform(const char *src, char *dst, size_t length, int transformData) { + memcpy(dst, src, length); + return length; + } + }; + + httpSocket->sendTransformed(message, length, callback, callbackData, 0); + hasHead = true; + } + + // todo: maybe this function should have a fast path for 0 length? + void end(const char *message = nullptr, size_t length = 0, + void(*callback)(void *httpResponse, void *data, bool cancelled, void *reserved) = nullptr, + void *callbackData = nullptr) { + + struct TransformData { + bool hasHead; + } transformData = {hasHead}; + + struct HttpTransformer { + + // todo: this should get TransformData! + static size_t estimate(const char *data, size_t length) { + return length + 128; + } + + static size_t transform(const char *src, char *dst, size_t length, TransformData transformData) { + // todo: sprintf is extremely slow + int offset = transformData.hasHead ? 0 : std::sprintf(dst, "HTTP/1.1 200 OK\r\nContent-Length: %u\r\n\r\n", (unsigned int) length); + memcpy(dst + offset, src, length); + return length + offset; + } + }; + + if (httpSocket->outstandingResponsesHead != this) { + HttpSocket::Queue::Message *messagePtr = httpSocket->allocMessage(HttpTransformer::estimate(message, length)); + messagePtr->length = HttpTransformer::transform(message, (char *) messagePtr->data, length, transformData); + messagePtr->callback = callback; + messagePtr->callbackData = callbackData; + messagePtr->nextMessage = messageQueue; + messageQueue = messagePtr; + hasEnded = true; + } else { + httpSocket->sendTransformed(message, length, callback, callbackData, transformData); + // move head as far as possible + HttpResponse *head = next; + while (head) { + // empty message queue + HttpSocket::Queue::Message *messagePtr = head->messageQueue; + while (messagePtr) { + HttpSocket::Queue::Message *nextMessage = messagePtr->nextMessage; + + bool wasTransferred; + if (httpSocket->write(messagePtr, wasTransferred)) { + if (!wasTransferred) { + httpSocket->freeMessage(messagePtr); + if (callback) { + callback(this, callbackData, false, nullptr); + } + } else { + messagePtr->callback = callback; + messagePtr->callbackData = callbackData; + } + } else { + httpSocket->freeMessage(messagePtr); + if (callback) { + callback(this, callbackData, true, nullptr); + } + goto updateHead; + } + messagePtr = nextMessage; + } + // cannot go beyond unfinished responses + if (!head->hasEnded) { + break; + } else { + HttpResponse *next = head->next; + head->freeResponse(httpSocket); + head = next; + } + } + updateHead: + httpSocket->outstandingResponsesHead = head; + if (!head) { + httpSocket->outstandingResponsesTail = nullptr; + } + + freeResponse(httpSocket); + } + } + + void setUserData(void *userData) { + this->userData = userData; + } + + void *getUserData() { + return userData; + } + + HttpSocket *getHttpSocket() { + return httpSocket; + } +}; + +} + +#endif // HTTPSOCKET_UWS_H -- cgit v1.2.3