From 9c8ea5908c7287f55972bce2bc801b8aca6aa1a9 Mon Sep 17 00:00:00 2001 From: gardners Date: Tue, 3 Jan 2012 08:57:52 +1030 Subject: [PATCH] Various fixes towards working internal web server. --- rhizome.h | 24 ++++-- rhizome_http.c | 205 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 166 insertions(+), 63 deletions(-) diff --git a/rhizome.h b/rhizome.h index 725c1003..54949e94 100644 --- a/rhizome.h +++ b/rhizome.h @@ -36,12 +36,15 @@ typedef struct rhizome_http_request { /* Nature of the request */ int request_type; #define RHIZOME_HTTP_REQUEST_RECEIVING -1 -#define RHIZOME_HTTP_REQUEST_FROMBUFFER 0 -#define RHIZOME_HTTP_REQUEST_FILE 1 -#define RHIZOME_HTTP_REQUEST_SUBSCRIBEDGROUPLIST 2 -#define RHIZOME_HTTP_REQUEST_ALLGROUPLIST 3 -#define RHIZOME_HTTP_REQUEST_BUNDLESINGROUP 4 -#define RHIZOME_HTTP_REQUEST_BUNDLEMANIFEST 5 +#define RHIZOME_HTTP_REQUEST_FROMBUFFER 1 +#define RHIZOME_HTTP_REQUEST_FILE 2 +#define RHIZOME_HTTP_REQUEST_SUBSCRIBEDGROUPLIST 4 +#define RHIZOME_HTTP_REQUEST_ALLGROUPLIST 8 +#define RHIZOME_HTTP_REQUEST_BUNDLESINGROUP 16 + // manifests are small enough to send from a buffer + // #define RHIZOME_HTTP_REQUEST_BUNDLEMANIFEST 32 + // for anything too big, we can just use a blob +#define RHIZOME_HTTP_REQUEST_BLOB 64 /* Local buffer of data to be sent. If a RHIZOME_HTTP_REQUEST_FROMBUFFER, then the buffer is sent, and when empty @@ -58,6 +61,12 @@ typedef struct rhizome_http_request { unsigned char source[1024]; long long source_index; + char *blob_table; + char *blob_column; + unsigned long long blob_rowid; + /* source_index used for offset in blob */ + unsigned long long blob_end; + } rhizome_http_request; #define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32 @@ -171,3 +180,6 @@ int rhizome_server_close_http_request(int i); int rhizome_server_http_send_bytes(int rn,rhizome_http_request *r); int rhizome_server_parse_http_request(int rn,rhizome_http_request *r); int rhizome_server_simple_http_response(rhizome_http_request *r,int result, char *response); +long long sqlite_exec_int64(char *sqlformat,...); +int rhizome_server_http_response_header(rhizome_http_request *r,int result, + char *mime_type,unsigned long long bytes); diff --git a/rhizome_http.c b/rhizome_http.c index 243007c3..48449176 100644 --- a/rhizome_http.c +++ b/rhizome_http.c @@ -117,57 +117,62 @@ int rhizome_server_poll() for(rn=0;rnrequest_type) { - case RHIZOME_HTTP_REQUEST_RECEIVING: - /* Keep reading until we have two CR/LFs in a row */ - - sigPipeFlag=0; - - /* Make socket non-blocking */ - fcntl(r->socket,F_SETFL,fcntl(r->socket, F_GETFL, NULL)|O_NONBLOCK); - - errno=0; - int bytes=read(r->socket,&r->request[r->request_length], - RHIZOME_HTTP_REQUEST_MAXLEN-r->request_length-1); - - /* If we got some data, see if we have found the end of the HTTP request */ - if (bytes>0) { - int i=r->request_length-160; - int lfcount=0; - if (i<0) i=0; - r->request_length+=bytes; - if (r->request_lengthrequest[r->request_length]=0; - dump("request",(unsigned char *)r->request,r->request_length); - for(;i<(r->request_length+bytes);i++) - { - switch(r->request[i]) { - case '\n': lfcount++; break; - case '\r': /* ignore CR */ break; - case 0: /* ignore NUL (telnet inserts them) */ break; - default: lfcount=0; break; - } - if (lfcount==2) break; - } - if (lfcount==2) { - /* We have the request. Now parse it to see if we can respond to it */ - rhizome_server_parse_http_request(rn,r); - } + switch(r->request_type) + { + case RHIZOME_HTTP_REQUEST_RECEIVING: + /* Keep reading until we have two CR/LFs in a row */ + WHY("receiving http request data"); - r->request_length+=bytes; - } + sigPipeFlag=0; + + /* Make socket non-blocking */ + fcntl(r->socket,F_SETFL,fcntl(r->socket, F_GETFL, NULL)|O_NONBLOCK); + + errno=0; + int bytes=read(r->socket,&r->request[r->request_length], + RHIZOME_HTTP_REQUEST_MAXLEN-r->request_length-1); + + /* If we got some data, see if we have found the end of the HTTP request */ + if (bytes>0) { + int i=r->request_length-160; + int lfcount=0; + if (i<0) i=0; + r->request_length+=bytes; + if (r->request_lengthrequest[r->request_length]=0; + dump("request",(unsigned char *)r->request,r->request_length); + for(;i<(r->request_length+bytes);i++) + { + switch(r->request[i]) { + case '\n': lfcount++; break; + case '\r': /* ignore CR */ break; + case 0: /* ignore NUL (telnet inserts them) */ break; + default: lfcount=0; break; + } + if (lfcount==2) break; + } + if (lfcount==2) { + /* We have the request. Now parse it to see if we can respond to it */ + rhizome_server_parse_http_request(rn,r); + } + + r->request_length+=bytes; + } - /* Make socket blocking again for poll()/select() */ - fcntl(r->socket,F_SETFL,fcntl(r->socket, F_GETFL, NULL)&(~O_NONBLOCK)); - - if (sigPipeFlag||((bytes==0)&&(errno==0))) { - /* broken pipe, so close connection */ - WHY("Closing connection due to sigpipe"); - rhizome_server_close_http_request(rn); - continue; - } - - break; + /* Make socket blocking again for poll()/select() */ + fcntl(r->socket,F_SETFL,fcntl(r->socket, F_GETFL, NULL)&(~O_NONBLOCK)); + + if (sigPipeFlag||((bytes==0)&&(errno==0))) { + /* broken pipe, so close connection */ + WHY("Closing connection due to sigpipe"); + rhizome_server_close_http_request(rn); + continue; + } + break; + default: + /* Socket already has request -- so just try to send some data. */ + rhizome_server_http_send_bytes(rn,r); + break; } WHY("Processing live HTTP requests not implemented."); } @@ -209,7 +214,9 @@ int rhizome_server_close_http_request(int i) int rhizome_server_free_http_request(rhizome_http_request *r) { if (r->buffer&&r->buffer_size) free(r->buffer); - + if (r->blob_table) free(r->blob_table); + if (r->blob_column) free(r->blob_column); + free(r); return 0; } @@ -258,8 +265,36 @@ int rhizome_server_parse_http_request(int rn,rhizome_http_request *r) id)==1) { /* Stream the specified file */ + int dud=0; + int i; printf("get /rhizome/file/ [%s]\n",id); - rhizome_server_simple_http_response(r,400,"

A specific file

\r\n"); + WHY("Check for range: header, and return 206 if returning partial content"); + for(i=0;i'f')||(id[i]=='\'')) dud++; + if (dud) rhizome_server_simple_http_response(r,400,"

That doesn't look like hex to me.

\r\n"); + else { + unsigned long long rowid = sqlite_exec_int64("select rowid from files where id='%s';",id); + sqlite3_blob *blob; + if (rowid>=0) + if (sqlite3_blob_open(rhizome_db,"main","files","id",rowid,0,&blob) + !=SQLITE_OK) + rowid=-1; + + if (rowid<0) { + rhizome_server_simple_http_response(r,404,"

Sorry, can't find that here.

\r\n"); + WHY("File not found / blob not opened"); + } + else { + r->blob_table=strdup("files"); + r->blob_column=strdup("id"); + r->blob_rowid=rowid; + r->source_index=0; + r->blob_end=sqlite3_blob_bytes(blob); + rhizome_server_http_response_header(r,200,"application/binary", + r->blob_end-r->source_index); + sqlite3_blob_close(blob); + WHY("opened blob and file"); + } + } } else if (sscanf("GET /rhizome/manifest/%[0-9a-f] HTTP/1.",r->request, id)==1) @@ -279,11 +314,28 @@ int rhizome_server_parse_http_request(int rn,rhizome_http_request *r) return 0; } + +/* Return appropriate message for HTTP response codes, both known and unknown. */ +#define A_VALUE_GREATER_THAN_FOUR (2+3) +char *httpResultString(int id) { + switch (id) { + case 200: return "OK"; break; + case 206: return "Partial Content"; break; + case 404: return "Not found"; break; + default: + case A_VALUE_GREATER_THAN_FOUR: + if (id>4) return "A suffusion of yellow"; + /* The following MUST be the longest string returned by this function */ + else return "THE JUDGEMENT OF KING WEN: Chun Signifies Difficulties At Outset, As Of Blade Of Grass Pushing Up Against Stone."; + } +} + int rhizome_server_simple_http_response(rhizome_http_request *r,int result, char *response) { - r->buffer_size=strlen(response)+strlen("HTTP/1.0 XXX Foo\r\n\r\n")+100; + r->buffer_size=strlen(response)+strlen("HTTP/1.0 000 \r\n\r\n")+strlen(httpResultString(A_VALUE_GREATER_THAN_FOUR))+100; + r->buffer=(unsigned char *)malloc(r->buffer_size); - snprintf((char *)r->buffer,r->buffer_size,"HTTP/1.0 %03d FooContent-type: text/html\r\nContent-length: %d\r\n\r\n%s",result,(int)strlen(response),response); + snprintf((char *)r->buffer,r->buffer_size,"HTTP/1.0 %03d %s\r\nContent-type: text/html\r\nContent-length: %d\r\n\r\n%s",result,httpResultString(result),(int)strlen(response),response); r->buffer_size=strlen((char *)r->buffer)+1; r->buffer_length=r->buffer_size-1; @@ -293,24 +345,47 @@ int rhizome_server_simple_http_response(rhizome_http_request *r,int result, char return 0; } +/* + return codes: + 1: connection still open. + 0: connection finished. + <0: an error occurred. +*/ int rhizome_server_http_send_bytes(int rn,rhizome_http_request *r) { int bytes; fcntl(r->socket,F_SETFL,fcntl(r->socket, F_GETFL, NULL)|O_NONBLOCK); - switch(r->request_type) + if (debug>1) fprintf(stderr,"Request #%d, type=0x%x\n",rn,r->request_type); + + /* Flush anything out of the buffer if present, before doing any further + processing */ + if (r->request_type&RHIZOME_HTTP_REQUEST_FROMBUFFER) { - case RHIZOME_HTTP_REQUEST_FROMBUFFER: bytes=r->buffer_length-r->buffer_offset; bytes=write(r->socket,&r->buffer[r->buffer_offset],bytes); if (bytes>0) { r->buffer_offset+=bytes; if (r->buffer_offset>=r->buffer_length) { /* Our work is done. close socket and go home */ - WHY("Finished sending data"); - return rhizome_server_close_http_request(rn); + r->request_type&=~RHIZOME_HTTP_REQUEST_FROMBUFFER; + if (!r->request_type) { + WHY("Finished sending data"); + return rhizome_server_close_http_request(rn); + } + } else { + /* Still more stuff in the buffer, so return now */ + return 1; } } + } + + switch(r->request_type) + { + case RHIZOME_HTTP_REQUEST_FROMBUFFER: + /* This really shouldn't happen! */ + + return WHY("Something impossible happened."); break; default: WHY("sending data from this type of HTTP request not implemented"); @@ -318,5 +393,21 @@ int rhizome_server_http_send_bytes(int rn,rhizome_http_request *r) } fcntl(r->socket,F_SETFL,fcntl(r->socket, F_GETFL, NULL)&(~O_NONBLOCK)); + return 1; +} + +int rhizome_server_http_response_header(rhizome_http_request *r,int result, + char *mime_type,unsigned long long bytes) +{ + r->buffer_size=bytes+strlen("HTTP/1.0 000 \r\n\r\n")+strlen(httpResultString(A_VALUE_GREATER_THAN_FOUR))+100; + r->buffer=(unsigned char *)malloc(r->buffer_size); + snprintf((char *)r->buffer,r->buffer_size,"HTTP/1.0 %03d \r\nContent-type: text/html\r\nContent-length: %lld\r\n\r\n",result,bytes); + + r->buffer_size=strlen((char *)r->buffer)+1; + r->buffer_length=r->buffer_size-1; + r->buffer_offset=0; + + r->request_type|=RHIZOME_HTTP_REQUEST_FROMBUFFER; return 0; } +