First really working router Version
This commit is contained in:
2
Makefile
2
Makefile
@@ -2,4 +2,4 @@ run: build
|
|||||||
./server
|
./server
|
||||||
|
|
||||||
build:
|
build:
|
||||||
g++ -std=c++20 server.cpp request.cpp response.cpp -g -o server
|
g++ -std=c++20 main.cpp router.cpp path.cpp http.hpp request.cpp response.cpp -g -o server
|
||||||
|
|||||||
161
http.hpp
Normal file
161
http.hpp
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
#ifndef STATUSCODE_H
|
||||||
|
#define STATUSCODE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class http {
|
||||||
|
public:
|
||||||
|
enum statusCode {
|
||||||
|
CONTINUE = 100,
|
||||||
|
SWITCHING_PROTOCOLS = 101,
|
||||||
|
PROCESSING = 102,
|
||||||
|
EARLY_HINTS = 103,
|
||||||
|
OK = 200,
|
||||||
|
CREATED = 201,
|
||||||
|
ACCEPTED = 202,
|
||||||
|
// Non auth 203
|
||||||
|
NO_CONTENT = 204,
|
||||||
|
RESET_CONTENT = 205,
|
||||||
|
PARTIAL_CONTENT = 206,
|
||||||
|
MULTIPLE_CHOICES = 300,
|
||||||
|
MOVED_PERMANENTLY = 301,
|
||||||
|
FOUND = 302,
|
||||||
|
SEE_OTHER = 303,
|
||||||
|
NOT_MODIFIED = 304,
|
||||||
|
USE_PROXY = 305,
|
||||||
|
SWITCH_PROXY = 306,
|
||||||
|
TEMPORARY_REDIRECT = 307,
|
||||||
|
PERMANENT_REDIRECT = 308,
|
||||||
|
BAD_REQUEST = 400,
|
||||||
|
UNAUTHORIZED = 401,
|
||||||
|
PAYMENT_REQUIRED = 402,
|
||||||
|
FORBIDDEN = 403,
|
||||||
|
NOT_FOUND = 404,
|
||||||
|
METHOD_NOT_ALLOWED = 405,
|
||||||
|
NOT_ACCEPTABLE = 406,
|
||||||
|
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
|
REQUEST_TIMEOUT = 408,
|
||||||
|
CONFLICT = 409,
|
||||||
|
GONE = 410,
|
||||||
|
LENGTH_REQUIRED = 411,
|
||||||
|
PRECONDITION_FAILED = 412,
|
||||||
|
PAYLOAD_TOO_LARGE = 413,
|
||||||
|
URI_TOO_LONG = 414,
|
||||||
|
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
|
RANGE_NOT_SATISFIABLE = 416,
|
||||||
|
EXPECTATION_FAILED = 417,
|
||||||
|
IM_A_TEAPOT = 418,
|
||||||
|
MISDIRECTION_REQUEST = 421,
|
||||||
|
UNPROCESSABLE_CONTENT = 422,
|
||||||
|
UPGRADE_REQUIRED = 426,
|
||||||
|
INTERNAL_SERVER_ERROR = 500,
|
||||||
|
NOT_IMPLEMENTED = 501,
|
||||||
|
BAD_GATEWAY = 502,
|
||||||
|
SERVICE_UNAVAILABLE = 503,
|
||||||
|
GATEWAY_TIMEOUT = 504,
|
||||||
|
HTTP_VERSION_NOT_SUPPORTED = 505
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static std::string StatusCodeString(const statusCode code) {
|
||||||
|
switch (code) {
|
||||||
|
case CONTINUE:
|
||||||
|
return "Continue";
|
||||||
|
case SWITCHING_PROTOCOLS:
|
||||||
|
return "Switching Protocols";
|
||||||
|
case PROCESSING:
|
||||||
|
return "Processing";
|
||||||
|
case EARLY_HINTS:
|
||||||
|
return "Early Hints";
|
||||||
|
case OK:
|
||||||
|
return "OK";
|
||||||
|
case CREATED:
|
||||||
|
return "Created";
|
||||||
|
case ACCEPTED:
|
||||||
|
return "Accepted";
|
||||||
|
case NO_CONTENT:
|
||||||
|
return "No Content";
|
||||||
|
case RESET_CONTENT:
|
||||||
|
return "Reset Content";
|
||||||
|
case PARTIAL_CONTENT:
|
||||||
|
return "Partial Content";
|
||||||
|
case MULTIPLE_CHOICES:
|
||||||
|
return "Multiple Choices";
|
||||||
|
case MOVED_PERMANENTLY:
|
||||||
|
return "Moved Permanently";
|
||||||
|
case FOUND:
|
||||||
|
return "Found";
|
||||||
|
case SEE_OTHER:
|
||||||
|
return "See Other";
|
||||||
|
case NOT_MODIFIED:
|
||||||
|
return "Not Modified";
|
||||||
|
case USE_PROXY:
|
||||||
|
return "Use Proxy";
|
||||||
|
case SWITCH_PROXY:
|
||||||
|
return "Switch Proxy";
|
||||||
|
case TEMPORARY_REDIRECT:
|
||||||
|
return "Temporary Redirect";
|
||||||
|
case PERMANENT_REDIRECT:
|
||||||
|
return "Permanent Redirect";
|
||||||
|
case BAD_REQUEST:
|
||||||
|
return "Bad Request";
|
||||||
|
case UNAUTHORIZED:
|
||||||
|
return "Unauthorized";
|
||||||
|
case PAYMENT_REQUIRED:
|
||||||
|
return "Payment Required";
|
||||||
|
case FORBIDDEN:
|
||||||
|
return "Forbidden";
|
||||||
|
case NOT_FOUND:
|
||||||
|
return "Not Found";
|
||||||
|
case METHOD_NOT_ALLOWED:
|
||||||
|
return "Method Not Allowed";
|
||||||
|
case NOT_ACCEPTABLE:
|
||||||
|
return "Not Acceptable";
|
||||||
|
case PROXY_AUTHENTICATION_REQUIRED:
|
||||||
|
return "Proxy Authentication Required";
|
||||||
|
case REQUEST_TIMEOUT:
|
||||||
|
return "Request Timeout";
|
||||||
|
case CONFLICT:
|
||||||
|
return "Conflict";
|
||||||
|
case GONE:
|
||||||
|
return "Gone";
|
||||||
|
case LENGTH_REQUIRED:
|
||||||
|
return "Length Required";
|
||||||
|
case PRECONDITION_FAILED:
|
||||||
|
return "Precondition Failed";
|
||||||
|
case PAYLOAD_TOO_LARGE:
|
||||||
|
return "Payload Too Large";
|
||||||
|
case URI_TOO_LONG:
|
||||||
|
return "URI Too Long";
|
||||||
|
case UNSUPPORTED_MEDIA_TYPE:
|
||||||
|
return "Unsupported Media Type";
|
||||||
|
case RANGE_NOT_SATISFIABLE:
|
||||||
|
return "Range Not Satisfiable";
|
||||||
|
case EXPECTATION_FAILED:
|
||||||
|
return "Expectation Failed";
|
||||||
|
case IM_A_TEAPOT:
|
||||||
|
return "I'm a teapot";
|
||||||
|
case MISDIRECTION_REQUEST:
|
||||||
|
return "Misdirection Request";
|
||||||
|
case UNPROCESSABLE_CONTENT:
|
||||||
|
return "Unprocessable Content";
|
||||||
|
case UPGRADE_REQUIRED:
|
||||||
|
return "Upgrade Required";
|
||||||
|
case INTERNAL_SERVER_ERROR:
|
||||||
|
return "Internal Server Error";
|
||||||
|
case NOT_IMPLEMENTED:
|
||||||
|
return "Not Implemented";
|
||||||
|
case BAD_GATEWAY:
|
||||||
|
return "Bad Gateway";
|
||||||
|
case SERVICE_UNAVAILABLE:
|
||||||
|
return "Service Unavailable";
|
||||||
|
case GATEWAY_TIMEOUT:
|
||||||
|
return "Gateway Timeout";
|
||||||
|
case HTTP_VERSION_NOT_SUPPORTED:
|
||||||
|
return "HTTP Version Not Supported";
|
||||||
|
default: // This is undefined Behaviour
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !STATUSCODE_H
|
||||||
78
main.cpp
Normal file
78
main.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "request.hpp"
|
||||||
|
#include "response.hpp"
|
||||||
|
#include "router.hpp"
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
Router router(8080);
|
||||||
|
router.Handle("/helloWorld", [](Request req, Response res) -> Response {
|
||||||
|
res.SetPayload("Hello World!");
|
||||||
|
res.SetContentType("text/plain");
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.Handle("/echo/{name}", [](Request req, Response res) -> Response {
|
||||||
|
std::string name = req.path.Get("name").value_or("No Name given");
|
||||||
|
res.SetPayload("Hello " + name);
|
||||||
|
res.SetContentType("text/plain");
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.Handle("/", [](Request req, Response res) -> Response {
|
||||||
|
res.SetPayload("Main");
|
||||||
|
res.SetContentType("text/plain");
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
router.Start();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// void server() {
|
||||||
|
// int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
// sockaddr_in serverAddress;
|
||||||
|
// serverAddress.sin_family = AF_INET;
|
||||||
|
// serverAddress.sin_port = htons(8080);
|
||||||
|
// serverAddress.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
//
|
||||||
|
// int err = bind(serverSocket, (struct sockaddr *)&serverAddress,
|
||||||
|
// sizeof(serverAddress));
|
||||||
|
// if (err != 0) {
|
||||||
|
// std::cout << "error binding " << strerror(errno) << std::endl;
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// err = listen(serverSocket, 5);
|
||||||
|
// if (err != 0) {
|
||||||
|
// std::cout << "error listening " << strerror(errno) << std::endl;
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// std::vector<std::byte> buffer(1024);
|
||||||
|
// while (true) {
|
||||||
|
// int clientSocket = accept(serverSocket, nullptr, nullptr);
|
||||||
|
// int read = recv(clientSocket, buffer.data(), buffer.size(), 0);
|
||||||
|
// buffer.resize(read);
|
||||||
|
// Request req(buffer);
|
||||||
|
// Response res = Response(http::statusCode::NOT_FOUND);
|
||||||
|
// if (req.HasData()) {
|
||||||
|
// auto data = req.Data();
|
||||||
|
// res.SetPayload(data);
|
||||||
|
// } else {
|
||||||
|
// res.SetPayload("Hello world!");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// res.SetContentType("text/plain");
|
||||||
|
// res.Send(clientSocket);
|
||||||
|
// close(clientSocket);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (shutdown(serverSocket, SHUT_RDWR) < 0) {
|
||||||
|
// std::cout << "error shutdown " << strerror(errno) << std::endl;
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (close(serverSocket) < 0) {
|
||||||
|
// std::cout << "error closing socket " << strerror(errno) << std::endl;
|
||||||
|
// exit(1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
37
path.cpp
Normal file
37
path.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#include "path.hpp"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
Path::Path(std::string path) {
|
||||||
|
int pos = path.find("?");
|
||||||
|
m_base = path.substr(0, pos);
|
||||||
|
path.erase(0, pos + 1);
|
||||||
|
m_query = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Path::Get(std::string name) {
|
||||||
|
if (m_variables.contains(name))
|
||||||
|
return m_variables.at(name);
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Path::Base() { return m_base; }
|
||||||
|
std::string Path::Query() { return m_query; }
|
||||||
|
|
||||||
|
void Path::Match(std::string pattern) {
|
||||||
|
int pos = 0;
|
||||||
|
std::string path = m_base;
|
||||||
|
while (pos != -1) {
|
||||||
|
pos = pattern.find('/');
|
||||||
|
std::string p = pattern.substr(0, pos);
|
||||||
|
|
||||||
|
int uPos = path.find('/');
|
||||||
|
std::string u = path.substr(0, uPos);
|
||||||
|
|
||||||
|
if (p.starts_with('{')) {
|
||||||
|
std::string name = p.substr(1, p.size() - 2);
|
||||||
|
m_variables.insert_or_assign(name, u);
|
||||||
|
}
|
||||||
|
pattern.erase(0, pos + 1);
|
||||||
|
path.erase(0, uPos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
path.hpp
Normal file
23
path.hpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef PATH_H
|
||||||
|
#define PATH_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Path {
|
||||||
|
private:
|
||||||
|
// std::string m_path;
|
||||||
|
std::string m_base;
|
||||||
|
std::string m_query;
|
||||||
|
std::map<std::string, std::string> m_variables;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Path(std::string path);
|
||||||
|
std::optional<std::string> Get(std::string name);
|
||||||
|
std::string Query();
|
||||||
|
std::string Base();
|
||||||
|
void Match(std::string pattern);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !PATH_H
|
||||||
@@ -8,7 +8,7 @@ bool Request::protocol(std::stringstream *ss, int *procPart, char c) {
|
|||||||
m_method = ss->str();
|
m_method = ss->str();
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
m_path = ss->str();
|
m_pathRaw = ss->str();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
m_protocol = ss->str();
|
m_protocol = ss->str();
|
||||||
@@ -24,7 +24,8 @@ bool Request::protocol(std::stringstream *ss, int *procPart, char c) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Request::Request(std::vector<std::byte> buf) {
|
// This is shit
|
||||||
|
Request::Request(std::vector<std::byte> buf) : path("") {
|
||||||
std::string name;
|
std::string name;
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
bool header = true;
|
bool header = true;
|
||||||
@@ -69,11 +70,12 @@ Request::Request(std::vector<std::byte> buf) {
|
|||||||
ss << c;
|
ss << c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
path = Path{m_pathRaw};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Request::Print() {
|
void Request::Print() {
|
||||||
std::cout << "Protocol: " << m_protocol << "\n"
|
std::cout << "Protocol: " << m_protocol << "\n"
|
||||||
<< "Req: " << m_method << " " << m_path << std::endl;
|
<< "Req: " << m_method << " " << m_pathRaw << std::endl;
|
||||||
for (const auto &[key, value] : m_headers) {
|
for (const auto &[key, value] : m_headers) {
|
||||||
std::cout << "[" << key << "]: [" << value << "]\n";
|
std::cout << "[" << key << "]: [" << value << "]\n";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#ifndef REQUEST_HEADER_H
|
#ifndef REQUEST_HEADER_H
|
||||||
#define REQUEST_HEADER_H
|
#define REQUEST_HEADER_H
|
||||||
|
|
||||||
|
#include "path.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -9,7 +11,7 @@ class Request {
|
|||||||
private:
|
private:
|
||||||
std::map<std::string, std::string> m_headers;
|
std::map<std::string, std::string> m_headers;
|
||||||
std::string m_method;
|
std::string m_method;
|
||||||
std::string m_path;
|
std::string m_pathRaw;
|
||||||
std::vector<std::byte> m_payload;
|
std::vector<std::byte> m_payload;
|
||||||
std::string m_protocol;
|
std::string m_protocol;
|
||||||
|
|
||||||
@@ -17,7 +19,8 @@ private:
|
|||||||
bool protocol(std::stringstream *ss, int *procPart, char c);
|
bool protocol(std::stringstream *ss, int *procPart, char c);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Request(std::vector<std::byte> buf);
|
Path path;
|
||||||
|
explicit Request(std::vector<std::byte> buf);
|
||||||
void Print();
|
void Print();
|
||||||
bool HasData();
|
bool HasData();
|
||||||
std::vector<std::byte> Data();
|
std::vector<std::byte> Data();
|
||||||
|
|||||||
@@ -7,13 +7,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
Response::Response(std::vector<std::byte> data) {
|
Response::Response(http::statusCode statusCode) { m_statusCode = statusCode; }
|
||||||
|
|
||||||
|
void Response::SetPayload(std::vector<std::byte> data) {
|
||||||
m_headers.insert(std::pair<std::string, std::string>(
|
m_headers.insert(std::pair<std::string, std::string>(
|
||||||
"content-length", std::to_string(data.size())));
|
"content-length", std::to_string(data.size())));
|
||||||
m_payload = data;
|
m_payload = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::Response(std::string data) {
|
void Response::SetPayload(std::string data) {
|
||||||
m_headers.insert(std::pair<std::string, std::string>(
|
m_headers.insert(std::pair<std::string, std::string>(
|
||||||
"content-length", std::to_string(std::strlen(data.data()))));
|
"content-length", std::to_string(std::strlen(data.data()))));
|
||||||
|
|
||||||
@@ -28,7 +30,8 @@ void Response::SetContentType(const std::string type) {
|
|||||||
|
|
||||||
void Response::Send(int clientSocket) {
|
void Response::Send(int clientSocket) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "HTTP/1.1 200 OK\n";
|
ss << "HTTP/1.1 " << m_statusCode << " "
|
||||||
|
<< http::StatusCodeString(m_statusCode) << "\n";
|
||||||
for (const auto &[key, value] : m_headers) {
|
for (const auto &[key, value] : m_headers) {
|
||||||
ss << key << ": " << value << "\n";
|
ss << key << ": " << value << "\n";
|
||||||
}
|
}
|
||||||
|
|||||||
11
response.hpp
11
response.hpp
@@ -1,16 +1,23 @@
|
|||||||
#ifndef RESPONSE_H
|
#ifndef RESPONSE_H
|
||||||
|
#define RESPONSE_H
|
||||||
|
|
||||||
|
#include "http.hpp"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Response {
|
class Response {
|
||||||
private:
|
private:
|
||||||
std::map<std::string, std::string> m_headers;
|
std::map<std::string, std::string> m_headers;
|
||||||
std::vector<std::byte> m_payload;
|
std::vector<std::byte> m_payload;
|
||||||
|
http::statusCode m_statusCode;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Response(std::vector<std::byte> data);
|
Response(http::statusCode statusCode);
|
||||||
Response(std::string data);
|
// Response(std::vector<std::byte> data);
|
||||||
|
// Response(std::string data);
|
||||||
|
void SetPayload(std::vector<std::byte> data);
|
||||||
|
void SetPayload(std::string data);
|
||||||
void SetContentType(const std::string type);
|
void SetContentType(const std::string type);
|
||||||
void Send(int clientSocket);
|
void Send(int clientSocket);
|
||||||
void Print();
|
void Print();
|
||||||
|
|||||||
73
router.cpp
Normal file
73
router.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include "router.hpp"
|
||||||
|
#include <csignal>
|
||||||
|
#include <iostream>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
Router::Router(int port) {
|
||||||
|
m_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
m_address.sin_family = AF_INET;
|
||||||
|
m_address.sin_port = htons(port);
|
||||||
|
m_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Router::Start() {
|
||||||
|
int err = bind(m_socket, (struct sockaddr *)&m_address, sizeof(m_address));
|
||||||
|
if (err != 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = listen(m_socket, 5);
|
||||||
|
if (err != 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
std::vector<std::byte> buffer(1024);
|
||||||
|
while (true) {
|
||||||
|
int client = accept(m_socket, nullptr, nullptr);
|
||||||
|
int read = recv(client, buffer.data(), buffer.size(), 0);
|
||||||
|
Request req(buffer);
|
||||||
|
Response res = Route(req);
|
||||||
|
res.Send(client);
|
||||||
|
close(client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Router::Handle(std::string pathPattern,
|
||||||
|
std::function<Response(Request, Response)> func) {
|
||||||
|
m_routes.insert_or_assign(pathPattern, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be better
|
||||||
|
// Probably dont use map but a tree for it, then traverse tree for routing
|
||||||
|
Response Router::Route(Request req) {
|
||||||
|
for (const auto &[key, value] : m_routes) {
|
||||||
|
int pos = 0;
|
||||||
|
std::string path = req.path.Base();
|
||||||
|
std::string pattern = key;
|
||||||
|
std::string patternCopy = key;
|
||||||
|
bool found = false;
|
||||||
|
while (pos != -1) {
|
||||||
|
found = true;
|
||||||
|
pos = pattern.find('/');
|
||||||
|
std::string p = pattern.substr(0, pos);
|
||||||
|
|
||||||
|
int uPos = path.find('/');
|
||||||
|
std::string u = path.substr(0, uPos);
|
||||||
|
|
||||||
|
if (!p.starts_with('{') && p != u) {
|
||||||
|
found = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern.erase(0, pos + 1);
|
||||||
|
path.erase(0, uPos + 1);
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
|
Response res(http::OK);
|
||||||
|
req.path.Match(patternCopy);
|
||||||
|
return value(req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(http::NOT_FOUND);
|
||||||
|
}
|
||||||
26
router.hpp
Normal file
26
router.hpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef ROUTER_H
|
||||||
|
#define ROUTER_H
|
||||||
|
|
||||||
|
#include "request.hpp"
|
||||||
|
#include "response.hpp"
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Router {
|
||||||
|
private:
|
||||||
|
std::map<std::string, std::function<Response(Request, Response)>> m_routes;
|
||||||
|
int m_socket;
|
||||||
|
sockaddr_in m_address;
|
||||||
|
Response Route(Request req);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Router(int port);
|
||||||
|
void Handle(std::string pathPattern,
|
||||||
|
std::function<Response(Request, Response)> func);
|
||||||
|
int Start();
|
||||||
|
int Stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !ROUTER_H
|
||||||
62
server.cpp
62
server.cpp
@@ -1,62 +0,0 @@
|
|||||||
#include "request.hpp"
|
|
||||||
#include "response.hpp"
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <ostream>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
sockaddr_in serverAddress;
|
|
||||||
serverAddress.sin_family = AF_INET;
|
|
||||||
serverAddress.sin_port = htons(8080);
|
|
||||||
serverAddress.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
|
|
||||||
int err = bind(serverSocket, (struct sockaddr *)&serverAddress,
|
|
||||||
sizeof(serverAddress));
|
|
||||||
if (err != 0) {
|
|
||||||
std::cout << "error binding " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
err = listen(serverSocket, 5);
|
|
||||||
if (err != 0) {
|
|
||||||
std::cout << "error listening " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::byte> buffer(1024);
|
|
||||||
while (true) {
|
|
||||||
int clientSocket = accept(serverSocket, nullptr, nullptr);
|
|
||||||
int read = recv(clientSocket, buffer.data(), buffer.size(), 0);
|
|
||||||
buffer.resize(read);
|
|
||||||
Request req(buffer);
|
|
||||||
Response res = Response("Hello world!");
|
|
||||||
if (req.HasData()) {
|
|
||||||
auto data = req.Data();
|
|
||||||
res = Response(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
res.SetContentType("text/plain");
|
|
||||||
res.Send(clientSocket);
|
|
||||||
close(clientSocket);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shutdown(serverSocket, SHUT_RDWR) < 0) {
|
|
||||||
std::cout << "error shutdown " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (close(serverSocket) < 0) {
|
|
||||||
std::cout << "error closing socket " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user