|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <zlib.h> |
| 4 | +#include <openssl/sha.h> |
| 5 | + |
| 6 | +#include <mutex> |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +struct HTTPRequest : NonCopyable { |
| 11 | + uint64_t connId; |
| 12 | + uWS::HttpResponse *res; |
| 13 | + |
| 14 | + std::string ipAddr; |
| 15 | + std::string url; |
| 16 | + uWS::HttpMethod method = uWS::HttpMethod::METHOD_INVALID; |
| 17 | + bool acceptGzip = false; |
| 18 | + |
| 19 | + std::string body; |
| 20 | + |
| 21 | + HTTPRequest(uint64_t connId, uWS::HttpResponse *res, uWS::HttpRequest req) : connId(connId), res(res) { |
| 22 | + res->hasHead = true; // We'll be sending our own headers |
| 23 | + |
| 24 | + ipAddr = res->httpSocket->getAddressBytes(); |
| 25 | + method = req.getMethod(); |
| 26 | + url = req.getUrl().toString(); |
| 27 | + acceptGzip = req.getHeader("accept-encoding").toStringView().find("gzip") != std::string::npos; |
| 28 | + } |
| 29 | +}; |
| 30 | + |
| 31 | + |
| 32 | +struct HTTPResponse : NonCopyable { |
| 33 | + std::string_view code = "200 OK"; |
| 34 | + std::string_view contentType = "text/html; charset=utf-8"; |
| 35 | + std::string extraHeaders; |
| 36 | + std::string body; |
| 37 | + bool noCompress = false; |
| 38 | + |
| 39 | + std::string getETag() { |
| 40 | + unsigned char hash[SHA256_DIGEST_LENGTH]; |
| 41 | + SHA256(reinterpret_cast<unsigned char*>(body.data()), body.size(), hash); |
| 42 | + return to_hex(std::string_view(reinterpret_cast<char*>(hash), SHA256_DIGEST_LENGTH/2)); |
| 43 | + } |
| 44 | + |
| 45 | + std::string encode(bool doCompress) { |
| 46 | + std::string eTag = getETag(); |
| 47 | + |
| 48 | + std::string compressed; |
| 49 | + bool didCompress = false; |
| 50 | + |
| 51 | + if (!noCompress && doCompress) { |
| 52 | + compressed.resize(body.size()); |
| 53 | + |
| 54 | + z_stream zs; |
| 55 | + zs.zalloc = Z_NULL; |
| 56 | + zs.zfree = Z_NULL; |
| 57 | + zs.opaque = Z_NULL; |
| 58 | + zs.avail_in = body.size(); |
| 59 | + zs.next_in = (Bytef*)body.data(); |
| 60 | + zs.avail_out = compressed.size(); |
| 61 | + zs.next_out = (Bytef*)compressed.data(); |
| 62 | + |
| 63 | + deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); |
| 64 | + auto ret1 = deflate(&zs, Z_FINISH); |
| 65 | + auto ret2 = deflateEnd(&zs); |
| 66 | + |
| 67 | + if (ret1 == Z_STREAM_END && ret2 == Z_OK) { |
| 68 | + compressed.resize(zs.total_out); |
| 69 | + didCompress = true; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + auto bodySize = didCompress ? compressed.size() : body.size(); |
| 74 | + |
| 75 | + std::string output; |
| 76 | + output.reserve(bodySize + 1024); |
| 77 | + |
| 78 | + output += "HTTP/1.1 "; |
| 79 | + output += code; |
| 80 | + output += "\r\nContent-Length: "; |
| 81 | + output += std::to_string(bodySize); |
| 82 | + output += "\r\nContent-Type: "; |
| 83 | + output += contentType; |
| 84 | + output += "\r\nETag: \""; |
| 85 | + output += eTag; |
| 86 | + output += "\"\r\n"; |
| 87 | + if (didCompress) output += "Content-Encoding: gzip\r\nVary: Accept-Encoding\r\n"; |
| 88 | + output += extraHeaders; |
| 89 | + output += "Connection: Keep-Alive\r\n\r\n"; |
| 90 | + output += didCompress ? compressed : body; |
| 91 | + |
| 92 | + return output; |
| 93 | + } |
| 94 | +}; |
0 commit comments