From ef28fcb0f59d11bdf9f574a999045bc24ca40e18 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Sun, 25 Dec 2016 10:14:11 +0100 Subject: Initial --- .gitignore | 3 + Makefile | 24 ++++++++ http.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ http.h | 16 ++++++ main.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++ memory.c | 11 ++++ memory.h | 13 +++++ util.c | 24 ++++++++ util.h | 7 +++ 9 files changed, 440 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 http.c create mode 100644 http.h create mode 100644 main.c create mode 100644 memory.c create mode 100644 memory.h create mode 100644 util.c create mode 100644 util.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7304726 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +cserver +*.o +*.dSYM diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1583287 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +CC = gcc +CFLAGS = -Wall -Wextra -std=c11 -fwrapv +ifneq ($(DEBUG),) + CFLAGS += -g +else + CFLAGS += -O2 +endif +BIN = cserver + +.PHONY: all clean remake + +all: $(BIN) + +clean: + rm -rf $(BIN) *.o *.dSYM + +remake: clean all + + +$(BIN): $(patsubst %.c,%.o,$(wildcard *.c)) + $(CC) -o $@ $^ + +%.o: %.cpp $(wildcard *.h) + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/http.c b/http.c new file mode 100644 index 0000000..69f3499 --- /dev/null +++ b/http.c @@ -0,0 +1,183 @@ +#include +#include +#include +#include +#include +#include +#include "http.h" +#include "memory.h" +#include "util.h" + + +static void free_list(char **list,int nitems){ + for(int i=0;i=0: length of string +static int sock_get_line(char **dst,int sock){ + int linesz=128,linelen=0; + char *line=malloc(linesz,char); + bool haveCR=false,alsoCR=false; + while(true){ + char c; + ssize_t ret=recv(sock,&c,1,0); + if(ret<=0){ + free(line); + *dst=NULL; + return ret-1; + } + if(c=='\r'){ + haveCR=true; + continue; + } else if(c=='\n'){ + break; + } else if(haveCR){ + haveCR=false; + alsoCR=true; + } + if(linelen+1+alsoCR==linesz){ + linesz*=2; + if(linesz==0){ + exit(255); // Integer overflow! + } + line=realloc(line,linesz,char); + } + if(alsoCR){ + line[linelen++]='\r'; + alsoCR=false; + } + line[linelen++]=c; + } + line[linelen]='\0'; + *dst=line; + return linelen; +} + +// Returns number of lines, or -1 if error +static int get_header_lines(char ***dst,int sock){ + int arrsz=16,arrlen=0; + char **arr=malloc(arrsz,char*); + while(true){ + char *line; + int linelen=sock_get_line(&line,sock); + if(linelen<0){ + free_list(arr,arrlen); + *dst=NULL; + return -1; + } + if(linelen==0){ + break; + } + if(arrlen+1==arrsz){ + arrsz*=2; + if(arrsz==0){ + exit(255); // Integer overflow! + } + arr=realloc(arr,arrsz,char*); + } + arr[arrlen++]=line; + } + arr[arrlen]=NULL; + *dst=arr; + return arrlen; +} + +static Headers* alloc_headers(void){ + Headers *headers=malloc(1,Headers); + headers->method=NULL; + headers->url=NULL; + headers->version=NULL; + headers->nheaders=0; + headers->headers=NULL; + return headers; +} + +void free_headers(Headers *headers){ + if(headers==NULL){ + return; + } + + if(headers->method)free(headers->method); + if(headers->url)free(headers->url); + if(headers->version)free(headers->version); + + if(headers->headers){ + for(int i=0;inheaders;i++){ + free(headers->headers[i][0]); + free(headers->headers[i][1]); + } + free(headers->headers); + } + + free(headers); +} + +Headers* http_get_headers(int sock){ + char **lines; + int nlines=get_header_lines(&lines,sock); + if(nlines<0){ + return NULL; + } + if(nlines==0){ + free_list(lines,nlines); + return NULL; + } + + char *spacep=strchr(lines[0],' '); + char *space2p=strrchr(lines[0],' '); + if(spacep==NULL||space2p==NULL){ + free_list(lines,nlines); + return NULL; + } + char *urlstart=spacep+1; + while(isspace(*urlstart)){ + urlstart++; + } + if(urlstart>space2p){ // No url? + free_list(lines,nlines); + return NULL; + } + + Headers *headers=alloc_headers(); + + headers->method=copy_buf(lines[0],spacep-lines[0]); + str_toupper(headers->method); + headers->url=copy_buf(urlstart,space2p-urlstart); + headers->version=copy_str(space2p); + str_toupper(headers->version); + + int hsz=16; + headers->nheaders=0; + headers->headers=malloc(hsz,Header); + + for(int i=1;inheaders+1==hsz){ + hsz*=2; + // Don't need to check overflow, because nlines would have + // overflowed already + headers->headers=realloc(headers->headers,hsz,Header); + } + headers->headers[headers->nheaders][0]=copy_buf(lines[i],colonp-lines[i]); + headers->headers[headers->nheaders][1]=copy_str(valuep); + headers->nheaders++; + } + headers->headers[headers->nheaders][0]=NULL; + headers->headers[headers->nheaders][1]=NULL; + + free_list(lines,nlines); + return headers; +} diff --git a/http.h b/http.h new file mode 100644 index 0000000..c0cad54 --- /dev/null +++ b/http.h @@ -0,0 +1,16 @@ +#pragma once + + +typedef char *Header[2]; + +typedef struct Headers{ + char *method,*url,*version; + + int nheaders; + Header *headers; +} Headers; + + +void free_headers(Headers *headers); + +Headers* http_get_headers(int sock); diff --git a/main.c b/main.c new file mode 100644 index 0000000..0747724 --- /dev/null +++ b/main.c @@ -0,0 +1,159 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(sentmethod); + sendallf(sock,"URL: %s\n",headers->url); + sendallf(sock,"Version: %s\n",headers->version); + for(int i=0;inheaders;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); + } +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..6f8253f --- /dev/null +++ b/memory.c @@ -0,0 +1,11 @@ +#include +#include "memory.h" + + +void* check_after_allocation(const char *funcname, void *ptr){ + if(ptr == NULL){ + perror(funcname); + exit(1); + } + return ptr; +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..076261d --- /dev/null +++ b/memory.h @@ -0,0 +1,13 @@ +#pragma once + +#include + + +#define malloc(num, type) \ + ((type*)check_after_allocation("malloc", malloc((num)*sizeof(type)))) +#define calloc(num, type) \ + ((type*)check_after_allocation("calloc", calloc((num), sizeof(type)))) +#define realloc(ptr, num, type) \ + ((type*)check_after_allocation("realloc", realloc(ptr, (num)*sizeof(type)))) + +void* check_after_allocation(const char *funcname, void *ptr); diff --git a/util.c b/util.c new file mode 100644 index 0000000..e8c92cc --- /dev/null +++ b/util.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include "memory.h" +#include "util.h" + + +char* copy_buf(char *buf,int len){ + char *dst=malloc(len+1,char); + memcpy(dst,buf,len); + dst[len]='\0'; + return dst; +} + +char* copy_str(char *str){ + return copy_buf(str,strlen(str)); +} + +void str_toupper(char *str){ + while(*str!='\0'){ + *str=toupper(*str); + str++; + } +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..1c99bea --- /dev/null +++ b/util.h @@ -0,0 +1,7 @@ +#pragma once + + +char* copy_buf(char *buf,int len); +char* copy_str(char *str); + +void str_toupper(char *str); -- cgit v1.2.3