Skip to content

Instantly share code, notes, and snippets.

@rma92
Created November 20, 2024 17:30
Show Gist options
  • Save rma92/5ab656b8752e6fc689c7a7385a18431f to your computer and use it in GitHub Desktop.
Save rma92/5ab656b8752e6fc689c7a7385a18431f to your computer and use it in GitHub Desktop.
Small webservers in C for Windows that can compile with Tiny C Compiler.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
//#pragma comment(lib, "Ws2_32.lib")
//\local\tcc32\tcc.exe -impdef %SYSTEMROOT%\system32\ws2_32.dll
//\local\tcc32\tcc.exe web1.c ws2_32.def
#define PORT 29102
#define BACKLOG 10
#define BUFSIZE 1024
// File data
const char *filenames[] = {"/metrics"};
const char *filedata[] = {"server_requests_total 42\n"};
void handle_client(SOCKET client_socket) {
char buffer[BUFSIZE];
int bytes_received = recv(client_socket, buffer, BUFSIZE - 1, 0);
if (bytes_received == SOCKET_ERROR) {
perror("recv");
closesocket(client_socket);
return;
}
buffer[bytes_received] = '\0'; // Null-terminate the request
printf("Received request:\n%s\n", buffer);
// Parse the request to get the requested path
char method[BUFSIZE], path[BUFSIZE], protocol[BUFSIZE];
sscanf(buffer, "%s %s %s", method, path, protocol);
// Only handle GET requests
if (strcmp(method, "GET") != 0) {
const char *response = "HTTP/1.1 405 Method Not Allowed\r\n\r\n";
send(client_socket, response, strlen(response), 0);
closesocket(client_socket);
return;
}
// Check if the requested path matches the known filenames
int found = 0;
for (size_t i = 0; i < sizeof(filenames) / sizeof(filenames[0]); ++i) {
if (strcmp(path, filenames[i]) == 0) {
found = 1;
const char *response_header = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n";
send(client_socket, response_header, strlen(response_header), 0);
send(client_socket, filedata[i], strlen(filedata[i]), 0);
break;
}
}
if (!found) {
const char *response = "HTTP/1.1 404 Not Found\r\n\r\n";
send(client_socket, response, strlen(response), 0);
}
closesocket(client_socket);
}
int main() {
WSADATA wsaData;
SOCKET server_socket;
struct sockaddr_in server_addr;
fd_set read_fds;
// Initialize Winsock
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
fprintf(stderr, "WSAStartup failed.\n");
return 1;
}
// Create a socket
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == INVALID_SOCKET) {
perror("socket");
WSACleanup();
return 1;
}
// Set up the server address
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(PORT);
// Bind the socket
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
perror("bind");
closesocket(server_socket);
WSACleanup();
return 1;
}
// Listen for incoming connections
if (listen(server_socket, BACKLOG) == SOCKET_ERROR) {
perror("listen");
closesocket(server_socket);
WSACleanup();
return 1;
}
printf("Server listening on http://127.0.0.1:%d\n", PORT);
// Main loop
while (1) {
FD_ZERO(&read_fds);
FD_SET(server_socket, &read_fds);
// Wait for activity
if (select(0, &read_fds, NULL, NULL, NULL) == SOCKET_ERROR) {
perror("select");
break;
}
// Check for new connections
if (FD_ISSET(server_socket, &read_fds)) {
struct sockaddr_in client_addr;
int addr_len = sizeof(client_addr);
SOCKET client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &addr_len);
if (client_socket == INVALID_SOCKET) {
perror("accept");
continue;
}
printf("New connection accepted.\n");
handle_client(client_socket);
}
}
// Cleanup
closesocket(server_socket);
WSACleanup();
return 0;
}
#include <stdio.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
//#pragma comment(lib, "ws2_32.lib")
//\local\tcc32\tcc.exe -impdef %SYSTEMROOT%\system32\ws2_32.dll
//\local\tcc32\tcc.exe web2.c ws2_32.def
#define PORT "29102"
#define BACKLOG 10
int main() {
WSADATA wsaData;
int iResult;
// Initialize Winsock
if ((iResult = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
// Prepare server address info
struct addrinfo hints, *res = NULL;
SOCKET listen_sock = INVALID_SOCKET;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP socket
hints.ai_flags = AI_PASSIVE; // Listen on any IP
if ((iResult = getaddrinfo(NULL, PORT, &hints, &res)) != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a listening socket
listen_sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listen_sock == INVALID_SOCKET) {
printf("Socket creation failed: %ld\n", WSAGetLastError());
freeaddrinfo(res);
WSACleanup();
return 1;
}
// Bind the socket
if (bind(listen_sock, res->ai_addr, (int)res->ai_addrlen) == SOCKET_ERROR) {
printf("Bind failed: %d\n", WSAGetLastError());
closesocket(listen_sock);
freeaddrinfo(res);
WSACleanup();
return 1;
}
freeaddrinfo(res);
// Start listening
if (listen(listen_sock, BACKLOG) == SOCKET_ERROR) {
printf("Listen failed: %d\n", WSAGetLastError());
closesocket(listen_sock);
WSACleanup();
return 1;
}
// Initialize the master file descriptor set
fd_set master_set, read_fds;
FD_ZERO(&master_set);
FD_SET(listen_sock, &master_set);
int fdmax = listen_sock;
// Define filenames and filedata arrays
const char *filenames[] = {"/metrics"};
const char *filedata[] = {"Hello, this is the /metrics endpoint.\n"};
printf("Server is running on localhost:%s\n", PORT);
// Main loop
while (1) {
read_fds = master_set; // Copy the master set
if (select(fdmax + 1, &read_fds, NULL, NULL, NULL) == SOCKET_ERROR) {
printf("Select failed: %d\n", WSAGetLastError());
break;
}
// Iterate through file descriptors
for (int i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) {
if (i == listen_sock) {
// Accept new connection
SOCKET client_sock;
struct sockaddr_storage client_addr;
int addr_len = sizeof(client_addr);
client_sock = accept(listen_sock, (struct sockaddr *)&client_addr, &addr_len);
if (client_sock == INVALID_SOCKET) {
printf("Accept failed: %d\n", WSAGetLastError());
} else {
FD_SET(client_sock, &master_set);
if (client_sock > fdmax) {
fdmax = client_sock;
}
}
} else {
// Handle client data
char buf[1024];
int recv_bytes = recv(i, buf, sizeof(buf) - 1, 0);
if (recv_bytes <= 0) {
if (recv_bytes == 0) {
printf("Connection closed by client.\n");
} else {
printf("Recv failed: %d\n", WSAGetLastError());
}
closesocket(i);
FD_CLR(i, &master_set);
} else {
buf[recv_bytes] = '\0';
char method[8], path[256], protocol[16];
sscanf(buf, "%s %s %s", method, path, protocol);
printf("Request: %s %s %s\n", method, path, protocol);
// Search for the requested file
int found = 0;
for (int j = 0; j < sizeof(filenames) / sizeof(filenames[0]); j++) {
if (strcmp(path, filenames[j]) == 0) {
char response[1024];
int content_length = strlen(filedata[j]);
sprintf(response, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s", content_length, filedata[j]);
send(i, response, strlen(response), 0);
found = 1;
break;
}
}
if (!found) {
const char *not_found = "HTTP/1.1 404 Not Found\r\nContent-Length: 13\r\n\r\n404 Not Found";
send(i, not_found, strlen(not_found), 0);
}
closesocket(i);
FD_CLR(i, &master_set);
}
}
}
}
}
// Cleanup
closesocket(listen_sock);
WSACleanup();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment