Skip to content

Instantly share code, notes, and snippets.

@aneury1
Last active July 14, 2023 20:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aneury1/270a2444bdf0dd7fa59c641a5305975f to your computer and use it in GitHub Desktop.
Save aneury1/270a2444bdf0dd7fa59c641a5305975f to your computer and use it in GitHub Desktop.
#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