Skip to content

Instantly share code, notes, and snippets.

@curiousleo
Last active April 24, 2018 19:57
Show Gist options
  • Save curiousleo/efd817d622b1956dfe812d79e164415f to your computer and use it in GitHub Desktop.
Save curiousleo/efd817d622b1956dfe812d79e164415f to your computer and use it in GitHub Desktop.
Oversimplified HTTP server in C++
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
#include <algorithm>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
using addrinfo_t = struct addrinfo;
class Connection {
int sock;
std::vector<unsigned char> buf;
std::string read_until(std::string_view delim);
public:
explicit Connection(int sock)
: sock(sock), buf(std::vector<unsigned char>()) {}
std::string read_line();
};
int bind_socket(std::string_view port);
int set_reusable(int sock);
static const int RECV_SIZE = 3;
int main() {
const int s = bind_socket("9322");
if (listen(s, 0) == -1) {
std::cerr << "listen: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
const int in = accept(s, nullptr, nullptr);
if (in == -1) {
std::cerr << "accept: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
Connection c(in);
std::string method, path, version;
std::istringstream(c.read_line()) >> method >> path >> version;
std::unordered_map<std::string, std::string> hdrs{};
for (std::string l = c.read_line(); !l.empty(); l = c.read_line()) {
const size_t nsep = l.find(':');
std::string k(l.cbegin(), l.cbegin() + nsep);
std::string v(l.cbegin() + nsep + 2, l.cend());
std::transform(k.begin(), k.end(), k.begin(), ::tolower);
hdrs[k] = v;
}
return 0;
}
std::string Connection::read_line() { return read_until("\r\n"); }
std::string Connection::read_until(std::string_view delim) {
auto it = search(buf.begin(), buf.end(), delim.begin(), delim.end());
for (; it == buf.end();
it = search(buf.begin(), buf.end(), delim.begin(), delim.end())) {
buf.resize(buf.size() + RECV_SIZE);
const int r = recvfrom(sock, &*(buf.end() - RECV_SIZE), RECV_SIZE, 0,
nullptr, nullptr);
if (r == -1) {
std::cerr << "recvfrom: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
if (r == 0) {
std::string s(std::make_move_iterator(buf.begin()),
std::make_move_iterator(buf.end()));
buf.clear();
return s;
}
}
std::string s(std::make_move_iterator(buf.begin()),
std::make_move_iterator(it));
buf.erase(buf.begin(), it + delim.size());
return s;
}
int bind_socket(std::string_view port) {
addrinfo_t hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
addrinfo_t* ai_head;
{
int err = getaddrinfo(nullptr, port.data(), &hints, &ai_head);
if (err != 0) {
std::cerr << "getaddrinfo: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
}
for (addrinfo_t* ai = ai_head; ai != nullptr; ai = ai->ai_next) {
const int s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s == -1) {
continue;
}
if (set_reusable(s) == -1) {
std::cerr << "setsockopt: " << strerror(errno) << std::endl;
exit(EXIT_FAILURE);
}
if (bind(s, ai->ai_addr, ai->ai_addrlen) == 0) {
freeaddrinfo(ai_head);
return s;
}
close(s);
}
std::cerr << "could not bind" << std::endl;
exit(EXIT_FAILURE);
}
int set_reusable(const int sock) {
const int yes = 1;
return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
static_cast<const void*>(&yes), sizeof(int));
}
#! /usr/bin/bash
clang-tidy -checks=*,-llvm-include-order -extra-arg=-std=c++17 -header-filter="^include" -p . -fix muhttp.cpp
clang-format -style=google -i muhttp.cpp
clang++ -std=c++17 -Wall -Wextra -Wpedantic -g -o muhttp muhttp.cpp
./muhttp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment