#include "response.h"
#include "listener.h"
-#define ERR(error) std::unexpected(error)
-#define NEW_LINE std::string_view("\r\n")
+#define Err(error) std::unexpected(error)
namespace http {
+ constexpr std::string_view NEW_LINE = "\r\n";
+
std::expected<Response, Error> send(Method, const Request& req);
std::expected<int8_t, Error> connect(const std::string_view& domain_name, const uint16_t port = 80);
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);
+ return Err(Error::SocketCreation);
}
server.addr.sin_family = AF_INET;
server.addr.sin_port = htons(port);
if (::bind(server.socketfd, (struct sockaddr *)&server.addr, sizeof(server.addr)) < 0) {
- return ERR(Error::SocketBind);
+ return Err(Error::SocketBind);
}
if (::listen(server.socketfd, backlog) < 0) {
- return ERR(Error::SocketListen);
+ return Err(Error::SocketListen);
}
return server;
socklen_t addrsize = sizeof(this->addr);
int8_t clientfd = ::accept(this->socketfd, (struct sockaddr *)&this->addr, &addrsize);
if (clientfd < 0) {
- return ERR(Error::SocketConnection);
+ return Err(Error::SocketConnection);
}
auto raw_req = read_raw_message(clientfd);
std::ostream &operator<<(std::ostream &os, const http::Request &req) {
return os << "Request { domain: " << std::quoted(req.domain_name) << ", port: " << req.port
- << ", host: " << std::quoted(req.host) << ", query: " << std::quoted(req.query)
- << ", accept: " << std::quoted(req.accept) << ", body: " << std::quoted(req.body)
- << " }";
+ << ", query: " << std::quoted(req.query) << ", body: " << std::quoted(req.body) << " }";
}
#include <cstdint>
#include <string_view>
#include <ostream>
+#include <unordered_map>
namespace http {
struct http_version {
struct Request {
uint16_t port = 80;
std::string_view domain_name;
- std::string_view host = domain_name;
std::string_view query = "/";
- std::string_view accept = "*/*";
std::string_view body = "";
http_version version = {.major = 1, .minor = 1};
+
+ std::unordered_map<std::string, std::string> optheaders = {};
};
} // namespace http
namespace http {
std::expected<Response, Error> Response::build(std::string_view raw_response) noexcept {
if (raw_response.empty()) {
- return ERR(Error::InvalidResponse);
+ return Err(Error::InvalidResponse);
}
Response resp;
resp.version = {.major = (uint8_t)std::stoi(major.data()),
.minor = (uint8_t)std::stoi(minor.data())};
} catch (...) {
- return ERR(Error::InvalidResponseHTTPVersion);
+ return Err(Error::InvalidResponseHTTPVersion);
}
try {
std::string_view status(*++word_iter);
resp.status = status_map[std::stoi(status.data())];
} catch (...) {
- return ERR(Error::InvalidResponseStatusCode);
+ return Err(Error::InvalidResponseStatusCode);
}
for (std::string_view line; lines_view_iter != lines_view.end() &&
hints.ai_socktype = SOCK_STREAM; // TCP only
if (getaddrinfo(domain_name.data(), std::to_string(port).c_str(), &hints, &addr_list)) {
- return ERR(Error::DNSResolution);
+ return Err(Error::DNSResolution);
}
int8_t remote_socketfd;
socket(it->second.ai_family, it->second.ai_socktype, it->second.ai_protocol);
if (remote_socketfd < 0) {
close(remote_socketfd);
- return ERR(Error::SocketConnection);
+ return Err(Error::SocketConnection);
}
} else {
struct addrinfo *remote = 0;
if (!remote) {
freeaddrinfo(addr_list);
- return ERR(Error::ServerNotFound);
+ return Err(Error::ServerNotFound);
}
ip_map[domain_name.data()] = *remote;
auto maybe_socketfd = connect(req.domain_name, req.port);
if (!maybe_socketfd.has_value()) {
- return ERR(maybe_socketfd.error());
+ return Err(maybe_socketfd.error());
}
std::string msg = build_request(method, req);
auto maybe_response = read_raw_message(maybe_socketfd.value());
if (!maybe_response.has_value()) {
- return ERR(maybe_response.error());
+ return Err(maybe_response.error());
}
close(maybe_socketfd.value());
std::stringstream ss;
ss << method << " " << req.query << " HTTP/" << (int)req.version.major << "."
<< (int)req.version.minor << NEW_LINE;
- ss << "Host: " << req.host << NEW_LINE;
- ss << "Accept: " << req.accept << NEW_LINE;
+ ss << "Host: " << req.domain_name << NEW_LINE;
+
+ for (const auto &header : req.optheaders) {
+ ss << header.first << ": " << header.second << NEW_LINE;
+ }
if (!req.body.empty()) {
- ss << "Content-Length: " << req.body.length() << NEW_LINE << NEW_LINE << req.body;
+ ss << "Content-Length: " << req.body.length() * sizeof(char) << NEW_LINE << NEW_LINE
+ << req.body;
} else {
ss << NEW_LINE;
}