URI parsing
This commit is contained in:
parent
c62c4f4ff9
commit
c567bb2447
@ -4,16 +4,18 @@ project(Rum VERSION 1.0)
|
|||||||
|
|
||||||
# compiler setup
|
# compiler setup
|
||||||
set(CMAKE_CXX_COMPILER "clang++")
|
set(CMAKE_CXX_COMPILER "clang++")
|
||||||
#set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") # warnings
|
||||||
#set(CMAKE_CXX_STANDARD_REQUIRED true)
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2b") # std c++23
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Werror") # warnings and errors
|
if (STATIC)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2b") # std c++23 for clang
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") # static
|
||||||
|
endif()
|
||||||
|
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") # static compile
|
if (DEBUG)
|
||||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") # debug symbols
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") # debug symbols
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") # enable optimisations
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2") # optimisations
|
||||||
|
|
||||||
# IDE setup (clangd)
|
# IDE setup (clangd)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
|
||||||
@ -28,4 +30,9 @@ add_custom_target(run
|
|||||||
COMMAND $<TARGET_FILE:server.bin>
|
COMMAND $<TARGET_FILE:server.bin>
|
||||||
DEPENDS server.bin
|
DEPENDS server.bin
|
||||||
COMMENT "Running server.bin"
|
COMMENT "Running server.bin"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(docker
|
||||||
|
COMMAND docker build -t dowerx/rum:${CMAKE_PROJECT_VERSION} ..
|
||||||
|
COMMENT "Building docker image"
|
||||||
)
|
)
|
@ -3,7 +3,4 @@
|
|||||||
#include "method.h"
|
#include "method.h"
|
||||||
#include "request.h"
|
#include "request.h"
|
||||||
#include "response.h"
|
#include "response.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
#define WORKERS 10
|
|
||||||
#define BUFFER_LEN 8196
|
|
@ -5,5 +5,5 @@ namespace Rum::HTTP {
|
|||||||
|
|
||||||
enum Method { GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE };
|
enum Method { GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE };
|
||||||
|
|
||||||
std::string method_to_string(Method m);
|
std::string to_string(Method method);
|
||||||
} // namespace Rum::HTTP
|
} // namespace Rum::HTTP
|
@ -3,15 +3,16 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "method.h"
|
#include "method.h"
|
||||||
|
#include "rum/http/uri.h"
|
||||||
|
|
||||||
namespace Rum::HTTP {
|
namespace Rum::HTTP {
|
||||||
class Request {
|
class Request {
|
||||||
public:
|
public:
|
||||||
Request() : method(GET), path(""), body("") {}
|
Request() : method(GET), uri(URI()), body("") {}
|
||||||
Request(Method method, std::string path) : method(method), path(path), body("") {}
|
Request(Method method, const URI& uri) : method(method), uri(uri), body("") {}
|
||||||
|
|
||||||
Method get_method() const { return method; }
|
Method get_method() const { return method; }
|
||||||
std::string get_path() const { return path; }
|
const URI& get_uri() const { return uri; }
|
||||||
|
|
||||||
const std::map<std::string, std::string>& get_headers() const { return headers; }
|
const std::map<std::string, std::string>& get_headers() const { return headers; }
|
||||||
std::string get_header(std::string name) const { return headers.at(name); }
|
std::string get_header(std::string name) const { return headers.at(name); }
|
||||||
@ -20,13 +21,13 @@ class Request {
|
|||||||
std::string get_body() const { return body; }
|
std::string get_body() const { return body; }
|
||||||
void set_body(std::string body) { this->body = body; }
|
void set_body(std::string body) { this->body = body; }
|
||||||
|
|
||||||
operator std::string () const;
|
operator std::string() const;
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& stream, const Request req);
|
friend std::ostream& operator<<(std::ostream& stream, const Request req);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Method method;
|
Method method;
|
||||||
std::string path;
|
URI uri;
|
||||||
std::map<std::string, std::string> headers;
|
std::map<std::string, std::string> headers;
|
||||||
std::string body;
|
std::string body;
|
||||||
};
|
};
|
||||||
|
@ -8,10 +8,11 @@ class Response {
|
|||||||
const int client_sock;
|
const int client_sock;
|
||||||
bool sent_header;
|
bool sent_header;
|
||||||
bool sent_body;
|
bool sent_body;
|
||||||
|
bool closed;
|
||||||
unsigned int code;
|
unsigned int code;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Response(int client_sock) : client_sock(client_sock), sent_header(false), sent_body(false), code(200) {}
|
Response(int client_sock) : client_sock(client_sock), sent_header(false), sent_body(false), closed(false), code(200) {}
|
||||||
~Response();
|
~Response();
|
||||||
|
|
||||||
void send_header(const std::string& name, const std::string& value);
|
void send_header(const std::string& name, const std::string& value);
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
#include "request.h"
|
#include "request.h"
|
||||||
#include "response.h"
|
#include "response.h"
|
||||||
|
|
||||||
#define WORKERS 10
|
#define DEFAULT_WORKER_COUNT 10
|
||||||
#define BUFFER_LEN 8196
|
#define DEFAULT_BUFFER_SIZE 8196
|
||||||
|
|
||||||
namespace Rum::HTTP {
|
namespace Rum::HTTP {
|
||||||
|
|
||||||
@ -25,6 +25,8 @@ class Server : public Rum::TCP::Server {
|
|||||||
sockaddr_in sockaddr;
|
sockaddr_in sockaddr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const size_t buffer_size;
|
||||||
|
|
||||||
std::vector<std::thread> workers;
|
std::vector<std::thread> workers;
|
||||||
std::queue<Task> tasks;
|
std::queue<Task> tasks;
|
||||||
std::mutex mtx;
|
std::mutex mtx;
|
||||||
@ -36,8 +38,8 @@ class Server : public Rum::TCP::Server {
|
|||||||
void handler(int client_sock, const sockaddr_in& client_address, char* buffer);
|
void handler(int client_sock, const sockaddr_in& client_address, char* buffer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Server(unsigned int port, size_t worker_count);
|
Server(unsigned int port, size_t worker_count, size_t buffer_size);
|
||||||
Server(unsigned int port) : Server(port, WORKERS) {}
|
Server(unsigned int port) : Server(port, DEFAULT_WORKER_COUNT, DEFAULT_BUFFER_SIZE) {}
|
||||||
~Server();
|
~Server();
|
||||||
void listen();
|
void listen();
|
||||||
|
|
||||||
|
34
inc/rum/http/uri.h
Normal file
34
inc/rum/http/uri.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <format>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace Rum::HTTP {
|
||||||
|
class URI {
|
||||||
|
private:
|
||||||
|
std::string scheme;
|
||||||
|
std::string user;
|
||||||
|
std::string password;
|
||||||
|
std::string host;
|
||||||
|
std::string port;
|
||||||
|
std::string path;
|
||||||
|
std::map<std::string, std::string> parameters;
|
||||||
|
std::string fragment;
|
||||||
|
|
||||||
|
public:
|
||||||
|
URI(const std::string& uri);
|
||||||
|
URI() {}
|
||||||
|
|
||||||
|
const std::string& get_scheme() const { return scheme; }
|
||||||
|
const std::string& get_user() const { return user; }
|
||||||
|
const std::string& get_password() const { return password; }
|
||||||
|
const std::string& get_host() const { return host; }
|
||||||
|
const std::string& get_port() const { return port; }
|
||||||
|
const std::string& get_path() const { return path; }
|
||||||
|
const std::map<std::string, std::string>& get_parameters() const { return parameters; }
|
||||||
|
const std::string& get_fragment() const { return fragment; }
|
||||||
|
|
||||||
|
operator std::string() const;
|
||||||
|
};
|
||||||
|
} // namespace Rum::HTTP
|
@ -4,16 +4,22 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#define MAX_PENDING 10
|
||||||
|
|
||||||
namespace Rum::TCP {
|
namespace Rum::TCP {
|
||||||
class Server {
|
class Server {
|
||||||
private:
|
private:
|
||||||
const int sock;
|
const int sock;
|
||||||
|
unsigned short port;
|
||||||
bool stop;
|
bool stop;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Server(unsigned short port);
|
Server(unsigned short port);
|
||||||
virtual ~Server();
|
virtual ~Server();
|
||||||
void listen(std::function<void(int, sockaddr_in)> handler) const;
|
|
||||||
|
unsigned short get_port() const { return port; }
|
||||||
|
|
||||||
|
void listen(const std::function<void(int, sockaddr_in)>& handler) const;
|
||||||
virtual void end() {
|
virtual void end() {
|
||||||
stop = true;
|
stop = true;
|
||||||
close(sock);
|
close(sock);
|
||||||
|
@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
namespace Rum::TCP {
|
namespace Rum::TCP {
|
||||||
|
|
||||||
std::string address_to_string(sockaddr_in address);
|
std::string to_string(const sockaddr_in& address);
|
||||||
|
|
||||||
} // namespace Rum::TCP
|
} // namespace Rum::TCP
|
@ -1,8 +1,8 @@
|
|||||||
#include <rum/http/method.h>
|
#include <rum/http/method.h>
|
||||||
|
|
||||||
namespace Rum::HTTP {
|
namespace Rum::HTTP {
|
||||||
std::string method_to_string(Method m) {
|
std::string to_string(Method method) {
|
||||||
switch (m) {
|
switch (method) {
|
||||||
case GET:
|
case GET:
|
||||||
return "GET";
|
return "GET";
|
||||||
case HEAD:
|
case HEAD:
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
#include <rum/http/request.h>
|
#include <rum/http/request.h>
|
||||||
|
#include <format>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "rum/http/method.h"
|
||||||
|
|
||||||
namespace Rum::HTTP {
|
namespace Rum::HTTP {
|
||||||
|
|
||||||
Request::operator std::string() const {
|
Request::operator std::string() const {
|
||||||
std::string result("Request{\n\tmethod: " + method_to_string(method) + "\n\tpath: \"" + path + "\"\n\theaders:\n");
|
std::string headers_string;
|
||||||
for (auto header : get_headers()) {
|
for (auto header : headers) {
|
||||||
result += "\t\t\"" + header.first + "\": \"" + header.second + "\"\n";
|
headers_string += "\t\t'" + header.first + "': '" + header.second + "'\n";
|
||||||
}
|
}
|
||||||
|
return std::vformat(
|
||||||
result += "\tbody: \"" + body + "\"\n}";
|
"Request{{\n"
|
||||||
|
"\tmethod: {}\n"
|
||||||
return result;
|
"\t{}\n"
|
||||||
|
"\theaders: \n{}"
|
||||||
|
"\tbody: \n'{}'"
|
||||||
|
"}}",
|
||||||
|
std::make_format_args(to_string(method), (std::string)uri, headers_string, body));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& stream, const Request req) {
|
std::ostream& operator<<(std::ostream& stream, const Request req) {
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace Rum::HTTP {
|
namespace Rum::HTTP {
|
||||||
std::string status_code_to_string(int code) {
|
std::string to_string(int code) {
|
||||||
static std::map<int, std::string> codes{{100, "Continue"},
|
static const std::map<int, std::string> codes{{100, "Continue"},
|
||||||
{101, "Switching Protocols"},
|
{101, "Switching Protocols"},
|
||||||
{102, "Processing"},
|
{102, "Processing"},
|
||||||
{103, "Early Hints"},
|
{103, "Early Hints"},
|
||||||
@ -77,15 +77,19 @@ std::string status_code_to_string(int code) {
|
|||||||
|
|
||||||
void Response::send_header(const std::string& name, const std::string& value) {
|
void Response::send_header(const std::string& name, const std::string& value) {
|
||||||
if (!sent_header) {
|
if (!sent_header) {
|
||||||
std::string resp("HTTP/1.1 " + std::to_string(code) + " " + status_code_to_string(code) + "\r\n");
|
std::string resp("HTTP/1.1 " + std::to_string(code) + " " + to_string(code) + "\r\n");
|
||||||
if (-1 == send(client_sock, resp.c_str(), resp.size(), 0))
|
if (-1 == send(client_sock, resp.c_str(), resp.size(), 0)) {
|
||||||
|
closed = true;
|
||||||
throw TCP::Error(TCP::Error::CLOSED);
|
throw TCP::Error(TCP::Error::CLOSED);
|
||||||
|
}
|
||||||
sent_header = true;
|
sent_header = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string header = name + ": " + value + "\r\n";
|
std::string header = name + ": " + value + "\r\n";
|
||||||
if (-1 == send(client_sock, header.c_str(), header.size(), 0))
|
if (-1 == send(client_sock, header.c_str(), header.size(), 0)) {
|
||||||
|
closed = true;
|
||||||
throw TCP::Error(TCP::Error::CLOSED);
|
throw TCP::Error(TCP::Error::CLOSED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Response::send_body(const std::string& value) {
|
void Response::send_body(const std::string& value) {
|
||||||
@ -94,18 +98,22 @@ void Response::send_body(const std::string& value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!sent_body) {
|
if (!sent_body) {
|
||||||
if (-1 == send(client_sock, "\r\n", 2, 0))
|
if (-1 == send(client_sock, "\r\n", 2, 0)) {
|
||||||
|
closed = true;
|
||||||
throw TCP::Error(TCP::Error::CLOSED);
|
throw TCP::Error(TCP::Error::CLOSED);
|
||||||
|
}
|
||||||
sent_body = true;
|
sent_body = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-1 == send(client_sock, value.c_str(), value.size(), 0))
|
if (-1 == send(client_sock, value.c_str(), value.size(), 0)) {
|
||||||
|
closed = true;
|
||||||
throw TCP::Error(TCP::Error::CLOSED);
|
throw TCP::Error(TCP::Error::CLOSED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Response::~Response() {
|
Response::~Response() {
|
||||||
if (!sent_header) {
|
if (!sent_header && !closed) {
|
||||||
std::string resp("HTTP/1.1 " + std::to_string(code) + " " + status_code_to_string(code));
|
std::string resp("HTTP/1.1 " + std::to_string(code) + " " + to_string(code));
|
||||||
send(client_sock, resp.c_str(), resp.size(), 0);
|
send(client_sock, resp.c_str(), resp.size(), 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
|
#include "rum/tcp/server.h"
|
||||||
#include <rum/http/server.h>
|
#include <rum/http/server.h>
|
||||||
#include <rum/tcp/error.h>
|
#include <rum/tcp/error.h>
|
||||||
#include <rum/tcp/utility.h>
|
#include <rum/tcp/utility.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Rum::HTTP {
|
namespace Rum::HTTP {
|
||||||
Server::Server(unsigned int port, size_t worker_count) : Rum::TCP::Server(port), stop(false) {
|
Server::Server(unsigned int port, size_t worker_count, size_t buffer_size) : Rum::TCP::Server(port), buffer_size(buffer_size), stop(false) {
|
||||||
for (size_t i = 0; i < worker_count; i++) {
|
for (size_t i = 0; i < worker_count; i++) {
|
||||||
std::thread worker([this, i]() {
|
std::thread worker([this, i]() {
|
||||||
char* buffer = new char[BUFFER_LEN]();
|
char* buffer = new char[this->buffer_size]();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
Task task;
|
Task task;
|
||||||
@ -30,7 +32,7 @@ Server::Server(unsigned int port, size_t worker_count) : Rum::TCP::Server(port),
|
|||||||
|
|
||||||
handler(task.client_sock, task.sockaddr, buffer);
|
handler(task.client_sock, task.sockaddr, buffer);
|
||||||
if (int status = close(task.client_sock); status == TCP::Error::UNKNOWN) {
|
if (int status = close(task.client_sock); status == TCP::Error::UNKNOWN) {
|
||||||
std::cerr << TCP::address_to_string(task.sockaddr) << ": " << TCP::Error((TCP::Error::Type)status).what() << std::endl;
|
std::cerr << TCP::to_string(task.sockaddr) << ": " << TCP::Error((TCP::Error::Type)status).what() << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -85,7 +87,7 @@ Method string_to_method(std::string text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Server::handler(int client_sock, const sockaddr_in& client_address, char* buffer) {
|
void Server::handler(int client_sock, const sockaddr_in& client_address, char* buffer) {
|
||||||
std::string address = TCP::address_to_string(client_address);
|
std::string address = TCP::to_string(client_address);
|
||||||
std::cout << address << ": connected" << std::endl;
|
std::cout << address << ": connected" << std::endl;
|
||||||
|
|
||||||
Request request;
|
Request request;
|
||||||
@ -96,7 +98,7 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
|
|||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ssize_t recieved = recv(client_sock, buffer, BUFFER_LEN, 0);
|
ssize_t recieved = recv(client_sock, buffer, buffer_size, 0);
|
||||||
switch (recieved) {
|
switch (recieved) {
|
||||||
case TCP::Error::CLOSED:
|
case TCP::Error::CLOSED:
|
||||||
std::cout << address << ": connection closed" << std::endl;
|
std::cout << address << ": connection closed" << std::endl;
|
||||||
@ -109,10 +111,11 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
|
|||||||
message += std::string(buffer, buffer + recieved);
|
message += std::string(buffer, buffer + recieved);
|
||||||
|
|
||||||
if (stage == METHOD && message.contains("\r\n")) {
|
if (stage == METHOD && message.contains("\r\n")) {
|
||||||
std::regex method_regex("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE) (\\/.*) .*");
|
std::regex method_regex("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE) (\\/.*) HTTP.*");
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
if (std::regex_match(message.cbegin(), message.cbegin() + message.find("\r\n"), match, method_regex)) {
|
if (std::regex_match(message.cbegin(), message.cbegin() + message.find("\r\n"), match, method_regex)) {
|
||||||
request = Request(string_to_method(match.str(1)), match.str(2));
|
// create request object
|
||||||
|
request = Request(string_to_method(match.str(1)), URI("http://0.0.0.0:" + std::to_string(TCP::Server::get_port()) + match.str(2)));
|
||||||
message = message.substr(message.find("\r\n"));
|
message = message.substr(message.find("\r\n"));
|
||||||
stage = HEADER;
|
stage = HEADER;
|
||||||
} else {
|
} else {
|
||||||
@ -148,9 +151,10 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
for (auto it = paths[request.get_method()].cbegin(); it != paths[request.get_method()].cend(); it++) {
|
for (auto it = paths[request.get_method()].cbegin(); it != paths[request.get_method()].cend(); it++) {
|
||||||
if (std::regex_match(request.get_path(), it->first)) {
|
if (std::regex_match(request.get_uri().get_path(), it->first)) {
|
||||||
it->second(request, resp);
|
it->second(request, resp);
|
||||||
found = true;
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
64
src/rum/http/uri.cpp
Normal file
64
src/rum/http/uri.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <rum/http/uri.h>
|
||||||
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
|
#include <regex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define MATCH(var, i) \
|
||||||
|
if (match[i].matched) \
|
||||||
|
var = match.str(i);
|
||||||
|
|
||||||
|
namespace Rum::HTTP {
|
||||||
|
|
||||||
|
URI::operator std::string() const {
|
||||||
|
std::string query;
|
||||||
|
for (auto param : parameters) {
|
||||||
|
query += "\t\t'" + param.first + "': '" + param.second + "'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vformat(
|
||||||
|
"URI{{"
|
||||||
|
"\n\tscheme: '{}'"
|
||||||
|
"\n\tuser: '{}'"
|
||||||
|
"\n\tpassword: '{}'"
|
||||||
|
"\n\thost: '{}'"
|
||||||
|
"\n\tport: '{}'"
|
||||||
|
"\n\tpath: '{}'"
|
||||||
|
"\n\tquery: \n{}"
|
||||||
|
"\n\tfragment: '{}'"
|
||||||
|
"\n}}",
|
||||||
|
std::make_format_args(scheme, user, password, host, port, path, query, fragment));
|
||||||
|
}
|
||||||
|
|
||||||
|
URI::URI(const std::string& uri) {
|
||||||
|
std::regex uri_regex(R"((([\w\d]+):\/\/)?(([\w\d]+)(:([\w\d]+)?)@)?([\w\d\.]+)(:(\d+))?(\/?[\w\d\.\/]+)?(\?([\w\d\=\&]+))?(\#([\w\d]*))?)");
|
||||||
|
|
||||||
|
std::smatch match;
|
||||||
|
|
||||||
|
std::cout << uri << std::endl;
|
||||||
|
|
||||||
|
if (std::regex_match(uri.cbegin(), uri.cend(), match, uri_regex)) {
|
||||||
|
MATCH(scheme, 2)
|
||||||
|
MATCH(user, 4)
|
||||||
|
MATCH(password, 6)
|
||||||
|
MATCH(host, 7)
|
||||||
|
MATCH(port, 9)
|
||||||
|
MATCH(path, 10)
|
||||||
|
std::string query;
|
||||||
|
MATCH(query, 12)
|
||||||
|
MATCH(fragment, 14)
|
||||||
|
|
||||||
|
std::stringstream parameters_string(query);
|
||||||
|
std::string parameter;
|
||||||
|
while (std::getline(parameters_string, parameter, '&')) {
|
||||||
|
if (size_t pos = parameter.find('='); pos != parameter.npos) {
|
||||||
|
parameters[parameter.substr(0, pos)] = parameter.substr(pos + 1);
|
||||||
|
} else {
|
||||||
|
parameters[parameter] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace Rum::HTTP
|
@ -3,21 +3,22 @@
|
|||||||
#include <rum/tcp/server.h>
|
#include <rum/tcp/server.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <csignal>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define MAX_PENDING 10
|
|
||||||
|
|
||||||
namespace Rum::TCP {
|
namespace Rum::TCP {
|
||||||
|
|
||||||
std::string address_to_string(sockaddr_in address) {
|
std::string to_string(const sockaddr_in& address) {
|
||||||
return std::string(inet_ntoa(address.sin_addr)) + ":" + std::to_string(ntohs(address.sin_port));
|
return std::string(inet_ntoa(address.sin_addr)) + ":" + std::to_string(ntohs(address.sin_port));
|
||||||
}
|
}
|
||||||
|
|
||||||
Server::Server(unsigned short port) : sock(socket(AF_INET, SOCK_STREAM, 0)), stop(false) {
|
Server::Server(unsigned short port) : sock(socket(AF_INET, SOCK_STREAM, 0)), port(port), stop(false) {
|
||||||
if (sock == -1)
|
if (sock == -1)
|
||||||
throw Error(Error::UNKNOWN);
|
throw Error(Error::UNKNOWN);
|
||||||
|
|
||||||
|
std::signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
sockaddr_in address = {.sin_family = AF_INET, .sin_port = htons(port), .sin_addr = {.s_addr = INADDR_ANY}};
|
sockaddr_in address = {.sin_family = AF_INET, .sin_port = htons(port), .sin_addr = {.s_addr = INADDR_ANY}};
|
||||||
|
|
||||||
if (bind(sock, (struct sockaddr*)&address, sizeof(address)) == -1)
|
if (bind(sock, (struct sockaddr*)&address, sizeof(address)) == -1)
|
||||||
@ -28,10 +29,9 @@ Server::~Server() {
|
|||||||
end();
|
end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::listen(std::function<void(int, sockaddr_in)> handler) const {
|
void Server::listen(const std::function<void(int, sockaddr_in)>& handler) const {
|
||||||
if (::listen(sock, MAX_PENDING) == -1)
|
if (::listen(sock, MAX_PENDING) == -1)
|
||||||
throw Error(Error::UNKNOWN);
|
throw Error(Error::UNKNOWN);
|
||||||
;
|
|
||||||
|
|
||||||
while (!stop) {
|
while (!stop) {
|
||||||
sockaddr_in client_address;
|
sockaddr_in client_address;
|
||||||
|
@ -8,24 +8,43 @@ Rum::HTTP::Server* server;
|
|||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
Flags::Parser parser;
|
Flags::Parser parser;
|
||||||
int* port = parser.add<int>("port", "tcp port number", true, 8080);
|
int* port = parser.add<int>("port", "tcp port number", false, 8080);
|
||||||
parser.parse(argc, argv);
|
unsigned long int* workers = parser.add<unsigned long int>("workers", "number of worker threads", false, 10);
|
||||||
|
|
||||||
|
bool* help = parser.add<bool>("help", "print help", false, false);
|
||||||
|
|
||||||
|
if (!parser.parse(argc, argv) || *help) {
|
||||||
|
parser.help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "Port: " << *port << std::endl;
|
std::cout << "Port: " << *port << std::endl;
|
||||||
|
std::cout << "Workers: " << *workers << std::endl;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
server = new Rum::HTTP::Server(*port);
|
server = new Rum::HTTP::Server(*port, *workers, DEFAULT_BUFFER_SIZE);
|
||||||
|
|
||||||
std::signal(SIGINT, [](int) {
|
std::signal(SIGINT, [](int) {
|
||||||
std::cout << "\nStopping server..." << std::endl;
|
std::cout << "\nStopping server..." << std::endl;
|
||||||
server->end();
|
server->end();
|
||||||
});
|
});
|
||||||
server->add_path<Rum::HTTP::GET>("/", [](const Rum::HTTP::Request&, Rum::HTTP::Response& resp) {
|
|
||||||
|
server->add_path<Rum::HTTP::GET>("/asd", [](const Rum::HTTP::Request& req, Rum::HTTP::Response& resp) {
|
||||||
std::cout << "request accepted" << std::endl;
|
std::cout << "request accepted" << std::endl;
|
||||||
resp.send_body("<h1>Hello World</h1>");
|
resp.send_body("<h1>asd</h1><pre>" + (std::string)req + "</pre>");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server->add_path<Rum::HTTP::GET>("/.*", [](const Rum::HTTP::Request& req, Rum::HTTP::Response& resp) {
|
||||||
|
std::cout << "request accepted" << std::endl;
|
||||||
|
resp.send_body("<h1>Hello World</h1><pre>" + (std::string)req + "</pre>");
|
||||||
|
});
|
||||||
|
|
||||||
server->listen();
|
server->listen();
|
||||||
|
|
||||||
delete server;
|
delete server;
|
||||||
|
|
||||||
} catch (Rum::TCP::Error&) {
|
} catch (Rum::TCP::Error&) {
|
||||||
std::cerr << "Failed to bind port " << *port << std::endl;
|
std::cerr << "Failed to bind port " << *port << std::endl;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user