Created
November 20, 2024 17:30
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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