namespace http {
enum class Error {
SocketCreation,
+ SocketBind,
+ SocketListen,
SocketConnection,
DNSResolution,
ServerNotFound,
std::expected<int8_t, Error> connect(const std::string_view& domain_name, const uint16_t port = 80);
std::string build_request(Method method, const Request &req);
- std::expected<std::string, Error> read_raw_response(const int8_t socketfd);
+ std::expected<std::string, Error> read_raw_message(const uint8_t socketfd);
namespace async {
std::future<std::expected<Response, Error>> send(Method method, const Request &req);
#include "listener.h"
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <iostream>
+
+#include "error.h"
+#include "http.h"
+
namespace http {
- std::expected<Listener, Error> Listener::create_on_local(uint16_t port) {
+ Listener::Listener() : routes({}), socketfd(::socket(AF_INET, SOCK_STREAM, 0)), addr({}) {}
+
+ std::expected<Listener, Error> Listener::on(uint16_t port, in_addr_t addr, uint16_t backlog) {
Listener server;
- ++port;
+ uint32_t opt = 1;
+ if (server.socketfd < 0 || ::setsockopt(server.socketfd, SOL_SOCKET,
+ SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) < 0) {
+ return ERR(Error::SocketCreation);
+ }
+
+ server.addr.sin_family = AF_INET;
+ server.addr.sin_addr.s_addr = htonl(addr);
+ server.addr.sin_port = htons(port);
+
+ if (::bind(server.socketfd, (struct sockaddr *)&server.addr, sizeof(server.addr)) < 0) {
+ return ERR(Error::SocketBind);
+ }
+
+ if (::listen(server.socketfd, backlog) < 0) {
+ return ERR(Error::SocketListen);
+ }
return server;
}
+ std::expected<void, Error> Listener::serve() {
+ socklen_t addrsize = sizeof(this->addr);
+ int8_t clientfd = ::accept(this->socketfd, (struct sockaddr *)&this->addr, &addrsize);
+ if (clientfd < 0) {
+ return ERR(Error::SocketConnection);
+ }
- void Listener::serve() {
+ auto raw_req = read_raw_message(clientfd);
+ if (raw_req.has_value()) {
+ std::cout << raw_req.value() << std::endl;
+ }
+ ::shutdown(clientfd, SHUT_RDWR);
+ ::close(clientfd);
+ return {};
}
} // namespace http
#pragma once
+#include <netinet/in.h>
+
#include <cstdint>
#include <expected>
#include <functional>
public:
~Listener() {}
- static std::expected<Listener, Error> create_on_local(uint16_t port);
- static std::expected<Listener, Error> create_on_lan(uint16_t port);
+ static inline std::expected<Listener, Error> on_local(uint16_t port, uint16_t backlog = 4096) {
+ return on(port, INADDR_LOOPBACK, backlog);
+ }
+
+ static inline std::expected<Listener, Error> on_LAN(uint16_t port, uint16_t backlog = 4096) {
+ return on(port, INADDR_ANY, backlog);
+ }
- void serve();
- void serve_async();
+ std::expected<void, Error> serve();
+ std::expected<void, Error> serve_async();
private:
- Listener() : routes({}) {}
+ Listener();
+
+ static std::expected<Listener, Error> on(uint16_t port, in_addr_t addr,
+ uint16_t backlog = 4096);
public:
std::unordered_map<std::string, std::function<void(const Request &)>> routes;
+
+ private:
+ int8_t socketfd;
+ struct sockaddr_in addr;
};
} // namespace http
#include <unistd.h>
#include <cstdint>
+#include <cstring>
#include <expected>
#include <iostream>
#include <sstream>
std::string msg = build_request(method, req);
::send((int)maybe_socketfd.value(), msg.c_str(), msg.size(), 0);
- auto maybe_response = read_raw_response(maybe_socketfd.value());
+ auto maybe_response = read_raw_message(maybe_socketfd.value());
if (!maybe_response.has_value()) {
return ERR(maybe_response.error());
}
return ss.str();
}
- std::expected<std::string, Error> read_raw_response(const int8_t socketfd) {
+ std::expected<std::string, Error> read_raw_message(const uint8_t socketfd) {
std::stringstream ss;
char buffer[BUFFSIZE] = "";
ssize_t bytes_read = 0;
while ((bytes_read = read((int)socketfd, buffer, BUFFSIZE - 1)) > 0) {
- if (bytes_read == -1) {
- return ERR(Error::InvalidRead);
- }
-
buffer[bytes_read] = '\0';
ss << buffer;
+
+ if (buffer[bytes_read - 2] == '\r' && buffer[bytes_read - 1] == '\n') {
+ break;
+ }
+ ::memset(buffer, 0, BUFFSIZE * sizeof(char));
}
return ss.str();