13#include <linux/stat.h>
14#include <netinet/in.h>
15#include <sys/socket.h>
18static std::string bind_address =
"0.0.0.0";
19static std::string serve_directory =
".";
20static uint16_t port = 8080;
22static const char HTTP_200_TEMPLATE[] =
"HTTP/1.1 200 OK\r\n"
23 "Content-Length: %d\r\n"
26static const char HTTP_400[] =
"HTTP/1.1 400 Bad Request\r\n"
27 "Content-Length: 0\r\n"
30static const char HTTP_404[] =
"HTTP/1.1 404 Not Found\r\n"
31 "Content-Length: 0\r\n"
34static const char HTTP_500[] =
"HTTP/1.1 500 Internal Server Error\r\n"
35 "Content-Length: 0\r\n"
38constexpr size_t BACKLOG = 128;
41 constexpr size_t CHUNK_SIZE = 8192;
44 if (pipe(pipe_fds) != 0) {
45 std::perror(
"Failed to create pipe");
56 std::cerr << std::format(
"Error during splice operation: {}\n", n);
63 std::cerr << std::format(
"Error during splice operation: {}\n", n);
72std::string parse_request(
const std::string &request) {
73 size_t pos = request.find(
"\r\n");
74 if (pos == std::string::npos) {
78 std::string request_line = request.substr(0, pos);
79 size_t method_end = request_line.find(
' ');
80 if (method_end == std::string::npos) {
84 size_t path_start = method_end + 1;
85 size_t path_end = request_line.find(
' ', path_start);
86 if (path_end == std::string::npos) {
90 return request_line.substr(path_start, path_end - path_start);
97 std::cerr << std::format(
"Read error: {}\n", n);
102 std::string request(buffer, n);
105 std::string path = parse_request(request);
108 client_fd,
condy::buffer(HTTP_400,
sizeof(HTTP_400) - 1), 0);
113 path =
"/index.html";
115 std::string file_path = serve_directory + path;
119 client_fd,
condy::buffer(HTTP_404,
sizeof(HTTP_404) - 1), 0);
124 struct statx statx_buf;
126 file_fd,
"", AT_EMPTY_PATH | AT_STATX_SYNC_AS_STAT,
127 STATX_SIZE | STATX_MODE, &statx_buf);
129 std::cerr << std::format(
"Failed to statx file: {}\n", r_stat);
131 client_fd,
condy::buffer(HTTP_500,
sizeof(HTTP_500) - 1), 0);
136 if (!S_ISREG(statx_buf.stx_mode)) {
138 client_fd,
condy::buffer(HTTP_404,
sizeof(HTTP_404) - 1), 0);
144 char header_buffer[128];
146 std::snprintf(header_buffer,
sizeof(header_buffer), HTTP_200_TEMPLATE,
147 static_cast<int>(statx_buf.stx_size));
150 co_await sendfile(client_fd, file_fd);
157 sockaddr_in client_addr;
158 socklen_t client_len =
sizeof(client_addr);
160 server_fd, (
struct sockaddr *)&client_addr, &client_len, 0);
162 std::cerr << std::format(
"Failed to accept connection: {}\n",
171void prepare_address(
const std::string &host, uint16_t port,
173 memset(&addr, 0,
sizeof(addr));
174 addr.sin_family = AF_INET;
175 addr.sin_port = htons(port);
176 inet_pton(AF_INET, host.c_str(), &addr.sin_addr);
179void usage(
const char *prog_name) {
180 std::cerr << std::format(
181 "Usage: {} [-h] [-b <address>] [-d <directory>] [-p <port>]\n"
182 " -h Show this help message\n"
183 " -b <address> Bind to the specified address (default: 0.0.0.0)\n"
184 " -d <directory> Serve directory (default: current directory)\n"
185 " -p <port> Port number to listen on (default: 8080)\n",
189int main(
int argc,
char **argv)
noexcept(
false) {
191 while ((opt = getopt(argc, argv,
"hb:d:p:")) != -1) {
197 bind_address = optarg;
200 serve_directory = optarg;
203 port =
static_cast<uint16_t
>(std::stoi(optarg));
211 sockaddr_in server_addr;
212 prepare_address(bind_address, port, server_addr);
214 int server_fd = socket(AF_INET, SOCK_STREAM, 0);
216 std::perror(
"Failed to create socket");
221 if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval,
222 sizeof(optval)) < 0) {
223 std::perror(
"Failed to set socket options");
228 if (bind(server_fd, (
struct sockaddr *)&server_addr,
sizeof(server_addr)) <
230 std::perror(
"Failed to bind socket");
235 if (listen(server_fd, BACKLOG) < 0) {
236 std::perror(
"Failed to listen on socket");
241 std::cout << std::format(
"Serving HTTP on port {} (http://{}:{}/) ...\n",
242 port, bind_address, port);
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.
Task< T, Allocator > co_spawn(Runtime &runtime, Coro< T, Allocator > coro) noexcept
Spawn a coroutine as a task in the given runtime.
MutableBuffer buffer(void *data, size_t size) noexcept
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.
auto async_accept(Fd fd, struct sockaddr *addr, socklen_t *addrlen, int flags)
See io_uring_prep_accept.