summaryrefslogtreecommitdiff
path: root/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'http.c')
-rw-r--r--http.c183
1 files changed, 183 insertions, 0 deletions
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 <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;
+}