Various fixes towards working internal web server.

This commit is contained in:
gardners 2012-01-03 08:57:52 +10:30
parent d29efac21f
commit 9c8ea5908c
2 changed files with 166 additions and 63 deletions

View File

@ -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);

View File

@ -117,57 +117,62 @@ int rhizome_server_poll()
for(rn=0;rn<rhizome_server_live_request_count;rn++)
{
rhizome_http_request *r=rhizome_live_http_requests[rn];
switch(r->request_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_length<RHIZOME_HTTP_REQUEST_MAXLEN)
r->request[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_length<RHIZOME_HTTP_REQUEST_MAXLEN)
r->request[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,"<html><h1>A specific file</h1></html>\r\n");
WHY("Check for range: header, and return 206 if returning partial content");
for(i=0;i<strlen(id);i++) if ((id[i]<'0')||(id[i]>'f')||(id[i]=='\'')) dud++;
if (dud) rhizome_server_simple_http_response(r,400,"<html><h1>That doesn't look like hex to me.</h1></html>\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,"<html><h1>Sorry, can't find that here.</h1></html>\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;
}