Last active
July 14, 2023 20:25
-
-
Save aneury1/270a2444bdf0dd7fa59c641a5305975f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <unordered_map> | |
#include <winsock2.h> | |
#include <ws2tcpip.h> | |
#include <thread> | |
#include <vector> | |
#include <iostream> | |
#include <string> | |
#include <vector> | |
#include <map> | |
#include <regex> | |
#include <sstream> | |
#pragma comment(lib, "ws2_32.lib") | |
const int MAX_CONNECTIONS = 10000; | |
// Define a structure to store client information | |
struct ClientInfo { | |
SOCKET sockfd; | |
sockaddr_in clientAddr; | |
}; | |
struct Request { | |
std::string url; | |
std::map<std::string, std::string> parameters; | |
}; | |
struct Response { | |
}; | |
typedef Response *(*ParserEndpoint)(Request* request); | |
std::map<std::string, ParserEndpoint> registered_endpoint; | |
ParserEndpoint parseRequest(Request request, ParserEndpoint notFoundHandler) { | |
ParserEndpoint ret = nullptr; | |
std::string url = request.url; | |
bool found = false; | |
std::map<std::string, std::string> parameters; // Create an empty map to store the parameters | |
for (const auto& endpoint : registered_endpoint) { | |
std::string pattern = endpoint.first; | |
std::regex regexPattern(std::regex_replace(pattern, std::regex(":\\w+"), "(\\w+)")); | |
std::smatch matches; | |
if (std::regex_match(url, matches, regexPattern)) { | |
ret = endpoint.second; | |
found = true; | |
std::regex paramRegex(":\\w+"); | |
std::sregex_iterator paramIterator(pattern.begin(), pattern.end(), paramRegex); | |
for (std::size_t i = 1; i < matches.size(); ++i) { | |
std::string paramName = paramIterator->str().substr(1); // Remove the leading ":" | |
std::string paramValue = matches[i].str(); | |
std::cout << "name " << paramName << " Value: " << paramValue<<"\n\n"; | |
parameters[paramName] = paramValue; // Store the parameter name-value pair in the map | |
++paramIterator; | |
} | |
break; | |
} | |
} | |
if (!found) { | |
if (!notFoundHandler) { | |
notFoundHandler = [](Request*)->Response* { | |
return new Response(); | |
}; | |
} | |
ret = notFoundHandler; | |
} | |
// Store the parameters in the Request object | |
request.parameters = parameters; | |
return ret; | |
} | |
std::unordered_map<std::string, std::string> parseRequest(const std::string& request) { | |
std::unordered_map<std::string, std::string> requestData; | |
std::istringstream iss(request); | |
std::string requestLine; | |
// Parse the request line | |
std::getline(iss, requestLine); | |
// Find the position of the first space character | |
size_t verbEndPos = requestLine.find(' '); | |
if (verbEndPos != std::string::npos) { | |
// Extract the verb | |
std::string verb = requestLine.substr(0, verbEndPos); | |
// Find the position of the second space character | |
size_t urlEndPos = requestLine.find(' ', verbEndPos + 1); | |
if (urlEndPos != std::string::npos) { | |
// Extract the URL | |
std::string url = requestLine.substr(verbEndPos + 1, urlEndPos - verbEndPos - 1); | |
// Save the verb and URL into the map | |
requestData["verb"] = verb; | |
requestData["url"] = url; | |
} | |
} | |
return requestData; | |
} | |
std::unordered_map<std::string, std::string> parseHeaders(const std::string& request) { | |
std::unordered_map<std::string, std::string> headers; | |
std::istringstream iss(request); | |
std::string line; | |
// Skip the first line (request line) | |
std::getline(iss, line); | |
// Parse headers | |
while (std::getline(iss, line) && !line.empty()) { | |
size_t colonPos = line.find(':'); | |
if (colonPos != std::string::npos) { | |
std::string headerName = line.substr(0, colonPos); | |
std::string headerValue = line.substr(colonPos + 2); // Skip the colon and space | |
// Remove any leading or trailing whitespaces from the header value | |
headerValue.erase(0, headerValue.find_first_not_of(" \t")); | |
headerValue.erase(headerValue.find_last_not_of(" \t") + 1); | |
headers[headerName] = headerValue; | |
} | |
} | |
return headers; | |
} | |
// Function to handle an individual client connection | |
void handleClient(ClientInfo* client) { | |
// Receive and process the request | |
char buffer[8194]={0x00}; | |
int bytesRead = recv(client->sockfd, buffer, sizeof(buffer), 0); | |
if (bytesRead > 0) { | |
std::string request(buffer, bytesRead); | |
auto headers = parseHeaders(request); | |
auto head = parseRequest(request); | |
std::string stream; | |
stream += "Http Verb and URL \n"; | |
stream += head["verb"] + " \n"; | |
stream += head["url"] +" \n\n"; | |
stream += "Http Headers"; | |
for (const auto& header : headers) { | |
stream += header.first + ": " + header.second +"\n"; | |
} | |
// Process the request and generate a response | |
std::string response = "HTTP/1.1 200 OK\r\nContent-Length: "; | |
response += std::to_string(stream.size()); | |
response += "\r\n\r\n"; | |
response += stream; | |
//12\r\n\r\nHello World!"; | |
// Send the response back to the client | |
send(client->sockfd, response.c_str(), response.length(), 0); | |
} | |
// Close the client connection | |
closesocket(client->sockfd); | |
delete client; | |
} | |
//Response* home(Request* request) { | |
// std::cout << "THis is a home function"; | |
// return nullptr; | |
//} | |
//Response* user(Request* request) { | |
// std::cout << "THis is a user function"; | |
// return nullptr; | |
//} | |
int main() { | |
// Initialize WinSock | |
WSADATA wsaData; | |
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { | |
std::cerr << "Failed to initialize WinSock" << std::endl; | |
return 1; | |
} | |
// Create a socket for incoming connections | |
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, 0); | |
if (serverSocket == INVALID_SOCKET) { | |
std::cerr << "Failed to create server socket" << std::endl; | |
return 1; | |
} | |
// Set socket options to reuse address and enable listening | |
int opt = 1; | |
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); | |
// Set up server address and port | |
sockaddr_in serverAddr{}; | |
serverAddr.sin_family = AF_INET; | |
serverAddr.sin_addr.s_addr = INADDR_ANY; | |
serverAddr.sin_port = htons(9090); | |
// Bind the socket to the server address | |
if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { | |
std::cerr << "Failed to bind server socket" << std::endl; | |
return 1; | |
} | |
// Start listening for incoming connections | |
if (listen(serverSocket, MAX_CONNECTIONS) == SOCKET_ERROR) { | |
std::cerr << "Failed to listen on server socket" << std::endl; | |
return 1; | |
} | |
std::cout << "Server started. Listening on port 8080..." << std::endl; | |
// Main event loop to handle incoming connections | |
while (true) { | |
// Accept incoming connection | |
sockaddr_in clientAddr{}; | |
int clientAddrLen = sizeof(clientAddr); | |
SOCKET clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen); | |
if (clientSocket == INVALID_SOCKET) { | |
std::cerr << "Failed to accept client connection" << std::endl; | |
continue; | |
} | |
// Create a new client info structure | |
ClientInfo* client = new ClientInfo; | |
client->sockfd = clientSocket; | |
client->clientAddr = clientAddr; | |
// Handle the client connection in a separate thread | |
std::thread clientThread(handleClient, client); | |
clientThread.detach(); | |
} | |
// Close the server socket | |
closesocket(serverSocket); | |
// Cleanup WinSock | |
WSACleanup(); | |
return 0; | |
} | |
void test() { | |
/*registered_endpoint["/"] = home; | |
registered_endpoint["/user/:id/:aneury"] = user; | |
Request request; | |
request.url = "/user/1/aneury"; | |
parseRequest(request, nullptr)(&request);*/ | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment