12#include <linux/stat.h>
13#include <netinet/in.h>
14#include <sys/socket.h>
17static std::string bind_address =
"0.0.0.0";
18static std::string serve_directory =
".";
19static uint16_t port = 8080;
21static const char HTTP_200_TEMPLATE[] =
"HTTP/1.1 200 OK\r\n"
22 "Content-Length: %d\r\n"
25static const char HTTP_400[] =
"HTTP/1.1 400 Bad Request\r\n"
26 "Content-Length: 0\r\n"
29static const char HTTP_404[] =
"HTTP/1.1 404 Not Found\r\n"
30 "Content-Length: 0\r\n"
33static const char HTTP_500[] =
"HTTP/1.1 500 Internal Server Error\r\n"
34 "Content-Length: 0\r\n"
37constexpr size_t BACKLOG = 128;
40 constexpr size_t CHUNK_SIZE = 8192;
43 if (pipe(pipe_fds) != 0) {
44 std::fprintf(stderr,
"Failed to create pipe: %d\n", errno);
55 std::fprintf(stderr,
"Error during splice operation: %d\n", n);
62 std::fprintf(stderr,
"Error during splice operation: %d\n", n);
71std::string parse_request(
const std::string &request) {
72 size_t pos = request.find(
"\r\n");
73 if (pos == std::string::npos) {
77 std::string request_line = request.substr(0, pos);
78 size_t method_end = request_line.find(
' ');
79 if (method_end == std::string::npos) {
83 size_t path_start = method_end + 1;
84 size_t path_end = request_line.find(
' ', path_start);
85 if (path_end == std::string::npos) {
89 return request_line.substr(path_start, path_end - path_start);
96 std::fprintf(stderr,
"Read error: %d\n", n);
101 std::string request(buffer, n);
104 std::string path = parse_request(request);
107 client_fd,
condy::buffer(HTTP_400,
sizeof(HTTP_400) - 1), 0);
112 path =
"/index.html";
114 std::string file_path = serve_directory + path;
118 client_fd,
condy::buffer(HTTP_404,
sizeof(HTTP_404) - 1), 0);
123 struct statx statx_buf;
125 file_fd,
"", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT,
126 STATX_SIZE | STATX_MODE, &statx_buf);
128 std::fprintf(stderr,
"Failed to statx file: %d\n", r_stat);
130 client_fd,
condy::buffer(HTTP_500,
sizeof(HTTP_500) - 1), 0);
135 if (!S_ISREG(statx_buf.stx_mode)) {
137 client_fd,
condy::buffer(HTTP_404,
sizeof(HTTP_404) - 1), 0);
143 char header_buffer[128];
145 std::snprintf(header_buffer,
sizeof(header_buffer), HTTP_200_TEMPLATE,
146 static_cast<int>(statx_buf.stx_size));
149 co_await sendfile(client_fd, file_fd);
156 sockaddr_in client_addr;
157 socklen_t client_len =
sizeof(client_addr);
159 server_fd, (
struct sockaddr *)&client_addr, &client_len, 0);
161 std::fprintf(stderr,
"Failed to accept connection: %d\n",
170void prepare_address(
const std::string &host, uint16_t port,
172 memset(&addr, 0,
sizeof(addr));
173 addr.sin_family = AF_INET;
174 addr.sin_port = htons(port);
175 inet_pton(AF_INET, host.c_str(), &addr.sin_addr);
178void usage(
const char *prog_name) {
180 "Usage: %s [-h] [-b <address>] [-d <directory>] [-p <port>]\n"
181 " -h Show this help message\n"
182 " -b <address> Bind to the specified address (default: 0.0.0.0)\n"
183 " -d <directory> Serve directory (default: current directory)\n"
184 " -p <port> Port number to listen on (default: 8080)\n",
188int main(
int argc,
char **argv)
noexcept(
false) {
190 while ((opt = getopt(argc, argv,
"hb:d:p:")) != -1) {
196 bind_address = optarg;
199 serve_directory = optarg;
202 port =
static_cast<uint16_t
>(std::stoi(optarg));
210 sockaddr_in server_addr;
211 prepare_address(bind_address, port, server_addr);
213 int server_fd = socket(AF_INET, SOCK_STREAM, 0);
215 std::perror(
"Failed to create socket");
220 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval,
221 sizeof(optval)) < 0) {
222 std::perror(
"Failed to set socket options");
227 if (bind(server_fd, (
struct sockaddr *)&server_addr,
sizeof(server_addr)) <
229 std::perror(
"Failed to bind socket");
234 if (listen(server_fd, BACKLOG) < 0) {
235 std::perror(
"Failed to listen on socket");
240 std::printf(
"Serving HTTP on port %d (http://%s:%d/) ...\n", port,
241 bind_address.c_str(), port);
Basic buffer types and conversion utilities.
Coroutine type used to define a coroutine function.
Main include file for the Condy library.
auto async_splice(Fd1 fd_in, int64_t off_in, Fd2 fd_out, int64_t off_out, unsigned int nbytes, unsigned int splice_flags)
See io_uring_prep_splice.
T sync_wait(Runtime &runtime, Coro< T, Allocator > coro)
Synchronously wait for a coroutine to complete in the given runtime.
auto async_statx(int dfd, const char *path, int flags, unsigned mask, struct statx *statxbuf)
See io_uring_prep_statx.
auto async_recv(Fd sockfd, Buffer &&buf, int flags)
See io_uring_prep_recv.
MutableBuffer buffer(void *data, size_t size)
Create a buffer object from various data sources.
auto async_close(int fd)
See io_uring_prep_close.
auto async_open(const char *path, int flags, mode_t mode)
See io_uring_prep_openat.
auto async_send(Fd sockfd, Buffer &&buf, int flags)
See io_uring_prep_send.
Task< T, Allocator > co_spawn(Runtime &runtime, Coro< T, Allocator > coro)
Spawn a coroutine as a task in the given runtime.
auto async_accept(Fd fd, struct sockaddr *addr, socklen_t *addrlen, int flags)
See io_uring_prep_accept.