Cookie and Header parsing

This commit is contained in:
BENEDEK László 2023-12-05 22:41:20 +01:00
parent c567bb2447
commit 21afe9ca13
12 changed files with 152 additions and 139 deletions

View File

@ -1,6 +1,6 @@
#pragma once
#include "method.h"
#include "request.h"
#include "response.h"
#include "server.h"
#include <rum/http/method.h>
#include <rum/http/request.h>
#include <rum/http/response.h>
#include <rum/http/server.h>

View File

@ -1,6 +1,7 @@
#pragma once
#include <string>
namespace Rum::HTTP {
enum Method { GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE };

View File

@ -1,9 +1,9 @@
#pragma once
#include <rum/http/method.h>
#include <rum/http/uri.h>
#include <map>
#include <string>
#include "method.h"
#include "rum/http/uri.h"
namespace Rum::HTTP {
class Request {
@ -12,12 +12,16 @@ class Request {
Request(Method method, const URI& uri) : method(method), uri(uri), body("") {}
Method get_method() const { return method; }
const URI& get_uri() const { return uri; }
URI& get_uri() { return uri; }
const std::map<std::string, std::string>& get_headers() const { return headers; }
std::string get_header(std::string name) const { return headers.at(name); }
void set_header(std::string name, std::string value) { headers[name] = value; }
const std::map<std::string, std::string>& get_cookies() const { return cookies; }
std::string get_cookie(std::string name) const { return cookies.at(name); }
void set_cookie(std::string name, std::string value) { cookies[name] = value; }
std::string get_body() const { return body; }
void set_body(std::string body) { this->body = body; }
@ -29,6 +33,7 @@ class Request {
Method method;
URI uri;
std::map<std::string, std::string> headers;
std::map<std::string, std::string> cookies;
std::string body;
};
} // namespace Rum::HTTP

View File

@ -1,22 +1,25 @@
#pragma once
#include <map>
#include <string>
namespace Rum::HTTP {
class Response {
private:
const int client_sock;
bool sent_header;
bool sent_body;
bool closed;
unsigned int code;
public:
Response(int client_sock) : client_sock(client_sock), sent_header(false), sent_body(false), closed(false), code(200) {}
Response(int client_sock) : client_sock(client_sock), code(200) {}
~Response();
void send_header(const std::string& name, const std::string& value);
void send_body(const std::string& value);
void set_code(unsigned int code) { this->code = code; }
void set_code(unsigned int code) {
if (code >= 100 && code < 600)
this->code = code;
}
std::map<std::string, std::string> cookies;
std::map<std::string, std::string> headers;
std::string body;
};
} // namespace Rum::HTTP

View File

@ -7,9 +7,9 @@
#include <queue>
#include <regex>
#include <vector>
#include "../tcp/server.h"
#include "request.h"
#include "response.h"
#include <rum/tcp/server.h>
#include <rum/http/request.h>
#include <rum/http/response.h>
#define DEFAULT_WORKER_COUNT 10
#define DEFAULT_BUFFER_SIZE 8196

View File

@ -29,6 +29,9 @@ class URI {
const std::map<std::string, std::string>& get_parameters() const { return parameters; }
const std::string& get_fragment() const { return fragment; }
void set_host(const std::string& host) { this->host = host; }
void set_port(const std::string& port) { this->port = port; }
operator std::string() const;
};
} // namespace Rum::HTTP

View File

@ -1,5 +1,5 @@
#pragma once
#include "error.h"
#include "server.h"
#include "utility.h"
#include <rum/tcp/error.h>
#include <rum/tcp/server.h>
#include <rum/tcp/utility.h>

View File

@ -1,8 +1,8 @@
#include <rum/http/method.h>
#include <rum/http/request.h>
#include <format>
#include <iostream>
#include <string>
#include "rum/http/method.h"
namespace Rum::HTTP {
@ -16,7 +16,7 @@ Request::operator std::string() const {
"\tmethod: {}\n"
"\t{}\n"
"\theaders: \n{}"
"\tbody: \n'{}'"
"\tbody: '{}'\n"
"}}",
std::make_format_args(to_string(method), (std::string)uri, headers_string, body));
}

View File

@ -1,6 +1,7 @@
#include <rum/http/response.h>
#include <rum/tcp/error.h>
#include <sys/socket.h>
#include <format>
#include <map>
#include <stdexcept>
@ -75,47 +76,21 @@ std::string to_string(int code) {
}
}
void Response::send_header(const std::string& name, const std::string& value) {
if (!sent_header) {
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)) {
closed = true;
throw TCP::Error(TCP::Error::CLOSED);
}
sent_header = true;
}
std::string header = name + ": " + value + "\r\n";
if (-1 == send(client_sock, header.c_str(), header.size(), 0)) {
closed = true;
throw TCP::Error(TCP::Error::CLOSED);
}
}
void Response::send_body(const std::string& value) {
if (!sent_header) {
send_header("Content-Type", "text/html");
}
if (!sent_body) {
if (-1 == send(client_sock, "\r\n", 2, 0)) {
closed = true;
throw TCP::Error(TCP::Error::CLOSED);
}
sent_body = true;
}
if (-1 == send(client_sock, value.c_str(), value.size(), 0)) {
closed = true;
throw TCP::Error(TCP::Error::CLOSED);
}
}
Response::~Response() {
if (!sent_header && !closed) {
std::string resp("HTTP/1.1 " + std::to_string(code) + " " + to_string(code));
send(client_sock, resp.c_str(), resp.size(), 0);
std::string headers_string;
for (auto header : headers) {
headers_string += header.first + ": " + header.second + "\r\n";
}
for (auto cookie : cookies) {
headers_string += "Set-cookie: " + cookie.first + "=" + cookie.second + "\r\n";
}
}
std::string message = std::vformat(
"HTTP/1.1 {} {}\r\n"
"{}"
"\r\n"
"{}",
std::make_format_args(std::to_string(code), to_string(code), headers_string, body));
send(client_sock, message.c_str(), message.size(), 0);
}
} // namespace Rum::HTTP

View File

@ -1,6 +1,6 @@
#include "rum/tcp/server.h"
#include <rum/http/server.h>
#include <rum/tcp/error.h>
#include <rum/tcp/server.h>
#include <rum/tcp/utility.h>
#include <iostream>
#include <regex>
@ -101,10 +101,10 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
ssize_t recieved = recv(client_sock, buffer, buffer_size, 0);
switch (recieved) {
case TCP::Error::CLOSED:
std::cout << address << ": connection closed" << std::endl;
std::cerr << address << ": connection closed" << std::endl;
return;
case TCP::Error::UNKNOWN:
std::cout << "socket error" << std::endl;
std::cerr << "socket error" << std::endl;
return;
}
@ -114,8 +114,12 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
std::regex method_regex("(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE) (\\/.*) HTTP.*");
std::smatch match;
if (std::regex_match(message.cbegin(), message.cbegin() + message.find("\r\n"), match, method_regex)) {
// create request object
try {
request = Request(string_to_method(match.str(1)), URI("http://0.0.0.0:" + std::to_string(TCP::Server::get_port()) + match.str(2)));
} catch (std::exception&) {
return;
}
message = message.substr(message.find("\r\n"));
stage = HEADER;
} else {
@ -127,8 +131,30 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
std::regex header_regex("(.*): (.*)");
for (std::sregex_iterator it = std::sregex_iterator(message.cbegin(), message.cbegin() + message.find("\r\n\r\n"), header_regex);
it != std::sregex_iterator(); it++) {
std::string key(it->str(1));
std::string value(it->str(2));
if (key == "Host") {
if (size_t pos = value.find(':'); pos != value.npos) {
request.get_uri().set_host(value.substr(0, pos));
request.get_uri().set_port(value.substr(pos + 1));
} else {
request.get_uri().set_host(value);
}
} else if (key == "Cookie") {
std::stringstream cookies_string(value);
std::string cookie;
while (std::getline(cookies_string, cookie, ';')) {
if (size_t pos = cookie.find('='); pos != cookie.npos) {
request.set_cookie(cookie.substr(0, pos), cookie.substr(pos + 1));
} else {
return;
}
}
} else {
request.set_header(it->str(1), it->str(2));
}
}
message = message.substr(message.find("\r\n\r\n"));
if (Method method = request.get_method(); method == POST || method == PUT)
@ -143,10 +169,9 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
}
}
std::cout << request << std::endl;
try {
Response resp(client_sock);
resp.headers["Content-type"] = "text/html";
bool found = false;
@ -160,7 +185,7 @@ void Server::handler(int client_sock, const sockaddr_in& client_address, char* b
if (!found) {
resp.set_code(404);
resp.send_body("<h1>404: Page not found :C</h1>");
resp.body = "<h1>404: Page not found :C</h1>";
}
} catch (std::out_of_range) {
} catch (TCP::Error) {

View File

@ -4,10 +4,6 @@
#include <regex>
#include <string>
#define MATCH(var, i) \
if (match[i].matched) \
var = match.str(i);
namespace Rum::HTTP {
URI::operator std::string() const {
@ -38,6 +34,10 @@ URI::URI(const std::string& uri) {
std::cout << uri << std::endl;
if (std::regex_match(uri.cbegin(), uri.cend(), match, uri_regex)) {
#define MATCH(var, i) \
if (match[i].matched) \
var = match.str(i);
MATCH(scheme, 2)
MATCH(user, 4)
MATCH(password, 6)

View File

@ -29,14 +29,15 @@ int main(int argc, char** argv) {
server->end();
});
server->add_path<Rum::HTTP::GET>("/asd", [](const Rum::HTTP::Request& req, 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;
resp.send_body("<h1>asd</h1><pre>" + (std::string)req + "</pre>");
resp.headers["Server"] = "Rum Barrel";
resp.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>");
resp.body = "<h1>Hello World</h1><pre>" + (std::string)req + "</pre>";
});
server->listen();