diff options
Diffstat (limited to 'http.c')
-rw-r--r-- | http.c | 183 |
1 files changed, 183 insertions, 0 deletions
@@ -0,0 +1,183 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <ctype.h> +#include <sys/socket.h> +#include "http.h" +#include "memory.h" +#include "util.h" + + +static void free_list(char **list,int nitems){ + for(int i=0;i<nitems;i++){ + free(list[i]); + } + free(list); +} + +// -2: error; -1: socket closed; >=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;i<headers->nheaders;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;i<nlines;i++){ + char *colonp=strchr(lines[i],':'); + if(colonp==NULL){ + free_list(lines,nlines); + free_headers(headers); + return NULL; + } + char *valuep=colonp+1; + while(isspace(*valuep)){ + valuep++; + } + + if(headers->nheaders+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; +} |