mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-04 06:32:00 +00:00 
			
		
		
		
	Alllll the groundwork
This commit is contained in:
		@@ -249,6 +249,8 @@ include_directories(
 | 
				
			|||||||
	"thirdparty/cpplinq"
 | 
						"thirdparty/cpplinq"
 | 
				
			||||||
	"thirdparty/cpp-httplib"
 | 
						"thirdparty/cpp-httplib"
 | 
				
			||||||
	"thirdparty/MD5"
 | 
						"thirdparty/MD5"
 | 
				
			||||||
 | 
						"thirdparty/nlohmann"
 | 
				
			||||||
 | 
						"thirdparty/picohttpparser"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
 | 
					# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ set(DCHATSERVER_SOURCES
 | 
				
			|||||||
	"ChatIgnoreList.cpp"
 | 
						"ChatIgnoreList.cpp"
 | 
				
			||||||
	"ChatPacketHandler.cpp"
 | 
						"ChatPacketHandler.cpp"
 | 
				
			||||||
	"PlayerContainer.cpp"
 | 
						"PlayerContainer.cpp"
 | 
				
			||||||
 | 
						"ChatWebAPI.cpp"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_executable(ChatServer "ChatServer.cpp")
 | 
					add_executable(ChatServer "ChatServer.cpp")
 | 
				
			||||||
@@ -12,5 +13,5 @@ add_library(dChatServer ${DCHATSERVER_SOURCES})
 | 
				
			|||||||
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
 | 
					target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
 | 
					target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
 | 
				
			||||||
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer)
 | 
					target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer picohttpparser)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,8 @@
 | 
				
			|||||||
#include "RakNetDefines.h"
 | 
					#include "RakNetDefines.h"
 | 
				
			||||||
#include "MessageIdentifiers.h"
 | 
					#include "MessageIdentifiers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ChatWebAPI.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Game {
 | 
					namespace Game {
 | 
				
			||||||
	Logger* logger = nullptr;
 | 
						Logger* logger = nullptr;
 | 
				
			||||||
	dServer* server = nullptr;
 | 
						dServer* server = nullptr;
 | 
				
			||||||
@@ -122,6 +124,14 @@ int main(int argc, char** argv) {
 | 
				
			|||||||
	uint32_t framesSinceMasterDisconnect = 0;
 | 
						uint32_t framesSinceMasterDisconnect = 0;
 | 
				
			||||||
	uint32_t framesSinceLastSQLPing = 0;
 | 
						uint32_t framesSinceLastSQLPing = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
 | 
				
			||||||
 | 
						ChatWebAPI chatwebapi(Game::config->GetValue("web_server_listen_ip"), GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("web_server_listen_port")).value_or(8080));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (web_server_enabled) {
 | 
				
			||||||
 | 
							LOG("Web server enabled, will process http requests.");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Game::logger->Flush(); // once immediately before main loop
 | 
						Game::logger->Flush(); // once immediately before main loop
 | 
				
			||||||
	while (!Game::ShouldShutdown()) {
 | 
						while (!Game::ShouldShutdown()) {
 | 
				
			||||||
		//Check if we're still connected to master:
 | 
							//Check if we're still connected to master:
 | 
				
			||||||
@@ -142,6 +152,10 @@ int main(int argc, char** argv) {
 | 
				
			|||||||
			Game::server->DeallocatePacket(packet);
 | 
								Game::server->DeallocatePacket(packet);
 | 
				
			||||||
			packet = nullptr;
 | 
								packet = nullptr;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							//Check and handle web requests:
 | 
				
			||||||
 | 
							if (web_server_enabled) {
 | 
				
			||||||
 | 
								chatwebapi.HandleRequest();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//Push our log every 30s:
 | 
							//Push our log every 30s:
 | 
				
			||||||
		if (framesSinceLastFlush >= logFlushTime) {
 | 
							if (framesSinceLastFlush >= logFlushTime) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										146
									
								
								dChatServer/ChatWebAPI.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								dChatServer/ChatWebAPI.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ChatWebAPI.h"
 | 
				
			||||||
 | 
					#include "Logger.h"
 | 
				
			||||||
 | 
					#include "Game.h"
 | 
				
			||||||
 | 
					#include "json.hpp"
 | 
				
			||||||
 | 
					#include "picohttpparser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
						const int BUFFER_SIZE = 30720;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChatWebAPI::ChatWebAPI(std::string ip_address, int port) :
 | 
				
			||||||
 | 
											m_ip_address(ip_address), 
 | 
				
			||||||
 | 
											m_port(port),
 | 
				
			||||||
 | 
											m_socket(),
 | 
				
			||||||
 | 
											m_new_socket(),
 | 
				
			||||||
 | 
											m_incomingMessage(),
 | 
				
			||||||
 | 
											m_socketAddress(),
 | 
				
			||||||
 | 
											m_socketAddress_len(sizeof(m_socketAddress)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_socketAddress.sin_family = AF_INET; // IPv4
 | 
				
			||||||
 | 
						m_socketAddress.sin_port = htons(m_port);
 | 
				
			||||||
 | 
						m_socketAddress.sin_addr.s_addr = inet_addr(m_ip_address.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m_socket = socket(AF_INET, SOCK_STREAM, 0);
 | 
				
			||||||
 | 
						if (m_socket < 0) {
 | 
				
			||||||
 | 
							LOG_DEBUG("Cannot create socket");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (bind(m_socket, reinterpret_cast<sockaddr*> (&m_socketAddress), m_socketAddress_len) < 0) {
 | 
				
			||||||
 | 
							LOG_DEBUG("Cannot connect socket to address");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (listen(m_socket, 20) < 0) {
 | 
				
			||||||
 | 
							LOG_DEBUG("Socket listen failed");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ChatWebAPI::~ChatWebAPI() {
 | 
				
			||||||
 | 
						close(m_socket);
 | 
				
			||||||
 | 
						close(m_new_socket);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ChatWebAPI::HandleRequest() {
 | 
				
			||||||
 | 
						m_tv.tv_sec = 0;
 | 
				
			||||||
 | 
						m_tv.tv_usec = 33;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						FD_ZERO(&fdread);
 | 
				
			||||||
 | 
						FD_SET(m_socket, &fdread);
 | 
				
			||||||
 | 
						int32_t selectStatus = select(m_socket + 1, &fdread, NULL, NULL, &m_tv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (selectStatus) {
 | 
				
			||||||
 | 
							case -1:
 | 
				
			||||||
 | 
								// error
 | 
				
			||||||
 | 
								LOG_DEBUG("Error in select");
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							case 0:
 | 
				
			||||||
 | 
								// timeout, I.E. nothing to read
 | 
				
			||||||
 | 
								// LOG_DEBUG("Timeout in select");
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default: { // available to read 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								m_new_socket = accept(m_socket, reinterpret_cast<sockaddr*> (&m_socketAddress), &m_socketAddress_len);
 | 
				
			||||||
 | 
								if (m_new_socket < 0) {
 | 
				
			||||||
 | 
									LOG_DEBUG("Failed to accept connection");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								int bytesReceived;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								char buffer[BUFFER_SIZE] = { 0 };
 | 
				
			||||||
 | 
								bytesReceived = read(m_new_socket, buffer, BUFFER_SIZE);
 | 
				
			||||||
 | 
								if (bytesReceived < 0) {
 | 
				
			||||||
 | 
									LOG_DEBUG("Failed to read bytes from client socket connection");
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									LOG_DEBUG("------ Received message from client ------");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								int status = 404;
 | 
				
			||||||
 | 
								std::string response = "{\"error\": \"not found\"}";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const char *method_c {};
 | 
				
			||||||
 | 
								const char *path_c {};
 | 
				
			||||||
 | 
								size_t buflen = 0, method_len, path_len, num_headers;
 | 
				
			||||||
 | 
								std::array<struct phr_header, 100> headers {};
 | 
				
			||||||
 | 
								int pret, minor_version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								num_headers = sizeof(headers) / sizeof(headers[0]);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								pret = phr_parse_request(buffer, strlen(buffer), &method_c, &method_len, &path_c, &path_len, &minor_version, headers.data(), &num_headers, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// cleanup
 | 
				
			||||||
 | 
								std::string method(method_c, method_len);
 | 
				
			||||||
 | 
								std::string path(path_c, path_len);
 | 
				
			||||||
 | 
								std::map<std::string, std::string> headerMap;
 | 
				
			||||||
 | 
								for (size_t i = 0; i != num_headers; ++i) {
 | 
				
			||||||
 | 
									std::string headerName(headers[i].name, headers[i].name_len);
 | 
				
			||||||
 | 
									std::string headerValue(headers[i].value, headers[i].value_len);
 | 
				
			||||||
 | 
									headerMap[headerName] = headerValue;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// log headers
 | 
				
			||||||
 | 
								for (auto& header : headerMap) {
 | 
				
			||||||
 | 
									LOG_DEBUG("header: %s: %s", header.first.c_str(), header.second.c_str());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								std::string content (buffer + pret);
 | 
				
			||||||
 | 
								LOG_DEBUG("content: %s", content.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (headerMap["Content-Type"] == "application/json") {
 | 
				
			||||||
 | 
									auto data = json::parse(content);
 | 
				
			||||||
 | 
									HandleAction(data);
 | 
				
			||||||
 | 
								}			
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						close(m_new_socket);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string ChatWebAPI::buildResponse(int status, std::string input) {
 | 
				
			||||||
 | 
						std::ostringstream ss;
 | 
				
			||||||
 | 
						ss << "HTTP/1.1 " << status <<  " OK\nContent-Type: application/json\nContent-Length: " << input.size() << "\n\n" << input;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ss.str();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ChatWebAPI::sendResponse(int status, std::string input) {
 | 
				
			||||||
 | 
						long bytesSent;
 | 
				
			||||||
 | 
						std::string response = buildResponse(status, input);
 | 
				
			||||||
 | 
						bytesSent = write(m_new_socket, response.c_str(), response.size());
 | 
				
			||||||
 | 
						if (bytesSent == response.size()) {
 | 
				
			||||||
 | 
							LOG_DEBUG("------ Server Response sent to client ------");
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							LOG_DEBUG("Error sending response to client");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ChatWebAPI::HandleAction(json data) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								dChatServer/ChatWebAPI.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								dChatServer/ChatWebAPI.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					#ifndef INCLUDED_CHATWEBAPI_LINUX
 | 
				
			||||||
 | 
					#define INCLUDED_CHATWEBAPI_LINUX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "json_fwd.hpp"
 | 
				
			||||||
 | 
					using json = nlohmann::json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChatWebAPI {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						ChatWebAPI(std::string ip_address, int port);
 | 
				
			||||||
 | 
						~ChatWebAPI();
 | 
				
			||||||
 | 
						void HandleRequest();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						std::string m_ip_address;
 | 
				
			||||||
 | 
						int m_port;
 | 
				
			||||||
 | 
						int m_socket;
 | 
				
			||||||
 | 
						int m_new_socket;
 | 
				
			||||||
 | 
						long m_incomingMessage;
 | 
				
			||||||
 | 
						struct sockaddr_in m_socketAddress;
 | 
				
			||||||
 | 
						unsigned int m_socketAddress_len;
 | 
				
			||||||
 | 
						std::string m_serverMessage;
 | 
				
			||||||
 | 
						struct timeval m_tv;
 | 
				
			||||||
 | 
						fd_set fdread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string buildResponse(int status, std::string input);
 | 
				
			||||||
 | 
						void sendResponse(int status, std::string input);
 | 
				
			||||||
 | 
						void HandleAction(json data);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
@@ -5,7 +5,8 @@ set(DNET_SOURCES "AuthPackets.cpp"
 | 
				
			|||||||
	"MasterPackets.cpp"
 | 
						"MasterPackets.cpp"
 | 
				
			||||||
	"PacketUtils.cpp"
 | 
						"PacketUtils.cpp"
 | 
				
			||||||
	"WorldPackets.cpp"
 | 
						"WorldPackets.cpp"
 | 
				
			||||||
	"ZoneInstanceManager.cpp")
 | 
						"ZoneInstanceManager.cpp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_library(dNet STATIC ${DNET_SOURCES})
 | 
					add_library(dNet STATIC ${DNET_SOURCES})
 | 
				
			||||||
target_link_libraries(dNet PRIVATE bcrypt MD5)
 | 
					target_link_libraries(dNet PRIVATE bcrypt MD5)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,3 +6,8 @@ max_number_of_best_friends=5
 | 
				
			|||||||
# Change the value below to what you would like this to be (50 is live accurate)
 | 
					# Change the value below to what you would like this to be (50 is live accurate)
 | 
				
			||||||
# going over 50 will be allowed in some secnarios, but proper handling will require client modding
 | 
					# going over 50 will be allowed in some secnarios, but proper handling will require client modding
 | 
				
			||||||
max_number_of_friends=50
 | 
					max_number_of_friends=50
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					web_server_enabled=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					web_server_listen_ip=127.0.0.1 # localhost
 | 
				
			||||||
 | 
					wed_server_listen_port=8080
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								thirdparty/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								thirdparty/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							@@ -65,3 +65,5 @@ if(UNIX AND NOT APPLE)
 | 
				
			|||||||
endif()
 | 
					endif()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_subdirectory(MD5)
 | 
					add_subdirectory(MD5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					add_subdirectory(picohttpparser)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24765
									
								
								thirdparty/nlohmann/json.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24765
									
								
								thirdparty/nlohmann/json.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										176
									
								
								thirdparty/nlohmann/json_fwd.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								thirdparty/nlohmann/json_fwd.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,176 @@
 | 
				
			|||||||
 | 
					//     __ _____ _____ _____
 | 
				
			||||||
 | 
					//  __|  |   __|     |   | |  JSON for Modern C++
 | 
				
			||||||
 | 
					// |  |  |__   |  |  | | | |  version 3.11.3
 | 
				
			||||||
 | 
					// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
 | 
				
			||||||
 | 
					#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint> // int64_t, uint64_t
 | 
				
			||||||
 | 
					#include <map> // map
 | 
				
			||||||
 | 
					#include <memory> // allocator
 | 
				
			||||||
 | 
					#include <string> // string
 | 
				
			||||||
 | 
					#include <vector> // vector
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #include <nlohmann/detail/abi_macros.hpp>
 | 
				
			||||||
 | 
					//     __ _____ _____ _____
 | 
				
			||||||
 | 
					//  __|  |   __|     |   | |  JSON for Modern C++
 | 
				
			||||||
 | 
					// |  |  |__   |  |  | | | |  version 3.11.3
 | 
				
			||||||
 | 
					// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// SPDX-FileCopyrightText: 2013-2023 Niels Lohmann <https://nlohmann.me>
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This file contains all macro definitions affecting or depending on the ABI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
 | 
				
			||||||
 | 
					    #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
 | 
				
			||||||
 | 
					        #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 11 || NLOHMANN_JSON_VERSION_PATCH != 3
 | 
				
			||||||
 | 
					            #warning "Already included a different version of the library!"
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					    #endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_VERSION_MAJOR 3   // NOLINT(modernize-macro-to-enum)
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_VERSION_MINOR 11  // NOLINT(modernize-macro-to-enum)
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_VERSION_PATCH 3   // NOLINT(modernize-macro-to-enum)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_DIAGNOSTICS
 | 
				
			||||||
 | 
					    #define JSON_DIAGNOSTICS 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
 | 
				
			||||||
 | 
					    #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if JSON_DIAGNOSTICS
 | 
				
			||||||
 | 
					    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
 | 
				
			||||||
 | 
					    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
 | 
				
			||||||
 | 
					    #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Construct the namespace ABI tags component
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b) json_abi ## a ## b
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b) \
 | 
				
			||||||
 | 
					    NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_ABI_TAGS                                       \
 | 
				
			||||||
 | 
					    NLOHMANN_JSON_ABI_TAGS_CONCAT(                                   \
 | 
				
			||||||
 | 
					            NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS,                       \
 | 
				
			||||||
 | 
					            NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Construct the namespace version component
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
 | 
				
			||||||
 | 
					    _v ## major ## _ ## minor ## _ ## patch
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
 | 
				
			||||||
 | 
					    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_VERSION
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_VERSION                                 \
 | 
				
			||||||
 | 
					    NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
 | 
				
			||||||
 | 
					                                           NLOHMANN_JSON_VERSION_MINOR, \
 | 
				
			||||||
 | 
					                                           NLOHMANN_JSON_VERSION_PATCH)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Combine namespace components
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
 | 
				
			||||||
 | 
					    NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NLOHMANN_JSON_NAMESPACE
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE               \
 | 
				
			||||||
 | 
					    nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
 | 
				
			||||||
 | 
					            NLOHMANN_JSON_ABI_TAGS,           \
 | 
				
			||||||
 | 
					            NLOHMANN_JSON_NAMESPACE_VERSION)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_BEGIN                \
 | 
				
			||||||
 | 
					    namespace nlohmann                               \
 | 
				
			||||||
 | 
					    {                                                \
 | 
				
			||||||
 | 
					    inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
 | 
				
			||||||
 | 
					                NLOHMANN_JSON_ABI_TAGS,              \
 | 
				
			||||||
 | 
					                NLOHMANN_JSON_NAMESPACE_VERSION)     \
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NLOHMANN_JSON_NAMESPACE_END
 | 
				
			||||||
 | 
					#define NLOHMANN_JSON_NAMESPACE_END                                     \
 | 
				
			||||||
 | 
					    }  /* namespace (inline namespace) NOLINT(readability/namespace) */ \
 | 
				
			||||||
 | 
					    }  // namespace nlohmann
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					@brief namespace for Niels Lohmann
 | 
				
			||||||
 | 
					@see https://github.com/nlohmann
 | 
				
			||||||
 | 
					@since version 1.0.0
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					@brief default JSONSerializer template argument
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This serializer ignores the template arguments and uses ADL
 | 
				
			||||||
 | 
					([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
 | 
				
			||||||
 | 
					for serialization.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					template<typename T = void, typename SFINAE = void>
 | 
				
			||||||
 | 
					struct adl_serializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// a class to store JSON values
 | 
				
			||||||
 | 
					/// @sa https://json.nlohmann.me/api/basic_json/
 | 
				
			||||||
 | 
					template<template<typename U, typename V, typename... Args> class ObjectType =
 | 
				
			||||||
 | 
					         std::map,
 | 
				
			||||||
 | 
					         template<typename U, typename... Args> class ArrayType = std::vector,
 | 
				
			||||||
 | 
					         class StringType = std::string, class BooleanType = bool,
 | 
				
			||||||
 | 
					         class NumberIntegerType = std::int64_t,
 | 
				
			||||||
 | 
					         class NumberUnsignedType = std::uint64_t,
 | 
				
			||||||
 | 
					         class NumberFloatType = double,
 | 
				
			||||||
 | 
					         template<typename U> class AllocatorType = std::allocator,
 | 
				
			||||||
 | 
					         template<typename T, typename SFINAE = void> class JSONSerializer =
 | 
				
			||||||
 | 
					         adl_serializer,
 | 
				
			||||||
 | 
					         class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
 | 
				
			||||||
 | 
					         class CustomBaseClass = void>
 | 
				
			||||||
 | 
					class basic_json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
 | 
				
			||||||
 | 
					/// @sa https://json.nlohmann.me/api/json_pointer/
 | 
				
			||||||
 | 
					template<typename RefStringType>
 | 
				
			||||||
 | 
					class json_pointer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*!
 | 
				
			||||||
 | 
					@brief default specialization
 | 
				
			||||||
 | 
					@sa https://json.nlohmann.me/api/json/
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					using json = basic_json<>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @brief a minimal map-like container that preserves insertion order
 | 
				
			||||||
 | 
					/// @sa https://json.nlohmann.me/api/ordered_map/
 | 
				
			||||||
 | 
					template<class Key, class T, class IgnoredLess, class Allocator>
 | 
				
			||||||
 | 
					struct ordered_map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// @brief specialization that maintains the insertion order of object keys
 | 
				
			||||||
 | 
					/// @sa https://json.nlohmann.me/api/ordered_json/
 | 
				
			||||||
 | 
					using ordered_json = basic_json<nlohmann::ordered_map>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NLOHMANN_JSON_NAMESPACE_END
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif  // INCLUDE_NLOHMANN_JSON_FWD_HPP_
 | 
				
			||||||
							
								
								
									
										1
									
								
								thirdparty/picohttpparser/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								thirdparty/picohttpparser/CMakeLists.txt
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					add_library(picohttpparser "picohttpparser.c")
 | 
				
			||||||
							
								
								
									
										685
									
								
								thirdparty/picohttpparser/picohttpparser.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										685
									
								
								thirdparty/picohttpparser/picohttpparser.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,685 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
 | 
				
			||||||
 | 
					 *                         Shigeo Mitsunari
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The software is licensed under either the MIT License (below) or the Perl
 | 
				
			||||||
 | 
					 * license.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#ifdef __SSE4_2__
 | 
				
			||||||
 | 
					#ifdef _MSC_VER
 | 
				
			||||||
 | 
					#include <nmmintrin.h>
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include <x86intrin.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include "picohttpparser.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if __GNUC__ >= 3
 | 
				
			||||||
 | 
					#define likely(x) __builtin_expect(!!(x), 1)
 | 
				
			||||||
 | 
					#define unlikely(x) __builtin_expect(!!(x), 0)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define likely(x) (x)
 | 
				
			||||||
 | 
					#define unlikely(x) (x)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _MSC_VER
 | 
				
			||||||
 | 
					#define ALIGNED(n) _declspec(align(n))
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define ALIGNED(n) __attribute__((aligned(n)))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHECK_EOF()                                                                                                                \
 | 
				
			||||||
 | 
					    if (buf == buf_end) {                                                                                                          \
 | 
				
			||||||
 | 
					        *ret = -2;                                                                                                                 \
 | 
				
			||||||
 | 
					        return NULL;                                                                                                               \
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EXPECT_CHAR_NO_CHECK(ch)                                                                                                   \
 | 
				
			||||||
 | 
					    if (*buf++ != ch) {                                                                                                            \
 | 
				
			||||||
 | 
					        *ret = -1;                                                                                                                 \
 | 
				
			||||||
 | 
					        return NULL;                                                                                                               \
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EXPECT_CHAR(ch)                                                                                                            \
 | 
				
			||||||
 | 
					    CHECK_EOF();                                                                                                                   \
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK(ch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ADVANCE_TOKEN(tok, toklen)                                                                                                 \
 | 
				
			||||||
 | 
					    do {                                                                                                                           \
 | 
				
			||||||
 | 
					        const char *tok_start = buf;                                                                                               \
 | 
				
			||||||
 | 
					        static const char ALIGNED(16) ranges2[16] = "\000\040\177\177";                                                            \
 | 
				
			||||||
 | 
					        int found2;                                                                                                                \
 | 
				
			||||||
 | 
					        buf = findchar_fast(buf, buf_end, ranges2, 4, &found2);                                                                    \
 | 
				
			||||||
 | 
					        if (!found2) {                                                                                                             \
 | 
				
			||||||
 | 
					            CHECK_EOF();                                                                                                           \
 | 
				
			||||||
 | 
					        }                                                                                                                          \
 | 
				
			||||||
 | 
					        while (1) {                                                                                                                \
 | 
				
			||||||
 | 
					            if (*buf == ' ') {                                                                                                     \
 | 
				
			||||||
 | 
					                break;                                                                                                             \
 | 
				
			||||||
 | 
					            } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {                                                                      \
 | 
				
			||||||
 | 
					                if ((unsigned char)*buf < '\040' || *buf == '\177') {                                                              \
 | 
				
			||||||
 | 
					                    *ret = -1;                                                                                                     \
 | 
				
			||||||
 | 
					                    return NULL;                                                                                                   \
 | 
				
			||||||
 | 
					                }                                                                                                                  \
 | 
				
			||||||
 | 
					            }                                                                                                                      \
 | 
				
			||||||
 | 
					            ++buf;                                                                                                                 \
 | 
				
			||||||
 | 
					            CHECK_EOF();                                                                                                           \
 | 
				
			||||||
 | 
					        }                                                                                                                          \
 | 
				
			||||||
 | 
					        tok = tok_start;                                                                                                           \
 | 
				
			||||||
 | 
					        toklen = buf - tok_start;                                                                                                  \
 | 
				
			||||||
 | 
					    } while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					                                    "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					                                    "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
 | 
				
			||||||
 | 
					                                    "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
 | 
				
			||||||
 | 
					                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
 | 
				
			||||||
 | 
					                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    *found = 0;
 | 
				
			||||||
 | 
					#if __SSE4_2__
 | 
				
			||||||
 | 
					    if (likely(buf_end - buf >= 16)) {
 | 
				
			||||||
 | 
					        __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size_t left = (buf_end - buf) & ~15;
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
 | 
				
			||||||
 | 
					            int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
 | 
				
			||||||
 | 
					            if (unlikely(r != 16)) {
 | 
				
			||||||
 | 
					                buf += r;
 | 
				
			||||||
 | 
					                *found = 1;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            buf += 16;
 | 
				
			||||||
 | 
					            left -= 16;
 | 
				
			||||||
 | 
					        } while (likely(left != 0));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    /* suppress unused parameter warning */
 | 
				
			||||||
 | 
					    (void)buf_end;
 | 
				
			||||||
 | 
					    (void)ranges;
 | 
				
			||||||
 | 
					    (void)ranges_size;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *token_start = buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __SSE4_2__
 | 
				
			||||||
 | 
					    static const char ALIGNED(16) ranges1[16] = "\0\010"    /* allow HT */
 | 
				
			||||||
 | 
					                                                "\012\037"  /* allow SP and up to but not including DEL */
 | 
				
			||||||
 | 
					                                                "\177\177"; /* allow chars w. MSB set */
 | 
				
			||||||
 | 
					    int found;
 | 
				
			||||||
 | 
					    buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
 | 
				
			||||||
 | 
					    if (found)
 | 
				
			||||||
 | 
					        goto FOUND_CTL;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
 | 
				
			||||||
 | 
					    while (likely(buf_end - buf >= 8)) {
 | 
				
			||||||
 | 
					#define DOIT()                                                                                                                     \
 | 
				
			||||||
 | 
					    do {                                                                                                                           \
 | 
				
			||||||
 | 
					        if (unlikely(!IS_PRINTABLE_ASCII(*buf)))                                                                                   \
 | 
				
			||||||
 | 
					            goto NonPrintable;                                                                                                     \
 | 
				
			||||||
 | 
					        ++buf;                                                                                                                     \
 | 
				
			||||||
 | 
					    } while (0)
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					        DOIT();
 | 
				
			||||||
 | 
					#undef DOIT
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					    NonPrintable:
 | 
				
			||||||
 | 
					        if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
 | 
				
			||||||
 | 
					            goto FOUND_CTL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    for (;; ++buf) {
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					        if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
 | 
				
			||||||
 | 
					            if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
 | 
				
			||||||
 | 
					                goto FOUND_CTL;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					FOUND_CTL:
 | 
				
			||||||
 | 
					    if (likely(*buf == '\015')) {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        EXPECT_CHAR('\012');
 | 
				
			||||||
 | 
					        *token_len = buf - 2 - token_start;
 | 
				
			||||||
 | 
					    } else if (*buf == '\012') {
 | 
				
			||||||
 | 
					        *token_len = buf - token_start;
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        *ret = -1;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    *token = token_start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ret_cnt = 0;
 | 
				
			||||||
 | 
					    buf = last_len < 3 ? buf : buf + last_len - 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					        if (*buf == '\015') {
 | 
				
			||||||
 | 
					            ++buf;
 | 
				
			||||||
 | 
					            CHECK_EOF();
 | 
				
			||||||
 | 
					            EXPECT_CHAR('\012');
 | 
				
			||||||
 | 
					            ++ret_cnt;
 | 
				
			||||||
 | 
					        } else if (*buf == '\012') {
 | 
				
			||||||
 | 
					            ++buf;
 | 
				
			||||||
 | 
					            ++ret_cnt;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            ++buf;
 | 
				
			||||||
 | 
					            ret_cnt = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (ret_cnt == 2) {
 | 
				
			||||||
 | 
					            return buf;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *ret = -2;
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PARSE_INT(valp_, mul_)                                                                                                     \
 | 
				
			||||||
 | 
					    if (*buf < '0' || '9' < *buf) {                                                                                                \
 | 
				
			||||||
 | 
					        buf++;                                                                                                                     \
 | 
				
			||||||
 | 
					        *ret = -1;                                                                                                                 \
 | 
				
			||||||
 | 
					        return NULL;                                                                                                               \
 | 
				
			||||||
 | 
					    }                                                                                                                              \
 | 
				
			||||||
 | 
					    *(valp_) = (mul_) * (*buf++ - '0');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PARSE_INT_3(valp_)                                                                                                         \
 | 
				
			||||||
 | 
					    do {                                                                                                                           \
 | 
				
			||||||
 | 
					        int res_ = 0;                                                                                                              \
 | 
				
			||||||
 | 
					        PARSE_INT(&res_, 100)                                                                                                      \
 | 
				
			||||||
 | 
					        *valp_ = res_;                                                                                                             \
 | 
				
			||||||
 | 
					        PARSE_INT(&res_, 10)                                                                                                       \
 | 
				
			||||||
 | 
					        *valp_ += res_;                                                                                                            \
 | 
				
			||||||
 | 
					        PARSE_INT(&res_, 1)                                                                                                        \
 | 
				
			||||||
 | 
					        *valp_ += res_;                                                                                                            \
 | 
				
			||||||
 | 
					    } while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* returned pointer is always within [buf, buf_end), or null */
 | 
				
			||||||
 | 
					static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char,
 | 
				
			||||||
 | 
					                               int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128
 | 
				
			||||||
 | 
					     * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */
 | 
				
			||||||
 | 
					    static const char ALIGNED(16) ranges[] = "\x00 "  /* control chars and up to SP */
 | 
				
			||||||
 | 
					                                             "\"\""   /* 0x22 */
 | 
				
			||||||
 | 
					                                             "()"     /* 0x28,0x29 */
 | 
				
			||||||
 | 
					                                             ",,"     /* 0x2c */
 | 
				
			||||||
 | 
					                                             "//"     /* 0x2f */
 | 
				
			||||||
 | 
					                                             ":@"     /* 0x3a-0x40 */
 | 
				
			||||||
 | 
					                                             "[]"     /* 0x5b-0x5d */
 | 
				
			||||||
 | 
					                                             "{\xff"; /* 0x7b-0xff */
 | 
				
			||||||
 | 
					    const char *buf_start = buf;
 | 
				
			||||||
 | 
					    int found;
 | 
				
			||||||
 | 
					    buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
 | 
				
			||||||
 | 
					    if (!found) {
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        if (*buf == next_char) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } else if (!token_char_map[(unsigned char)*buf]) {
 | 
				
			||||||
 | 
					            *ret = -1;
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    *token = buf_start;
 | 
				
			||||||
 | 
					    *token_len = buf - buf_start;
 | 
				
			||||||
 | 
					    return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* returned pointer is always within [buf, buf_end), or null */
 | 
				
			||||||
 | 
					static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* we want at least [HTTP/1.<two chars>] to try to parse */
 | 
				
			||||||
 | 
					    if (buf_end - buf < 9) {
 | 
				
			||||||
 | 
					        *ret = -2;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('H');
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('T');
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('T');
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('P');
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('/');
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('1');
 | 
				
			||||||
 | 
					    EXPECT_CHAR_NO_CHECK('.');
 | 
				
			||||||
 | 
					    PARSE_INT(minor_version, 1);
 | 
				
			||||||
 | 
					    return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
 | 
				
			||||||
 | 
					                                 size_t max_headers, int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (;; ++*num_headers) {
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					        if (*buf == '\015') {
 | 
				
			||||||
 | 
					            ++buf;
 | 
				
			||||||
 | 
					            EXPECT_CHAR('\012');
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        } else if (*buf == '\012') {
 | 
				
			||||||
 | 
					            ++buf;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (*num_headers == max_headers) {
 | 
				
			||||||
 | 
					            *ret = -1;
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
 | 
				
			||||||
 | 
					            /* parsing name, but do not discard SP before colon, see
 | 
				
			||||||
 | 
					             * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
 | 
				
			||||||
 | 
					            if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) {
 | 
				
			||||||
 | 
					                return NULL;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (headers[*num_headers].name_len == 0) {
 | 
				
			||||||
 | 
					                *ret = -1;
 | 
				
			||||||
 | 
					                return NULL;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ++buf;
 | 
				
			||||||
 | 
					            for (;; ++buf) {
 | 
				
			||||||
 | 
					                CHECK_EOF();
 | 
				
			||||||
 | 
					                if (!(*buf == ' ' || *buf == '\t')) {
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            headers[*num_headers].name = NULL;
 | 
				
			||||||
 | 
					            headers[*num_headers].name_len = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const char *value;
 | 
				
			||||||
 | 
					        size_t value_len;
 | 
				
			||||||
 | 
					        if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
 | 
				
			||||||
 | 
					            return NULL;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* remove trailing SPs and HTABs */
 | 
				
			||||||
 | 
					        const char *value_end = value + value_len;
 | 
				
			||||||
 | 
					        for (; value_end != value; --value_end) {
 | 
				
			||||||
 | 
					            const char c = *(value_end - 1);
 | 
				
			||||||
 | 
					            if (!(c == ' ' || c == '\t')) {
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        headers[*num_headers].value = value;
 | 
				
			||||||
 | 
					        headers[*num_headers].value_len = value_end - value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return buf;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
 | 
				
			||||||
 | 
					                                 size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
 | 
				
			||||||
 | 
					                                 size_t max_headers, int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* skip first empty line (some clients add CRLF after POST content) */
 | 
				
			||||||
 | 
					    CHECK_EOF();
 | 
				
			||||||
 | 
					    if (*buf == '\015') {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        EXPECT_CHAR('\012');
 | 
				
			||||||
 | 
					    } else if (*buf == '\012') {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* parse request line */
 | 
				
			||||||
 | 
					    if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					    } while (*buf == ' ');
 | 
				
			||||||
 | 
					    ADVANCE_TOKEN(*path, *path_len);
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					    } while (*buf == ' ');
 | 
				
			||||||
 | 
					    if (*method_len == 0 || *path_len == 0) {
 | 
				
			||||||
 | 
					        *ret = -1;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (*buf == '\015') {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        EXPECT_CHAR('\012');
 | 
				
			||||||
 | 
					    } else if (*buf == '\012') {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        *ret = -1;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
 | 
				
			||||||
 | 
					                      size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *buf = buf_start, *buf_end = buf_start + len;
 | 
				
			||||||
 | 
					    size_t max_headers = *num_headers;
 | 
				
			||||||
 | 
					    int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *method = NULL;
 | 
				
			||||||
 | 
					    *method_len = 0;
 | 
				
			||||||
 | 
					    *path = NULL;
 | 
				
			||||||
 | 
					    *path_len = 0;
 | 
				
			||||||
 | 
					    *minor_version = -1;
 | 
				
			||||||
 | 
					    *num_headers = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* if last_len != 0, check if the request is complete (a fast countermeasure
 | 
				
			||||||
 | 
					       againt slowloris */
 | 
				
			||||||
 | 
					    if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
 | 
				
			||||||
 | 
					                             &r)) == NULL) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (int)(buf - buf_start);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
 | 
				
			||||||
 | 
					                                  size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* parse "HTTP/1.x" */
 | 
				
			||||||
 | 
					    if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /* skip space */
 | 
				
			||||||
 | 
					    if (*buf != ' ') {
 | 
				
			||||||
 | 
					        *ret = -1;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					        ++buf;
 | 
				
			||||||
 | 
					        CHECK_EOF();
 | 
				
			||||||
 | 
					    } while (*buf == ' ');
 | 
				
			||||||
 | 
					    /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
 | 
				
			||||||
 | 
					    if (buf_end - buf < 4) {
 | 
				
			||||||
 | 
					        *ret = -2;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    PARSE_INT_3(status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* get message including preceding space */
 | 
				
			||||||
 | 
					    if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (*msg_len == 0) {
 | 
				
			||||||
 | 
					        /* ok */
 | 
				
			||||||
 | 
					    } else if (**msg == ' ') {
 | 
				
			||||||
 | 
					        /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP
 | 
				
			||||||
 | 
					         * before running past the end of the given buffer. */
 | 
				
			||||||
 | 
					        do {
 | 
				
			||||||
 | 
					            ++*msg;
 | 
				
			||||||
 | 
					            --*msg_len;
 | 
				
			||||||
 | 
					        } while (**msg == ' ');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* garbage found after status code */
 | 
				
			||||||
 | 
					        *ret = -1;
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
 | 
				
			||||||
 | 
					                       struct phr_header *headers, size_t *num_headers, size_t last_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *buf = buf_start, *buf_end = buf + len;
 | 
				
			||||||
 | 
					    size_t max_headers = *num_headers;
 | 
				
			||||||
 | 
					    int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *minor_version = -1;
 | 
				
			||||||
 | 
					    *status = 0;
 | 
				
			||||||
 | 
					    *msg = NULL;
 | 
				
			||||||
 | 
					    *msg_len = 0;
 | 
				
			||||||
 | 
					    *num_headers = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* if last_len != 0, check if the response is complete (a fast countermeasure
 | 
				
			||||||
 | 
					       against slowloris */
 | 
				
			||||||
 | 
					    if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (int)(buf - buf_start);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const char *buf = buf_start, *buf_end = buf + len;
 | 
				
			||||||
 | 
					    size_t max_headers = *num_headers;
 | 
				
			||||||
 | 
					    int r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *num_headers = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* if last_len != 0, check if the response is complete (a fast countermeasure
 | 
				
			||||||
 | 
					       against slowloris */
 | 
				
			||||||
 | 
					    if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (int)(buf - buf_start);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum {
 | 
				
			||||||
 | 
					    CHUNKED_IN_CHUNK_SIZE,
 | 
				
			||||||
 | 
					    CHUNKED_IN_CHUNK_EXT,
 | 
				
			||||||
 | 
					    CHUNKED_IN_CHUNK_DATA,
 | 
				
			||||||
 | 
					    CHUNKED_IN_CHUNK_CRLF,
 | 
				
			||||||
 | 
					    CHUNKED_IN_TRAILERS_LINE_HEAD,
 | 
				
			||||||
 | 
					    CHUNKED_IN_TRAILERS_LINE_MIDDLE
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int decode_hex(int ch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if ('0' <= ch && ch <= '9') {
 | 
				
			||||||
 | 
					        return ch - '0';
 | 
				
			||||||
 | 
					    } else if ('A' <= ch && ch <= 'F') {
 | 
				
			||||||
 | 
					        return ch - 'A' + 0xa;
 | 
				
			||||||
 | 
					    } else if ('a' <= ch && ch <= 'f') {
 | 
				
			||||||
 | 
					        return ch - 'a' + 0xa;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    size_t dst = 0, src = 0, bufsz = *_bufsz;
 | 
				
			||||||
 | 
					    ssize_t ret = -2; /* incomplete */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    decoder->_total_read += bufsz;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        switch (decoder->_state) {
 | 
				
			||||||
 | 
					        case CHUNKED_IN_CHUNK_SIZE:
 | 
				
			||||||
 | 
					            for (;; ++src) {
 | 
				
			||||||
 | 
					                int v;
 | 
				
			||||||
 | 
					                if (src == bufsz)
 | 
				
			||||||
 | 
					                    goto Exit;
 | 
				
			||||||
 | 
					                if ((v = decode_hex(buf[src])) == -1) {
 | 
				
			||||||
 | 
					                    if (decoder->_hex_count == 0) {
 | 
				
			||||||
 | 
					                        ret = -1;
 | 
				
			||||||
 | 
					                        goto Exit;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    /* the only characters that may appear after the chunk size are BWS, semicolon, or CRLF */
 | 
				
			||||||
 | 
					                    switch (buf[src]) {
 | 
				
			||||||
 | 
					                    case ' ':
 | 
				
			||||||
 | 
					                    case '\011':
 | 
				
			||||||
 | 
					                    case ';':
 | 
				
			||||||
 | 
					                    case '\012':
 | 
				
			||||||
 | 
					                    case '\015':
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        ret = -1;
 | 
				
			||||||
 | 
					                        goto Exit;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (decoder->_hex_count == sizeof(size_t) * 2) {
 | 
				
			||||||
 | 
					                    ret = -1;
 | 
				
			||||||
 | 
					                    goto Exit;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
 | 
				
			||||||
 | 
					                ++decoder->_hex_count;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            decoder->_hex_count = 0;
 | 
				
			||||||
 | 
					            decoder->_state = CHUNKED_IN_CHUNK_EXT;
 | 
				
			||||||
 | 
					        /* fallthru */
 | 
				
			||||||
 | 
					        case CHUNKED_IN_CHUNK_EXT:
 | 
				
			||||||
 | 
					            /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
 | 
				
			||||||
 | 
					            for (;; ++src) {
 | 
				
			||||||
 | 
					                if (src == bufsz)
 | 
				
			||||||
 | 
					                    goto Exit;
 | 
				
			||||||
 | 
					                if (buf[src] == '\012')
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ++src;
 | 
				
			||||||
 | 
					            if (decoder->bytes_left_in_chunk == 0) {
 | 
				
			||||||
 | 
					                if (decoder->consume_trailer) {
 | 
				
			||||||
 | 
					                    decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    goto Complete;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            decoder->_state = CHUNKED_IN_CHUNK_DATA;
 | 
				
			||||||
 | 
					        /* fallthru */
 | 
				
			||||||
 | 
					        case CHUNKED_IN_CHUNK_DATA: {
 | 
				
			||||||
 | 
					            size_t avail = bufsz - src;
 | 
				
			||||||
 | 
					            if (avail < decoder->bytes_left_in_chunk) {
 | 
				
			||||||
 | 
					                if (dst != src)
 | 
				
			||||||
 | 
					                    memmove(buf + dst, buf + src, avail);
 | 
				
			||||||
 | 
					                src += avail;
 | 
				
			||||||
 | 
					                dst += avail;
 | 
				
			||||||
 | 
					                decoder->bytes_left_in_chunk -= avail;
 | 
				
			||||||
 | 
					                goto Exit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (dst != src)
 | 
				
			||||||
 | 
					                memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
 | 
				
			||||||
 | 
					            src += decoder->bytes_left_in_chunk;
 | 
				
			||||||
 | 
					            dst += decoder->bytes_left_in_chunk;
 | 
				
			||||||
 | 
					            decoder->bytes_left_in_chunk = 0;
 | 
				
			||||||
 | 
					            decoder->_state = CHUNKED_IN_CHUNK_CRLF;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* fallthru */
 | 
				
			||||||
 | 
					        case CHUNKED_IN_CHUNK_CRLF:
 | 
				
			||||||
 | 
					            for (;; ++src) {
 | 
				
			||||||
 | 
					                if (src == bufsz)
 | 
				
			||||||
 | 
					                    goto Exit;
 | 
				
			||||||
 | 
					                if (buf[src] != '\015')
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (buf[src] != '\012') {
 | 
				
			||||||
 | 
					                ret = -1;
 | 
				
			||||||
 | 
					                goto Exit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ++src;
 | 
				
			||||||
 | 
					            decoder->_state = CHUNKED_IN_CHUNK_SIZE;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        case CHUNKED_IN_TRAILERS_LINE_HEAD:
 | 
				
			||||||
 | 
					            for (;; ++src) {
 | 
				
			||||||
 | 
					                if (src == bufsz)
 | 
				
			||||||
 | 
					                    goto Exit;
 | 
				
			||||||
 | 
					                if (buf[src] != '\015')
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (buf[src++] == '\012')
 | 
				
			||||||
 | 
					                goto Complete;
 | 
				
			||||||
 | 
					            decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
 | 
				
			||||||
 | 
					        /* fallthru */
 | 
				
			||||||
 | 
					        case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
 | 
				
			||||||
 | 
					            for (;; ++src) {
 | 
				
			||||||
 | 
					                if (src == bufsz)
 | 
				
			||||||
 | 
					                    goto Exit;
 | 
				
			||||||
 | 
					                if (buf[src] == '\012')
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            ++src;
 | 
				
			||||||
 | 
					            decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					            assert(!"decoder is corrupt");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Complete:
 | 
				
			||||||
 | 
					    ret = bufsz - src;
 | 
				
			||||||
 | 
					Exit:
 | 
				
			||||||
 | 
					    if (dst != src)
 | 
				
			||||||
 | 
					        memmove(buf + dst, buf + src, bufsz - src);
 | 
				
			||||||
 | 
					    *_bufsz = dst;
 | 
				
			||||||
 | 
					    /* if incomplete but the overhead of the chunked encoding is >=100KB and >80%, signal an error */
 | 
				
			||||||
 | 
					    if (ret == -2) {
 | 
				
			||||||
 | 
					        decoder->_total_overhead += bufsz - dst;
 | 
				
			||||||
 | 
					        if (decoder->_total_overhead >= 100 * 1024 && decoder->_total_read - decoder->_total_overhead < decoder->_total_read / 4)
 | 
				
			||||||
 | 
					            ret = -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return decoder->_state == CHUNKED_IN_CHUNK_DATA;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#undef CHECK_EOF
 | 
				
			||||||
 | 
					#undef EXPECT_CHAR
 | 
				
			||||||
 | 
					#undef ADVANCE_TOKEN
 | 
				
			||||||
							
								
								
									
										90
									
								
								thirdparty/picohttpparser/picohttpparser.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								thirdparty/picohttpparser/picohttpparser.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
 | 
				
			||||||
 | 
					 *                         Shigeo Mitsunari
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The software is licensed under either the MIT License (below) or the Perl
 | 
				
			||||||
 | 
					 * license.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef picohttpparser_h
 | 
				
			||||||
 | 
					#define picohttpparser_h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _MSC_VER
 | 
				
			||||||
 | 
					#define ssize_t intptr_t
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* contains name and value of a header (name == NULL if is a continuing line
 | 
				
			||||||
 | 
					 * of a multiline header */
 | 
				
			||||||
 | 
					struct phr_header {
 | 
				
			||||||
 | 
					    const char *name;
 | 
				
			||||||
 | 
					    size_t name_len;
 | 
				
			||||||
 | 
					    const char *value;
 | 
				
			||||||
 | 
					    size_t value_len;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* returns number of bytes consumed if successful, -2 if request is partial,
 | 
				
			||||||
 | 
					 * -1 if failed */
 | 
				
			||||||
 | 
					int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len,
 | 
				
			||||||
 | 
					                      int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ditto */
 | 
				
			||||||
 | 
					int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
 | 
				
			||||||
 | 
					                       struct phr_header *headers, size_t *num_headers, size_t last_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ditto */
 | 
				
			||||||
 | 
					int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* should be zero-filled before start */
 | 
				
			||||||
 | 
					struct phr_chunked_decoder {
 | 
				
			||||||
 | 
					    size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
 | 
				
			||||||
 | 
					    char consume_trailer;       /* if trailing headers should be consumed */
 | 
				
			||||||
 | 
					    char _hex_count;
 | 
				
			||||||
 | 
					    char _state;
 | 
				
			||||||
 | 
					    uint64_t _total_read;
 | 
				
			||||||
 | 
					    uint64_t _total_overhead;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-
 | 
				
			||||||
 | 
					 * encoding headers.  When the function returns without an error, bufsz is
 | 
				
			||||||
 | 
					 * updated to the length of the decoded data available.  Applications should
 | 
				
			||||||
 | 
					 * repeatedly call the function while it returns -2 (incomplete) every time
 | 
				
			||||||
 | 
					 * supplying newly arrived data.  If the end of the chunked-encoded data is
 | 
				
			||||||
 | 
					 * found, the function returns a non-negative number indicating the number of
 | 
				
			||||||
 | 
					 * octets left undecoded, that starts from the offset returned by `*bufsz`.
 | 
				
			||||||
 | 
					 * Returns -1 on error.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* returns if the chunked decoder is in middle of chunked data */
 | 
				
			||||||
 | 
					int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		Reference in New Issue
	
	Block a user