DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
WebApplication.cpp
Go to the documentation of this file.
1#include "pch.h"
6#include "System/Console.h"
11#include "System/Convert.h"
14#include <string>
15#include <vector>
16#include <algorithm>
17
18namespace DotNetDupe {
19 namespace WebAppCore {
20 namespace Builder {
21
22 namespace Internal {
23 std::vector<std::string> GetPathSegments(const std::string& path);
24 bool MatchRoute(const std::vector<std::string>& patternSegs, const std::vector<std::string>& pathSegs, std::vector<std::pair<std::string, std::string>>& extractedParams);
25 void ParseServerUrl(const std::string& sUrl, std::string& host, int& port);
26 }
27
29 : m_spServices(spServices), m_bRunning(false), m_nPort(0) {}
30
35
37 : m_spServices(std::move(other.m_spServices)),
38 m_getHandlers(std::move(other.m_getHandlers)),
39 m_postHandlers(std::move(other.m_postHandlers)),
40 m_putHandlers(std::move(other.m_putHandlers)),
41 m_deleteHandlers(std::move(other.m_deleteHandlers)),
42 m_pListener(std::move(other.m_pListener)),
43 m_bRunning(other.m_bRunning),
44 m_sHost(std::move(other.m_sHost)),
45 m_nPort(other.m_nPort),
46 m_controllerRegistrars(std::move(other.m_controllerRegistrars)) {}
47
50 if (this != &other) {
52 m_spServices = std::move(other.m_spServices);
53 m_getHandlers = std::move(other.m_getHandlers);
54 m_postHandlers = std::move(other.m_postHandlers);
55 m_putHandlers = std::move(other.m_putHandlers);
56 m_deleteHandlers = std::move(other.m_deleteHandlers);
57 m_pListener = std::move(other.m_pListener);
58 m_bRunning = other.m_bRunning;
59 m_sHost = std::move(other.m_sHost);
60 m_nPort = other.m_nPort;
61 m_controllerRegistrars = std::move(other.m_controllerRegistrars);
62 }
64 return *this;
65 }
66
71
74 m_getHandlers.Add(pattern, handler);
75 }
76
79 m_postHandlers.Add(pattern, handler);
80 }
81
84 m_putHandlers.Add(pattern, handler);
85 }
86
89 m_deleteHandlers.Add(pattern, handler);
90 }
91
94 m_wsHandlers.Add(pattern, handler);
95 }
96
100 auto keys = m_wsHandlers.GetKeys();
101 for (int i = 0; i < keys.GetLength(); ++i) {
102 list.Add(keys[i]);
103 }
105 return list;
106 }
107
110 auto keys = m_wsHandlers.GetKeys();
111 for (int i = 0; i < keys.GetLength(); ++i) {
112 if (keys[i] == path) return true;
113 }
114 return false;
115 }
116
119 for (int i = 0; i < m_controllerRegistrars.GetCount(); ++i) {
120 m_controllerRegistrars[i](m_spSelf);
121 }
122 }
123
124 struct ConnectionContext : public System::Object {
126 ConnectionContext(System::SmartPointer<System::Net::Sockets::TcpClient> pC) : pClient(std::move(pC)) {}
127 };
128
129 void WebApplication::Run(const System::String& url, int threadCount) {
131 if (threadCount > 0) System::Threading::ThreadPool::SetMinThreads(threadCount);
132 std::string host; int port = 5000;
134 Internal::ParseServerUrl(url.GetRawString(), host, port);
136 StartServerLoop(System::String(host.c_str()), port);
137 }
138
139 void WebApplication::QueueAcceptedClient(System::SmartPointer<System::Net::Sockets::TcpClient> pClient) {
141 auto spCtx = System::SmartPointer<ConnectionContext>::NewShared(std::move(pClient));
143 try { HandleConnection(std::move(spCtx->pClient)); }
144 catch (const DotNetDupe::System::Exception& ex) { System::Console::WriteLine(System::String("[Server] ") + ex.What()); }
145 catch (const std::exception& ex) { System::Console::WriteLine(System::String("[Server] ") + ex.what()); }
146 }, nullptr);
147 }
148
149 void WebApplication::StartServerLoop(const System::String& host, int port) {
151 m_sHost = host; m_nPort = port;
153 m_pListener->Start();
154 m_bRunning = true;
156 try {
157 while (m_bRunning) {
158 auto pClient = m_pListener->AcceptTcpClient();
159 if (!pClient.IsNull()) QueueAcceptedClient(std::move(pClient));
160 }
161 } catch (...) { (void)0; }
162 }
163
166 if (!m_bRunning) return;
167 m_bRunning = false;
169 if (!m_pListener.IsNull()) m_pListener->Stop();
170 try {
172 dummy.Connect(m_sHost, m_nPort);
173 dummy.Close();
174 } catch (...) { (void)0; }
175 m_spSelf = nullptr;
176 }
177
178 static bool ReadHeaderLines(const System::SmartPointer<System::IO::Stream>& stream, std::vector<std::string>& lines, bool isRunning) {
180 char c; std::string currentLine;
181 while (isRunning && stream->Read(&c, 0, 1) > 0) {
182 if (c == '\n') {
183 if (!currentLine.empty() && currentLine.back() == '\r') currentLine.pop_back();
184 if (currentLine.empty()) break;
185 lines.push_back(currentLine);
186 currentLine.clear();
187 } else {
188 currentLine += c;
189 }
190 }
192 return !lines.empty();
193 }
194
195 static void ParseQueryParams(const std::string& queryStr, Http::HttpRequest* req) {
197 size_t start = 0;
198 while (start < queryStr.length()) {
199 size_t ampersand = queryStr.find('&', start);
200 std::string pair = (ampersand == std::string::npos) ? queryStr.substr(start) : queryStr.substr(start, ampersand - start);
201 size_t equals = pair.find('=');
202 if (equals != std::string::npos) {
203 req->GetQuery()[System::String(pair.substr(0, equals).c_str())] = System::String(pair.substr(equals + 1).c_str());
204 } else if (!pair.empty()) {
205 req->GetQuery()[System::String(pair.c_str())] = System::String("");
206 }
207 if (ampersand == std::string::npos) break;
208 start = ampersand + 1;
209 }
210 }
211
212 static std::string ParseRequestLine(const std::string& reqLine, Http::HttpRequest* req) {
214 size_t sp1 = reqLine.find(' '), sp2 = reqLine.find(' ', sp1 + 1);
215 if (sp1 == std::string::npos || sp2 == std::string::npos) return "";
216 std::string method = reqLine.substr(0, sp1);
217 std::string fullPath = reqLine.substr(sp1 + 1, sp2 - sp1 - 1);
218 req->SetMethod(System::String(method.c_str()));
219 size_t q = fullPath.find('?');
220 std::string path = (q != std::string::npos) ? fullPath.substr(0, q) : fullPath;
221 if (q != std::string::npos) ParseQueryParams(fullPath.substr(q + 1), req);
222 req->SetPath(System::String(path.c_str()));
224 return method;
225 }
226
227 static void ReadHeadersAndBody(const System::SmartPointer<System::IO::Stream>& stream, const std::vector<std::string>& lines, Http::HttpRequest* req) {
229 int contentLength = 0;
230 for (size_t i = 1; i < lines.size(); ++i) {
231 size_t colon = lines[i].find(':');
232 if (colon == std::string::npos) continue;
233 std::string name = lines[i].substr(0, colon);
234 std::string val = lines[i].substr(colon + 1);
235 size_t f = val.find_first_not_of(" \t");
236 if (f != std::string::npos) val = val.substr(f, val.find_last_not_of(" \t\r\n") - f + 1);
237 std::string nameLower = name;
238 std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower);
239 req->GetHeaders()[System::String(nameLower.c_str())] = System::String(val.c_str());
240 if (nameLower == "content-length") try { contentLength = std::stoi(val); } catch (...) { contentLength = 0; }
241 }
243 if (contentLength > 0) {
244 std::string body(contentLength, '\0');
245 stream->Read(body.data(), 0, contentLength);
246 req->SetBody(System::String(body.c_str()));
247 }
248 }
249
252 System::String msg;
253 try {
254 while (pWebSocket->ReceiveText(msg)) {
255 if (msg.IsEmpty() || msg == "__DISCONNECT__") break;
256 try { pWsHandler->OnMessage(pWsContext, msg); } catch (...) { (void)0; }
257 }
258 } catch (...) { (void)0; }
259 }
260
264 std::string hsResponse = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: " + std::string(sAcceptKey.GetRawString() ? sAcceptKey.GetRawString() : "") + "\r\n\r\n";
265 stream->Write(hsResponse.data(), 0, static_cast<int>(hsResponse.length()));
268 auto pWsContext = System::SmartPointer<WebSockets::WebSocketContext>::NewShared(spContext, pWebSocket);
269 try { pWsHandler->OnConnected(pWsContext); } catch (...) { (void)0; }
271 RunWsReceiveLoop(pWebSocket, pWsContext, pWsHandler);
272 try { pWsHandler->OnDisconnected(pWsContext); } catch (...) { (void)0; }
274 return true;
275 }
276
277 static void SendHttpResponseData(const System::SmartPointer<System::IO::Stream>& stream, Http::HttpResponse* resp, const std::string& method) {
279 std::string respBody = resp->GetBody().GetRawString();
280 int code = resp->GetStatusCode();
281 std::string statusMsg = (code == 404) ? "Not Found" : ((code == 500) ? "Internal Server Error" : ((code == 201) ? "Created" : ((code == 204) ? "No Content" : "OK")));
282 std::string respStr = "HTTP/1.1 " + std::to_string(code) + " " + statusMsg + "\r\nContent-Type: " + std::string(resp->GetContentType().GetRawString() ? resp->GetContentType().GetRawString() : "") + "\r\nContent-Length: " + std::to_string(respBody.length()) + "\r\nConnection: close\r\nServer: DotNetDupeWebApplication/1.0\r\n";
283 auto keys = resp->GetHeaders().GetKeys(); auto values = resp->GetHeaders().GetValues();
284 for (int i = 0; i < keys.GetLength(); ++i) {
285 respStr += std::string(keys[i].GetRawString() ? keys[i].GetRawString() : "") + ": " + std::string(values[i].GetRawString() ? values[i].GetRawString() : "") + "\r\n";
286 }
287 respStr += "\r\n";
289 stream->Write(respStr.data(), 0, static_cast<int>(respStr.length()));
290 if (!respBody.empty() && method != "HEAD") stream->Write(respBody.data(), 0, static_cast<int>(respBody.length()));
291 }
292
295 if (map.TryGetValue(req->GetPath(), pHandler)) return true;
297 std::vector<std::string> pathSegs = Internal::GetPathSegments(req->GetPath().GetRawString());
298 auto keys = map.GetKeys();
299 for (int i = 0; i < keys.GetLength(); ++i) {
300 std::vector<std::string> patternSegs = Internal::GetPathSegments(keys[i].GetRawString());
301 std::vector<std::pair<std::string, std::string>> extractedParams;
302 if (Internal::MatchRoute(patternSegs, pathSegs, extractedParams)) {
303 for (const auto& pair : extractedParams) req->GetRouteValues()[System::String(pair.first.c_str())] = System::String(pair.second.c_str());
304 return map.TryGetValue(keys[i], pHandler);
305 }
306 }
307 return false;
308 }
309
310 static bool MatchTokenString(const std::string& val, const std::string& target) {
312 size_t start = 0;
313 while (start < val.length()) {
314 size_t comma = val.find(',', start);
315 std::string token = (comma == std::string::npos) ? val.substr(start) : val.substr(start, comma - start);
316 size_t first = token.find_first_not_of(" \t\r\n");
317 if (first != std::string::npos && token.substr(first, token.find_last_not_of(" \t\r\n") - first + 1) == target) return true;
318 if (comma == std::string::npos) break;
319 start = comma + 1;
320 }
321 return false;
322 }
323
324 static bool HeaderContainsToken(const System::String& headerVal, const std::string& targetToken) {
326 std::string val = headerVal.GetRawString() ? headerVal.GetRawString() : "";
327 std::transform(val.begin(), val.end(), val.begin(), ::tolower);
328 std::string target = targetToken;
329 std::transform(target.begin(), target.end(), target.begin(), ::tolower);
330 return MatchTokenString(val, target);
331 }
332
333 static void SendWebSocketErrorResponse(const System::SmartPointer<System::IO::Stream>& stream, int statusCode, const char* pMsg) {
335 std::string body = pMsg;
336 std::string statusText = (statusCode == 426) ? "Upgrade Required" : "Bad Request";
337 std::string resp = "HTTP/1.1 " + std::to_string(statusCode) + " " + statusText + "\r\nContent-Type: text/plain\r\nContent-Length: " + std::to_string(body.length()) + "\r\nConnection: close\r\n";
338 if (statusCode == 426) resp += "Upgrade: websocket\r\nConnection: Upgrade\r\n";
339 resp += "\r\n" + body;
341 stream->Write(resp.data(), 0, static_cast<int>(resp.length()));
342 }
343
346 System::String sUpgrade, sConnection;
347 req->GetHeaders().TryGetValue("upgrade", sUpgrade);
348 req->GetHeaders().TryGetValue("connection", sConnection);
349 req->GetHeaders().TryGetValue("sec-websocket-key", sSecKey);
350 if (sUpgrade.IsEmpty() || sUpgrade.ToLower() != "websocket") return 426;
351 if (!HeaderContainsToken(sConnection, "upgrade") || sSecKey.IsEmpty()) return 400;
352 return 101;
353 }
354
357 auto spRequest = spContext->GetRequest();
359 if (!wsMap.TryGetValue(spRequest->GetPath(), spWsHandler) || spWsHandler.IsNull()) return false;
360
362 System::String sSecKey;
363 int status = ValidateWebSocketHandshake(spRequest.Get(), sSecKey);
364 if (status != 101) {
365 SendWebSocketErrorResponse(stream, status, (status == 426) ? "426 Upgrade Required" : "400 Bad Request");
366 return true;
367 }
369 ProcessWsSession(stream, spContext, spWsHandler, sSecKey);
370 return true;
371 }
372
375 if (bFound) {
376 try { pResp->SetBody(pHandler(spContext)); }
377 catch (const DotNetDupe::System::Exception& ex) { pResp->SetStatusCode(System::Net::HttpStatusCode::InternalServerError); pResp->SetBody(System::String("500 Internal Server Error: ") + ex.What()); }
378 catch (...) { pResp->SetStatusCode(System::Net::HttpStatusCode::InternalServerError); pResp->SetBody("500 Internal Server Error"); }
379 } else {
381 pResp->SetBody("404 Not Found");
382 }
383 }
384
385 static void RouteAndSendResponse(const std::string& method, const System::SmartPointer<Http::HttpContext>& spContext, const System::SmartPointer<System::IO::Stream>& spBaseStream,
392 bool bFound = false;
393 if (method == "GET" || method == "HEAD") bFound = MatchAndFindHandler(spContext->GetRequest().Get(), getHandlers, pHandler);
394 else if (method == "POST") bFound = MatchAndFindHandler(spContext->GetRequest().Get(), postHandlers, pHandler);
395 else if (method == "PUT") bFound = MatchAndFindHandler(spContext->GetRequest().Get(), putHandlers, pHandler);
396 else if (method == "DELETE") bFound = MatchAndFindHandler(spContext->GetRequest().Get(), deleteHandlers, pHandler);
398 DispatchResponse(spContext->GetResponse().Get(), bFound, pHandler, spContext);
399 if (spContext->GetResponse()->IsHeadersSent()) spContext->GetResponse()->Flush();
400 else SendHttpResponseData(spBaseStream, spContext->GetResponse().Get(), method);
401 }
402
403 void WebApplication::HandleConnection(System::SmartPointer<System::Net::Sockets::TcpClient> spClient) {
405 auto stream = spClient->GetStream();
406 if (stream.IsNull()) return;
407 std::vector<std::string> lines;
408 System::SmartPointer<System::IO::Stream> spBaseStream = stream;
409 if (!ReadHeaderLines(spBaseStream, lines, m_bRunning)) return;
412 std::string method = ParseRequestLine(lines[0], spContext->GetRequest().Get());
413 ReadHeadersAndBody(spBaseStream, lines, spContext->GetRequest().Get());
414 spContext->GetResponse()->BindStream(stream);
416 if (TryHandleWebSocket(stream, spContext, m_wsHandlers)) { spClient->Close(); return; }
417 RouteAndSendResponse(method, spContext, spBaseStream, m_getHandlers, m_postHandlers, m_putHandlers, m_deleteHandlers);
418 spClient->Close();
419 }
420
421 }
422 }
423}
Represents standard input, output, and error streams for console applications.
Converts a base data type to another base data type, and encodes/decodes Base64 data.
Defines HTTP status codes defined for HTTP/1.1 and HTTP/2.
Exception thrown when a network socket error occurs per RFC 793 / RFC 768.
Provides client connections for TCP network services.
Listens for connections from TCP network clients.
Provides a pool of threads that can be used to execute tasks and work items.
Represents an unknown or unmapped exception encountered during execution.
Represents the web application used to configure the HTTP pipeline and routes mirroring ASP....
Builder pattern orchestrator for configuring services, controllers, and constructing WebApplication i...
Represents an RFC 6455 full-duplex WebSocket connection over a NetworkStream.
Context and handler abstractions for WebSockets integrated into the WebAppCore HTTP server pipeline.
Represents a collection of keys and values.
Definition Dictionary.h:66
bool TryGetValue(const TKey &key, TValue &value) const
Definition Dictionary.h:285
Represents a strongly typed list of objects accessible by index.
Definition List.h:29
void Add(const T &item)
Adds an object to the end of the List.
Definition List.h:138
Represents errors that occur during application execution.
Definition Exception.h:19
const char * What() const
Gets a message that describes the current exception.
Definition Exception.h:46
Primary template declaration for the Func delegate family.
Definition Func.h:42
Provides client connections for TCP network services.
Definition TcpClient.h:25
void Close()
Disposes this TcpClient instance and closes the underlying connection.
Definition TcpClient.cpp:65
void Connect(const String &ip, int port)
Connects the client to a remote TCP host using the specified IP address and port number.
Definition TcpClient.cpp:42
static String ComputeSecWebSocketAccept(const String &secWebSocketKey)
Computes the RFC 6455 Sec-WebSocket-Accept handshake header from a client challenge key.
Supports all classes in the DotNetDupe class hierarchy.
Definition Object.h:18
A unified smart pointer that supports both unique and shared ownership semantics.
static SmartPointer< T > NewShared()
Creates a Shared SmartPointer, default constructing T.
bool IsNull() const noexcept
Checks if the SmartPointer is null.
T * Get() const noexcept
Gets the raw pointer.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
String ToLower() const
Definition String.cpp:672
const char * GetRawString() const
Definition String.cpp:230
static bool SetMinThreads(int iMinThreads)
Sets the minimum number of threads the thread pool creates on demand as new requests are made.
static bool QueueUserWorkItem(WaitCallback callback)
Queues a method for execution. The method executes when a thread pool thread becomes available.
void MapDelete(const DotNetDupe::System::String &pattern, DotNetDupe::System::Func< DotNetDupe::System::String, DotNetDupe::System::SmartPointer< Http::HttpContext > > handler)
Registers an HTTP DELETE route endpoint with a synchronous lambda or delegate handler.
void MapPost(const DotNetDupe::System::String &pattern, DotNetDupe::System::Func< DotNetDupe::System::String, DotNetDupe::System::SmartPointer< Http::HttpContext > > handler)
Registers an HTTP POST route endpoint with a synchronous lambda or delegate handler.
DotNetDupe::System::Collections::Generic::List< DotNetDupe::System::String > GetWebSocketRoutes() const
Retrieves the list of all currently mapped WebSocket route patterns.
void Stop()
Stops the active TCP listener and releases connection worker threads.
bool HasWebSocketRoute(const DotNetDupe::System::String &path) const
Determines whether a registered WebSocket route matches the specified path.
void MapWebSocket(const DotNetDupe::System::String &pattern, DotNetDupe::System::SmartPointer< WebSockets::IWebSocketHandler > handler)
Maps an RFC 6455 WebSocket endpoint to an asynchronous lifecycle handler.
void MapControllers()
Dispatches and binds all registered controller routes to this application pipeline.
WebApplication(const DotNetDupe::System::SmartPointer< DotNetDupe::System::IServiceProvider > &spServices)
void MapPut(const DotNetDupe::System::String &pattern, DotNetDupe::System::Func< DotNetDupe::System::String, DotNetDupe::System::SmartPointer< Http::HttpContext > > handler)
Registers an HTTP PUT route endpoint with a synchronous lambda or delegate handler.
static DotNetDupe::System::SmartPointer< WebApplicationBuilder > CreateBuilder()
Initializes a new instance of the WebApplicationBuilder class with pre-configured defaults.
WebApplication & operator=(const WebApplication &)=delete
void Run(const DotNetDupe::System::String &url="http://127.0.0.1:5000", int threadCount=10)
Starts the embedded HTTP/WebSocket server listener on the specified URL and worker thread pool.
void MapGet(const DotNetDupe::System::String &pattern, DotNetDupe::System::Func< DotNetDupe::System::String, DotNetDupe::System::SmartPointer< Http::HttpContext > > handler)
Registers an HTTP GET route endpoint with a synchronous lambda or delegate handler.
Encapsulates an incoming HTTP request within the WebAppCore pipeline.
Definition HttpContext.h:25
void SetPath(const DotNetDupe::System::String &path)
Sets the request URI path.
Definition HttpContext.h:47
DotNetDupe::System::Collections::Generic::Dictionary< DotNetDupe::System::String, DotNetDupe::System::String > & GetRouteValues()
Gets the mutable dictionary of matched route parameter values.
Definition HttpContext.h:75
void SetMethod(const DotNetDupe::System::String &method)
Sets the HTTP request method verb.
Definition HttpContext.h:39
DotNetDupe::System::Collections::Generic::Dictionary< DotNetDupe::System::String, DotNetDupe::System::String > & GetQuery()
Gets the mutable dictionary of decoded URL query parameters.
Definition HttpContext.h:67
DotNetDupe::System::Collections::Generic::Dictionary< DotNetDupe::System::String, DotNetDupe::System::String > & GetHeaders()
Gets the mutable dictionary of HTTP request headers.
Definition HttpContext.h:59
DotNetDupe::System::String GetPath() const
Gets the request URI path.
Definition HttpContext.h:43
void SetBody(const DotNetDupe::System::String &body)
Sets the raw HTTP request body string.
Definition HttpContext.h:55
Encapsulates an outgoing HTTP response within the WebAppCore pipeline.
Definition HttpContext.h:97
void SetStatusCode(int code)
Sets the integer HTTP response status code.
DotNetDupe::System::String GetContentType() const
Gets the Content-Type header string.
int GetStatusCode() const
Gets the integer HTTP response status code.
DotNetDupe::System::String GetBody() const
Gets the accumulated response body string.
DotNetDupe::System::Collections::Generic::Dictionary< DotNetDupe::System::String, DotNetDupe::System::String > & GetHeaders()
Gets the mutable dictionary of HTTP response headers.
void SetBody(const DotNetDupe::System::String &body)
Sets the response body content.
void ParseServerUrl(const std::string &sUrl, std::string &host, int &port)
bool MatchRoute(const std::vector< std::string > &patternSegs, const std::vector< std::string > &pathSegs, std::vector< std::pair< std::string, std::string > > &extractedParams)
std::vector< std::string > GetPathSegments(const std::string &path)
static std::string ParseRequestLine(const std::string &reqLine, Http::HttpRequest *req)
static bool MatchTokenString(const std::string &val, const std::string &target)
static void SendHttpResponseData(const System::SmartPointer< System::IO::Stream > &stream, Http::HttpResponse *resp, const std::string &method)
static void DispatchResponse(Http::HttpResponse *pResp, bool bFound, System::Func< System::String, System::SmartPointer< Http::HttpContext > > &pHandler, const System::SmartPointer< Http::HttpContext > &spContext)
static void SendWebSocketErrorResponse(const System::SmartPointer< System::IO::Stream > &stream, int statusCode, const char *pMsg)
static bool MatchAndFindHandler(Http::HttpRequest *req, const System::Collections::Generic::Dictionary< System::String, System::Func< System::String, System::SmartPointer< Http::HttpContext > > > &map, System::Func< System::String, System::SmartPointer< Http::HttpContext > > &pHandler)
static void RunWsReceiveLoop(const System::SmartPointer< System::Net::WebSockets::WebSocket > &pWebSocket, const System::SmartPointer< WebSockets::WebSocketContext > &pWsContext, const System::SmartPointer< WebSockets::IWebSocketHandler > &pWsHandler)
static bool ReadHeaderLines(const System::SmartPointer< System::IO::Stream > &stream, std::vector< std::string > &lines, bool isRunning)
static void ReadHeadersAndBody(const System::SmartPointer< System::IO::Stream > &stream, const std::vector< std::string > &lines, Http::HttpRequest *req)
static bool ProcessWsSession(const System::SmartPointer< System::Net::Sockets::NetworkStream > &stream, const System::SmartPointer< Http::HttpContext > &spContext, const System::SmartPointer< WebSockets::IWebSocketHandler > &pWsHandler, const System::String &sSecKey)
static void RouteAndSendResponse(const std::string &method, const System::SmartPointer< Http::HttpContext > &spContext, const System::SmartPointer< System::IO::Stream > &spBaseStream, const System::Collections::Generic::Dictionary< System::String, System::Func< System::String, System::SmartPointer< Http::HttpContext > > > &getHandlers, const System::Collections::Generic::Dictionary< System::String, System::Func< System::String, System::SmartPointer< Http::HttpContext > > > &postHandlers, const System::Collections::Generic::Dictionary< System::String, System::Func< System::String, System::SmartPointer< Http::HttpContext > > > &putHandlers, const System::Collections::Generic::Dictionary< System::String, System::Func< System::String, System::SmartPointer< Http::HttpContext > > > &deleteHandlers)
static int ValidateWebSocketHandshake(const Http::HttpRequest *req, System::String &sSecKey)
static void ParseQueryParams(const std::string &queryStr, Http::HttpRequest *req)
static bool TryHandleWebSocket(const System::SmartPointer< System::Net::Sockets::NetworkStream > &stream, const System::SmartPointer< Http::HttpContext > &spContext, const System::Collections::Generic::Dictionary< System::String, System::SmartPointer< WebSockets::IWebSocketHandler > > &wsMap)
static bool HeaderContainsToken(const System::String &headerVal, const std::string &targetToken)