From dc071c74a501c38468a02f0e1a816ff49dac7e8f Mon Sep 17 00:00:00 2001 From: Per Hedbor <ph@opera.com> Date: Sun, 14 Nov 1999 01:47:21 +0100 Subject: [PATCH] Initial version in pike Rev: src/modules/CommonLog/.cvsignore:1.1 Rev: src/modules/CommonLog/Makefile.in:1.1 Rev: src/modules/CommonLog/acconfig.h:1.1 Rev: src/modules/CommonLog/clf.c:1.1 Rev: src/modules/CommonLog/configure.in:1.1 Rev: src/modules/CommonLog/testsuite.in:1.1 Rev: src/modules/HTTPLoop/.cvsignore:1.1 Rev: src/modules/HTTPLoop/Makefile.in:1.1 Rev: src/modules/HTTPLoop/accept_and_parse.c:1.1 Rev: src/modules/HTTPLoop/accept_and_parse.h:1.1 Rev: src/modules/HTTPLoop/acconfig.h:1.1 Rev: src/modules/HTTPLoop/cache.c:1.1 Rev: src/modules/HTTPLoop/cache.h:1.1 Rev: src/modules/HTTPLoop/configure.in:1.1 Rev: src/modules/HTTPLoop/extensions:1.1 Rev: src/modules/HTTPLoop/filesystem.c:1.1 Rev: src/modules/HTTPLoop/filesystem.h:1.1 Rev: src/modules/HTTPLoop/log.c:1.1 Rev: src/modules/HTTPLoop/log.h:1.1 Rev: src/modules/HTTPLoop/requestobject.c:1.1 Rev: src/modules/HTTPLoop/requestobject.h:1.1 Rev: src/modules/HTTPLoop/static_strings.h:1.1 Rev: src/modules/HTTPLoop/test.pike:1.1 Rev: src/modules/HTTPLoop/testsuite.in:1.1 Rev: src/modules/HTTPLoop/timeout.c:1.1 Rev: src/modules/HTTPLoop/timeout.h:1.1 Rev: src/modules/HTTPLoop/util.c:1.1 Rev: src/modules/HTTPLoop/util.h:1.1 Rev: src/modules/HTTPLoop/wwwserver.pike:1.1 --- .gitattributes | 10 + src/modules/CommonLog/.cvsignore | 4 + src/modules/CommonLog/.gitignore | 4 + src/modules/CommonLog/Makefile.in | 17 + src/modules/CommonLog/acconfig.h | 11 + src/modules/CommonLog/clf.c | 726 +++++++++++++++ src/modules/CommonLog/configure.in | 13 + src/modules/CommonLog/testsuite.in | 0 src/modules/HTTPLoop/.cvsignore | 4 + src/modules/HTTPLoop/.gitignore | 4 + src/modules/HTTPLoop/Makefile.in | 22 + src/modules/HTTPLoop/accept_and_parse.c | 787 ++++++++++++++++ src/modules/HTTPLoop/accept_and_parse.h | 158 ++++ src/modules/HTTPLoop/acconfig.h | 9 + src/modules/HTTPLoop/cache.c | 215 +++++ src/modules/HTTPLoop/cache.h | 19 + src/modules/HTTPLoop/configure.in | 143 +++ src/modules/HTTPLoop/extensions | 219 +++++ src/modules/HTTPLoop/filesystem.c | 81 ++ src/modules/HTTPLoop/filesystem.h | 7 + src/modules/HTTPLoop/log.c | 237 +++++ src/modules/HTTPLoop/log.h | 9 + src/modules/HTTPLoop/requestobject.c | 1092 +++++++++++++++++++++++ src/modules/HTTPLoop/requestobject.h | 13 + src/modules/HTTPLoop/static_strings.h | 42 + src/modules/HTTPLoop/test.pike | 100 +++ src/modules/HTTPLoop/testsuite.in | 1 + src/modules/HTTPLoop/timeout.c | 263 ++++++ src/modules/HTTPLoop/timeout.h | 8 + src/modules/HTTPLoop/util.c | 105 +++ src/modules/HTTPLoop/util.h | 10 + src/modules/HTTPLoop/wwwserver.pike | 194 ++++ 32 files changed, 4527 insertions(+) create mode 100644 src/modules/CommonLog/.cvsignore create mode 100644 src/modules/CommonLog/.gitignore create mode 100644 src/modules/CommonLog/Makefile.in create mode 100644 src/modules/CommonLog/acconfig.h create mode 100644 src/modules/CommonLog/clf.c create mode 100755 src/modules/CommonLog/configure.in create mode 100644 src/modules/CommonLog/testsuite.in create mode 100644 src/modules/HTTPLoop/.cvsignore create mode 100644 src/modules/HTTPLoop/.gitignore create mode 100644 src/modules/HTTPLoop/Makefile.in create mode 100644 src/modules/HTTPLoop/accept_and_parse.c create mode 100644 src/modules/HTTPLoop/accept_and_parse.h create mode 100644 src/modules/HTTPLoop/acconfig.h create mode 100644 src/modules/HTTPLoop/cache.c create mode 100644 src/modules/HTTPLoop/cache.h create mode 100644 src/modules/HTTPLoop/configure.in create mode 100644 src/modules/HTTPLoop/extensions create mode 100644 src/modules/HTTPLoop/filesystem.c create mode 100644 src/modules/HTTPLoop/filesystem.h create mode 100644 src/modules/HTTPLoop/log.c create mode 100644 src/modules/HTTPLoop/log.h create mode 100644 src/modules/HTTPLoop/requestobject.c create mode 100644 src/modules/HTTPLoop/requestobject.h create mode 100644 src/modules/HTTPLoop/static_strings.h create mode 100644 src/modules/HTTPLoop/test.pike create mode 100644 src/modules/HTTPLoop/testsuite.in create mode 100644 src/modules/HTTPLoop/timeout.c create mode 100644 src/modules/HTTPLoop/timeout.h create mode 100644 src/modules/HTTPLoop/util.c create mode 100644 src/modules/HTTPLoop/util.h create mode 100644 src/modules/HTTPLoop/wwwserver.pike diff --git a/.gitattributes b/.gitattributes index 1ced9d96be..5c45faa2aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -177,6 +177,10 @@ testfont binary /src/module.h foreign_ident /src/module_support.c foreign_ident /src/module_support.h foreign_ident +/src/modules/CommonLog/Makefile.in foreign_ident +/src/modules/CommonLog/acconfig.h foreign_ident +/src/modules/CommonLog/clf.c foreign_ident +/src/modules/CommonLog/configure.in foreign_ident /src/modules/Gdbm/Makefile.in foreign_ident /src/modules/Gdbm/acconfig.h foreign_ident /src/modules/Gdbm/configure.in foreign_ident @@ -197,6 +201,12 @@ testfont binary /src/modules/Gz/gz_test.c foreign_ident /src/modules/Gz/testsuite.in foreign_ident /src/modules/Gz/zlibmod.c foreign_ident +/src/modules/HTTPLoop/Makefile.in foreign_ident +/src/modules/HTTPLoop/acconfig.h foreign_ident +/src/modules/HTTPLoop/configure.in foreign_ident +/src/modules/HTTPLoop/extensions foreign_ident +/src/modules/HTTPLoop/requestobject.c foreign_ident +/src/modules/HTTPLoop/testsuite.in foreign_ident /src/modules/Image/Makefile foreign_ident /src/modules/Image/Makefile.in foreign_ident /src/modules/Image/acconfig.h foreign_ident diff --git a/src/modules/CommonLog/.cvsignore b/src/modules/CommonLog/.cvsignore new file mode 100644 index 0000000000..5af582f193 --- /dev/null +++ b/src/modules/CommonLog/.cvsignore @@ -0,0 +1,4 @@ +configure +clf_machine.h.in +dependencies +stamp-h.in diff --git a/src/modules/CommonLog/.gitignore b/src/modules/CommonLog/.gitignore new file mode 100644 index 0000000000..d4eea05f4a --- /dev/null +++ b/src/modules/CommonLog/.gitignore @@ -0,0 +1,4 @@ +/configure +/clf_machine.h.in +/dependencies +/stamp-h.in diff --git a/src/modules/CommonLog/Makefile.in b/src/modules/CommonLog/Makefile.in new file mode 100644 index 0000000000..f28118a8e9 --- /dev/null +++ b/src/modules/CommonLog/Makefile.in @@ -0,0 +1,17 @@ +# +# $Id: Makefile.in,v 1.1 1999/11/14 00:47:21 per Exp $ +# +# Makefile for CommonLog +# + +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@ +OBJS=clf.o +MODULE_LDFLAGS=@LDFLAGS@ + +CONFIG_HEADERS=@CONFIG_HEADERS@ + +@dynamic_module_makefile@ +@dependencies@ + diff --git a/src/modules/CommonLog/acconfig.h b/src/modules/CommonLog/acconfig.h new file mode 100644 index 0000000000..f279ca45ce --- /dev/null +++ b/src/modules/CommonLog/acconfig.h @@ -0,0 +1,11 @@ +/* + * $Id: acconfig.h,v 1.1 1999/11/14 00:47:21 per Exp $ + */ + +#ifndef COMMONLOG_MACHINE_H +#define COMMONLOG_MACHINE_H + +@TOP@ +@BOTTOM@ + +#endif /* COMMONLOG_MACHINE_H */ diff --git a/src/modules/CommonLog/clf.c b/src/modules/CommonLog/clf.c new file mode 100644 index 0000000000..3b0d0c54c7 --- /dev/null +++ b/src/modules/CommonLog/clf.c @@ -0,0 +1,726 @@ + +#include "global.h" +RCSID("$Id: clf.c,v 1.1 1999/11/14 00:47:21 per Exp $"); +#include "fdlib.h" +#include "stralloc.h" +#include "pike_macros.h" +#include "object.h" +#include "program.h" +#include "interpret.h" +#include "builtin_functions.h" +#include "module_support.h" +#include "error.h" + +#include "threads.h" +#include <stdio.h> +#include <fcntl.h> + +/** Forward declarations of functions implementing Pike functions **/ + +static void f_read_clf( INT32 args ); + +extern int fd_from_object(struct object *o); + + +/** Global tables **/ + +#ifndef CHAR_BIT +#define CHAR_BIT 9 /* Should be safe for most hardware */ +#endif /* !CHAR_BIT */ + +static char char_class[1<<CHAR_BIT]; + +#define CLS_WSPACE 0 +#define CLS_CRLF 1 +#define CLS_TOKEN 2 +#define CLS_DIGIT 3 +#define CLS_QUOTE 4 +#define CLS_LBRACK 5 +#define CLS_RBRACK 6 +#define CLS_SLASH 7 +#define CLS_COLON 8 +#define CLS_HYPHEN 9 +#define CLS_MINUS 9 +#define CLS_PLUS 10 + + +/* How much to read each round in the loop. */ +#define CLF_BLOCK_SIZE 4096 + +/** Externally available functions **/ + +/* Initialize and start module */ + +void pike_module_init( void ) +{ + int i; + + MEMSET(char_class, CLS_TOKEN, sizeof(char_class)); + for(i='\0'; i<=' '; i++) + char_class[i] = CLS_WSPACE; + for(i='0'; i<='9'; i++) + char_class[i] = CLS_DIGIT; + char_class['\n'] = CLS_CRLF; + char_class['\r'] = CLS_CRLF; + char_class['"'] = CLS_QUOTE; + char_class['['] = CLS_LBRACK; + char_class[']'] = CLS_RBRACK; + char_class['/'] = CLS_SLASH; + char_class[':'] = CLS_COLON; + char_class['-'] = CLS_HYPHEN; + char_class['+'] = CLS_PLUS; + + add_function_constant( "read", f_read_clf, + "function(function(array(string|int),int|void:void)," + "string|object,int|void:int)", 0 ); +} + + +/* Restore and exit module */ + +void pike_module_exit( void ) +{ +} + + +/** Functions implementing Pike functions **/ + +/* CommonLog.read() */ + +static void f_read_clf( INT32 args ) +{ + char *read_buf; + struct svalue *logfun, *file; + FD f = -1; + int cls, c, my_fd=1, state=0, tzs=0; + char *char_pointer; + INT32 v=0, yy=0, mm=0, dd=0, h=0, m=0, s=0, tz=0, offs0=0, len=0; + struct svalue *old_sp; + /* #define DYNAMIC_BUF */ +#ifdef DYNAMIC_BUF + dynamic_buffer buf; +#else +#define BUFSET(X) do { if(bufpos == bufsize) { bufsize *= 2; buf = realloc(buf, bufsize+1); } buf[bufpos++] = c; } while(0) +#define PUSHBUF() do { push_string( make_shared_binary_string( buf,bufpos ) ); bufpos=0; } while(0) + char *buf; + int bufsize=CLF_BLOCK_SIZE, bufpos=0; +#endif + + ONERROR unwind_protect; + + if(args>2 && sp[-1].type == T_INT) { + offs0 = sp[-1].u.integer; + pop_n_elems(1); + --args; + } + old_sp = sp; + + get_all_args("CommonLog.read", args, "%*%*", &logfun, &file); + if(logfun->type != T_FUNCTION) + error("Bad argument 1 to CommonLog.read, expected function.\n"); + + if(file->type == T_OBJECT) + { + f = fd_from_object(file->u.object); + + if(f == -1) + error("CommonLog.read: File is not open.\n"); + my_fd = 0; + } else if(file->type == T_STRING && + file->u.string->size_shift == 0) { + THREADS_ALLOW(); + do { + f=fd_open(STR0(file->u.string), fd_RDONLY, 0); + } while(f < 0 && errno == EINTR); + THREADS_DISALLOW(); + + if(errno < 0) + error("CommonLog.read(): Failed to open file for reading (errno=%d).\n", + errno); + } else + error("Bad argument 1 to CommonLog.read, expected string or object .\n"); + +#ifdef HAVE_LSEEK64 + lseek64(f, offs0, SEEK_SET); +#else + fd_lseek(f, offs0, SEEK_SET); +#endif + read_buf = malloc(CLF_BLOCK_SIZE+1); +#ifndef DYNAMIC_BUF + buf = malloc(bufsize); +#endif + while(1) { + THREADS_ALLOW(); + do { + len = fd_read(f, read_buf, CLF_BLOCK_SIZE); + } while(len < 0 && errno == EINTR); + THREADS_DISALLOW(); + if(len == 0) + break; /* nothing more to read. */ + if(len < 0) + break; + char_pointer = read_buf; + while(len--) { + offs0++; + c = char_pointer[0]; + char_pointer ++; + cls = char_class[c]; +#ifdef TRACE_DFA + fprintf(stderr, "DFA(%d): '%c' ", state, (c<32? '.':c)); + switch(cls) { + case CLS_WSPACE: fprintf(stderr, "CLS_WSPACE"); break; + case CLS_CRLF: fprintf(stderr, "CLS_CRLF"); break; + case CLS_TOKEN: fprintf(stderr, "CLS_TOKEN"); break; + case CLS_DIGIT: fprintf(stderr, "CLS_DIGIT"); break; + case CLS_QUOTE: fprintf(stderr, "CLS_QUOTE"); break; + case CLS_LBRACK: fprintf(stderr, "CLS_LBRACK"); break; + case CLS_RBRACK: fprintf(stderr, "CLS_RBRACK"); break; + case CLS_SLASH: fprintf(stderr, "CLS_SLASH"); break; + case CLS_COLON: fprintf(stderr, "CLS_COLON"); break; + case CLS_HYPHEN: fprintf(stderr, "CLS_HYPHEN/CLS_MINUS"); break; + case CLS_PLUS: fprintf(stderr, "CLS_PLUS"); break; + default: fprintf(stderr, "???"); + } + fprintf(stderr, " %d items on stack\n", sp-old_sp); +#endif + switch(state) { + case 0: + if(sp != old_sp) { + if(sp == old_sp+15) { + f_aggregate(15); + push_int(offs0); + apply_svalue(logfun, 2); + pop_stack(); + } else + pop_n_elems(sp-old_sp); + } + if(cls > CLS_CRLF) { + if(cls == CLS_HYPHEN) { + push_int(0); + state = 2; + break; + } +#ifdef DYNAMIC_BUF + buf.s.str = NULL; + initialize_buf( &buf ); + low_my_putchar( c, &buf ); +#else + bufpos = 0; + BUFSET(c); +#endif + state=1; + } + break; + case 1: + if(cls > CLS_CRLF) { +#ifdef DYNAMIC_BUF + low_my_putchar( c, &buf ); +#else + BUFSET(c); +#endif + break; + } +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); /* remotehost */ +#else + PUSHBUF(); +#endif + state = (cls == CLS_WSPACE? 2:0); + break; + case 2: + if(cls > CLS_CRLF) { + if(cls == CLS_HYPHEN) { + push_int(0); + state = 4; + break; + } +#ifdef DYNAMIC_BUF + buf.s.str = NULL; + initialize_buf( &buf ); + low_my_putchar( c, &buf ); +#else + bufpos = 0; + BUFSET(c); +#endif + + state=3; + } else if(cls == CLS_CRLF) + state=0; + break; + case 3: + if(cls > CLS_CRLF) { +#ifdef DYNAMIC_BUF + low_my_putchar( c, &buf ); +#else + BUFSET(c); +#endif + + break; + } +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); /* rfc931 */ +#else + PUSHBUF(); /* rfc931 */ +#endif + state = (cls == CLS_WSPACE? 4:0); + break; + case 4: + if(cls > CLS_CRLF) { + if(cls == CLS_HYPHEN) { + push_int(0); + state = 6; + break; + } +#ifdef DYNAMIC_BUF + buf.s.str = NULL; + initialize_buf( &buf ); + low_my_putchar( c, &buf ); +#else + bufpos = 0; + BUFSET(c); +#endif + + state=5; + } else if(cls == CLS_CRLF) + state=0; + break; + case 5: + if(cls > CLS_CRLF) { +#ifdef DYNAMIC_BUF + low_my_putchar( c, &buf ); +#else + BUFSET(c); +#endif + + break; + } +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); /* authuser */ +#else + PUSHBUF(); /* authuser */ +#endif + state = (cls == CLS_WSPACE? 6:0); + break; + case 6: + if(cls == CLS_LBRACK) + state = 15; + else if(cls == CLS_CRLF) + state = 0; + else if(cls == CLS_HYPHEN) { + push_int(0); + push_int(0); + push_int(0); + state = 7; + } + break; + case 7: + if(cls == CLS_QUOTE) { +#ifdef DYNAMIC_BUF + buf.s.str = NULL; + initialize_buf( &buf ); +#else + bufpos = 0; +#endif + state = 31; + } else if(cls == CLS_CRLF) + state = 0; + else if(cls == CLS_HYPHEN) { + push_int(0); + push_int(0); + push_int(0); + state = 10; + } + break; + case 8: + if(cls == CLS_QUOTE) + state = 9; + else if(cls == CLS_CRLF) { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); +#else + PUSHBUF(); +#endif + state = 0; + } else +#ifdef DYNAMIC_BUF + low_my_putchar( c, &buf ); +#else + BUFSET(c); +#endif + + break; + case 9: + if(cls > CLS_CRLF) { +#ifdef DYNAMIC_BUF + low_my_putchar( '"', &buf); + low_my_putchar( c, &buf); +#else + BUFSET('"'); + BUFSET(c); +#endif + state = 8; + break; + } +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); /* protocol */ +#else + PUSHBUF(); /* protoocl */ +#endif + state = (cls == CLS_CRLF? 0 : 10); + break; + case 10: + if(cls == CLS_DIGIT) { + v = c&0xf; + state = 11; + } else if(cls == CLS_CRLF) + state = 0; + else if(cls == CLS_HYPHEN) { + push_int(0); + state = 12; + } + break; + case 11: + if(cls == CLS_DIGIT) + v = v*10+(c&0xf); + else if(cls == CLS_WSPACE) { + push_int(v); /* status */ + state = 12; + } else state = 0; + break; + case 12: + if(cls == CLS_DIGIT) { + v = c&0xf; + state = 13; + } else if(cls == CLS_CRLF) + state = 0; + else if(cls == CLS_HYPHEN) { + push_int(0); + state = 14; + } + break; + case 13: + if(cls == CLS_DIGIT) + v = v*10+(c&0xf); + else { + push_int(v); /* bytes */ + state = (cls == CLS_CRLF? 0:14); + } + break; + case 14: + if(cls == CLS_CRLF) + state = 0; + break; + + case 15: + if(cls == CLS_DIGIT) { + dd = c&0xf; + state = 16; + } else + state = (cls == CLS_CRLF? 0:14); + break; + case 16: + /* getting day */ + if(cls == CLS_DIGIT) + dd = dd*10+(c&0xf); + else if(cls == CLS_SLASH) + state = 17; + else + state = (cls == CLS_CRLF? 0:14); + break; + case 17: + if(cls == CLS_DIGIT) { + mm = c&0xf; + state = 18; + } else if(cls == CLS_TOKEN) { + mm = c|0x20; + state = 21; + } else + state = (cls == CLS_CRLF? 0:14); + break; + case 18: + /* getting numeric month */ + if(cls == CLS_DIGIT) + mm = mm*10+(c&0xf); + else if(cls == CLS_SLASH) + state = 19; + else + state = (cls == CLS_CRLF? 0:14); + break; + case 19: + if(cls == CLS_DIGIT) { + yy = c&0xf; + state = 20; + } else + state = (cls == CLS_CRLF? 0:14); + break; + case 20: + /* getting year */ + if(cls == CLS_DIGIT) + yy = yy*10+(c&0xf); + else if(cls == CLS_COLON) + state = 22; + else + state = (cls == CLS_CRLF? 0:14); + break; + case 21: + /* getting textual month */ + if(cls == CLS_TOKEN) + mm = (mm<<8)|c|0x20; + else if(cls == CLS_SLASH) { + state = 19; + switch(mm) { + case ('j'<<16)|('a'<<8)|'n': mm=1; break; + case ('f'<<16)|('e'<<8)|'b': mm=2; break; + case ('m'<<16)|('a'<<8)|'r': mm=3; break; + case ('a'<<16)|('p'<<8)|'r': mm=4; break; + case ('m'<<16)|('a'<<8)|'y': mm=5; break; + case ('j'<<16)|('u'<<8)|'n': mm=6; break; + case ('j'<<16)|('u'<<8)|'l': mm=7; break; + case ('a'<<16)|('u'<<8)|'g': mm=8; break; + case ('s'<<16)|('e'<<8)|'p': mm=9; break; + case ('o'<<16)|('c'<<8)|'t': mm=10; break; + case ('n'<<16)|('o'<<8)|'v': mm=11; break; + case ('d'<<16)|('e'<<8)|'c': mm=12; break; + default: + state = 14; + } + } + break; + case 22: + if(cls == CLS_DIGIT) { + h = c&0xf; + state = 23; + } else + state = (cls == CLS_CRLF? 0:14); + break; + case 23: + /* getting hour */ + if(cls == CLS_DIGIT) + h = h*10+(c&0xf); + else if(cls == CLS_COLON) + state = 24; + else + state = (cls == CLS_CRLF? 0:14); + break; + case 24: + if(cls == CLS_DIGIT) { + m = c&0xf; + state = 25; + } else + state = (cls == CLS_CRLF? 0:14); + break; + case 25: + /* getting minute */ + if(cls == CLS_DIGIT) + m = m*10+(c&0xf); + else if(cls == CLS_COLON) + state = 26; + else + state = (cls == CLS_CRLF? 0:14); + break; + case 26: + if(cls == CLS_DIGIT) { + s = c&0xf; + state = 27; + } else + state = (cls == CLS_CRLF? 0:14); + break; + case 27: + /* getting second */ + if(cls == CLS_DIGIT) + s = s*10+(c&0xf); + else if(cls == CLS_WSPACE) + state = 28; + else + state = (cls == CLS_CRLF? 0:14); + break; + case 28: + if(cls>=CLS_MINUS) { + state = 29; + tzs = cls!=CLS_PLUS; + tz = 0; + } else if(cls == CLS_DIGIT) { + state = 29; + tzs = 0; + tz = c&0xf; + } else if(cls==CLS_CRLF) + state = 0; + break; + case 29: + /* getting timezone */ + if(cls == CLS_DIGIT) + tz = tz*10+(c&0xf); + else { + if(tzs) + tz = -tz; + push_int(yy); + push_int(mm); + push_int(dd); + push_int(h); + push_int(m); + push_int(s); + push_int(tz); + if(cls == CLS_RBRACK) + state = 7; + else + state = (cls == CLS_CRLF? 0 : 30); + } + break; + case 30: + if(cls == CLS_RBRACK) + state = 7; + else if(cls == CLS_CRLF) + state = 0; + break; + case 31: + if(cls == CLS_QUOTE) { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); +#else + PUSHBUF(); +#endif + push_int(0); + push_int(0); + state = 10; + } else if(cls >= CLS_TOKEN) +#ifdef DYNAMIC_BUF + low_my_putchar( c, &buf ); +#else + BUFSET(c); +#endif + + else { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); /* method */ +#else + PUSHBUF(); /* method */ +#endif + state = (cls == CLS_CRLF? 0 : 32); + } + break; + case 32: + if(cls == CLS_QUOTE) { + push_int(0); + push_int(0); + state = 10; + } else if(cls >= CLS_TOKEN) { +#ifdef DYNAMIC_BUF + buf.s.str = NULL; + initialize_buf( &buf ); + low_my_putchar( c, &buf ); +#else + bufpos = 0; + BUFSET(c); +#endif + + state = 33; + } else + if(cls == CLS_CRLF) + state = 0; + break; + case 33: + if(cls == CLS_QUOTE) + state = 34; + else if(cls == CLS_CRLF) { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); +#else + PUSHBUF(); +#endif + state = 0; + } else if(cls == CLS_WSPACE) { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); /* path */ +#else + PUSHBUF(); /* path */ +#endif + state = 35; + } else +#ifdef DYNAMIC_BUF + low_my_putchar( c, &buf ); +#else + BUFSET(c); +#endif + + break; + case 34: + if(cls >= CLS_TOKEN) { +#ifdef DYNAMIC_BUF + low_my_putchar( '"', &buf ); + low_my_putchar( c, &buf ); +#else + BUFSET('"'); + BUFSET(c); +#endif + + state = 33; + } else if(cls == CLS_CRLF) { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); +#else + PUSHBUF(); +#endif + state = 0; + } else { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); +#else + PUSHBUF(); +#endif + push_text("HTTP/0.9"); + state = 10; + } + break; + case 35: + if(cls == CLS_QUOTE) { + push_text("HTTP/0.9"); + state = 10; + } else if(cls >= CLS_TOKEN) { +#ifdef DYNAMIC_BUF + buf.s.str = NULL; + initialize_buf( &buf ); + low_my_putchar( c, &buf ); +#else + bufpos = 0; + BUFSET(c); +#endif + + state = 8; + } else + if(cls == CLS_CRLF) + state = 0; + break; + } + } + } + if(state == 1 || state == 3 || state == 5 || + state == 8 || state == 9 || + state == 31 || state == 33 || state == 34) { +#ifdef DYNAMIC_BUF + push_string( low_free_buf( &buf ) ); +#else + PUSHBUF(); +#endif + } + if(sp == old_sp + 15) { + f_aggregate(15); + push_int(offs0); + apply_svalue(logfun, 2); + pop_stack(); + } + free(read_buf); +#ifndef DYNAMIC_BUF + free(buf); +#endif + if(my_fd) + /* If my_fd == 0, the second argument was an object and thus we don't + * want to free it. + */ + fd_close(f); + pop_n_elems(sp-old_sp+args); + push_int(offs0); +} + + + + diff --git a/src/modules/CommonLog/configure.in b/src/modules/CommonLog/configure.in new file mode 100755 index 0000000000..d55780712d --- /dev/null +++ b/src/modules/CommonLog/configure.in @@ -0,0 +1,13 @@ +# +# $Id: configure.in,v 1.1 1999/11/14 00:47:21 per Exp $ +# +# configure.in for CommonLog. +# + +AC_REVISION("$Id: configure.in,v 1.1 1999/11/14 00:47:21 per Exp $") +AC_INIT(clf.c) +AC_CONFIG_HEADER(clf_machine.h) + +AC_MODULE_INIT() + +AC_OUTPUT(Makefile, [echo foo >stamp-h]) diff --git a/src/modules/CommonLog/testsuite.in b/src/modules/CommonLog/testsuite.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/modules/HTTPLoop/.cvsignore b/src/modules/HTTPLoop/.cvsignore new file mode 100644 index 0000000000..78360801d6 --- /dev/null +++ b/src/modules/HTTPLoop/.cvsignore @@ -0,0 +1,4 @@ +configure +dependencies +config.h.in +stamp-h.in diff --git a/src/modules/HTTPLoop/.gitignore b/src/modules/HTTPLoop/.gitignore new file mode 100644 index 0000000000..8a856a6add --- /dev/null +++ b/src/modules/HTTPLoop/.gitignore @@ -0,0 +1,4 @@ +/configure +/dependencies +/config.h.in +/stamp-h.in diff --git a/src/modules/HTTPLoop/Makefile.in b/src/modules/HTTPLoop/Makefile.in new file mode 100644 index 0000000000..6837036bc8 --- /dev/null +++ b/src/modules/HTTPLoop/Makefile.in @@ -0,0 +1,22 @@ +# $Id: Makefile.in,v 1.1 1999/11/14 00:43:14 per Exp $ +SRCDIR=@srcdir@ +VPATH=@srcdir@:@srcdir@/../..:../.. +OBJS=accept_and_parse.o util.o cache.o requestobject.o log.o timeout.o +# filesystem.o +MODNAME=HTTPAccept +MODULE_LDFLAGS=@LDFLAGS@ + +CONFIG_HEADERS=@CONFIG_HEADERS@ + +@SET_MAKE@ + +@dynamic_module_makefile@ + +pike: all + cd ../..; $(MAKE) "AR=$(AR)" + +pure: all + cd ../..; $(MAKE) "AR=$(AR)" pure + + +@dependencies@ diff --git a/src/modules/HTTPLoop/accept_and_parse.c b/src/modules/HTTPLoop/accept_and_parse.c new file mode 100644 index 0000000000..6a3a4634b3 --- /dev/null +++ b/src/modules/HTTPLoop/accept_and_parse.c @@ -0,0 +1,787 @@ +/* Hohum. Here we go. This is try number four for a more optimized + * Roxen. + */ + +#include "config.h" +/* #define AAP_DEBUG */ + +#define PARSE_FAILED ("HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/html\r\n\r\nRequest parsing failed.\r\n") + +#include <global.h> + +#include <array.h> +#include <backend.h> +#include <machine.h> +#include <mapping.h> +#include <module_support.h> +#include <multiset.h> +#include <object.h> +#include <operators.h> +#include <pike_memory.h> +#include <fdlib.h> +#include <program.h> +#include <stralloc.h> +#include <svalue.h> +#include <threads.h> + +#ifdef _REENTRANT +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "accept_and_parse.h" +#include "log.h" +#include "cache.h" +#include "requestobject.h" +#include "util.h" +#include "timeout.h" + +static struct callback *my_callback; +struct program *request_program; +struct program *c_request_program; +struct program *accept_loop_program; + + +/* Define external global string variables ... */ +#define STRING(X,Y) extern struct pike_string *X +#include "static_strings.h" +#undef STRING +/* there.. */ + +void f_aggregate(INT32); + +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a)<(b)?(b):(a)) +#endif + +#define MAXLEN (1024*1024*10) +static MUTEX_T queue_mutex; +static struct args *request, *last; + +/* This should probably be improved to include the reason for the + * failure. Currently, all failed requests get the same (hardcoded) + * error message. + * + * It is virtually impossible to call a pike function from here, so that + * is not an option. + */ +static void failed(struct args *arg) +{ + WRITE(arg->fd, PARSE_FAILED, strlen(PARSE_FAILED)); +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: Failed\n"); +#endif /* AAP_DEBUG */ + close(arg->fd); + if(arg->res.data) + free(arg->res.data); + free(arg); +} + + +/* Parse a request. This function is somewhat obscure for performance + * reasons. + */ +static int parse(struct args *arg) +{ + int s1=0, s2=0, i; + struct cache_entry *ce, *p; + /* get: URL, Protocol, method, headers*/ + for(i=0;i<arg->res.data_len;i++) + if(arg->res.data[i] == ' ') { + if(!s1) + s1=i; + else + s2=i; + } else if(arg->res.data[i]=='\r') + break; + + if(!s1) + { + failed( arg ); + return 0; + } + + /* GET http://www.roxen.com/foo.html HTTP/1.1\r + * 0 ^s1 ^s2 ^i + */ + + if(!s2) + arg->res.protocol = s_http_09; + else if(!memcmp("HTTP/1.", arg->res.data+s2+1, 7)) + { + if(arg->res.data[s2+8]=='0') + arg->res.protocol = s_http_10; + else if(arg->res.data[s2+8]=='1') + arg->res.protocol = s_http_11; + } + else + arg->res.protocol = 0; + + arg->res.method_len = s1; + + if(arg->res.protocol != s_http_09) + arg->res.header_start = i+2; + else + arg->res.header_start = 0; + + + /* find content */ + arg->res.content_len=0; + aap_get_header(arg, "content-length", H_INT, &arg->res.content_len); + if((arg->res.data_len - arg->res.body_start) < arg->res.content_len) + { + int nr; + /* read the rest of the request.. OBS: This is done without any + * timeout right now. It is relatively easy to add one, though. + * The only problem is that this might be an upload of a _large_ file, + * if so the upload could take an hour or more. So there can be no + * sensible default timeout. The only option is to reshedule the timeout + * for each and every read. + * + * This code should probably not allocate infinite amounts of + * memory either... + * + * TODO: rewrite this to use a mmaped file if the size is bigger than + * 1Mb or so. + * + * This could cause trouble with the leftovers code below, so that + * would have to be changed as well. + */ + arg->res.data=realloc(arg->res.data, + arg->res.body_start+arg->res.content_len); + while( arg->res.data_len < arg->res.body_start+arg->res.content_len) + { + while(((nr = fd_read(arg->fd, arg->res.data+arg->res.data_len, + (arg->res.body_start+arg->res.content_len)- + arg->res.data_len)) < 0) && errno == EINTR); + if(nr <= 0) + { + failed(arg); + return 0; + } + arg->res.data_len += nr; + } + } + /* ok.. now we should find the leftovers... This is any extra + * data not belonging to this request that has already been read. + * Since it is rather hard to unread things in a portable way, we simply + * store them in the result structure for the next request. + */ + + arg->res.leftovers_len= + (arg->res.data_len-arg->res.body_start-arg->res.content_len); + if(arg->res.leftovers_len) + arg->res.leftovers= + (arg->res.data+arg->res.body_start+arg->res.content_len); + + arg->res.url = arg->res.data+s1+1; + arg->res.url_len = (s2?s2:i)-s1-1; + + { + int hv; + struct pstring h; + h.len=0; + h.str=""; + if(aap_get_header(arg, "host", H_STRING, &h)) + { + arg->res.host = h.str; + arg->res.host_len = h.len; + } else { + arg->res.host = arg->res.data; + arg->res.host_len = 0; + } + + if(arg->cache->max_size && arg->res.data[0]== 'G') /* GET, presumably */ + { + if(!aap_get_header(arg, "pragma", H_EXISTS, 0)) + if((ce = aap_cache_lookup(arg->res.url, arg->res.url_len, + arg->res.host, arg->res.host_len, + arg->cache,0, &p, &hv)) && ce->data) + { + int len = WRITE(arg->fd, ce->data->str, ce->data->len); + LOG(len, arg, atoi(ce->data->str+MY_MIN(ce->data->len, 9))); + simple_aap_free_cache_entry( arg->cache, ce ); + /* if keepalive... */ + if((arg->res.protocol==s_http_11) + ||aap_get_header(arg, "connection", H_EXISTS, 0)) + { + return -1; + } +#ifdef AAP_DEBUG + fprintf(stderr, "Closing connection...\n"); +#endif /* AAP_DEBUG */ + close(arg->fd); + free(arg->res.data); + free(arg); + return 0; + } + } + } + return 1; +} + +void aap_handle_connection(struct args *arg) +{ + char *buffer, *p, *tmp; + int pos, buffer_len; +#ifdef HAVE_TIMEOUTS + int *timeout = NULL; +#endif + start: + pos=0; + buffer_len=8192; + + if(arg->res.data && arg->res.data_len > 0) + { + p = buffer = arg->res.data; + buffer_len = MAX(arg->res.data_len,8192); + arg->res.data=0; + } + else + p = buffer = malloc(8192); + + if(arg->res.leftovers && arg->res.leftovers_len) + { + if(!buffer) + { + perror("AAP: Failed to allocate buffer (leftovers)"); + failed(arg); + return; + } + buffer_len = arg->res.leftovers_len; + MEMCPY(buffer, arg->res.leftovers, arg->res.leftovers_len); + pos = arg->res.leftovers_len; + arg->res.leftovers=0; + if((tmp = my_memmem("\r\n\r\n", 4, buffer, pos))) + goto ok; + p += arg->res.leftovers_len; + } + + if(!buffer) + { + perror("AAP: Failed to allocate buffer"); + failed(arg); + return; + } +#ifdef HAVE_TIMEOUTS + timeout = aap_add_timeout_thr(th_self(), arg->timeout); + while( !(*timeout) ) +#else + while(1) +#endif /* HAVE_TIMEOUTS */ + { + int data_read = fd_read(arg->fd, p, buffer_len-pos); + if(data_read <= 0) + { + free(buffer); +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: Read error/eof.\n"); +#endif /* AAP_DEBUG */ + close(arg->fd); + free(arg); +#ifdef HAVE_TIMEOUTS + aap_remove_timeout_thr( timeout ); +#endif + return; + } + pos += data_read; + if((tmp = my_memmem("\r\n\r\n", 4, MAX(p-3, buffer), + data_read+(p-3>buffer?3:0)))) + goto ok; + p += data_read; + if(pos >= buffer_len) + { + buffer_len *= 2; + if(buffer_len > MAXLEN) + break; + + buffer = realloc(buffer, buffer_len); + p = buffer+pos; + if(!buffer) + { + perror("AAP: Failed to allocate memory (reading)"); + break; + } + } + } + + arg->res.data = buffer; + failed( arg ); +#ifdef HAVE_TIMEOUTS + aap_remove_timeout_thr( timeout ); +#endif + return; + + ok: +#ifdef HAVE_TIMEOUTS + if (timeout) { + aap_remove_timeout_thr( timeout ); + } +#endif /* HAVE_TIMEOUTS */ + arg->res.body_start = (tmp+4)-buffer; + arg->res.data = buffer; + arg->res.data_len = pos; + switch(parse(arg)) + { + case 1: + mt_lock(&queue_mutex); + if(!request) + { + request = last = arg; + arg->next = 0; + } + else + { + last->next = arg; + last = arg; + arg->next = 0; + } + mt_unlock(&queue_mutex); + wake_up_backend(); + return; + + case -1: + goto start; + + case 0: + ; + } +} + +#ifndef HAVE_AND_USE_POLL +#undef HAVE_POLL +#endif /* !HAVE_AND_USE_POLL */ + +#ifdef HAVE_POLL +#ifdef HAVE_POLL_H +#include <poll.h> +#else /* !HAVE_POLL_H */ +#ifdef HAVE_SYS_POLL_H +#include <sys/poll.h> +#else /* !HAVE_SYS_POLL_H */ +/* No <poll.h> or <sys/poll.h> */ +#undef HAVE_POLL +#endif /* HAVE_SYS_POLL_H */ +#endif /* HAVE_POLL_H */ + +#ifndef POLLRDBAND +#define POLLRDBAND POLLPRI +#endif /* !POLLRDBAND */ + +#endif /* HAVE_POLL */ + +static void low_accept_loop(struct args *arg) +{ + struct args *arg2 = malloc(sizeof(struct args)); + ACCEPT_SIZE_T len = sizeof(arg->from); +#if 0 && defined(SOLARIS) && defined(HAVE_POLL) + struct pollfd fds[1]; + fds[0].fd = arg->fd; + fds[0].events = POLLIN|POLLRDBAND; +#endif + while(1) + { + MEMCPY(arg2, arg, sizeof(struct args)); +#if 0 && defined(SOLARIS) && defined(HAVE_POLL) + while(poll(fds,1,-1)==-1) /* this makes it possible to close the fd. */ + ; +#endif + arg2->fd = fd_accept(arg->fd, (struct sockaddr *)&arg2->from, &len); + if(arg2->fd != -1) + { + th_farm((void (*)(void *))aap_handle_connection, arg2); + arg2 = malloc(sizeof(struct args)); + arg2->res.leftovers = 0; + } else { + if(errno == EBADF) + { + int i; + struct cache_entry *e, *t; + struct cache *c, *p = NULL; + struct log *l, *n = NULL; + /* oups. */ + mt_lock( &interpreter_lock ); + for(i=0; i<CACHE_HTABLE_SIZE; i++) + { + e = arg->cache->htable[i]; + while(e) + { + t = e; + e = e->next; + t->next = 0; + free_string(t->data); + free(t->url); + free(t->host); + free(t); + } + } + while(arg->log->log_head) + { + struct log_entry *l = arg->log->log_head->next; + free(arg->log->log_head); + arg->log->log_head = l; + } + + c = first_cache; + while(c && c != arg->cache) {p=c;c = c->next;} + if(c) + { + if(p) + p->next = c->next; + else + first_cache = c->next; + c->gone = 1; + free(c); + } + + + l = aap_first_log; + while(l && l != arg->log) {n=l;l = l->next;} + if(l) + { + if(n) n->next = l->next; + else aap_first_log = l->next; + free(l); + } + mt_unlock( &interpreter_lock ); + free(arg2); + free(arg); + return; /* No more accept here.. */ + } + } + } +} + + +static void finished_p(struct callback *foo, void *b, void *c) +{ + while(request) + { + struct args *arg; + struct object *o; + struct c_request_object *obj; + mt_lock(&queue_mutex); + arg = request; + request = arg->next; + mt_unlock(&queue_mutex); +#if defined(DEBUG) || defined(PIKE_DEBUG) + if(!arg->res.url) /* not parsed!? */ + fatal("AAP: Very odd request.\n"); +#endif + + if(arg->cache->unclean) + /* This must be done from the backend. + * That's why we do it here. + */ + aap_clean_cache(arg->cache, 0); + + o = clone_object( request_program, 0 ); /* see requestobject.c */ + obj = (struct c_request_object *)get_storage(o, c_request_program ); + MEMSET(obj, 0, sizeof(struct c_request_object)); + obj->request = arg; + obj->done_headers = allocate_mapping( 20 ); + obj->misc_variables = allocate_mapping( 40 ); + + apply(o, "__init", 0); pop_stack(); + + push_object( o ); + assign_svalue_no_free(sp++, &arg->args); + + /* CATCH? */ + apply_svalue(&arg->cb, 2); + pop_stack(); + } +} + +static void f_accept_with_http_parse(INT32 nargs) +{ +/* From socket.c */ + struct port + { + int fd; + int my_errno; + struct svalue accept_callback; + struct svalue id; + }; + int ms, dolog; + int to; + struct object *port; + struct svalue *fun, *cb, *program; + struct cache *c; + struct args *args = LTHIS; + get_all_args("accept_http_loop", nargs, "%o%*%*%*%d%d%d", &port, &program, + &fun, &cb, &ms, &dolog, &to); + MEMSET(args, 0, sizeof(struct args)); + if(dolog) + { + struct log *log = malloc(sizeof(struct log)); + MEMSET(log, 0, sizeof(struct log)); + args->log = log; + log->next = aap_first_log; + aap_first_log = log; + } + c = malloc(sizeof(struct cache)); + c->next = first_cache; + first_cache = c; + MEMSET(c, 0, sizeof(struct cache)); + args->cache = c; + c->max_size = ms; + args->fd = ((struct port *)port->storage)->fd; + args->filesystem = NULL; +#ifdef HAVE_TIMEOUTS + args->timeout = to; +#endif + assign_svalue_no_free(&args->cb, fun); + assign_svalue_no_free(&args->args, cb); + + request_program = program_from_svalue( program ); + if(!request_program) + { + free(args); + error("Invalid request program\n"); + } + if(!my_callback) + my_callback = add_backend_callback( finished_p, 0, 0 ); + + { + int i; + for( i = 0; i<8; i++ ) + th_farm((void (*)(void *))low_accept_loop, (void *)args); + } +} + +static void f_cache_status(INT32 args) +{ + struct cache *c = LTHIS->cache; + pop_n_elems(args); + push_text("hits"); push_int(c->hits); + push_text("misses"); push_int(c->misses); + push_text("stale"); push_int(c->stale); + push_text("size"); push_int(c->size); + push_text("entries"); push_int(c->entries); + push_text("max_size"); push_int(c->max_size); + + push_text("sent_bytes"); push_int(c->sent_data); c->sent_data=0; + push_text("num_request"); push_int(c->num_requests); c->num_requests=0; + push_text("received_bytes"); push_int(c->received_data);c->received_data=0; + f_aggregate_mapping( 18 ); +} + +#endif /* _REENTRANT */ + +void f_aap_add_filesystem( INT32 args ) +{ + int nosyms = 0; + struct pike_string *basedir, *mountpoint; + struct array *noparse; + struct pstring **skip_extensions; + + if(args == 4) + get_all_args( "add_filesystem", args, + "%s%s%a%d", &basedir, &mountpoint, &noparse, &nosyms ); + else + get_all_args( "add_filesystem", args, + "%s%s%a", &basedir, &mountpoint, &noparse ); + + +} + + + + +#ifndef OFFSETOF +#define OFFSETOF(str_type, field) ((long)& (((struct str_type *)0)->field)) +#endif + +void pike_module_init() +{ +#ifdef _REENTRANT + int offset; +#define STRING(X,Y) X=make_shared_string(Y) +#include "static_strings.h" +#undef STRING + +#ifdef HAVE_TIMEOUTS + aap_init_timeouts(); +#endif + + start_new_program(); +#ifdef ADD_STORAGE + ADD_STORAGE(struct args); +#else + add_storage(sizeof(struct args)); +#endif + add_function("create", f_accept_with_http_parse, + "function(object,program,function,mixed,int,int,int:void)", 0); + add_function("cache_status", f_cache_status,"function(void:mapping)", 0); + add_function("log_as_array",f_aap_log_as_array,"function(void:array(object))",0); + add_function("log_as_commonlog_to_file", f_aap_log_as_commonlog_to_file, + "function(object:int)", 0); + + add_function("log_size", f_aap_log_size, "function(void:int)", 0); + add_function("logp", f_aap_log_exists, "function(void:int)", 0); + +#if 0 + add_function( "add_filesystem", f_aap_add_filesystem, + "function(string,string,array(string),int|void:void)", 0); + add_function( "add_contenttype", f_aap_add_contenttype, + "function(strign,string:void)", 0); +#ifdef FS_STATS + add_function( "filesystem_stats", f_filesystem_stats, + "function(void:mapping)", 0); +#endif +#endif + + add_program_constant("Loop", accept_loop_program = end_program(), 0); + + start_new_program(); +#define OFFSET(X) (offset + OFFSETOF(log_object,X)) +#ifdef ADD_STORAGE + offset=ADD_STORAGE(struct log_object); +#else + offset=add_storage(sizeof(struct log_object)); +#endif + map_variable("time", "int", 0, OFFSET(time), T_INT); + map_variable("sent_bytes", "int", 0, OFFSET(sent_bytes), T_INT); + map_variable("reply", "int", 0, OFFSET(reply), T_INT); + map_variable("received_bytes", "int", 0, OFFSET(received_bytes), T_INT); + map_variable("raw", "string", 0, OFFSET(raw), T_STRING); + map_variable("url", "string", 0, OFFSET(url), T_STRING); + map_variable("method", "string", 0, OFFSET(method), T_STRING); + map_variable("protocol", "string", 0, OFFSET(protocol), T_STRING); + map_variable("from", "string", 0, OFFSET(from), T_STRING); + add_program_constant("logentry", (aap_log_object_program=end_program()), 0); + + + start_new_program(); +#ifdef ADD_STORAGE + ADD_STORAGE(struct c_request_object); +#else + add_storage(sizeof(struct c_request_object)); +#endif + + add_function("`[]", f_aap_index_op, "function(string:mixed)",0); + add_function("`->", f_aap_index_op, "function(string:mixed)",0); + +/* add_function("`->=", f_index_equal_op, "function(string,mixed:mixed)",0); */ +/* add_function("`[]=", f_index_equal_op, "function(string,mixed:mixed)",0); */ + + add_function("scan_for_query", f_aap_scan_for_query, + "function(string:string)", OPT_TRY_OPTIMIZE); + + add_function("end", f_aap_end, "function(string|void,int|void:void)", 0); + add_function("send", f_aap_output, "function(string:void)", 0); + add_function("reply", f_aap_reply, + "function(string|void,object|void,int|void:void)", 0); + add_function("__init", f_aap_reqo__init, "function(void:void)", 0); + add_function("reply_with_cache", f_aap_reply_with_cache, + "function(string,int:void)", 0); + set_init_callback( aap_init_request_object ); + set_exit_callback( aap_exit_request_object ); + add_program_constant("prog", (c_request_program = end_program()), 0); +#endif /* _REENTRANT */ +} + +void pike_module_exit() +{ +#ifdef _REENTRANT + struct log *log = aap_first_log; + /* This is very dangerous, since the + * accept threads are still going strong. + */ + + /* Tell the handle_timeouts thread to go and die. + * It will be dead in ~1s. + * We wait at the end of this function to ensure that this time + * has passed. + */ +#ifdef HAVE_TIMEOUTS + aap_exit_timeouts(); +#endif + + /* Lock all the global mutexes. This will freeze the accept threads + * sooner or later. At least no more requests will be done to the + * pike level. + */ + mt_lock( &queue_mutex ); +#ifdef HAVE_TIMEOUTS + mt_lock( &aap_timeout_mutex ); +#endif + /* Now, in theory, if all threads are stopped, we can free all the data. + * BUT: There is no way to know _when_ all threads are stopped, they may + * be in read() or something similar. Also, the locking of aap_timeout_mutex + * above disables the timeout, so they might linger in read() indefinately. + * Thus, we cannot free _all_ structures, only the ones that are always + * protected by mutexes. Most notably, we must leave the main 'log' and + * 'cache' structures alive. We can, on the other hand, free all data _in_ + * the cache and log structures. + */ + + while(log) + { + mt_lock( &log->log_lock ); /* This lock is _not_ unlocked again.. */ + { + struct log_entry *log_head = log->log_head; + struct log *next = log->next; + while(log_head) + { + struct log_entry *l = log_head->next; + free(log_head); + log_head = l; + } + log->next = NULL; + log->log_head = log->log_tail = NULL; + log = next; + } + } + + while(first_cache) + { + int i; + struct cache_entry *e, *t; + struct cache *next; + mt_lock(&first_cache->mutex); /* This lock is _not_ unlocked again.. */ + next = first_cache->next; + for(i=0; i<CACHE_HTABLE_SIZE; i++) + { + e = first_cache->htable[i]; + while(e) + { + t = e; + e = e->next; + t->next = 0; + free_string(t->data); + free(t->url); + free(t->host); + free(t); + } + first_cache->htable[i]=0; + } + first_cache->next = NULL; + first_cache = next; + } + + /* This will free all the string constants. It might be dangerous, + * but should not be so. No thread should enter the pike level code + * again, since all mutex locks are now locked. + */ +#define STRING(X,Y) free_string(X) +#include "static_strings.h" +#undef STRING + +#endif /* _REENTRANT */ +} + diff --git a/src/modules/HTTPLoop/accept_and_parse.h b/src/modules/HTTPLoop/accept_and_parse.h new file mode 100644 index 0000000000..075d4c01eb --- /dev/null +++ b/src/modules/HTTPLoop/accept_and_parse.h @@ -0,0 +1,158 @@ +#define CACHE_HTABLE_SIZE 40951 + +struct res +{ + struct pike_string *protocol; + + int header_start; + int method_len; + int body_start; + + char *url; + int url_len; + + char *host; + int host_len; + + char *content; + int content_len; + + char *leftovers; + int leftovers_len; + + char *data; + int data_len; +}; + +struct cache_entry +{ + struct cache_entry *next; + struct pike_string *data; + time_t stale_at; + char *url; int url_len; + char *host; int host_len; + short dead; + short refs; +}; + +struct file_ret +{ + int fd; + off_t size; + time_t mtime; +}; + +struct pstring +{ + int len; + char *str; +}; + + +#define FS_STATS +struct filesystem +{ + struct pstring base; + +#ifdef FS_STATS /* These can be wrong, but should give an indication.. */ + int lookups; + int hits; + int notfile; + int noperm; +#endif +}; + +struct cache +{ + MUTEX_T mutex; + struct cache *next; + struct cache_entry *htable[CACHE_HTABLE_SIZE]; + int size, entries, max_size; + int hits, misses, stale; + int num_requests, sent_data, received_data; + short unclean; + short gone; +}; + +struct args +{ + int fd; + struct args *next; + struct res res; + int timeout; + + struct svalue cb; + struct svalue args; + struct sockaddr_in from; + struct cache *cache; + struct filesystem *filesystem; + struct log *log; +}; + +struct log_entry +{ + struct log_entry *next; + int t; + int sent_bytes; + int reply; + int received_bytes; + struct pstring raw; + struct pstring url; + struct sockaddr_in from; + struct pstring method; + struct pike_string *protocol; +}; + +struct log +{ + struct log *next; + struct log_entry *log_head; + struct log_entry *log_tail; + MUTEX_T log_lock; +}; + + +struct log_object +{ + int time; + int reply; + int sent_bytes; + int received_bytes; + struct pike_string *raw; + struct pike_string *url; + struct pike_string *method; + struct pike_string *protocol; + struct pike_string *from; +}; + + +struct c_request_object +{ + struct args *request; + struct mapping *done_headers; + struct mapping *misc_variables; + int headers_parsed; +}; + +#define MY_MIN(a,b) ((a)<(b)?(a):(b)) + +#define LOG(X,Y,Z) do { \ + if((Y)->cache) {\ + (Y)->cache->num_requests++;\ + (Y)->cache->sent_data+=(X);\ + (Y)->cache->received_data+=(Y)->res.data_len;\ + }\ + if ((Y)->log) { \ + aap_log_append((X),(Y),(Z)); \ + }\ + } while(0) + +#define WRITE(X,Y,Z) aap_swrite(X,Y,Z) +#undef THIS +#define THIS ((struct c_request_object *)fp->current_storage) +#define LTHIS ((struct args *)fp->current_storage) + + + +void aap_handle_connection(struct args *arg); + diff --git a/src/modules/HTTPLoop/acconfig.h b/src/modules/HTTPLoop/acconfig.h new file mode 100644 index 0000000000..9850f83432 --- /dev/null +++ b/src/modules/HTTPLoop/acconfig.h @@ -0,0 +1,9 @@ +/* + * $Id: acconfig.h,v 1.1 1999/11/14 00:43:14 per Exp $ + */ +#undef CAN_HAVE_SENDFILE +#undef CAN_HAVE_LINUX_SYSCALL4 +#undef CAN_HAVE_NONSHARED_LINUX_SYSCALL4 + +/* Define if your sendfile() takes 7 args (FreeBSD style) */ +#undef HAVE_FREEBSD_SENDFILE diff --git a/src/modules/HTTPLoop/cache.c b/src/modules/HTTPLoop/cache.c new file mode 100644 index 0000000000..042b76578f --- /dev/null +++ b/src/modules/HTTPLoop/cache.c @@ -0,0 +1,215 @@ +#include "config.h" +#include <global.h> +#include <threads.h> +#include <stralloc.h> +#include <fdlib.h> + +#ifdef _REENTRANT +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "accept_and_parse.h" +#include "cache.h" +#include "util.h" + +struct cache *first_cache; + +static int cache_hash(char *s, int len) +{ + unsigned int res = len * 9471111; + while(len--) { res=res<<1 ^ ((res&(~0x7ffffff))>>31); res ^= s[len]; } + return (res % CACHE_HTABLE_SIZE)/2; +} /* ^^ OBS! */ + +static void really_free_cache_entry(struct cache *c, struct cache_entry *e, + struct cache_entry *prev, int b) +{ +#ifdef DEBUG + extern int d_flag; + if(d_flag>2) + { + if(b!=(cache_hash(e->url, e->url_len) + + cache_hash(e->host, e->host_len))) + fatal("Cache entry did not hash to the same spot\n"); + if(!mt_trylock( & c->mutex )) + fatal("Cache free_entry running unlocked\n"); + if(prev && prev->next != e) + fatal("prev->next != e\n"); + } +#endif + if(!prev) + c->htable[ b ] = e->next; + else + prev->next = e->next; + + c->size -= e->data->len; + c->entries--; + free_string(e->data); + free(e->host); + free(e->url); + free(e); +} + + + + +void aap_free_cache_entry(struct cache *c, struct cache_entry *e, + struct cache_entry *prev, int b) +{ +#ifdef DEBUG + if(e->refs<=0) + fatal("Freeing free cache entry\n"); +#endif + if(!--e->refs) really_free_cache_entry(c,e,prev,b); +} + +void simple_aap_free_cache_entry(struct cache *c, struct cache_entry *e) +{ + mt_lock( &c->mutex ); + if(!--e->refs) + { + struct cache_entry *t, *p=0; + int hv = cache_hash(e->url, e->url_len)+cache_hash(e->host,e->host_len); + t = c->htable[ hv ]; + while(t) + { + if( t == e ) + { + really_free_cache_entry(c,t,p,hv); + break; + } + p=t; + t=t->next; + } + } + mt_unlock( &c->mutex ); +} + + +void aap_cache_insert(struct cache_entry *ce, struct cache *c) +{ + struct cache_entry *head, *p; + char *t; + int hv; +#ifdef DEBUG + extern int d_flag; + if((d_flag > 2) && !mt_trylock( & c->mutex )) + fatal("Cache insert running unlocked\n"); +#endif + c->size += ce->data->len; + if((head = aap_cache_lookup(ce->url, ce->url_len, + ce->host, ce->host_len, c, 1, + &p, &hv)) && !head->dead) + { + c->size -= head->data->len; + if(head->data != ce->data) + { + free_string(head->data); + head->data = ce->data; + } else + free_string(head->data); + head->stale_at = ce->stale_at; + head->dead = 0; + aap_free_cache_entry( c, head, p, hv ); + free(ce); + } else { + c->entries++; + t = malloc(ce->url_len); MEMCPY(t,ce->url,ce->url_len); ce->url = t; + t = malloc(ce->host_len+1); MEMCPY(t,ce->host,ce->host_len); ce->host = t; + ce->next = c->htable[hv]; + ce->refs = 1; + c->htable[hv] = ce; + } +} + +struct cache_entry *aap_cache_lookup(char *s, int len,char *ho, int hlen, + struct cache *c, int nolock, + struct cache_entry **p, int *hv) +{ + int h = cache_hash(s, len) + cache_hash(ho,hlen); + struct cache_entry *e, *prev=NULL; + + *hv = h; + if(!nolock) + mt_lock(&c->mutex); +#ifdef DEBUG + else + { + extern int d_flag; + if((d_flag>2) && !mt_trylock( & c->mutex )) + fatal("Cache lookup running unlocked\n"); + } +#endif + *p = 0; + e = c->htable[h]; + while(e) + { + if(!e->dead && e->url_len == len && e->host_len == hlen + && !memcmp(e->url,s,len) && !memcmp(e->host,ho,hlen)) + { + int t = aap_get_time(); + if(e->stale_at < t) + { + c->unclean = 1; + /* We cannot free the pike-string from here. + * Let the clean-up code handle that instead. + */ + e->dead = 1; + c->stale++; + if(!nolock) mt_unlock(&c->mutex); + return 0; + } + c->hits++; + /* cache hit. Lets add it to the top of the list */ + if(c->htable[h] != e) + { + if(prev) prev->next = e->next; + e->next = c->htable[h]; + c->htable[h] = e; + } + if(!nolock) mt_unlock(&c->mutex); + e->refs++; + return e; + } + *p = prev = e; + e = e->next; + } + c->misses++; + if(!nolock) mt_unlock(&c->mutex); + return 0; +} + + +void aap_clean_cache(struct cache *c, int nolock) +{ + int i; + struct cache_entry *e, *p, *prev = 0; + if(!nolock) mt_lock(&c->mutex); + for(i=0; i<CACHE_HTABLE_SIZE; i++) + { + e = c->htable[i]; + prev=0; + while(e) + { + p = e->next; + if(e->dead) + aap_free_cache_entry(c, e, prev, i); + else + prev = e; + e = p; + } + } + if(!nolock) mt_unlock(&c->mutex); +} +#endif diff --git a/src/modules/HTTPLoop/cache.h b/src/modules/HTTPLoop/cache.h new file mode 100644 index 0000000000..cc034754a5 --- /dev/null +++ b/src/modules/HTTPLoop/cache.h @@ -0,0 +1,19 @@ +struct cache_entry *aap_cache_lookup(char *s, int len, + char *h, int hlen, + struct cache *c, int nl, + struct cache_entry **p, + int *hv); + + +void aap_free_cache_entry(struct cache *c, struct cache_entry *e, + struct cache_entry *prev, int b); + +void simple_aap_free_cache_entry(struct cache *c, struct cache_entry *e); + +void aap_cache_insert(struct cache_entry *ce, struct cache *c); + +void aap_clean_cache(struct cache *ce, int nolock); + +extern struct cache *first_cache; + + diff --git a/src/modules/HTTPLoop/configure.in b/src/modules/HTTPLoop/configure.in new file mode 100644 index 0000000000..95b47f113e --- /dev/null +++ b/src/modules/HTTPLoop/configure.in @@ -0,0 +1,143 @@ +# $Id: configure.in,v 1.1 1999/11/14 00:43:14 per Exp $ +AC_INIT(accept_and_parse.c) +AC_CONFIG_HEADER(config.h) + +AC_SET_MAKE + +sinclude(../module_configure.in) + +AC_CHECK_FUNCS(poll gmtime_r gmtime sendfile) + +AC_CHECK_HEADERS(poll.h sys/poll.h sys/socket.h netinet/in.h arpa/inet.h \ + asm/unistd.h sys/uio.h) + +AC_SUBST(RANLIB) + +if test "$ac_cv_func_sendfile" = "yes"; then + AC_MSG_CHECKING(if sendfile takes 4(Linux) or 7(FreeBSD) arguments) + AC_CACHE_VAL(pike_cv_freebsd_sendfile, [ + AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + ], [ + return sendfile(0,0,0,0,(void *)0,(void *)0,0); + ], [ + # Probably FreeBSD-style, but we need to check that + # we indeed have a prototype... + AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + ], [ + /* This should fail on FreeBSD + * If it succeeds, we don't have a prototype, + * which would indicate Linux. + */ + return sendfile(0,0,(void *)0,0); + ], [ pike_cv_freebsd_sendfile=no ], [ pike_cv_freebsd_sendfile=yes ]) + ], [ pike_cv_freebsd_sendfile=no ]) + ]) + if test "$pike_cv_freebsd_sendfile" = "yes"; then + AC_MSG_RESULT([7 - FreeBSD style]) + AC_DEFINE(HAVE_FREEBSD_SENDFILE) + else + AC_MSG_RESULT([4 - Linux style]) + fi +else + if test "${pike_cv_sys_os}" = "Linux"; then + if test "$pike_cv_sys_ccshared" != ""; then + AC_MSG_CHECKING(sendfile on linux without sendfile in header files with shared code) + AC_CACHE_VAL(pike_cv_sendfile_for_early_linux_with_new_gcc_shared, [ + OLD_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $pike_cv_sys_ccshared" + AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/errno.h> +#include <asm/unistd.h> +#ifndef __NR_sendfile +# define __NR_sendfile 187 +#endif +_syscall4(ssize_t,sendfile,int,out,int,in,off_t*,off,size_t,size); + ], [], [pike_cv_sendfile_for_early_linux_with_new_gcc_shared=yes], + [pike_cv_sendfile_for_early_linux_with_new_gcc_shared=no]) + CFLAGS="$OLD_CFLAGS" + ]) + + if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_shared" = "yes" ; then + AC_DEFINE(CAN_HAVE_LINUX_SYSCALL4) + AC_MSG_RESULT(perhaps) + else + AC_MSG_RESULT(no) + fi + else + pike_cv_sendfile_for_early_linux_with_new_gcc_shared=no + fi + + if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_shared" = "no" ; then + AC_MSG_CHECKING(sendfile on linux without sendfile in header files with nonshared code) + AC_CACHE_VAL(pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared, [ + AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/errno.h> +#include <asm/unistd.h> +#ifndef __NR_sendfile +# define __NR_sendfile 187 +#endif +_syscall4(ssize_t,sendfile,int,out,int,in,off_t*,off,size_t,size); + ], [], [pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared=yes], + [pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared=no]) + ]) + + if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared" = "yes" ; then + AC_DEFINE(CAN_HAVE_NONSHARED_LINUX_SYSCALL4) + AC_MSG_RESULT(perhaps) + else + AC_MSG_RESULT(no) + fi + fi + + if test "$pike_cv_sendfile_for_early_linux_with_new_gcc_shared" = "no" -o \ + "$pike_cv_sendfile_for_early_linux_with_new_gcc_nonshared" = "no"; then + AC_MSG_CHECKING(sendfile on linux without sendfile in header files with new gcc) + + AC_CACHE_VAL(pike_cv_sendfile_for_early_linux, [ + AC_TRY_COMPILE([ +#include <sys/types.h> +#include <sys/errno.h> + +int sendfile(int out, int in, int *off, int size) +{ + int retval; + asm volatile("pushl %%ebx\n\t" + "movl %%edi,%%ebx\n\t" + "int $0x80\n\t" + "popl %%ebx" + :"=a" (retval) + :"0" (187), + "D" (out), /* pseudo-ebx */ + "c" (in), + "d" (off), + "S" (size) + ); + if ((unsigned long) retval > (unsigned long)-1000) + { + errno = -retval; + retval = -1; + } + return retval; +} + ],[],[pike_cv_sendfile_for_early_linux=yes], + [pike_cv_sendfile_for_early_linux=no])]) + + if test "$pike_cv_sendfile_for_early_linux" = "yes" ; then + AC_DEFINE(CAN_HAVE_SENDFILE) + AC_MSG_RESULT(perhaps) + else + AC_MSG_RESULT(no) + fi + fi + fi +fi + +AC_OUTPUT(Makefile,echo FOO >stamp-h ) diff --git a/src/modules/HTTPLoop/extensions b/src/modules/HTTPLoop/extensions new file mode 100644 index 0000000000..6b56a70e29 --- /dev/null +++ b/src/modules/HTTPLoop/extensions @@ -0,0 +1,219 @@ +# $Id: extensions,v 1.1 1999/11/14 00:43:14 per Exp $ +# name="Extension database"; +# doc="The database used to map from extension to mime-type (example: *.gif to image/gif)"; +# + +aif audio/x-aiff +aifc audio/x-aiff +aiff audio/x-aiff +au audio/basic +m3u audio/x-mpegurl +mid audio/midi +midi audio/midi +mod audio/x-mod +mp2 audio/x-mpeg +mp3 audio/x-mpeg +mp3url audio/x-mpegurl +mpa audio/x-mpeg +psid audio/x-psid +# This is the standard extension used by the RealVideo 5.0 encoder +rm audio/x-pn-realaudio +ra audio/x-pn-realaudio +ram audio/x-pn-realaudio +rpm audio/x-pn-realaudio-plugin +sid audio/x-psid +snd audio/basic +vox audio/voxware +wav audio/x-wav + +fif image/fif +gif image/gif +ief image/ief +iff image/ilbm +ilbm image/ilbm +jpe image/jpeg +jpeg image/jpeg +jpg image/jpeg +pbm image/x-portable-bitmap +pgm image/x-portable-graymap +pict image/pict +pjpg image/jpeg +png image/png +pnm image/x-portable-anymap +ppm image/x-portable-pixmap +ras image/x-cmu-raster +rgb image/x-rgb +rs image/x-sun-raster +tga image/x-targa +tif image/tiff +tiff image/tiff +xbm image/x-xbitmap +xpm image/x-xpixmap +xwd image/x-xwindowdump + +# AutoCad (AutoDesk) +dwf drawing/x-dwf + +anim3 video/x-anim +anim5 video/x-anim +anim7 video/x-anim +anim8 video/x-anim +avi video/x-msvideo +fli video/x-fli +mov video/quicktime +movie video/x-sgi-movie +mpe video/mpeg +mpeg video/mpeg +mpg video/mpeg +qt video/quicktime +viv video/vnd.vivo +vivo video/vnd.vivo +vos video/vosaic + +c text/x-c-code +cc text/x-c++-code +css text/css +etx text/x-setext +h text/x-include-file +htm text/html +html text/html +java text/plain +rtx text/richtext +rxml text/roxencode +sgm text/x-sgml +sgml text/x-sgml +shtml text/html +spider text/spidercode +spml text/spinnercode +spml text/spidercode +tsv text/tab-separated-values +txt text/plain + +aab application/x-authorware-bin +aam application/x-authorware-map +aas application/x-authorware-seg +ai application/postscript +arj application/octet-stream +asn application/astound +asp application/x-asap +bcpio application/x-bcpio +bin application/octet-stream +cert application/x-x509-ca-cert +cer application/x-x509-ca-cert +cdf application/x-netcdf +cdr application/x-coreldraw +cdrw application/x-coreldraw +chat application/x-chat +class application/x-java-vm +com application/octet-stream +cpio application/x-cpio +csh application/x-csh +dll application/octet-stream +dnt application/donut +# Too general, but also too common to not be defined. +doc application/msword +donut application/donut +drv application/octet-stream +dvi application/x-dvi +eps application/postscript +evy application/x-envoy +exe application/octet-stream +gtar application/x-gtar +hdf application/x-hdf +hqx application/mac-binhex40 +iw application/iconauthor +iwm application/iconauthor +jar application/x-java-archive +js application/x-javascript +latex application/x-latex +lha application/lha +lzh application/lha +man application/x-troff-man +map application/x-imagemap +me application/x-troff-me +mif application/x-mif +mm application/x-meme +ms application/x-troff-ms +msw application/binary +mw application/math +nc application/x-netcdf +oda application/oda +pdf application/pdf +pfr application/font-tdpfr +pl application/x-perl +# Oracle plug-ins +po application/x-form +pot application/mspowerpoint +ppt application/mspowerpoint +ppz application/mspowerpoint +pps application/mspowerpoint +ps application/postscript +quake application/x-qplug-plugin +roff application/x-troff +rtf application/rtf +sh application/x-sh +shar application/x-shar +sit application/x-stuffit +so application/octet-stream +spl application/futuresplash +spr application/x-sprite +sprite application/x-sprite +src application/x-wais-source +sv4cpic application/x-sv4cpio +sv4crc application/x-sv4crc +swf application/x-shockwave-flash +t application/x-troff +tar application/unix-tar +tcl application/x-tcl +tex application/x-tex +texi application/x-texinfo +texinfo application/x-texinfo +tgz application/unix-tar x-gzip +tr application/x-troff +tsp application/dsptype +user application/x-x509-user-cert +usr application/x-x509-user-cert +ustar application/x-ustar +vcd application/x-cdlink +vmd application/vocaltec-media-desc +vmf application/vocaltec-media-file +wpc application/wpc +xls application/vnd.ms-excel +zip application/zip + +# This one hasn't been registred with IANA yet, but is added anyway. +# LivePicture PhotoVista RealSpace +ivr i-world/i-vrml + +wrl x-world/x-vrml +wrml x-world/x-vrml + +talk plugin/talker + +# Diskmasher -- Amiga thingie +dms application/x-diskmasher + +# Windows Write +wri application/x-write + +ice x-conference/x-cooltalk + +# MacroMedia Director (http://www.macromedia.com/) +dcr application/x-director +dir application/x-director +dxr application/x-director + +# Netscape proxy config files. +nspc application/x-ns-proxy-autoconfig +pac application/x-ns-proxy-autoconfig + +# Certificate Authority Certificate (recognized at least by Netscape) +ca_cert application/x-x509-ca-cert + +# This is supposed to be just 'gzip' and 'compress', but Netscape +# barfs.. Fixed in Netscape 3.*, but quite a lot of people still run 2.* +Z STRIP x-compress +gz STRIP x-gzip + +uu STRIP uuencode +b64 STRIP base64 diff --git a/src/modules/HTTPLoop/filesystem.c b/src/modules/HTTPLoop/filesystem.c new file mode 100644 index 0000000000..66d84772fb --- /dev/null +++ b/src/modules/HTTPLoop/filesystem.c @@ -0,0 +1,81 @@ +#include "config.h" +#include <global.h> +#include <threads.h> +#include <stralloc.h> +#include <fdlib.h> + +#ifdef _REENTRANT +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "accept_and_parse.h" +#include "filesystem.h" +#include "cache.h" +#include "util.h" + + +struct file_ret *aap_find_file( char *s, int len, + char *ho, int hlen, + struct filesystem *f ) +{ + struct stat s; + char *fname = alloca( fs->base.len + len + hlen + 2); + int res; + + +#ifdef FS_STATS + fs->lookups++; +#endif + MEMCPY( fname, fs->base.str, fs->base.len ); + MEMCPY( fname+fs->base.len, ho, hlen ); + MEMCPY( fname+fs->base.len+hlen, "/", 1 ); + MEMCPY( fname+fs->base.len+hlen+1, s, len ); + + res = stat( fname, &s ); + + if(!res) /* ok. The file exists. */ + { + if(S_ISREG( s.st_mode )) /* and it is a file... */ + { + int fd; + fd = open( fname, O_RDONLY, 0 ); + if(fd != -1) /* and it can be opened... */ + { + struct file_ret *r = malloc(sizeof(struct file_ret)); + r->fd = fd; + r->size = s.st_size; + r->mtime = s.st_mtime; +#ifdef FS_STATS + fs->hits++; +#endif + return r; + } +#ifdef FS_STATS + else + fs->noperm++; +#endif + } +#ifdef FS_STATS + else + fs->notfile++; +#endif + } +#ifdef FS_STATS + else + fs->nofile++; +#endif + return 0; +} + +#endif diff --git a/src/modules/HTTPLoop/filesystem.h b/src/modules/HTTPLoop/filesystem.h new file mode 100644 index 0000000000..24ebc7a352 --- /dev/null +++ b/src/modules/HTTPLoop/filesystem.h @@ -0,0 +1,7 @@ +struct file_ret *aap_find_file( char *s, int len, + char *ho, int hlen, + struct filesystem *f ); + +void set_filesystem( struct args *a, struct pstring loc ) +{ +} diff --git a/src/modules/HTTPLoop/log.c b/src/modules/HTTPLoop/log.c new file mode 100644 index 0000000000..2afeb4269c --- /dev/null +++ b/src/modules/HTTPLoop/log.c @@ -0,0 +1,237 @@ +#include "config.h" +#include <global.h> + +#include <array.h> +#include <backend.h> +#include <machine.h> +#include <mapping.h> +#include <module_support.h> +#include <multiset.h> +#include <object.h> +#include <operators.h> +#include <pike_memory.h> +#include <program.h> +#include <stralloc.h> +#include <svalue.h> +#include <threads.h> +#include <fdlib.h> + +#ifdef _REENTRANT +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "accept_and_parse.h" +#include "log.h" +#include "requestobject.h" +#include "util.h" + +struct program *aap_log_object_program; +struct log *aap_first_log; + +static void push_log_entry(struct log_entry *le) +{ + struct object *o = clone_object( aap_log_object_program, 0 ); + struct log_object *lo = (struct log_object*)o->storage; + lo->time = le->t; + lo->sent_bytes = le->sent_bytes; + lo->reply = le->reply; + lo->received_bytes = le->received_bytes; + lo->raw = make_shared_binary_string(le->raw.str, le->raw.len); + lo->url = make_shared_binary_string(le->url.str, le->url.len); + lo->method = make_shared_binary_string(le->method.str, le->method.len); + lo->protocol = le->protocol; + le->protocol->refs++; + lo->from = make_shared_string( inet_ntoa(le->from.sin_addr) ); + push_object( o ); +} + +void f_aap_log_as_array(INT32 args) +{ + struct log_entry *le; + struct log *l = LTHIS->log; + int n = 0; + pop_n_elems(args); + + mt_lock( &l->log_lock ); + le = l->log_head; + l->log_head = l->log_tail = 0; + mt_unlock( &l->log_lock ); + + while(le) + { + struct log_entry *l; + n++; + push_log_entry(le); + l = le->next; + free(le); + le = l; + } + { + extern void f_aggregate(INT32 args); + f_aggregate(n); + } +} + +void f_aap_log_exists(INT32 args) +{ + if(LTHIS->log->log_head) + push_int(1); + else + push_int(0); +} + +void f_aap_log_size(INT32 args) +{ + int n=1; + struct log *l = LTHIS->log; + struct log_entry *le; + if(!l) { + push_int(0); + return; + } + mt_lock( &l->log_lock ); + le = l->log_head; + while((le = le->next)) + n++; + mt_unlock( &l->log_lock ); + push_int(n); +} + +void f_aap_log_as_commonlog_to_file(INT32 args) +{ + struct log_entry *le; + struct log *l = LTHIS->log; + int n = 0; + int mfd, ot=0; + struct object *f; + struct tm tm; + FILE *foo; + static char *month[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Oct", "Sep", "Nov", "Dec", + }; + + get_all_args("log_as_commonlog_to_file", args, "%o", &f); + f->refs++; + + pop_n_elems(args); + apply(f, "query_fd", 0); + mfd = dup(sp[-1].u.integer); + if(mfd < 1)error("Bad fileobject to ->log_as_commonlog_to_file\n"); + pop_stack(); + + foo = fdopen( mfd, "w" ); + if(!foo) + error("Bad fileobject to ->log_as_commonlog_to_file\n"); + + THREADS_ALLOW(); + + mt_lock( &l->log_lock ); + le = l->log_head; + l->log_head = l->log_tail = 0; + mt_unlock( &l->log_lock ); + + while(le) + { + int i; + struct log_entry *l = le->next; + /* remotehost rfc931 authuser [date] "request" status bytes */ + if(le->t != ot) + { + time_t t = (time_t)le->t; +#ifdef HAVE_GMTIME_R + gmtime_r( &t, &tm ); +#else +#ifdef HAVE_GMTIME + tm = *gmtime( &t ); /* This will break if two threads run + gmtime() at once. */ +#else +#ifdef HAVE_LOCALTIME + tm = *localtime( &t ); /* This will break if two threads run + localtime() at once. */ +#endif +#endif +#endif + ot = le->t; + } + + /* date format: [03/Feb/1998:23:08:20 +0000] */ + + /* GET [URL] HTTP/1.0 */ + for(i=13; i<le->raw.len; i++) + if(le->raw.str[i] == '\r') + { + le->raw.str[i] = 0; + break; + } + + fprintf(foo, + "%d.%d.%d.%d - %s [%02d/%s/%d:%02d:%02d:%02d +0000] \"%s\" %d %d\n", + ((unsigned char *)&le->from.sin_addr)[ 0 ], + ((unsigned char *)&le->from.sin_addr)[ 1 ], + ((unsigned char *)&le->from.sin_addr)[ 2 ], + ((unsigned char *)&le->from.sin_addr)[ 3 ], /* hostname */ + "-", /* remote-user */ + tm.tm_mday, month[tm.tm_mon], tm.tm_year+1900, + tm.tm_hour, tm.tm_min, tm.tm_sec, /* date */ + le->raw.str, /* request line */ + le->reply, /* reply code */ + le->sent_bytes); /* bytes transfered */ + free(le); + n++; + le = l; + } + fclose(foo); + close(mfd); + THREADS_DISALLOW(); + push_int(n); +} + +void aap_log_append(int sent, struct args *arg, int reply) +{ + struct log *l = arg->log; + /* we do not incude the body, only the headers et al.. */ + struct log_entry *le=malloc(sizeof(struct log_entry)+arg->res.body_start-3); + char *data_to=((char *)le)+sizeof(struct log_entry); + + le->t = aap_get_time(); + le->sent_bytes = sent; + le->reply = reply; + le->received_bytes = arg->res.body_start + arg->res.content_len; + MEMCPY(data_to, arg->res.data, arg->res.body_start-4); + le->raw.str = data_to; + le->raw.len = arg->res.body_start-4; + le->url.str = (data_to + (int)(arg->res.url-arg->res.data)); + le->url.len = arg->res.url_len; + le->from = arg->from; + le->method.str = data_to; + le->method.len = arg->res.method_len; + le->protocol = arg->res.protocol; + le->next = 0; + + mt_lock( &l->log_lock ); + if(l->log_head) + { + l->log_tail->next = le; + l->log_tail = le; + } + else + { + l->log_head = le; + l->log_tail = le; + } + mt_unlock( &l->log_lock ); +} + +#endif diff --git a/src/modules/HTTPLoop/log.h b/src/modules/HTTPLoop/log.h new file mode 100644 index 0000000000..c9e7435b25 --- /dev/null +++ b/src/modules/HTTPLoop/log.h @@ -0,0 +1,9 @@ +void f_aap_log_as_array(INT32 args); +void f_aap_log_exists(INT32 args); +void f_aap_log_size(INT32 args); +void f_aap_log_as_commonlog_to_file(INT32 args); +void aap_log_append(int sent, struct args *arg, int reply); + + +extern struct log *aap_first_log; +extern struct program *aap_log_object_program; diff --git a/src/modules/HTTPLoop/requestobject.c b/src/modules/HTTPLoop/requestobject.c new file mode 100644 index 0000000000..1b8525ae1e --- /dev/null +++ b/src/modules/HTTPLoop/requestobject.c @@ -0,0 +1,1092 @@ +/* + * $Id: requestobject.c,v 1.1 1999/11/14 00:43:15 per Exp $ + */ + +#include "global.h" +#include "config.h" + +#include "array.h" +#include "backend.h" +#include "machine.h" +#include "mapping.h" +#include "module_support.h" +#include "multiset.h" +#include "object.h" +#include "operators.h" +#include "pike_memory.h" +#include "program.h" +#include "constants.h" +#include "stralloc.h" +#include "svalue.h" +#include "threads.h" +#include "fdlib.h" + +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef _REENTRANT +#include "accept_and_parse.h" +#include "log.h" +#include "util.h" +#include "cache.h" +#include "requestobject.h" + +extern void f_aggregate(INT32 args); /* no prototype? */ + +/* Used when fatal() can't be. */ +#define DWERROR(X) write(2, X, sizeof(X) - sizeof("")) + +#if defined(CAN_HAVE_LINUX_SYSCALL4) || \ + !defined(DYNAMIC_MODULE) && defined(CAN_HAVE_NONSHARED_LINUX_SYSCALL4) + +#include <asm/unistd.h> +#ifndef __NR_sendfile +#define __NR_sendfile 187 +#endif +_syscall4(ssize_t,sendfile,int,out,int,in,off_t*,off,size_t,size); + +#elif defined(CAN_HAVE_SENDFILE) + +ssize_t sendfile(int out, int in, off_t *off, size_t size) +{ + ssize_t retval; + asm volatile( + "pushl %%ebx\n\t" + "movl %%edi,%%ebx\n\t" + "int $0x80\n\t" + "popl %%ebx" + :"=a" (retval) + :"0" (187), + "D" (out), /* pseudo-ebx (put it in edi)*/ + "c" (in), + "d" (off), + "S" (size) + ); + if ((unsigned long) retval > (unsigned long)-1000) + { + errno = -retval; + retval = -1; + } + return retval; +} +# define HAVE_SENDFILE +#endif + + +/* Define local global string variables ... */ +#define STRING(X,Y) /*extern*/ struct pike_string *X +#include "static_strings.h" +#undef STRING +/* there.. */ + +#define SINSERT(MAP,INDEX,VAL) \ +do { \ + push_string(VAL); \ + push_string(INDEX); \ + mapping_insert((MAP),(sp-1),(sp-2)); \ + sp-=2; \ +} while(0) + +#define IINSERT(MAP,INDEX,VAL)\ +do { \ + push_int(VAL); \ + push_string(INDEX); \ + mapping_insert((MAP),sp-1,sp-2); \ + sp -= 2; \ +} while(0) + +#define TINSERT(MAP,INDEX,VAL,LEN)\ +do { \ + push_string(make_shared_binary_string((VAL),(LEN))); \ + push_string(INDEX); \ + mapping_insert((MAP),sp-1,sp-2); \ + sp--; \ + pop_stack(); \ +} while(0) + +#define AINSERT(MAP,INDEX,VAL)\ +do { \ + push_array(VAL); \ + push_string(INDEX); \ + mapping_insert((MAP),sp-1,sp-2); \ + sp -= 2; \ +} while(0) + +#define OINSERT(MAP,INDEX,VAL)\ +do { \ + push_object(VAL); \ + push_string(INDEX); \ + mapping_insert((MAP),sp-1,sp-2); \ + sp -= 2; \ +} while(0) + +#define MSINSERT(MAP,INDEX,VAL)\ +do { \ + push_multiset(val); \ + push_string(INDEX); \ + mapping_insert((MAP),sp-1,sp-2); \ + sp -= 2; \ +} while(0) + +#define MAINSERT(MAP,INDEX,VAL)\ +do { \ + push_mapping(VAL); \ + push_string(INDEX); \ + mapping_insert((MAP),sp-1,sp-2); \ + sp -= 2; \ +} while(0) + + +static int dhex(char what) +{ + if(what >= '0' && what <= '9') return what - '0'; + if(what >= 'A' && what <= 'F') return 10+what-'A'; + if(what >= 'a' && what <= 'f') return 10+what-'a'; + return 0; +} + + +void f_aap_scan_for_query(INT32 args) +{ + struct pike_string *_s; + char *s, *work_area; + int len, i,j, begin=0, c; + if(args) + { + get_all_args("HTTP C object->scan_for_query(string f)", args, "%S", &_s); + s = (char *)_s->str; + len = _s->len; + } + else + { + s = THIS->request->res.url; + len = THIS->request->res.url_len; + } + + /* [http://host:port]/(pre,states)/[url[?query]] */ + work_area = malloc(len); + + /* find '?' if any */ + for(j=i=0;i<len;i++) + { + switch(c=s[i]) + { + case '?': + goto done; + case '%': + if(i<len-2) + { + c = (dhex(s[i+1])<<4) + dhex(s[i+2]); + i+=2; + } + } + work_area[j++]=c; + } + + done: + +/* Now find prestates, if any */ + j--; + if(j > 3 && work_area[1] == '(' && work_area[0]=='/') + { + int k, n=0, last=2; + for(k = 2; k < j; k++) + { + switch(work_area[k]) + { + case ',': + push_string(make_shared_binary_string(work_area+last,k-last)); + n++; + last = k+1; + break; + case ')': + push_string(make_shared_binary_string(work_area+last,k-last)); + n++; + begin = k+1; + f_aggregate_multiset( n ); + goto done2; + } + } + pop_n_elems(n); + f_aggregate_multiset( 0 ); + } else { + f_aggregate_multiset( 0 ); + } + done2: + push_string(s_prestate); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; pop_stack(); + + TINSERT(THIS->misc_variables, s_not_query, work_area+begin, j-begin+1); + free(work_area); + + if(i < len) + TINSERT(THIS->misc_variables, s_query, s+i+1, (len-i)-1); + else + IINSERT(THIS->misc_variables, s_query, 0); + + push_string(s_variables); map_delete(THIS->misc_variables, sp-1); sp--; + push_string(s_rest_query); map_delete(THIS->misc_variables, sp-1); sp--; +} + +#define VAR_MAP_INSERT() \ + struct svalue *s; \ + push_string(make_shared_binary_string( dec+lamp, leq-lamp)); \ + if((s = low_mapping_lookup(v, sp-1))) \ + { \ + dec[leq] = 0; \ + s->u.string->refs++; \ + map_delete(v, sp-1); \ + push_string(s->u.string); \ + push_string(make_shared_binary_string( dec+leq, dl-leq)); \ + f_add(2); \ + } else \ + push_string(make_shared_binary_string( dec+leq+1, dl-leq-1)); \ + mapping_insert(v, sp-2, sp-1); \ + pop_n_elems(2) + + +static void decode_x_url_mixed(char *in, int l, struct mapping *v, char *dec, + char *rest_query, char **rp) +{ + int pos = 0, lamp = 0, leq=0, dl; + + for(dl = pos = 0; pos<l; pos++, dl++) + { + unsigned char c; + switch(c=in[pos]) + { + case '=': leq = dl; break; + case '+': c = ' '; break; + case '&': + if(leq) + { + VAR_MAP_INSERT(); + } else if(rp) { + int i; + for(i=lamp-1;i<dl;i++) *((*rp)++)=dec[i]; + } + leq = 0; + lamp = dl+1; + break; + case '%': + if(pos<l-2) + { + c = (dhex(in[pos+1])<<4) + dhex(in[pos+2]); + pos+=2; + } + } + dec[dl]=c; + } + if(leq) + { + VAR_MAP_INSERT(); + } else if(rp) { + int i; + for(i=lamp-1;i<dl;i++) *((*rp)++)=dec[i]; + } +} + +static void parse_query(void) +{ + struct svalue *q; + struct mapping *v = allocate_mapping(10); /* variables */ + push_string(s_query); + if(!(q = low_mapping_lookup(THIS->misc_variables, sp-1))) + f_aap_scan_for_query(0); + q = low_mapping_lookup(THIS->misc_variables, sp-1); + sp--; + + if(q->type == T_STRING) + { + char *dec = malloc(q->u.string->len*2+1); + char *rest_query = dec+q->u.string->len+1, *rp=rest_query; + decode_x_url_mixed(q->u.string->str,q->u.string->len,v,dec,rest_query,&rp); + push_string(make_shared_binary_string(rest_query,rp-rest_query)); + push_string(s_rest_query); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; pop_stack(); + free(dec); + } else { + push_int(0); push_string(s_rest_query); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; pop_stack(); + } + + if(THIS->request->res.content_len && + THIS->request->res.data[1]=='O') + { + struct pstring ct; + int nope = 0; + if(aap_get_header(THIS->request, "content-type", T_STRING, &ct)) + { + if(ct.str[0]=='m') /* multipart is not wanted here... */ + nope=1; + } + if(!nope) + { + char *tmp = malloc(THIS->request->res.content_len); + decode_x_url_mixed(THIS->request->res.data+ + THIS->request->res.body_start, + THIS->request->res.content_len,v,tmp,0,0); + free(tmp); + } + } + push_mapping(v); push_string(s_variables); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; pop_stack(); +} + +static void parse_headers(void) +{ + struct svalue *tmp; + struct mapping *headers = THIS->done_headers; + int os=0,i,j,l=THIS->request->res.body_start-THIS->request->res.header_start; + unsigned char *in= + (unsigned char*)THIS->request->res.data + THIS->request->res.header_start; + + THIS->headers_parsed = 1; + for(i=0; i<l; i++) + { + switch(in[i]) + { + case ':': + /* in[os..i-1] == the header */ + for(j=os;j<i;j++) if(in[j] > 63 && in[j] < 91) in[j]+=32; + push_string(make_shared_binary_string((char*)in+os,i-os)); + os = i+1; + while(in[os]==' ') os++; + for(j=os;j<l;j++) if(in[j] == '\r') break; + push_string(make_shared_binary_string((char*)in+os,j-os)); + f_aggregate(1); + if((tmp = low_mapping_lookup(headers, sp-2))) + { + tmp->u.array->refs++; + push_array(tmp->u.array); + map_delete(headers, sp-3); + f_add(2); + } + mapping_insert(headers, sp-2, sp-1); + pop_n_elems(2); + os = i = j+2; + } + } +} + +void f_aap_index_op(INT32 args) +{ + struct svalue *res; + struct pike_string *s; + if (!args) error("HTTP C object->`[]: too few args\n"); + pop_n_elems(args-1); + if(!THIS->misc_variables) + { + struct svalue s; + object_index_no_free2(&s, fp->current_object, sp-1); + pop_stack(); + *sp=s; + sp++; + return; + } + + if((res = low_mapping_lookup(THIS->misc_variables, sp-1))) + { + pop_stack(); + assign_svalue_no_free( sp++, res ); + return; + } + + if(!THIS->request) error("Reply called. No data available\n"); + get_all_args("`[]", args, "%S", &s); + + if(s == s_not_query || s==s_query || s==s_prestate) + { + f_aap_scan_for_query(0); + f_aap_index_op(1); + return; + } + + if(s == s_my_fd) + { + struct object *file_make_object_from_fd(int fd, int mode, int guess); + /* 0x3800 is from modules/files/file.h, + * FILE_READ|FILE_WRITE|FILE_SET_CLOSE_ON_EXEC + */ + push_object(file_make_object_from_fd + (dup(THIS->request->fd),0x3800, + SOCKET_CAPABILITIES|PIPE_CAPABILITIES)); + push_string(s_my_fd); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; /* the wanted value is on the stack now */ + } + + if(s == s_remoteaddr) + { + push_text(inet_ntoa(THIS->request->from.sin_addr)); + push_string(s_remoteaddr); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + + if(s == s_method) + { + push_string(make_shared_binary_string(THIS->request->res.data, + THIS->request->res.method_len)); + push_string(s_method); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + + if(s == s_raw) + { + pop_stack(); + push_string(make_shared_binary_string(THIS->request->res.data, + THIS->request->res.data_len)); + push_string(s_raw); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + + if(s == s_rest_query || s == s_variables) + { + parse_query(); + f_aap_index_op(1); + return; + } + + if(s == s_headers) + { + if(!THIS->headers_parsed) parse_headers(); + THIS->done_headers->refs++; + push_mapping(THIS->done_headers); + return; + } + + /* FIXME: Not actually implemented */ +/* if(s == s_cookies || s == s_config) */ +/* { */ +/* if(!THIS->headers_parsed) parse_headers(); */ + +/* f_aap_index_op(1); */ +/* return; */ +/* } */ + + if(s == s_pragma) + { + struct svalue *tmp; + if(!THIS->headers_parsed) parse_headers(); + if((tmp = low_mapping_lookup(THIS->done_headers, sp-1))) + { + pop_stack(); + push_multiset(mkmultiset( tmp->u.array )); + } else { + pop_stack(); + f_aggregate_multiset(0); + } + push_string(s_pragma); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + + if(s == s_client) + { + struct svalue *tmp; + if(!THIS->headers_parsed) parse_headers(); + pop_stack(); + push_string(s_user_agent); + if((tmp = low_mapping_lookup(THIS->done_headers, sp-1))) + assign_svalue_no_free( sp-1, tmp ); + else + { + sp--; + f_aggregate(0); + } + push_string(s_client); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + + if(s == s_referer) + { + struct svalue *tmp; + if(!THIS->headers_parsed) parse_headers(); + if((tmp = low_mapping_lookup(THIS->done_headers, sp-1))) + assign_svalue( sp-1, tmp ); + else + { + pop_stack(); + push_int(0); + } + push_string(s_referer); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + + if(s == s_since) + { + struct svalue *tmp; + if(!THIS->headers_parsed) parse_headers(); + pop_stack(); + push_string(s_if_modified_since); + if((tmp = low_mapping_lookup(THIS->done_headers, sp-1))) + { + tmp->u.array->item[0].u.string->refs++; + sp[-1] = tmp->u.array->item[0]; + } + else + { + sp[-1].type = T_INT; + sp[-1].u.integer = 0; + } + push_string(s_since); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + + f_aap_index_op(1); + return; + } + + if( s == s_supports ) + { + struct svalue *tmp; + pop_stack(); + push_constant_text("roxen"); + if((tmp = low_mapping_lookup(get_builtin_constants(), sp-1)) + && tmp->type == T_OBJECT) + { + pop_stack( ); + ref_push_object( tmp->u.object ); + push_constant_text( "find_supports" ); + f_index( 2 ); + ref_push_string(s_client); + f_aap_index_op( 1 ); + push_constant_text(""); + f_multiply( 2 ); + apply_svalue( sp-2, 1 ); + push_string(s_supports); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + stack_swap(); + pop_stack(); + + } else { + pop_stack(); + f_aggregate_multiset( 0 ); + push_string(s_supports); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + } + + + + /* + if(contents = o->misc["accept-encoding"]) + { + foreach((contents-" ")/",", string e) { + if (lower_case(e) == "gzip") { + o->supports["autogunzip"] = 1; + } + } + } + */ + + return; + } + + +/* if(s == s_realauth || s == s_rawauth) */ +/* { */ +/* pop_stack(); */ +/* push_int(0); */ +/* return; */ +/* } */ + + if(s == s_data) + { + pop_stack(); + push_string(make_shared_binary_string(THIS->request->res.data + + THIS->request->res.body_start, + THIS->request->res.content_len)); + push_string(s_data); + mapping_insert(THIS->misc_variables, sp-1, sp-2); + sp--; + return; + } + { + struct svalue s; + object_index_no_free2(&s, fp->current_object, sp-1); + pop_stack(); + *sp=s; + sp++; + } +} + +/* static void f_index_equal_op(INT32 args) */ +/* { */ + +/* } */ + +void f_aap_end(INT32 args) +{ + /* end connection. */ +} + +void f_aap_output(INT32 args) +{ + if(sp[-1].type != T_STRING) error("Bad argument 1 to output\n"); + WRITE(THIS->request->fd, sp[-1].u.string->str, sp[-1].u.string->len); +} + +#define BUFFER 8192 +struct thread_args *done; + +struct send_args +{ + struct args *to; + int from_fd; + char *data; + int data_len; + int len; + int sent; + char buffer[BUFFER]; +}; + +/* WARNING! This function is running _without_ any stack etc. */ +void actually_send(struct send_args *a) +{ + int fail, first=0; + char foo[10]; + foo[9]=0; foo[6]=0; + +#ifdef HAVE_FREEBSD_SENDFILE + if (a->len) + { + struct iovec vec; + struct sf_hdtr headers = { NULL, 0, NULL, 0 }; + off_t off = 0; + off_t sent = 0; + size_t len = a->len; + +#ifdef AAP_DEBUG + fprintf(stderr, "sendfile... \n"); +#endif + + if (a->data) { + /* Set up the iovec */ + vec.iov_base = a->data; + vec.iov_len = a->data_len; + headers.headers = &vec; + headers.hdr_cnt = 1; + } + + if (a->len < 0) { + /* Negative: Send all */ + len = 0; + } + + if ((off = tell(a->from_fd)) < 0) { + /* Probably a pipe, so sendfile() will probably fail anyway, + * but it doesn't hurt to try... + */ + off = 0; + } + + if (sendfile(a->from_fd, a->to->fd, off, len, + &headers, &sent, 0) < 0) { + /* FIXME: We aren't looking very hard at the errno, since + * sent usually tells us what to do. + */ + switch(errno) { + case EBADF: /* Either of the fds is invalid */ + case ENOTSOCK: /* to_fd is not a socket */ + case EINVAL: /* from_fd is not a file, to_fd not a SOCK_STREAM, + * or offset is negative or out of range. + */ + case ENOTCONN: /* to_fd is not a connected socket */ + case EPIPE: /* to_fd is closed at the other end */ + case EIO: /* Error reading from from_fd */ + if (!sent) { + /* Probably a bad fd-combo. Try sending by hand. */ + goto send_data; + } + break; + case EFAULT: /* Invalid address specified as arg */ + /* NOTE: Can't use fatal(), since we're not in a valid Pike context. */ + DWERROR("FreeBSD-style sendfile() returned EFAULT.\n"); + abort(); + break; + case EAGAIN: /* socket in nonblocking mode, sent is valid */ + break; + } + } + + /* At this point sent will always contain the actual number + * of bytes sent. + */ + a->sent += sent; + + if (a->data) { + if (sent < a->data_len) { + a->data += sent; + a->data_len -= sent; + sent = 0; + + /* Make sure we don't terminate early due to len being 0. */ + goto send_data; + } else { + sent -= a->data_len; + a->data = NULL; + a->data_len = 0; + } + } + + if (len) { + a->len -= sent; + if (!a->len) { + goto end; + } + goto normal; + } else { + /* Assume all was sent */ + a->len = 0; + goto end; + } + } + + send_data: +#endif /* !HAVE_FREEBSD_SENDFILE */ + +#ifdef AAP_DEBUG + fprintf(stderr, "actually_send... \n"); +#endif + if(a->data) + { + MEMCPY(foo, a->data+MY_MIN((a->data_len-4),9), 4); + first=1; +#ifdef TCP_CORK +#ifdef AAP_DEBUG + fprintf(stderr, "cork... \n"); +#endif + { + int true=1; + setsockopt( a->to->fd, SOL_TCP, TCP_CORK, &true, 4 ); + } +#endif + fail = WRITE(a->to->fd, a->data, a->data_len); + a->sent += fail; + if(fail != a->data_len) + goto end; + } + fail = 0; + +#if !defined(HAVE_FREEBSD_SENDFILE) && defined(HAVE_SENDFILE) + if(a->len) + { +#ifdef AAP_DEBUG + fprintf(stderr, "pre sendfile... \n"); +#endif + if(!first) + { + first=1; + fail = read(a->from_fd, foo, 10); + if(fail < 0) + goto end; + WRITE( a->to->fd, foo, fail ); + a->len -= fail; + } +#ifdef AAP_DEBUG + fprintf(stderr, "sendfile... \n"); +#endif + switch(fail = sendfile(a->to->fd, a->from_fd, NULL, a->len )) + { + case -ENOSYS: +#ifdef AAP_DEBUG + fprintf(stderr, "syscall does not exist.\n"); +#endif + goto normal; + default: + if(fail != a->len) + fprintf(stderr, "sendfile returned %d; len=%d\n", fail, a->len); + } + goto end; + } +#endif /* HAVE_SENDFILE && !HAVE_FREEBSD_SENDFILE */ + + normal: +#ifdef AAP_DEBUG + fprintf(stderr, "using normal fallback... \n"); +#endif +#ifdef DIRECTIO_ON + if(a->len > (65536*4)) + directio(a->from_fd, DIRECTIO_ON); +#endif + + /* + * Ugly optimization... + * Removes the sign-bit from the len. + * => -1 => 0x7fffffff, which means that cgi will work. + * + * /grubba 1998-08-30 + */ + a->len &= 0x7fffffff; + + while(a->len) + { + int nread, written=0; + nread = fd_read(a->from_fd, a->buffer, MY_MIN(BUFFER,a->len)); + if(!first) + { + first=1; + MEMCPY(foo,a->buffer+9,5); + } + if(nread <= 0) + { + if((nread < 0) && (errno == EINTR)) + continue; + else + { + /* CGI's will come here at eof (nread == 0), + * and get keep-alive disabled. + */ + fail = 1; + break; + } + } + if(fail || (WRITE(a->to->fd, a->buffer, nread) != nread)) + goto end; + } + + end: +#ifdef TCP_CORK + { + int false = 0; + setsockopt( a->to->fd, SOL_TCP, TCP_CORK, &false, 4 ); + } +#endif + { + struct args *arg = a->to; + LOG(a->sent, a->to, atoi(foo)); + if(a->from_fd) close(a->from_fd); + if(a->data) free(a->data); + free(a); + + if(!fail && + ((arg->res.protocol==s_http_11) + ||aap_get_header(arg, "Connection", H_EXISTS, 0))) + { + aap_handle_connection(arg); + } + else + { + if(arg->res.data) free(arg->res.data); + if(arg->fd) close(arg->fd); + free(arg); + } + } +} + +void f_aap_reply(INT32 args) +{ + int reply_string=0, reply_object=0; + struct send_args *q; + if(!THIS->request) + error("reply already called.\n"); + if(args && sp[-args].type == T_STRING) + reply_string = 1; + + if(args>1) + { + if(args<3) + error("->reply(string|void pre,object(Stdio.file) fd,int len)\n"); + if(sp[-args+1].type != T_OBJECT) + error("Bad argument 2 to reply\n"); + if(sp[-args+2].type != T_INT) + error("Bad argument 3 to reply\n"); + reply_object = 1; + } + + if(reply_string && !reply_object) + { + if(sp[-1].u.string->len < 8192) + { + int amnt = WRITE(THIS->request->fd,sp[-1].u.string->str, + sp[-1].u.string->len); + LOG(amnt, THIS->request, + atoi(sp[-1].u.string->str+MY_MIN(sp[-1].u.string->len,9))); + if((sp[-1].u.string->len == amnt) && + ((THIS->request->res.protocol==s_http_11) + ||aap_get_header(THIS->request, "Connection", H_EXISTS, 0))) + { + th_farm((void (*)(void *))aap_handle_connection, + (void *)THIS->request); + THIS->request = 0; + return; + } else { + if(THIS->request->res.data) + free(THIS->request->res.data); + close(THIS->request->fd); + free(THIS->request); + THIS->request = 0; + return; + } + } + } + + q = malloc(sizeof(struct send_args)); + q->to = THIS->request; + THIS->request = 0; + + if(reply_object) + { + /* safe_apply() needed to avoid leak of q */ + safe_apply(sp[-2].u.object, "query_fd", 0); + if((sp[-1].type != T_INT) || (sp[-1].u.integer <= 0)) + { + free(q); + error("Bad fileobject to request_object->reply()\n"); + } + if((q->from_fd = dup(sp[-1].u.integer)) == -1) + error("Bad file object to request_object->reply()\n"); + pop_stack(); + + q->len = sp[-1].u.integer; + } else { + q->from_fd = 0; + q->len = 0; + } + + if(reply_string) + { + char *s = malloc(sp[-args].u.string->len); + MEMCPY(s, sp[-args].u.string->str, sp[-args].u.string->len); + q->data = s; + q->data_len = sp[-args].u.string->len; + } else { + q->data = 0; + q->data_len = 0; + } + q->sent = 0; + + th_farm( (void (*)(void *))actually_send, (void *)q ); + + pop_n_elems(args); + push_int(0); +} + +void f_aap_reply_with_cache(INT32 args) +{ + struct cache_entry *ce; + struct pike_string *reply; + int time_to_keep, t, freed=0; + if(!THIS->request) + error("Reply already called.\n"); + + get_all_args("reply_with_cache", args, "%S%d", &reply, &time_to_keep); + + if(reply->len < THIS->request->cache->max_size/2) + { + if(THIS->request->cache->gone) /* freed..*/ + { + close(THIS->request->fd); + if(THIS->request->res.data) + free(THIS->request->res.data); + free(THIS->request); + THIS->request = 0; + return; + } + t = aap_get_time(); + mt_lock(&THIS->request->cache->mutex); + if(THIS->request->cache->size > THIS->request->cache->max_size) + { + struct cache_entry *p,*pp=0,*ppp=0; + if(THIS->request->cache->unclean) + aap_clean_cache(THIS->request->cache, 1); + while(THIS->request->cache->size > THIS->request->cache->max_size) + { + int i; + freed=0; + for(i = 0; i<CACHE_HTABLE_SIZE; i++) + { + p = THIS->request->cache->htable[i]; + ppp=pp=0; + while(p) + { + ppp = pp; + pp = p; + p = p->next; + } + if(pp) aap_free_cache_entry(THIS->request->cache,pp,ppp,i); + freed++; + if(THIS->request->cache->size < THIS->request->cache->max_size) + break; + } + if(!freed) /* no way.. */ + break; + } + } + ce = malloc(sizeof(struct cache_entry)); + MEMSET(ce, 0, sizeof(struct cache_entry)); + ce->stale_at = t+time_to_keep; + ce->data = reply; + ce->url = THIS->request->res.url; + ce->url_len = THIS->request->res.url_len; + reply->refs++; + + ce->host = THIS->request->res.host; + ce->host_len = THIS->request->res.host_len; + aap_cache_insert(ce, THIS->request->cache); + mt_unlock(&THIS->request->cache->mutex); + } + pop_stack(); + f_aap_reply(1); +} + +void f_aap_reqo__init(INT32 args) +{ + if(THIS->request->res.protocol) + SINSERT(THIS->misc_variables, s_prot, THIS->request->res.protocol); + IINSERT(THIS->misc_variables, s_time, aap_get_time()); + TINSERT(THIS->misc_variables, s_rawurl, + THIS->request->res.url, THIS->request->res.url_len); +} + +void aap_init_request_object(struct object *o) +{ + MEMSET(THIS, 0, sizeof(*THIS)); +} + +void aap_exit_request_object(struct object *o) +{ + if(THIS->request) + { + close(THIS->request->fd); + if(THIS->request->res.data) free(THIS->request->res.data); + free(THIS->request); + } + if(THIS->misc_variables) + free_mapping(THIS->misc_variables); + if(THIS->done_headers) + free_mapping(THIS->done_headers); +} +#endif diff --git a/src/modules/HTTPLoop/requestobject.h b/src/modules/HTTPLoop/requestobject.h new file mode 100644 index 0000000000..fbe34d1aec --- /dev/null +++ b/src/modules/HTTPLoop/requestobject.h @@ -0,0 +1,13 @@ +void f_aap_scan_for_query(INT32 args); +void f_aap_index_op(INT32 args); +void f_aap_end(INT32 args); +void f_aap_output(INT32 args); +void f_aap_reply(INT32 args); +void f_aap_reply_with_cache(INT32 args); +void f_aap_reqo__init(INT32 args); +void aap_init_request_object(struct object *o); +void aap_exit_request_object(struct object *o); + + + + diff --git a/src/modules/HTTPLoop/static_strings.h b/src/modules/HTTPLoop/static_strings.h new file mode 100644 index 0000000000..98ee9767fa --- /dev/null +++ b/src/modules/HTTPLoop/static_strings.h @@ -0,0 +1,42 @@ +/* methods */ + +STRING(s_http_09,"HTTP/0.9"); +STRING(s_http_10,"HTTP/1.0"); +STRING(s_http_11,"HTTP/1.1"); + +/* headers */ +STRING(s_user_agent,"user-agent"); +STRING(s_if_modified_since,"if-modified-since"); + +/* variables */ + +STRING(s_not_query,"not_query"); +STRING(s_query,"query"); +STRING(s_prestate,"prestate"); +STRING(s_time,"time"); +STRING(s_my_fd,"my_fd"); +STRING(s_prot,"prot"); +STRING(s_method,"method"); +STRING(s_rawurl,"rawurl"); +STRING(s_raw,"raw"); +STRING(s_data,"data"); +STRING(s_remoteaddr,"remoteaddr"); +STRING(s_headers,"headers"); +STRING(s_pragma,"pragma"); +STRING(s_client,"client"); +STRING(s_referer,"referer"); +STRING(s_since,"since"); + + +STRING(s_variables,"variables"); +STRING(s_rest_query,"rest_query"); + +/* To be implemented */ + +STRING(s_cookies,"cookies"); +/* STRING(s_config,"config"); */ + +STRING(s_rawauth,"rawauth"); +STRING(s_realauth,"realauth"); +STRING(s_supports, "supports"); + diff --git a/src/modules/HTTPLoop/test.pike b/src/modules/HTTPLoop/test.pike new file mode 100644 index 0000000000..9720abae60 --- /dev/null +++ b/src/modules/HTTPLoop/test.pike @@ -0,0 +1,100 @@ +string MP; + +class request_program +{ + inherit HTTPAccept.prog; +} + +string type_from_fname(string fname) +{ + if(sscanf(fname, "%*s.ht")) return "text/html"; + if(sscanf(fname, "%*s.gi")) return "image/gif"; + if(sscanf(fname, "%*s.jp")) return "image/jpeg"; + return "text/plain"; +} + +void handle(object o) +{ + object fd = Stdio.File(); + string q = o->not_query; + int len; + if(!strlen(q)) q="foo"; + if(q[-1]=='/') + q += "index.html"; + sscanf(q, "%*[/]%s", q); + if(fd->open(combine_path(MP, q), "r")) + { + len = fd->stat()[1]; + string data = + ("HTTP/1.0 200 Ok\r\n" + "Server: Quick'n'dirty\r\n" + "Content-type: "+type_from_fname(q)+"\r\n" + "Connection: Keep-Alive\r\n" + "Content-length: "+len+"\r\n" + "\r\n"); + if(len < 65535) + { + data += fd->read(); + o->reply_with_cache( data, 20 ); + } else { + o->reply(data, fd, len); + } + } else { + string er = "No such file"; + o->reply_with_cache("HTTP/1.0 402 No such file\r\n" + "Server: Quick'n'dirty\r\n" + "Content-type: text/plain\r\n" + "Connection: Keep-Alive\r\n" + "Content-length: "+strlen(er)+"\r\n" + "\r\n"+er, 200); + } +} + +function lw; +#define PCT(X) ((int)(((X)/(float)(c->total+0.1))*100)) + +void cs() +{ + call_out(cs, 10); + catch + { + mapping c = HTTPAccept.cache_status()[0]; + werror("\nCache statistics\n"); + c->total = c->hits + c->misses + c->stale; + werror(" %d elements in cache, size is %1.1fKb max is %1.1fMb\n" + " %d cache lookups, %d%% hits, %d%% misses and %d%% stale.\n", + c->entries, c->size/1024.0, c->max_size/(1024*1024.0), + c->total, PCT(c->hits), PCT(c->misses), PCT(c->stale)); + }; +} + +object port; +int main(int argc, array argv) +{ + MP = argv[-1]; + object t = Stdio.File(); + t->open("log", "cwt"); + + thread_create(lambda(object lf) + { + while(1) + { + HTTPAccept.log_as_commonlog_to_file(lf); + sleep(2); + } + }, t); + +#if efun(thread_set_concurrency) + thread_set_concurrency(10); +#endif + port = files.port(); + if(!port->bind(3000)) + { + werror("Bind failed.\n"); + return 1; + } + HTTPAccept.accept_http_loop( port, request_program, handle, 0, 1024*1024, 1); + call_out(cs, 0, 0); +// call_out(destruct, 20, port); + return -1; +} diff --git a/src/modules/HTTPLoop/testsuite.in b/src/modules/HTTPLoop/testsuite.in new file mode 100644 index 0000000000..eb84989585 --- /dev/null +++ b/src/modules/HTTPLoop/testsuite.in @@ -0,0 +1 @@ +// $Id: testsuite.in,v 1.1 1999/11/14 00:43:15 per Exp $ diff --git a/src/modules/HTTPLoop/timeout.c b/src/modules/HTTPLoop/timeout.c new file mode 100644 index 0000000000..35db6afec2 --- /dev/null +++ b/src/modules/HTTPLoop/timeout.c @@ -0,0 +1,263 @@ +#include "config.h" +#ifndef __NT__ +#include <global.h> +#include <threads.h> +#include <stralloc.h> +#include <signal.h> +#include <fdlib.h> + +#ifdef _REENTRANT +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "accept_and_parse.h" +#include "timeout.h" +#include "util.h" + +#ifdef HAVE_POLL +#ifdef HAVE_POLL_H +#include <poll.h> +#else /* !HAVE_POLL_H */ +#ifdef HAVE_SYS_POLL_H +#include <sys/poll.h> +#else /* !HAVE_SYS_POLL_H */ +#undef HAVE_POLL +#endif /* HAVE_SYS_POLL_H */ +#endif /* HAVE_POLL_H */ +#endif /* HAVE_POLL */ + +MUTEX_T aap_timeout_mutex; + +struct timeout { + int raised; + struct timeout *next, *prev; + THREAD_T thr; + int when; +} *first_timeout, *last_timeout; + +struct timeout *next_free_timeout; + + + +/* + * Debug code, since I for some reason always manage + * to mess up my double linked lists (this time I had forgot a mt_lock().. + */ +#ifdef DEBUG +extern int d_flag; +/* + * If I call the 'fatal' in pike, I run out of stack space. This is + * not a good idea, since you cannot debug this program with gdb + * if this happends. Thus this macro... + */ +#define FATAL(X) do{fprintf(stderr,"%s:%d: %s",__FILE__,__LINE__,X);abort();}while(0) + +static void debug_tchain(void) +{ + if(first_timeout) + { + struct timeout *t = first_timeout, *p=NULL; + if(!last_timeout) + FATAL("First timeout, but no last timeout.\n"); + if(last_timeout->next) + FATAL("Impossible timeout list: lt->next != NULL\n"); + + while( t ) + { + p = t; + t = t->next; + if(p == t) + FATAL("Impossible timeout list: t->next==t\n"); + if(t->prev != p) + FATAL("Impossible timeout list: t->prev!=p\n"); + } + if(p != last_timeout) + FATAL("End of to list != lt\n"); + } +} +#endif + + +#define CHUNK 1000 + +static struct timeout *new_timeout(THREAD_T thr, int secs) +{ + struct timeout *t; + if(next_free_timeout) + { + t = next_free_timeout; + next_free_timeout = t->next; + } else { + int i; + char *foo = malloc(sizeof(struct timeout) * CHUNK); + for(i = 1; i<CHUNK; i++) + { + struct timeout *c = (struct timeout *)(foo+(i*sizeof(struct timeout))); + c->next = next_free_timeout; + next_free_timeout = c; + } + t = (struct timeout *)foo; + } + t->next = NULL; + t->prev = NULL; + t->thr = thr; + t->raised = 0; + t->when = aap_get_time() + secs; + return t; +} + +static void free_timeout( struct timeout *t ) +{ + t->next = next_free_timeout; + next_free_timeout = t; +} + +int *aap_add_timeout_thr(THREAD_T thr, int secs) +{ + struct timeout *to; + mt_lock( &aap_timeout_mutex ); + to = new_timeout( thr, secs ); + if(last_timeout) + { + last_timeout->next = to; + to->prev = last_timeout; + last_timeout = to; + } else + last_timeout = first_timeout = to; +#ifdef DEBUG + if(d_flag>2) debug_tchain(); +#endif + mt_unlock( &aap_timeout_mutex ); + return &to->raised; +} + + +#ifndef OFFSETOF +#define OFFSETOF(str_type, field) ((long)& (((struct str_type *)0)->field)) +#endif + +void aap_remove_timeout_thr(int *to) +{ + mt_lock( &aap_timeout_mutex ); + { + /* This offset is most likely 0. + * But who knows what odd things compilers + * can come up with when you are not looking... + */ + struct timeout *t=(struct timeout *)(((char *)to) + -OFFSETOF(timeout,raised)); + if(t) + { + if(t->next) + t->next->prev = t->prev; + else + last_timeout = t->prev; + if(t->prev) + t->prev->next = t->next; + else + first_timeout = t->next; + free_timeout( t ); + } + } +#ifdef DEBUG + if(d_flag>2) debug_tchain(); +#endif + mt_unlock( &aap_timeout_mutex ); +} + +static volatile int aap_time_to_die = 0; + +static void *handle_timeouts(void *ignored) +{ + while(!aap_time_to_die) + { + if(first_timeout) + { + mt_lock( &aap_timeout_mutex ); + { + struct timeout *t = first_timeout; + if(t) + { + if(t->when < aap_get_time()) + { + t->raised++; + th_kill( t->thr, SIGCHLD ); + } + if(last_timeout != first_timeout) + { + first_timeout = t->next; + first_timeout->prev = NULL; + t->next=NULL; + last_timeout->next = t; + t->prev = last_timeout; + last_timeout = t; + } + } + } + mt_unlock( &aap_timeout_mutex ); + } +#ifdef DEBUG + if(d_flag>2) debug_tchain(); +#endif +#ifdef HAVE_POLL + poll(0,0,300); +#else + sleep(1); +#endif + } + /* + * Now we're dead... + */ +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: handle_timeout() is now dead.\n"); +#endif /* AAP_DEBUG */ + return(NULL); +} + +static THREAD_T aap_timeout_thread; + +void aap_init_timeouts(void) +{ +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: aap_init_timeouts.\n"); +#endif /* AAP_DEBUG */ + mt_init(&aap_timeout_mutex); + th_create_small(&aap_timeout_thread, handle_timeouts, 0); +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: handle_timeouts started.\n"); +#endif /* AAP_DEBUG */ +} + +void aap_exit_timeouts(void) +{ + void *res; + +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: aap_exit_timeouts.\n"); +#endif /* AAP_DEBUG */ + aap_time_to_die = 1; + if (thread_id) { + THREADS_ALLOW(); + th_join(aap_timeout_thread, &res); + THREADS_DISALLOW(); + } else { + th_join(aap_timeout_thread, &res); + } +#ifdef AAP_DEBUG + fprintf(stderr, "AAP: aap_exit_timeouts done.\n"); +#endif /* AAP_DEBUG */ +} + +#endif +#endif diff --git a/src/modules/HTTPLoop/timeout.h b/src/modules/HTTPLoop/timeout.h new file mode 100644 index 0000000000..246616ae39 --- /dev/null +++ b/src/modules/HTTPLoop/timeout.h @@ -0,0 +1,8 @@ +#if 0 +void aap_init_timeouts(void); +void aap_exit_timeouts(void); +void aap_remove_timeout_thr(int *hmm); +int *aap_add_timeout_thr(THREAD_T thr, int secs); + +extern MUTEX_T aap_timeout_mutex; +#endif diff --git a/src/modules/HTTPLoop/util.c b/src/modules/HTTPLoop/util.c new file mode 100644 index 0000000000..ff9ab2c614 --- /dev/null +++ b/src/modules/HTTPLoop/util.c @@ -0,0 +1,105 @@ +#include <global.h> +#include <threads.h> +#include <fdlib.h> + +#ifdef _REENTRANT +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#include "accept_and_parse.h" +#include "util.h" + + +int aap_get_time(void) +{ + static int t = 0; + static int last_time; + if(!(t++%10)) last_time = TIME(0); + return last_time; +} + +int aap_swrite(int to, char *buf, int towrite) +{ + int res=0; + int sent = 0; + while(towrite) + { + while((res = fd_write(to, buf, towrite)) < 0) + { + switch(errno) + { + case EAGAIN: + case EINTR: + continue; + + default: + if(errno != EPIPE) + perror("accept_and_parse->request->shuffle: While writing"); + res = 0; + return sent; + } + } + towrite -= res; + buf += res; + sent += res; + } + return sent; +} + +int aap_get_header(struct args *req, char *header, int operation, void *res) +{ + int os=0,i,hl=strlen(header),l=req->res.body_start-req->res.header_start; + char *in = req->res.data + req->res.header_start; + for(i=0; i<l; i++) + { + switch(in[i]) + { + case ':': + /* in[os..i-1] == the header */ + if(i-os == hl) + { + int j; + for(j=0;j<hl; j++) + if((in[os+j]&95) != (header[j]&95)) + break; /* no match */ + if(j == hl) + { + struct pstring *r = res; + switch(operation) + { + case H_EXISTS: + return 1; + case H_INT: + *((int *)res) = atoi(in+i+1); + return 1; + case H_STRING: + os = i+1; + for(j=os;j<l;j++) if(in[j] == '\r') break; + /* okie.. */ + while(in[os]==' ') os++; + r->len = j-os; + r->str = in+os; + return 1; + } + } + } + case '\r': + case '\n': + os = i+1; + } + } + return 0; +} +#endif + diff --git a/src/modules/HTTPLoop/util.h b/src/modules/HTTPLoop/util.h new file mode 100644 index 0000000000..0078f90c87 --- /dev/null +++ b/src/modules/HTTPLoop/util.h @@ -0,0 +1,10 @@ +int aap_get_time(void); +int aap_swrite(int to, char *buf, int towrite); + +#define H_EXISTS 0 +#define H_INT 1 +#define H_STRING 2 + + +int aap_get_header(struct args *req, char *header, int operation, void *res); + diff --git a/src/modules/HTTPLoop/wwwserver.pike b/src/modules/HTTPLoop/wwwserver.pike new file mode 100644 index 0000000000..a1182ff179 --- /dev/null +++ b/src/modules/HTTPLoop/wwwserver.pike @@ -0,0 +1,194 @@ +import Stdio; + +mapping exts = ([]); +constant days = ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }); +constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }); + +constant prefix = ({ "bytes", "kB", "MB", "GB", "TB", "HB"}); + +string sizetostring( int size ) +{ + int s; + if(size<0) + return "--------"; + s=size*10; + size = 0; + while( s > 10240 || size==0) + { + s /= 1024; + size ++; + } + return sprintf("%d.%01d %s", s/10, s%10, prefix[ size ]); +} + + +// The server root. +#ifdef __NT__ +#define BASE "H:\\html\\per.hedbor.org\\" +#else +#define BASE "/tmp/" +#endif + +#define PORT 4002 + +string http_date(int t) +{ + mapping l = localtime(t); + +#if 1 + + t += l->timezone - 3600*l->isdst; + l = localtime(t); + + return(sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", + days[l->wday], l->mday, months[l->mon], 1900+l->year, + l->hour, l->min, l->sec)); + +#else + string s=ctime(t + l->timezone - 3600*l->isdst); + return (s[0..2] + sprintf(", %02d ", (int)s[8..9]) + + s[4..6]+" "+(1900+l->year) + + s[10..18]+" GMT"); +#endif /* 1 */ +} + + +class request_program +{ + inherit HTTPAccept.prog; +} + +void handle(object o) +{ + string url = o->not_query; + string f = combine_path(BASE, url[1..]); + array s; + if(f[-1] == '/' && (s = file_stat(f+"index.html"))) { + url += "index.html"; + f += "index.html"; + } else + s = file_stat(f); + if(!s || s[1] == -1 || search(f, BASE) == -1) + { + string nofile = "<title>No such file</title>" + "<h1>No such file or directory: "+f+"</h1>"; + o->reply_with_cache("HTTP/1.0 404 Unknown file\r\n" + "Content-type: text/html\r\n" + "Content-Length: "+strlen(nofile)+"\r\n" + "MIME-Version: 1.0\r\n" + "Date: "+http_date(time(1))+"\r\n" + "Server: Neo-FastSpeed\r\n" + "Connection: Keep-Alive\r\n" + "\r\n" + +nofile, 60); + } + else if(s[1] == -2) + { + if(f[-1] == '/' || f[-1]=='\\') + { + string head = ("HTTP/1.1 200 Ok\r\n" + "Content-type: text/html\r\n" + "Date: "+http_date(time(1))+"\r\n" + "Last-Modified: "+http_date(s[4])+"\r\n" + "MIME-Version: 1.0\r\n" + "Server: Neo-FastSpeed\r\n" + "Connection: Keep-Alive\r\n"); + string res = ("<title>Directory listing of "+url+"</title>" + "<h1 align=center>Directory listing of "+ + url+"</h1>\n<pre>"); + if(url != "/") + res += "<b><a href=../>Parent Directory...</a></b>\n\n"; + string file2; + foreach(sort(get_dir(f)||({})), string file) { + int size = file_size(f+file); + int islink; +#if constant(readlink) + catch { readlink(f+file); islink = 1; }; +#endif + string ext = (file / ".") [-1]; + string ctype = (size >= 0 ? exts[ext] || "text/plain" : + "Directory"); + if(size < -1) + file += "/"; + if(islink) + file2 = "@"+file; + else + file2 = file; + res += sprintf("<a href=%s>%-30s</a> %15s %s\n", + file, file2, sizetostring(size), + ctype); + } + return o->reply_with_cache(head + + "Content-Length: "+strlen(res)+ + "\r\n\r\n"+res, 30); + } else { + o->reply_with_cache("HTTP/1.0 302 Redirect\r\n" + "Content-type: text/plain\r\n" + "Date: "+http_date(time(1))+"\r\n" + "MIME-Version: 1.0\r\n" + "Server: Neo-FastSpeed\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: 0\r\n" + "Location: "+url+"/"+ + (o->query ? "?"+o->query : "")+ + "\r\n\r\n", 60); + } + } else { + string ext = (f / ".") [-1]; + string ctype = exts[ext] || "text/plain"; +#if 1 + o->reply_with_cache("HTTP/1.1 200 Ok\r\n" + "Content-type: "+ctype+"\r\n" + "Content-Length: "+s[1]+"\r\n" + "Date: "+http_date(time(1))+"\r\n" + "Last-Modified: "+http_date(s[4])+"\r\n" + "MIME-Version: 1.0\r\n" + "Server: Neo-FastSpeed\r\n" + "Connection: Keep-Alive\r\n" + "\r\n"+ + read_bytes(f), 20); +#else + o->reply("HTTP/1.1 200 Ok\r\n" + "Content-type: "+ctype+"\r\n" + "Content-Length: "+s[1]+"\r\n" + "Date: "+http_date(time(1))+"\r\n" + "Last-Modified: "+http_date(s[4])+"\r\n" + "MIME-Version: 1.0\r\n" + "Server: Neo-FastSpeed\r\n" + "Connection: Keep-Alive\r\n" + "\r\n", + Stdio.File(f, "r"), s[1]); +#endif + destruct(o); + } +} + +object port; +object l; + +int main(int argc, array (string) argv) +{ +#if efun(thread_set_concurrency) + thread_set_concurrency(100); +#endif + port = Stdio.Port(); + array foo; + foreach(read_file((argv[0] - "wwwserver.pike") + "extensions")/"\n", + string s) { + if(strlen(s) && s[0] != '#' && (foo = (s / "\t" - ({""}))) && + sizeof(foo) == 2) + exts[foo[0]] = foo[1]; + } + werror(sprintf("Found %d extensions...\n", sizeof(exts))); + if(!port->bind(PORT)) + { + werror("Bind failed.\n"); + return 1; + } + + l = HTTPAccept.Loop( port, request_program, handle, 0, 1024*1024, 0, + 0); + werror("WWW-server listening to port "+PORT+".\n"); + return -1; +} -- GitLab