#include #ifdef _WIN32 #include #include #include #define CLOSE_SOCKET closesocket #else #include #include #include #include #include #include #include #define SOCKET int #define INVALID_SOCKET -1 #define CLOSE_SOCKET close #endif #include // This example showcases how to use the engine interface // to build a blocking HTTP server that works on Windows // and Linux. // Callback used by the engine to manage dynamic memory static void *memfunc(HTTP_MemoryFuncTag tag, void *ptr, int len, void *data) { (void) data; switch (tag) { case HTTP_MEMFUNC_MALLOC: return malloc(len); case HTTP_MEMFUNC_FREE: free(ptr); return NULL; } return NULL; } static void produce_response(HTTP_Engine *eng) { // All considerations in simple_server.c for how responses // are built also applies here, where http_engine_XXX functions // are used instead of http_response_XXX functions. // Set the response status http_engine_status(eng, 200); // Set zero or more headers http_engine_header(eng, HTTP_STR("Server: tinyhttp")); // Set some bytes in the body http_engine_body(eng, HTTP_STR("Hello, world!")); // This is one difference from the http_response_XXX API. // It's possible to write response content directly into // the engine's output buffer. This avoids copies in some // circumstances. char msg[] = " What's up??"; // First, set how many bytes the output buffer will need // to hold at least: http_engine_bodycap(eng, sizeof(msg)-1); // Now get the location for the write. The returned pointer // points to a region of size "cap", which equal or greater // to the previously set minimum capacity. int cap; char *dst = http_engine_bodybuf(eng, &cap); // If an error occurred internally, the returned pointer will // be NULL and the capacity 0. In this case, you can just skip // this write. The engine will automatically be closed when the // "http_engine_done" function is called. if (dst) { memcpy(dst, msg, sizeof(msg)-1); // Tell the engine how many bytes the application wrote to the // provided buffer. http_engine_bodyack(eng, sizeof(msg)-1); } // If an error occurs, you can undo all progress and start // from scratch int error = rand() & 1; if (error) { http_engine_undo(eng); http_engine_status(eng, 500); http_engine_done(eng); return; } http_engine_done(eng); } int main(void) { // Interface and port the server will be listening on. char *addr = "127.0.0.1"; int port = 8080; #ifdef _WIN32 WSADATA wd; if (WSAStartup(MAKEWORD(2, 2), &wd)) return -1; #endif // Create the listening socket SOCKET listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd == INVALID_SOCKET) return -1; // Ignore the cooldown time for the bound interface to // avoid that annoying "address in use" error int reuse = 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void*) &reuse, sizeof(reuse)); // Fill out the address struct struct sockaddr_in bind_buf; { struct in_addr addr_buf; if (inet_pton(AF_INET, addr, &addr_buf) != 1) return -1; bind_buf.sin_family = AF_INET; bind_buf.sin_port = htons(port); bind_buf.sin_addr = addr_buf; memset(&bind_buf.sin_zero, 0, sizeof(bind_buf.sin_zero)); } // Associate the listening socket to the interface if (bind(listen_fd, (struct sockaddr*) &bind_buf, sizeof(bind_buf)) < 0) return -1; // Allow incoming connections if (listen(listen_fd, 32) < 0) return -1; for (;;) { printf("Waiting for a connection\n"); // Get an incoming connection from the kernel SOCKET client_fd = accept(listen_fd, NULL, NULL); if (client_fd == INVALID_SOCKET) continue; printf("New connection\n"); // Initialize the HTTP state machine HTTP_Engine eng; http_engine_init(&eng, 0, memfunc, NULL); for (;;) { // At this point, the engine can be in one // of four states: // 1) RECV_BUF: The engine is waiting for bytes from the network // 2) SEND_BUF: The engine wants to write bytes from the network // 3) CLOSED: The connection shut down at the HTTP layer // 4) PREP_STATUS: A request was received and the associated response // needs to be generated. HTTP_EngineState state = http_engine_state(&eng); if (state == HTTP_ENGINE_STATE_SERVER_PREP_STATUS) { produce_response(&eng); } else if (state == HTTP_ENGINE_STATE_SERVER_RECV_BUF) { printf("Receiving bytes\n"); // Get a pointer to the engine's input buffer int cap; char *dst = http_engine_recvbuf(&eng, &cap); // The application can write up to "cap" bytes // to the "dst" buffer. int ret = recv(client_fd, dst, cap, 0); if (ret <= 0) { // If the peer disconnected or an error occurred, // we can "close" the engine. This makes it so any // further operation on the engine will be a no-op // and the next time we query the state we will get // CLOSED. http_engine_close(&eng); ret = 0; } printf("Received %d bytes\n", ret); // Tell the engine how many bytes we wrote to // the buffer. http_engine_recvack(&eng, ret); } else if (state == HTTP_ENGINE_STATE_SERVER_SEND_BUF) { // This code is the same as the recv case except // we read from the buffer instead of writing. printf("Sending bytes\n"); int len; char *src = http_engine_sendbuf(&eng, &len); // Here "src" points to "len" bytes that need to // be sent over the network. int ret = send(client_fd, src, len, 0); if (ret < 0) { http_engine_close(&eng); ret = 0; } printf("Sent %d bytes\n", ret); http_engine_sendack(&eng, ret); } else { // HTTP_ENGINE_STATE_SERVER_CLOSED printf("HTTP close\n"); break; } } printf("Closing connection\n"); http_engine_free(&eng); CLOSE_SOCKET(client_fd); } CLOSE_SOCKET(listen_fd); #ifdef _WIN32 WSACleanup(); #endif return 0; }