diff options
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 159 |
1 files changed, 159 insertions, 0 deletions
@@ -0,0 +1,159 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <signal.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <errno.h> +#include "http.h" +#include "memory.h" + + +#define PORT 8080 + + +int num_threads=0; + + +static void signal_handler(int sig){ + if(sig==SIGCHLD){ + int saved_errno=errno; + while(true){ + int status; + pid_t pid=waitpid(-1,&status,WNOHANG); + if(pid<=0){ + break; + } + if(WIFEXITED(status)){ + num_threads--; + } + } + errno=saved_errno; + } +} + + +// Returns -1 on error, 0 on success +static int sendall(int sock,const char *buf,ssize_t len){ + if(len==-1){ + len=strlen(buf); + } + + ssize_t sent=0; + while(sent<len){ + ssize_t ret=send(sock,buf+sent,len-sent,0); + if(ret<0){ + if(errno==EINTR){ + continue; + } else { + return -1; + } + } + sent+=ret; + } + + return 0; +} + +__attribute__((format (printf, 2, 3))) +static int sendallf(int sock,const char *format,...){ + va_list ap; + va_start(ap,format); + char *buf; + int len=vasprintf(&buf,format,ap); + va_end(ap); + if(len<0){ + return -1; + } + int ret=sendall(sock,buf,len); + free(buf); + return ret; +} + +static void connection_handler(int sock){ + Headers *headers=http_get_headers(sock); + if(headers==NULL){ + return; + } + + sendall(sock,"HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain; charset=UTF-8\r\n" + "Server: cserver\r\n" + "\r\n",-1); + + sendallf(sock,"Method: %s\n",headers->method); + sendallf(sock,"URL: %s\n",headers->url); + sendallf(sock,"Version: %s\n",headers->version); + for(int i=0;i<headers->nheaders;i++){ + sendallf(sock,"Header '%s': '%s'\n",headers->headers[i][0],headers->headers[i][1]); + } +} + + +int main(void){ + int sock=socket(PF_INET,SOCK_STREAM,0); + if(sock<0){ + perror("socket"); + return 1; + } + + int one=1; + if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(int))<0){ + perror("setsockopt"); + return 1; + } + + struct sockaddr_in sin; + sin.sin_family=AF_INET; + sin.sin_addr.s_addr=htonl(INADDR_ANY); + sin.sin_port=htons(PORT); + + if(bind(sock,(struct sockaddr*)&sin,sizeof(sin))<0){ + close(sock); + perror("bind"); + return 1; + } + + if(listen(sock,8)<0){ + perror("listen"); + return 1; + } + + signal(SIGCHLD,signal_handler); + + printf("Listening on port %d\n",PORT); + + while(true){ + struct sockaddr_storage client_addr; + socklen_t client_addr_sz=sizeof(client_addr); + int clientsock=accept(sock,(struct sockaddr*)&client_addr,&client_addr_sz); + if(clientsock<0){ + perror("accept"); + continue; + } + + char str[INET_ADDRSTRLEN]; + inet_ntop(client_addr.ss_family,&((struct sockaddr_in*)&client_addr)->sin_addr, + str,sizeof(str)); + + pid_t pid=fork(); + if(pid==0){ + close(sock); + connection_handler(clientsock); + close(clientsock); + exit(0); + } + if(pid<0){ + perror("fork"); + return 1; + } + num_threads++; + printf("Accept from %s; child pid %d, %d thread%s\n",str,pid,num_threads, + num_threads==1?"":"s"); + close(clientsock); + } +} |