/** * @file minihttpd.c * @author Mathieu Bunel * @brief Minimal HTTP 0.9 deamon. * @version 0.3 * @date 2008 * * Minimal http server. * @todo Protect against bad formatted request like GET //bin/sh or GET /../foo. * @todo Make robust. * @todo directory handling (if /foo/ is request, send /foo/index.html) * * 0.3 * Create a thread on each new connection * * * 0.2 * Able to provide files. * Answers index.html on 'GET /' request. * Answers error page if request page is unknown. * * 0.1 * Accepts one connection, disconnect after answer. * GET request without arg, just answer an hello world page. * * */ /** * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #ifdef WIN32 #include #include typedef void thread_return; #else #include #include #include #include #include #include #include typedef void *thread_return; #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define closesocket(s) close(s) typedef int SOCKET; typedef struct sockaddr_in SOCKADDR_IN; typedef struct sockaddr SOCKADDR; typedef struct in_addr IN_ADDR; #endif #ifndef socklen_t #define socklen_t int #endif #ifdef _DEBUG #define TRACE(a) printf(a) #define TRACEd(a) printf("%d",a) #else #define TRACE(a) /**/ #define TRACEd(a) /**/ #endif #define PORT 7766 #define DEFAULT_PORT 8080 #define VERSION 0.3 #define SND_BUF_SIZE 128 #define QUERY_BUF_SIZE 1024 #define ERROR_404 "HTTP/0.9 200 OK\r\n\ Content-Type: text/html\r\n\r\n\ \ minihttpd\ \ minihttpd 0.3
\ Error file not found.
\ \ \r\n\r\n" void send_file(FILE * pFile, SOCKET sock) { TRACE("send_file\n"); char buffer[SND_BUF_SIZE]; size_t cSize, cRead, cSend; if (NULL == pFile) { return; } /* get file size */ fseek(pFile, 0, SEEK_SET); fseek(pFile, 0, SEEK_END); cSize = ftell(pFile); #ifdef _DEBUG printf("size: %uko\n", cSize / 1024); #endif /* go back to file start */ fseek(pFile, 0, SEEK_SET); while (0 != cSize) { /* read portion of file */ cRead = fread(buffer, SND_BUF_SIZE < cSize ? SND_BUF_SIZE : cSize, 1, pFile); if (cRead < 1) { perror("fread()"); break; } /* send file */ cSend = send(sock, buffer, SND_BUF_SIZE < cSize ? SND_BUF_SIZE : cSize, 0); if (cSend < 1) { perror("send()"); break; } if (cSize > SND_BUF_SIZE) { cSize -= SND_BUF_SIZE; } else { cSize = 0; break; } } } void parse_request(char *pRequest, size_t size, SOCKET sock) { TRACE("parse_request("); TRACE(pRequest); TRACE(", "); TRACEd(size); TRACE(")\n"); char reqPage[SND_BUF_SIZE] = { 0 }; size_t i = 0; FILE *pFile; if ((NULL == pRequest) || (size < 1)) { return; } if (0 == strncmp("GET /", pRequest, 5)) { /* Can't use strtok, pRequest might not have '\0' delimiter */ for (i = 5; i < size; ++i) { if (' ' == pRequest[i]) { break; } } } if (i > 5) { strncpy(reqPage, pRequest + 5, i - 5); } else { strcpy(reqPage, "index.html"); printf("Default page requested... sending index.html\n"); } pFile = fopen(reqPage, "rb"); if (NULL != pFile) { #ifdef _DEBUG printf("Page \"%s\" requested\n", reqPage); #endif send_file(pFile, sock); fclose(pFile); } else { send(sock, ERROR_404, strlen(ERROR_404), 0); printf("Page \"%s\" not found\n", reqPage); } } /** */ thread_return wait_query(void *pcsock) { /* Waiting query */ int cReceived; char buffer[QUERY_BUF_SIZE]; if (pcsock) { SOCKET csock = *(SOCKET *) pcsock; cReceived = recv(csock, buffer, QUERY_BUF_SIZE - 1, 0); if (cReceived < 0) { perror("recv()"); } else { parse_request(buffer, cReceived, csock); } /* closing connection to client */ closesocket(csock); } #ifndef WIN32 return NULL; #endif } /** * init function. * today, on ly used to init Win32 sockets */ void init(void) { #ifdef WIN32 /* init socket windows */ WSADATA wsa; WSAStartup(MAKEWORD(2, 2), &wsa); #endif } /** * init function. * today, on ly used to desinit Win32 sockets */ void desinit(void) { #ifdef WIN32 /* desinit socket windows */ WSACleanup(); printf("system desinited\n"); #endif } /** * program entry point * usage: minihttpd [tcp_port] */ int main(int argc, char *argv[]) { /* init */ init(); printf("init [OK]\n"); /* desinit preparation */ atexit(desinit); /* listening socket creation */ SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); if (INVALID_SOCKET == sock) { perror("socket()"); exit(errno); } printf("socket creation [OK]\n"); SOCKADDR_IN sin /*= { 0 }*/ ; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_family = AF_INET; if (1 == argc) { sin.sin_port = htons(DEFAULT_PORT); } else { sin.sin_port = htons(strtol(argv[1], NULL, 10)); } if (SOCKET_ERROR == bind(sock, (SOCKADDR *) & sin, sizeof(sin))) { perror("bind()"); exit(errno); } printf("configuration [OK]\n"); while (1) { /* Listening */ if (SOCKET_ERROR == listen(sock, 1)) { perror("listen()"); exit(errno); } /* Acknowledgment */ SOCKADDR_IN csin; SOCKET csock; socklen_t sinsize = sizeof csin; csock = accept(sock, (SOCKADDR *) & csin, &sinsize); if (INVALID_SOCKET == csock) { perror("accept()"); exit(errno); } #ifdef WIAIT_QUERY_IN_A_THREAD #ifdef WIN32 _beginthread(wait_query, 0, &csock); #else pthread_t thread; pthread_create(&thread, NULL, wait_query, &csock); #endif #else wait_query(&csock); #endif } /* closing listen socket */ closesocket(sock); printf("socket closed\n"); return EXIT_SUCCESS; }