Index: Makefile.in =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/Makefile.in,v retrieving revision 1.1.1.2 retrieving revision 1.2 diff -c -r1.1.1.2 -r1.2 *** Makefile.in 2002/02/05 22:52:50 1.1.1.2 --- Makefile.in 2002/06/09 21:31:34 1.2 *************** *** 48,54 **** CC = @CC@ CCOPT = @V_CCOPT@ ! DEFS = @DEFS@ INCLS = -I. CFLAGS = $(CCOPT) $(DEFS) $(INCLS) LDFLAGS = @LDFLAGS@ --- 48,54 ---- CC = @CC@ CCOPT = @V_CCOPT@ ! DEFS = @DEFS@ -DUSE_SENDFILE=1 INCLS = -I. CFLAGS = $(CCOPT) $(DEFS) $(INCLS) LDFLAGS = @LDFLAGS@ Index: config.h =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/config.h,v retrieving revision 1.1.1.2 retrieving revision 1.2 diff -c -r1.1.1.2 -r1.2 *** config.h 2002/02/05 22:52:51 1.1.1.2 --- config.h 2002/09/24 15:22:47 1.2 *************** *** 126,132 **** --- 126,134 ---- ** If you undefine this then thttpd will not implement authentication ** at all and will not check for auth files, which saves a bit of CPU time. */ + #if 0 #define AUTH_FILE ".htpasswd" + #endif /* CONFIGURE: The default character set name to use with text MIME types. ** This gets substituted into the MIME types where they have a "%s". Index: configure =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/configure,v retrieving revision 1.1.1.2 retrieving revision 1.2 diff -c -r1.1.1.2 -r1.2 *** configure 2002/02/05 22:52:51 1.1.1.2 --- configure 2002/09/24 15:22:47 1.2 *************** *** 867,877 **** echo "$ac_t""$ac_cv_lbl_gcc_vers" 1>&6 if test "$ac_cv_lbl_gcc_vers" -gt 1 ; then ! V_CCOPT="-O2" fi fi if test -f .devel ; then ! V_CCOPT="-g $V_CCOPT -Wall -Wmissing-prototypes -Wstrict-prototypes" fi echo $ac_n "checking how to link static binaries""... $ac_c" 1>&6 --- 867,877 ---- echo "$ac_t""$ac_cv_lbl_gcc_vers" 1>&6 if test "$ac_cv_lbl_gcc_vers" -gt 1 ; then ! V_CCOPT="-O2 -Wall -Wmissing-prototypes -Wstrict-prototypes" fi fi if test -f .devel ; then ! V_CCOPT="-g $V_CCOPT" fi echo $ac_n "checking how to link static binaries""... $ac_c" 1>&6 Index: fdwatch.c =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/fdwatch.c,v retrieving revision 1.1.1.2 retrieving revision 1.4 diff -c -r1.1.1.2 -r1.4 *** fdwatch.c 2002/02/05 22:52:54 1.1.1.2 --- fdwatch.c 2002/09/24 15:22:48 1.4 *************** *** 95,100 **** --- 95,101 ---- # endif /* HAVE_POLL */ #endif /* HAVE_KQUEUE */ + int sendLowWater = 0; /* Routines. */ *************** *** 239,245 **** void* ! fdwatch_get_client_data( int ridx ) { int fd; --- 240,246 ---- void* ! fdwatch_get_client_data( int ridx, int *fdstateP ) { int fd; *************** *** 259,264 **** --- 260,266 ---- if ( fd < 0 || fd >= nfiles ) return (void*) 0; + *fdstateP = fd_rw[fd]; return fd_data[fd]; } *************** *** 293,298 **** --- 295,302 ---- #ifdef HAVE_KQUEUE + /* use separate change and event lists */ + static struct kevent* kqchangelist; static struct kevent* kqevents; static int nkqevents; static int* kqrfdidx; *************** *** 306,316 **** if ( kq == -1 ) return -1; kqevents = (struct kevent*) malloc( sizeof(struct kevent) * nfiles ); kqrfdidx = (int*) malloc( sizeof(int) * nfiles ); ! if ( kqevents == (struct kevent*) 0 || kqrfdidx == (int*) 0 ) return -1; (void) memset( kqevents, 0, sizeof(struct kevent) * nfiles ); (void) memset( kqrfdidx, 0, sizeof(int) * nfiles ); return 0; } --- 310,326 ---- if ( kq == -1 ) return -1; kqevents = (struct kevent*) malloc( sizeof(struct kevent) * nfiles ); + kqchangelist = (struct kevent*) malloc( sizeof(struct kevent) * nfiles ); kqrfdidx = (int*) malloc( sizeof(int) * nfiles ); ! ! if ( kqevents == (struct kevent*) 0 || kqrfdidx == (int*) 0 ! || kqchangelist == (struct kevent*) 0 ) return -1; + (void) memset( kqevents, 0, sizeof(struct kevent) * nfiles ); (void) memset( kqrfdidx, 0, sizeof(int) * nfiles ); + (void) memset( kqchangelist, 0, sizeof(struct kevent) * nfiles ); + return 0; } *************** *** 318,329 **** static void kqueue_add_fd( int fd, int rw ) { ! kqevents[nkqevents].ident = fd; ! kqevents[nkqevents].flags = EV_ADD; switch ( rw ) { ! case FDW_READ: kqevents[nkqevents].filter = EVFILT_READ; break; ! case FDW_WRITE: kqevents[nkqevents].filter = EVFILT_WRITE; break; default: break; } ++nkqevents; --- 328,353 ---- static void kqueue_add_fd( int fd, int rw ) { ! kqchangelist[nkqevents].ident = fd; ! kqchangelist[nkqevents].flags = EV_ADD; switch ( rw ) { ! case FDW_READ: kqchangelist[nkqevents].filter = EVFILT_READ; ! kqchangelist[nkqevents].fflags = 0; ! break; ! case FDW_WRITE: kqchangelist[nkqevents].filter = EVFILT_WRITE; ! if (0 == sendLowWater) ! kqchangelist[nkqevents].fflags = 0; ! else { ! kqchangelist[nkqevents].fflags = NOTE_LOWAT; ! kqchangelist[nkqevents].data = sendLowWater; ! } ! break; ! case FDW_VNODE: kqchangelist[nkqevents].filter = EVFILT_VNODE; ! kqchangelist[nkqevents].flags |= EV_ENABLE | EV_CLEAR; ! kqchangelist[nkqevents].fflags = NOTE_DELETE | NOTE_WRITE | ! NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE; ! break; default: break; } ++nkqevents; *************** *** 333,344 **** static void kqueue_del_fd( int fd ) { ! kqevents[nkqevents].ident = fd; ! kqevents[nkqevents].flags = EV_DELETE; switch ( fd_rw[fd] ) { ! case FDW_READ: kqevents[nkqevents].filter = EVFILT_READ; break; ! case FDW_WRITE: kqevents[nkqevents].filter = EVFILT_WRITE; break; } ++nkqevents; } --- 357,371 ---- static void kqueue_del_fd( int fd ) { ! kqchangelist[nkqevents].ident = fd; ! kqchangelist[nkqevents].flags = EV_DELETE; ! kqchangelist[nkqevents].fflags = 0; switch ( fd_rw[fd] ) { ! case FDW_READ: kqchangelist[nkqevents].filter = EVFILT_READ; break; ! case FDW_WRITE: kqchangelist[nkqevents].filter = EVFILT_WRITE; break; ! case FDW_VNODE: kqchangelist[nkqevents].filter = EVFILT_VNODE; break; ! default: break; } ++nkqevents; } *************** *** 350,363 **** int i, r; if ( timeout_msecs == INFTIM ) ! r = kevent( ! kq, kqevents, nkqevents, kqevents, nfiles, (struct timespec*) 0 ); else { struct timespec ts; ts.tv_sec = timeout_msecs / 1000L; ts.tv_nsec = ( timeout_msecs % 1000L ) * 1000000L; ! r = kevent( kq, kqevents, nkqevents, kqevents, nfiles, &ts ); } nkqevents = 0; if ( r == -1 ) --- 377,390 ---- int i, r; if ( timeout_msecs == INFTIM ) ! r = kevent( kq, kqchangelist, nkqevents, kqevents, nfiles, ! (struct timespec*) 0 ); else { struct timespec ts; ts.tv_sec = timeout_msecs / 1000L; ts.tv_nsec = ( timeout_msecs % 1000L ) * 1000000L; ! r = kevent( kq, kqchangelist, nkqevents, kqevents, nfiles, &ts ); } nkqevents = 0; if ( r == -1 ) *************** *** 381,389 **** return 0; switch ( fd_rw[fd] ) { ! case FDW_READ: return kqevents[ridx].filter == EVFILT_READ; ! case FDW_WRITE: return kqevents[ridx].filter == EVFILT_WRITE; ! default: return 0; } } --- 408,421 ---- return 0; switch ( fd_rw[fd] ) { ! case FDW_READ: ! return kqevents[ridx].filter == EVFILT_READ; ! case FDW_WRITE: ! return kqevents[ridx].filter == EVFILT_WRITE; ! case FDW_VNODE: ! return kqevents[ridx].filter == EVFILT_VNODE; ! default: ! return 0; } } Index: fdwatch.h =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/fdwatch.h,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -c -r1.1.1.1 -r1.2 *** fdwatch.h 2001/12/10 19:23:47 1.1.1.1 --- fdwatch.h 2002/09/24 15:22:48 1.2 *************** *** 53,58 **** --- 53,59 ---- #define FDW_READ 0 #define FDW_WRITE 1 + #define FDW_VNODE 2 #ifndef INFTIM #define INFTIM -1 *************** *** 81,89 **** /* Get the client data for an event. The argument is an index into the ** set of ready descriptors returned by fdwatch(). */ ! extern void* fdwatch_get_client_data( int ridx ); /* Generate debugging statistics syslog message. */ extern void fdwatch_logstats( long secs ); #endif /* _FDWATCH_H_ */ --- 82,93 ---- /* Get the client data for an event. The argument is an index into the ** set of ready descriptors returned by fdwatch(). */ ! extern void* fdwatch_get_client_data( int ridx, int *fdstateP ); /* Generate debugging statistics syslog message. */ extern void fdwatch_logstats( long secs ); + + /* low water mark for use with kevent/sendfile */ + extern int sendLowWater; #endif /* _FDWATCH_H_ */ Index: libhttpd.c =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/libhttpd.c,v retrieving revision 1.1.1.2 retrieving revision 1.3 diff -c -r1.1.1.2 -r1.3 *** libhttpd.c 2002/02/05 22:52:52 1.1.1.2 --- libhttpd.c 2002/09/24 15:22:48 1.3 *************** *** 35,40 **** --- 35,42 ---- #define EXPOSED_SERVER_SOFTWARE "thttpd" #endif /* SHOW_SERVER_VERSION */ + #define EXPOSED_SERVER_OK " 200 OK\r\nServer: " EXPOSED_SERVER_SOFTWARE "\r\nAccept-Ranges: bytes\r\nDate: " + #include #include #include *************** *** 99,104 **** --- 101,110 ---- #define SHUT_WR 1 #endif + #ifndef __FreeBSD__ + #undef USE_SENDFILE + #endif + #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif *************** *** 107,112 **** --- 113,122 ---- #endif + #if defined(__i386) && defined(__FreeBSD__) + #include + #endif + /* Forwards. */ static void child_reaper( ClientData client_data, struct timeval* nowP ); static int do_reap( void ); *************** *** 115,121 **** static int initialize_listen_socket( httpd_sockaddr* saP ); static void unlisten( httpd_server* hs ); static void add_response( httpd_conn* hc, char* str ); ! static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod ); static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ); static void send_response_tail( httpd_conn* hc ); static void defang( char* str, char* dfstr, int dfsize ); --- 125,132 ---- static int initialize_listen_socket( httpd_sockaddr* saP ); static void unlisten( httpd_server* hs ); static void add_response( httpd_conn* hc, char* str ); ! static void add_response_len( httpd_conn* hc, char* str, int len ); ! static void send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, char *mtime, char *fileInfo, int fileInfoLen ); static void send_response( httpd_conn* hc, int status, char* title, char* extraheads, char* form, char* arg ); static void send_response_tail( httpd_conn* hc ); static void defang( char* str, char* dfstr, int dfsize ); *************** *** 141,151 **** static int tilde_map_2( httpd_conn* hc ); #endif /* TILDE_MAP_2 */ static int vhost_map( httpd_conn* hc ); ! static char* expand_symlinks( char* path, char** restP, int no_symlink, int tildemapped ); ! static char* bufgets( httpd_conn* hc ); ! static void de_dotdot( char* file ); static void init_mime( void ); - static void figure_mime( httpd_conn* hc ); #ifdef CGI_TIMELIMIT static void cgi_kill2( ClientData client_data, struct timeval* nowP ); static void cgi_kill( ClientData client_data, struct timeval* nowP ); --- 152,161 ---- static int tilde_map_2( httpd_conn* hc ); #endif /* TILDE_MAP_2 */ static int vhost_map( httpd_conn* hc ); ! static char* expand_symlinks( httpd_conn* hc, char* path, char** restP, int no_symlink, int tildemapped ); ! static char* bufgets( httpd_conn* hc, int *buflen ); ! static void de_dotdot( char* file, int *newlen ); static void init_mime( void ); #ifdef CGI_TIMELIMIT static void cgi_kill2( ClientData client_data, struct timeval* nowP ); static void cgi_kill( ClientData client_data, struct timeval* nowP ); *************** *** 175,180 **** --- 185,197 ---- static int reap_time; + /* Cache the current timestamp, set in fill_nowbuf */ + const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT\r\n"; + static char nowbuf[40]; + + /* Length of "OK" string */ + static int server_ok_len = 0; + static void child_reaper( ClientData client_data, struct timeval* nowP ) { *************** *** 577,585 **** static void add_response( httpd_conn* hc, char* str ) { ! int len; ! len = strlen( str ); httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len ); (void) memcpy( &(hc->response[hc->responselen]), str, len ); hc->responselen += len; --- 594,606 ---- static void add_response( httpd_conn* hc, char* str ) { ! add_response_len( hc, str, strlen(str) ); ! } ! /* Append a string to the buffer waiting to be sent as response. */ ! static void ! add_response_len( httpd_conn* hc, char* str, int len ) ! { httpd_realloc_str( &hc->response, &hc->maxresponse, hc->responselen + len ); (void) memcpy( &(hc->response[hc->responselen]), str, len ); hc->responselen += len; *************** *** 631,644 **** } } static void ! send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, time_t mod ) { - time_t now; - const char* rfc1123fmt = "%a, %d %b %Y %H:%M:%S GMT"; - char nowbuf[100]; - char modbuf[100]; char fixed_type[500]; char buf[1000]; int partial_content; --- 652,672 ---- } } + void + time_rfc1123( char *buf, int sz, time_t tm) + { + (void) strftime( buf, sz, rfc1123fmt, gmtime( &tm ) ); + } + + void + fill_nowbuf( time_t now ) + { + time_rfc1123(nowbuf, sizeof(nowbuf), now); + } static void ! send_mime( httpd_conn* hc, int status, char* title, char* encodings, char* extraheads, char* type, int length, char *mtime, char *fileInfo, int fileInfoLen ) { char fixed_type[500]; char buf[1000]; int partial_content; *************** *** 661,678 **** else partial_content = 0; ! now = time( (time_t*) 0 ); ! if ( mod == (time_t) 0 ) ! mod = now; ! (void) strftime( nowbuf, sizeof(nowbuf), rfc1123fmt, gmtime( &now ) ); ! (void) strftime( modbuf, sizeof(modbuf), rfc1123fmt, gmtime( &mod ) ); ! (void) my_snprintf( ! fixed_type, sizeof(fixed_type), type, hc->hs->charset ); ! (void) my_snprintf( buf, sizeof(buf), ! "%.20s %d %s\r\nServer: %s\r\nContent-Type: %s\r\nDate: %s\r\nLast-Modified: %s\r\nAccept-Ranges: bytes\r\nConnection: close\r\n", ! hc->protocol, status, title, EXPOSED_SERVER_SOFTWARE, fixed_type, ! nowbuf, modbuf ); add_response( hc, buf ); if ( encodings[0] != '\0' ) { (void) my_snprintf( buf, sizeof(buf), --- 689,741 ---- else partial_content = 0; ! if ( NULL == mtime ) ! mtime = nowbuf; ! ! if (hc->keep_alive && !hc->forbid_keep_alive && ! (length >= 0) && (200 == status)) ! hc->kept_alive = 1; ! ! memcpy(buf, hc->protocol, 20); ! buf[20] = '\0'; add_response( hc, buf ); + + if (200 == status) { + add_response_len( hc, EXPOSED_SERVER_OK, server_ok_len); + } + else { + (void) my_snprintf( buf, sizeof(buf), " %d %s\r\nServer: %s\r\nAccept-Ranges: bytes\r\nDate: ", + status, title, EXPOSED_SERVER_SOFTWARE ); + add_response( hc, buf ); + } + + add_response_len( hc, nowbuf, 31 ); + + if (fileInfo && (fileInfoLen > 0) && !partial_content) { + add_response_len( hc, fileInfo, fileInfoLen); + } + else { + (void) my_snprintf( fixed_type, sizeof(fixed_type), type, hc->hs->charset ); + (void) my_snprintf( buf, sizeof(buf), + "Content-Type: %s\r\nLast-Modified: %s", + fixed_type, mtime); + add_response( hc, buf ); + + if ( !partial_content && (length >= 0) ) + { + (void) my_snprintf( buf, sizeof(buf), + "Content-Length: %d\r\n", length ); + add_response( hc, buf ); + } + } + + if (hc->kept_alive) { + add_response_len( hc, "Connection: keep-alive\r\n", 24); + } + else { + add_response_len( hc, "Connection: close\r\n", 19); + } + if ( encodings[0] != '\0' ) { (void) my_snprintf( buf, sizeof(buf), *************** *** 687,698 **** (long) ( hc->end_byte_loc - hc->init_byte_loc + 1 ) ); add_response( hc, buf ); } - else if ( length >= 0 ) - { - (void) my_snprintf( buf, sizeof(buf), - "Content-Length: %d\r\n", length ); - add_response( hc, buf ); - } if ( hc->hs->p3p[0] != '\0' ) { (void) my_snprintf( buf, sizeof(buf), "P3P: %s\r\n", hc->hs->p3p ); --- 750,755 ---- *************** *** 700,706 **** } if ( extraheads[0] != '\0' ) add_response( hc, extraheads ); ! add_response( hc, "\r\n" ); } } --- 757,763 ---- } if ( extraheads[0] != '\0' ) add_response( hc, extraheads ); ! add_response_len( hc, "\r\n", 2 ); } } *************** *** 709,715 **** static long str_alloc_size = 0; void ! httpd_realloc_str( char** strP, int* maxsizeP, int size ) { if ( *maxsizeP == 0 ) { --- 766,772 ---- static long str_alloc_size = 0; void ! httpd_realloc_str_real( char** strP, int* maxsizeP, int size ) { if ( *maxsizeP == 0 ) { *************** *** 727,732 **** --- 784,790 ---- } else return; + if ( *strP == (char*) 0 ) { syslog( *************** *** 742,748 **** { char defanged_arg[1000], buf[2000]; ! send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 ); (void) my_snprintf( buf, sizeof(buf), "%d %s\n

%d %s

\n", status, title, status, title ); --- 800,806 ---- { char defanged_arg[1000], buf[2000]; ! send_mime( hc, status, title, "", extraheads, "text/html", -1, 0, NULL, 0 ); (void) my_snprintf( buf, sizeof(buf), "%d %s\n

%d %s

\n", status, title, status, title ); *************** *** 851,857 **** fp = fopen( filename, "r" ); if ( fp == (FILE*) 0 ) return 0; ! send_mime( hc, status, title, "", extraheads, "text/html", -1, 0 ); for (;;) { r = fread( buf, 1, sizeof(buf) - 1, fp ); --- 909,915 ---- fp = fopen( filename, "r" ); if ( fp == (FILE*) 0 ) return 0; ! send_mime( hc, status, title, "", extraheads, "text/html", -1, 0, NULL, 0 ); for (;;) { r = fread( buf, 1, sizeof(buf) - 1, fp ); *************** *** 1313,1319 **** (void) strcat( hc->altdir, "/" ); (void) strcat( hc->altdir, postfix ); } ! alt = expand_symlinks( hc->altdir, &rest, 0, 1 ); if ( rest[0] != '\0' ) return 0; httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) ); --- 1371,1377 ---- (void) strcat( hc->altdir, "/" ); (void) strcat( hc->altdir, postfix ); } ! alt = expand_symlinks( hc, hc->altdir, &rest, 0, 1 ); if ( rest[0] != '\0' ) return 0; httpd_realloc_str( &hc->altdir, &hc->maxaltdir, strlen( alt ) ); *************** *** 1428,1434 **** ** without excessive mallocs. */ static char* ! expand_symlinks( char* path, char** restP, int no_symlink, int tildemapped ) { static char* checked; static char* rest; --- 1486,1493 ---- ** without excessive mallocs. */ static char* ! expand_symlinks( httpd_conn* hc, char* path, char** restP, ! int no_symlink, int tildemapped ) { static char* checked; static char* rest; *************** *** 1453,1461 **** ** approved CGIs any more. The workaround is to use the central ** URL for the CGI instead of a local symlinked one. */ ! struct stat sb; ! if ( stat( path, &sb ) != -1 ) { checkedlen = strlen( path ); httpd_realloc_str( &checked, &maxchecked, checkedlen ); (void) strcpy( checked, path ); --- 1512,1521 ---- ** approved CGIs any more. The workaround is to use the central ** URL for the CGI instead of a local symlinked one. */ ! if ( (mmc_statlookup( path, &(hc->sb), &(hc->fn_hash)) != 0) || ! (stat( path, &(hc->sb) ) != -1) ) { + hc->sb_inited = 1; checkedlen = strlen( path ); httpd_realloc_str( &checked, &maxchecked, checkedlen ); (void) strcpy( checked, path ); *************** *** 1644,1649 **** --- 1704,1788 ---- return checked; } + void + httpd_init_conn( httpd_conn *hc ) + { + #ifdef TILDE_MAP_2 + hc->maxaltdir = 0; + #endif /* TILDE_MAP_2 */ + hc->read_idx = 0; + hc->checked_idx = 0; + hc->next_req_idx = 0; + hc->checked_state = CHST_FIRSTWORD; + hc->method = METHOD_UNKNOWN; + hc->status = 0; + hc->bytes_to_send = 0; + hc->bytes_sent = 0; + hc->encodedurl = ""; + hc->decodedurl[0] = '\0'; + hc->protocol = "UNKNOWN"; + hc->origfilename[0] = '\0'; + hc->expnfilename[0] = '\0'; + hc->encodings[0] = '\0'; + hc->pathinfo[0] = '\0'; + hc->query[0] = '\0'; + hc->referer = ""; + hc->useragent = ""; + hc->accept[0] = '\0'; + hc->accepte[0] = '\0'; + hc->acceptl = ""; + hc->cookie = ""; + hc->contenttype = ""; + hc->reqhost[0] = '\0'; + hc->hdrhost = ""; + hc->hostdir[0] = '\0'; + hc->authorization = ""; + hc->remoteuser[0] = '\0'; + hc->response[0] = '\0'; + #ifdef TILDE_MAP_2 + hc->altdir[0] = '\0'; + #endif /* TILDE_MAP_2 */ + hc->responselen = 0; + hc->if_modified_since = (time_t) -1; + hc->range_if = (time_t) -1; + hc->contentlength = -1; + hc->type = ""; + hc->hostname = (char*) 0; + hc->mime_flag = 1; + hc->one_one = 0; + hc->got_range = 0; + hc->tildemapped = 0; + hc->init_byte_loc = 0; + hc->end_byte_loc = -1; + hc->keep_alive = 0; + hc->kept_alive = 0; + hc->forbid_keep_alive = 0; + hc->should_linger = 0; + hc->file_address = (char*) 0; + } + + + void + httpd_init_pconn( httpd_conn* hc ) + { + /* Stuff that must survive over persistent connection. + * Currently we need only variables related to the read buffer + */ + int next_req_idx = hc->next_req_idx; + int checked_idx = hc->checked_idx; + int read_idx = hc->read_idx; + + /* Keep accumulating requests in the read buffer. + * handle_read relocates the buffer if necessary. + */ + + httpd_init_conn( hc ); + + hc->next_req_idx = next_req_idx; + hc->checked_idx = checked_idx; + hc->read_idx = read_idx; + } + int httpd_get_conn( httpd_server* hs, int listen_fd, httpd_conn* hc ) *************** *** 1653,1668 **** if ( ! hc->initialized ) { ! hc->read_size = 0; ! httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 ); ! hc->maxdecodedurl = hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = hc->maxpathinfo = hc->maxquery = hc->maxaccept = hc->maxaccepte = hc->maxreqhost = hc->maxhostdir = hc->maxremoteuser = hc->maxresponse = 0; ! #ifdef TILDE_MAP_2 ! hc->maxaltdir = 0; ! #endif /* TILDE_MAP_2 */ httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 ); httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 ); httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 ); --- 1792,1804 ---- if ( ! hc->initialized ) { ! hc->read_size = 0; ! hc->maxdecodedurl = hc->maxorigfilename = hc->maxexpnfilename = hc->maxencodings = hc->maxpathinfo = hc->maxquery = hc->maxaccept = hc->maxaccepte = hc->maxreqhost = hc->maxhostdir = hc->maxremoteuser = hc->maxresponse = 0; ! httpd_realloc_str( &hc->read_buf, &hc->read_size, 500 ); httpd_realloc_str( &hc->decodedurl, &hc->maxdecodedurl, 1 ); httpd_realloc_str( &hc->origfilename, &hc->maxorigfilename, 1 ); httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, 0 ); *************** *** 1678,1683 **** --- 1814,1823 ---- #ifdef TILDE_MAP_2 httpd_realloc_str( &hc->altdir, &hc->maxaltdir, 0 ); #endif /* TILDE_MAP_2 */ + + /* hykim: mmc opt1 */ + hc->map = NULL; + hc->initialized = 1; } *************** *** 1702,1753 **** hc->hs = hs; memset( &hc->client_addr, 0, sizeof(hc->client_addr) ); memcpy( &hc->client_addr, &sa, sockaddr_len( &sa ) ); ! hc->read_idx = 0; ! hc->checked_idx = 0; ! hc->checked_state = CHST_FIRSTWORD; ! hc->method = METHOD_UNKNOWN; ! hc->status = 0; ! hc->bytes_to_send = 0; ! hc->bytes_sent = 0; ! hc->encodedurl = ""; ! hc->decodedurl[0] = '\0'; ! hc->protocol = "UNKNOWN"; ! hc->origfilename[0] = '\0'; ! hc->expnfilename[0] = '\0'; ! hc->encodings[0] = '\0'; ! hc->pathinfo[0] = '\0'; ! hc->query[0] = '\0'; ! hc->referer = ""; ! hc->useragent = ""; ! hc->accept[0] = '\0'; ! hc->accepte[0] = '\0'; ! hc->acceptl = ""; ! hc->cookie = ""; ! hc->contenttype = ""; ! hc->reqhost[0] = '\0'; ! hc->hdrhost = ""; ! hc->hostdir[0] = '\0'; ! hc->authorization = ""; ! hc->remoteuser[0] = '\0'; ! hc->response[0] = '\0'; ! #ifdef TILDE_MAP_2 ! hc->altdir[0] = '\0'; ! #endif /* TILDE_MAP_2 */ ! hc->responselen = 0; ! hc->if_modified_since = (time_t) -1; ! hc->range_if = (time_t) -1; ! hc->contentlength = -1; ! hc->type = ""; ! hc->hostname = (char*) 0; ! hc->mime_flag = 1; ! hc->one_one = 0; ! hc->got_range = 0; ! hc->tildemapped = 0; ! hc->init_byte_loc = 0; ! hc->end_byte_loc = -1; ! hc->keep_alive = 0; ! hc->should_linger = 0; ! hc->file_address = (char*) 0; return GC_OK; } --- 1842,1848 ---- hc->hs = hs; memset( &hc->client_addr, 0, sizeof(hc->client_addr) ); memcpy( &hc->client_addr, &sa, sockaddr_len( &sa ) ); ! httpd_init_conn(hc); return GC_OK; } *************** *** 1763,1924 **** int httpd_got_request( httpd_conn* hc ) { ! char c; ! ! for ( ; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) ! { ! c = hc->read_buf[hc->checked_idx]; ! switch ( hc->checked_state ) ! { ! case CHST_FIRSTWORD: ! switch ( c ) ! { ! case ' ': case '\t': ! hc->checked_state = CHST_FIRSTWS; ! break; ! case '\n': case '\r': hc->checked_state = CHST_BOGUS; - return GR_BAD_REQUEST; - } - break; - case CHST_FIRSTWS: - switch ( c ) - { - case ' ': case '\t': break; ! case '\n': case '\r': ! hc->checked_state = CHST_BOGUS; ! return GR_BAD_REQUEST; ! default: hc->checked_state = CHST_SECONDWORD; break; ! } break; ! case CHST_SECONDWORD: ! switch ( c ) ! { ! case ' ': case '\t': ! hc->checked_state = CHST_SECONDWS; ! break; ! case '\n': case '\r': ! /* The first line has only two words - an HTTP/0.9 request. */ ! return GR_GOT_REQUEST; ! } break; ! case CHST_SECONDWS: ! switch ( c ) ! { ! case ' ': case '\t': ! break; ! case '\n': case '\r': hc->checked_state = CHST_BOGUS; ! return GR_BAD_REQUEST; ! default: hc->checked_state = CHST_THIRDWORD; break; ! } break; ! case CHST_THIRDWORD: ! switch ( c ) ! { ! case ' ': case '\t': ! hc->checked_state = CHST_THIRDWS; ! break; ! case '\n': ! hc->checked_state = CHST_LF; ! break; ! case '\r': ! hc->checked_state = CHST_CR; ! break; ! } break; ! case CHST_THIRDWS: ! switch ( c ) ! { ! case ' ': case '\t': ! break; ! case '\n': hc->checked_state = CHST_LF; break; ! case '\r': hc->checked_state = CHST_CR; break; ! default: hc->checked_state = CHST_BOGUS; - return GR_BAD_REQUEST; - } - break; - case CHST_LINE: - switch ( c ) - { - case '\n': - hc->checked_state = CHST_LF; break; ! case '\r': ! hc->checked_state = CHST_CR; ! break; } break; ! case CHST_LF: ! switch ( c ) ! { ! case '\n': /* Two newlines in a row - a blank line - end of request. */ return GR_GOT_REQUEST; ! case '\r': hc->checked_state = CHST_CR; break; ! default: hc->checked_state = CHST_LINE; break; ! } break; ! case CHST_CR: ! switch ( c ) ! { ! case '\n': hc->checked_state = CHST_CRLF; break; ! case '\r': /* Two returns in a row - end of request. */ return GR_GOT_REQUEST; ! default: hc->checked_state = CHST_LINE; break; ! } break; ! case CHST_CRLF: ! switch ( c ) ! { ! case '\n': /* Two newlines in a row - end of request. */ return GR_GOT_REQUEST; ! case '\r': hc->checked_state = CHST_CRLFCR; break; ! default: hc->checked_state = CHST_LINE; break; ! } break; case CHST_CRLFCR: ! switch ( c ) ! { ! case '\n': case '\r': /* Two CRLFs or two CRs in a row - end of request. */ return GR_GOT_REQUEST; ! default: hc->checked_state = CHST_LINE; break; ! } break; ! case CHST_BOGUS: return GR_BAD_REQUEST; ! } ! } ! return GR_NO_REQUEST; ! } int httpd_parse_request( httpd_conn* hc ) --- 1858,2070 ---- int httpd_got_request( httpd_conn* hc ) { ! char *bpos, *cpos, *epos; ! ! bpos = cpos = &hc->read_buf[hc->checked_idx]; ! epos = &hc->read_buf[hc->read_idx]; ! ! while (cpos < epos) { ! switch ( hc->checked_state ) ! { ! case CHST_FIRSTWORD: ! while (!isspace(*cpos) && (cpos < epos)) ! cpos++; ! if (cpos == epos) { ! break; ! } ! ! if (('\n' == *cpos) || ('\r' == *cpos)) { ! hc->checked_state = CHST_BOGUS; ! } ! else { ! hc->checked_state = CHST_FIRSTWS; ! } ! cpos++; ! break; ! ! case CHST_FIRSTWS: ! while (cpos < epos) { ! char ch = *cpos; ! if (('\n' == ch) || ('\r' == ch)) { hc->checked_state = CHST_BOGUS; break; ! } ! else if ((' ' != ch) && ('\t' != ch)) { hc->checked_state = CHST_SECONDWORD; break; ! } ! cpos++; ! } ! cpos++; break; ! ! case CHST_SECONDWORD: ! while (!isspace(*cpos) && (cpos < epos)) ! cpos++; ! if (cpos == epos) { ! break; ! } ! ! if (('\n' == *cpos) || ('\r' == *cpos)) { ! /* The first line has only two words - an HTTP/0.9 request. */ ! hc->checked_idx += cpos - bpos; ! return GR_GOT_REQUEST; ! } ! else { ! hc->checked_state = CHST_SECONDWS; ! } ! cpos++; break; ! ! case CHST_SECONDWS: ! while (cpos < epos) { ! char ch = *cpos; ! if (('\n' == ch) || ('\r' == ch)) { hc->checked_state = CHST_BOGUS; ! break; ! } ! else if ((' ' != ch) && ('\t' != ch)) { hc->checked_state = CHST_THIRDWORD; break; ! } ! cpos++; ! } ! cpos++; break; ! ! case CHST_THIRDWORD: ! while (!isspace(*cpos) && (cpos < epos)) ! cpos++; ! if (cpos == epos) { ! break; ! } ! ! if ('\n' == *cpos) { ! hc->checked_state = CHST_LF; ! } else if ('\r' == *cpos) { ! hc->checked_state = CHST_CR; ! } else { ! hc->checked_state = CHST_THIRDWS; ! } ! cpos++; break; ! ! case CHST_THIRDWS: ! while (cpos < epos) { ! char ch = *cpos; ! if ('\n' == ch) { hc->checked_state = CHST_LF; break; ! } ! else if ('\r' == ch) { hc->checked_state = CHST_CR; break; ! } ! else if ((' ' != ch) && ('\t' != ch)) { hc->checked_state = CHST_BOGUS; break; ! } ! cpos++; ! } ! cpos++; ! break; ! ! case CHST_LINE: ! while ((hc->checked_state == CHST_LINE) && (cpos < epos)) { ! while (!isspace(*cpos) && (cpos < epos)) ! cpos++; ! if (cpos != epos) { ! if ('\n' == *cpos) { ! hc->checked_state = CHST_LF; ! break; ! } ! else if ('\r' == *cpos) { ! hc->checked_state = CHST_CR; ! break; } + else + cpos++; + } + } + if (cpos != epos) + cpos++; break; ! ! case CHST_LF: ! switch ( *cpos ) ! { ! case '\n': /* Two newlines in a row - a blank line - end of request. */ + hc->checked_idx += cpos - bpos; return GR_GOT_REQUEST; ! case '\r': hc->checked_state = CHST_CR; break; ! default: hc->checked_state = CHST_LINE; break; ! } ! cpos++; break; ! ! case CHST_CR: ! switch ( *cpos ) ! { ! case '\n': hc->checked_state = CHST_CRLF; break; ! case '\r': /* Two returns in a row - end of request. */ + hc->checked_idx += cpos - bpos; return GR_GOT_REQUEST; ! default: hc->checked_state = CHST_LINE; break; ! } ! cpos++; break; ! ! case CHST_CRLF: ! switch ( *cpos ) ! { ! case '\n': /* Two newlines in a row - end of request. */ + hc->checked_idx += cpos - bpos; return GR_GOT_REQUEST; ! case '\r': hc->checked_state = CHST_CRLFCR; break; ! default: hc->checked_state = CHST_LINE; break; ! } ! cpos++; break; + case CHST_CRLFCR: ! switch ( *cpos ) ! { ! case '\n': case '\r': /* Two CRLFs or two CRs in a row - end of request. */ + hc->checked_idx += cpos - bpos; return GR_GOT_REQUEST; ! default: hc->checked_state = CHST_LINE; break; ! } ! cpos++; break; ! ! case CHST_BOGUS: ! hc->checked_idx += cpos - bpos; return GR_BAD_REQUEST; ! } ! } + hc->checked_idx += cpos - bpos; + return GR_NO_REQUEST; + } + int httpd_parse_request( httpd_conn* hc ) *************** *** 1931,1949 **** char* eol; char* cp; char* pi; ! ! hc->checked_idx = 0; /* reset */ ! method_str = bufgets( hc ); ! url = strpbrk( method_str, " \t\n\r" ); ! if ( url == (char*) 0 ) { httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); return -1; } *url++ = '\0'; ! url += strspn( url, " \t\n\r" ); ! protocol = strpbrk( url, " \t\n\r" ); ! if ( protocol == (char*) 0 ) { protocol = "HTTP/0.9"; hc->mime_flag = 0; --- 2077,2106 ---- char* eol; char* cp; char* pi; ! int methlen; ! int namelen; ! int aclen; ! int cplen; ! int pilen; ! int ofnlen; ! ! hc->checked_idx = hc->next_req_idx; /* reset */ ! method_str = bufgets( hc, &methlen ); ! url = method_str; ! while (!isspace(*url) && ('\0' != *url)) ! url++; ! if ( *url == '\0' ) { httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); return -1; } *url++ = '\0'; ! while (isspace(*url) && ('\0' != *url)) ! url++; ! protocol = url; ! while (!isspace(*protocol) && ('\0' != *protocol)) ! protocol++; ! if ( *protocol == '\0' ) { protocol = "HTTP/0.9"; hc->mime_flag = 0; *************** *** 1951,1968 **** else { *protocol++ = '\0'; ! protocol += strspn( protocol, " \t\n\r" ); if ( *protocol != '\0' ) { ! eol = strpbrk( protocol, " \t\n\r" ); ! if ( eol != (char*) 0 ) *eol = '\0'; ! if ( strcasecmp( protocol, "HTTP/1.0" ) != 0 ) ! hc->one_one = 1; } } /* Check for HTTP/1.1 absolute URL. */ ! if ( strncasecmp( url, "http://", 7 ) == 0 ) { if ( ! hc->one_one ) { --- 2108,2130 ---- else { *protocol++ = '\0'; ! while (isspace(*protocol) && ('\0' != *protocol)) ! protocol++; if ( *protocol != '\0' ) { ! eol = protocol; ! while (!isspace(*eol) && ('\0' != *eol)) ! eol++; ! if ( *eol != '\0' ) *eol = '\0'; ! if ( (memcmp(protocol, "HTTP/1.0", 9) != 0) && ! (strcasecmp( protocol, "HTTP/1.0" ) != 0)) ! hc->one_one = 1; } } /* Check for HTTP/1.1 absolute URL. */ ! if ( ('h' == tolower(*url)) && ! (strncasecmp( url, "http://", 7 ) == 0 )) { if ( ! hc->one_one ) { *************** *** 1982,1993 **** *url = '/'; } ! if ( strcasecmp( method_str, httpd_method_str( METHOD_GET ) ) == 0 ) hc->method = METHOD_GET; ! else if ( strcasecmp( method_str, httpd_method_str( METHOD_HEAD ) ) == 0 ) ! hc->method = METHOD_HEAD; ! else if ( strcasecmp( method_str, httpd_method_str( METHOD_POST ) ) == 0 ) hc->method = METHOD_POST; else { httpd_send_err( hc, 501, err501title, "", err501form, method_str ); --- 2144,2164 ---- *url = '/'; } ! if ( memcmp( method_str, "GET", 4 ) == 0 || ! strcasecmp( method_str, "GET" ) == 0 ) hc->method = METHOD_GET; ! else if ( memcmp( method_str, "POST", 5 ) == 0 || ! strcasecmp( method_str, "POST" ) == 0 ) ! { hc->method = METHOD_POST; + hc->forbid_keep_alive = 1; + } + else if ( memcmp( method_str, "HEAD", 5 ) == 0 || + strcasecmp( method_str, "HEAD" ) == 0 ) + { + hc->method = METHOD_HEAD; + hc->forbid_keep_alive = 1; + } else { httpd_send_err( hc, 501, err501title, "", err501form, method_str ); *************** *** 1999,2005 **** &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) ); strdecode( hc->decodedurl, hc->encodedurl ); ! de_dotdot( hc->decodedurl ); if ( hc->decodedurl[0] != '/' || hc->decodedurl[1] == '/' || ( hc->decodedurl[1] == '.' && hc->decodedurl[2] == '.' && ( hc->decodedurl[3] == '\0' || hc->decodedurl[3] == '/' ) ) ) --- 2170,2176 ---- &hc->decodedurl, &hc->maxdecodedurl, strlen( hc->encodedurl ) ); strdecode( hc->decodedurl, hc->encodedurl ); ! de_dotdot( hc->decodedurl, &namelen ); if ( hc->decodedurl[0] != '/' || hc->decodedurl[1] == '/' || ( hc->decodedurl[1] == '.' && hc->decodedurl[2] == '.' && ( hc->decodedurl[3] == '\0' || hc->decodedurl[3] == '/' ) ) ) *************** *** 2011,2021 **** hc->protocol = protocol; httpd_realloc_str( ! &hc->origfilename, &hc->maxorigfilename, strlen( hc->decodedurl ) ); ! (void) strcpy( hc->origfilename, &hc->decodedurl[1] ); /* Special case for top-level URL. */ if ( hc->origfilename[0] == '\0' ) ! (void) strcpy( hc->origfilename, "." ); /* Extract query string from encoded URL. */ cp = strchr( hc->encodedurl, '?' ); --- 2182,2192 ---- hc->protocol = protocol; httpd_realloc_str( ! &hc->origfilename, &hc->maxorigfilename, namelen ); ! memcpy( hc->origfilename, &hc->decodedurl[1], namelen); /* Special case for top-level URL. */ if ( hc->origfilename[0] == '\0' ) ! memcpy( hc->origfilename, ".", 2 ); /* Extract query string from encoded URL. */ cp = strchr( hc->encodedurl, '?' ); *************** *** 2033,2183 **** if ( hc->mime_flag ) { /* Read the MIME headers. */ ! while ( ( buf = bufgets( hc ) ) != (char*) 0 ) { if ( buf[0] == '\0' ) break; ! if ( strncasecmp( buf, "Referer:", 8 ) == 0 ) ! { ! cp = &buf[8]; ! cp += strspn( cp, " \t" ); ! hc->referer = cp; ! } ! else if ( strncasecmp( buf, "User-Agent:", 11 ) == 0 ) ! { ! cp = &buf[11]; ! cp += strspn( cp, " \t" ); ! hc->useragent = cp; ! } ! else if ( strncasecmp( buf, "Host:", 5 ) == 0 ) ! { ! cp = &buf[5]; ! cp += strspn( cp, " \t" ); ! hc->hdrhost = cp; ! cp = strchr( hc->hdrhost, ':' ); ! if ( cp != (char*) 0 ) ! *cp = '\0'; ! } ! else if ( strncasecmp( buf, "Accept:", 7 ) == 0 ) ! { ! cp = &buf[7]; ! cp += strspn( cp, " \t" ); ! if ( hc->accept[0] != '\0' ) ! { ! if ( strlen( hc->accept ) > 5000 ) ! { ! syslog( ! LOG_ERR, "%.80s way too much Accept: data", ! httpd_ntoa( &hc->client_addr ) ); ! continue; ! } ! httpd_realloc_str( ! &hc->accept, &hc->maxaccept, ! strlen( hc->accept ) + 2 + strlen( cp ) ); ! (void) strcat( hc->accept, ", " ); ! } ! else ! httpd_realloc_str( ! &hc->accept, &hc->maxaccept, strlen( cp ) ); ! (void) strcat( hc->accept, cp ); ! } ! else if ( strncasecmp( buf, "Accept-Encoding:", 16 ) == 0 ) ! { ! cp = &buf[16]; ! cp += strspn( cp, " \t" ); ! if ( hc->accepte[0] != '\0' ) ! { ! if ( strlen( hc->accepte ) > 5000 ) ! { ! syslog( ! LOG_ERR, "%.80s way too much Accept-Encoding: data", ! httpd_ntoa( &hc->client_addr ) ); ! continue; ! } ! httpd_realloc_str( ! &hc->accepte, &hc->maxaccepte, ! strlen( hc->accepte ) + 2 + strlen( cp ) ); ! (void) strcat( hc->accepte, ", " ); ! } ! else ! httpd_realloc_str( ! &hc->accepte, &hc->maxaccepte, strlen( cp ) ); ! (void) strcpy( hc->accepte, cp ); ! } ! else if ( strncasecmp( buf, "Accept-Language:", 16 ) == 0 ) ! { ! cp = &buf[16]; ! cp += strspn( cp, " \t" ); ! hc->acceptl = cp; ! } ! else if ( strncasecmp( buf, "If-Modified-Since:", 18 ) == 0 ) ! { ! cp = &buf[18]; ! hc->if_modified_since = tdate_parse( cp ); ! if ( hc->if_modified_since == (time_t) -1 ) ! syslog( LOG_DEBUG, "unparsable time: %.80s", cp ); ! } ! else if ( strncasecmp( buf, "Cookie:", 7 ) == 0 ) ! { ! cp = &buf[7]; ! cp += strspn( cp, " \t" ); ! hc->cookie = cp; ! } ! else if ( strncasecmp( buf, "Range:", 6 ) == 0 ) ! { ! /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */ ! if ( strchr( buf, ',' ) == (char*) 0 ) ! { ! char* cp_dash; ! cp = strpbrk( buf, "=" ); if ( cp != (char*) 0 ) ! { ! cp_dash = strchr( cp + 1, '-' ); ! if ( cp_dash != (char*) 0 && cp_dash != cp + 1 ) ! { ! *cp_dash = '\0'; ! hc->got_range = 1; ! hc->init_byte_loc = atol( cp + 1 ); ! if ( isdigit( (int) cp_dash[1] ) ) ! hc->end_byte_loc = atol( cp_dash + 1 ); ! } ! } } ! } ! else if ( strncasecmp( buf, "Range-If:", 9 ) == 0 || ! strncasecmp( buf, "If-Range:", 9 ) == 0 ) ! { ! cp = &buf[9]; ! hc->range_if = tdate_parse( cp ); ! if ( hc->range_if == (time_t) -1 ) ! syslog( LOG_DEBUG, "unparsable time: %.80s", cp ); ! } ! else if ( strncasecmp( buf, "Content-Type:", 13 ) == 0 ) ! { ! cp = &buf[13]; ! cp += strspn( cp, " \t" ); ! hc->contenttype = cp; ! } ! else if ( strncasecmp( buf, "Content-Length:", 15 ) == 0 ) ! { ! cp = &buf[15]; ! hc->contentlength = atol( cp ); ! } ! else if ( strncasecmp( buf, "Authorization:", 14 ) == 0 ) ! { ! cp = &buf[14]; ! cp += strspn( cp, " \t" ); ! hc->authorization = cp; ! } ! else if ( strncasecmp( buf, "Connection:", 11 ) == 0 ) ! { ! cp = &buf[11]; ! cp += strspn( cp, " \t" ); ! if ( strcasecmp( cp, "keep-alive" ) == 0 ) ! hc->keep_alive = 1; ! } #ifdef LOG_UNKNOWN_HEADERS ! else if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 || strncasecmp( buf, "Accept-Language:", 16 ) == 0 || strncasecmp( buf, "Agent:", 6 ) == 0 || strncasecmp( buf, "Cache-Control:", 14 ) == 0 || --- 2204,2439 ---- if ( hc->mime_flag ) { /* Read the MIME headers. */ ! int buflen; ! while ( ( buf = bufgets( hc, &buflen ) ) != (char*) 0 ) { + char * colposn; if ( buf[0] == '\0' ) + { + hc->next_req_idx = hc->checked_idx; break; ! } ! if (NULL != (colposn = memchr(buf, ':', buflen))) { ! /* good header! */ ! switch (colposn - buf + 1 ) { ! case 5: ! if ( memcmp( buf, "Host", 4 ) == 0 || ! strncasecmp( buf, "Host", 4 ) == 0) ! { ! cp = &buf[5]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->hdrhost = cp; ! cp = strchr( hc->hdrhost, ':' ); if ( cp != (char*) 0 ) ! *cp = '\0'; ! continue; ! } ! break; ! case 6: ! if ( memcmp( buf, "Range", 5 ) == 0 || ! strncasecmp( buf, "Range", 5 ) == 0) ! { ! /* Only support %d- and %d-%d, not %d-%d,%d-%d or -%d. */ ! if ( strchr( buf, ',' ) == (char*) 0 ) ! { ! char* cp_dash; ! cp = strpbrk( buf, "=" ); ! if ( cp != (char*) 0 ) ! { ! cp_dash = strchr( cp + 1, '-' ); ! if ( cp_dash != (char*) 0 && cp_dash != cp + 1 ) ! { ! *cp_dash = '\0'; ! hc->got_range = 1; ! hc->init_byte_loc = atol( cp + 1 ); ! if ( isdigit( (int) cp_dash[1] ) ) ! hc->end_byte_loc = atol( cp_dash + 1 ); ! } ! } ! } ! continue; ! } ! break; ! case 7: ! if ( 'a' == tolower(buf[0]) && ! (memcmp( buf, "Accept", 6 ) == 0 || ! strncasecmp( buf, "Accept", 6 ) == 0)) ! { ! cp = &buf[7]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! cplen = strlen(cp); ! if ( hc->accept[0] != '\0' ) ! { ! aclen = strlen(hc->accept); ! if ( aclen > 5000 ) ! { ! syslog( ! LOG_ERR, "%.80s way too much Accept: data", ! httpd_ntoa( &hc->client_addr ) ); ! continue; ! } ! httpd_realloc_str( ! &hc->accept, &hc->maxaccept, ! aclen + 2 + cplen ); ! memcpy ( hc->accept + aclen, ", ", 2); ! aclen += 2; ! } ! else { ! aclen = 0; ! httpd_realloc_str( ! &hc->accept, &hc->maxaccept, cplen); } ! ! memcpy(hc->accept + aclen, cp, cplen + 1); ! continue; ! } ! else if ( memcmp( buf, "Cookie", 6 ) == 0 || ! strncasecmp( buf, "Cookie", 6 ) == 0) ! { ! cp = &buf[7]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->cookie = cp; ! continue; ! } ! ! break; ! case 8: ! if ( memcmp( buf, "Referer", 7 ) == 0 || ! strncasecmp( buf, "Referer", 7 ) == 0) ! { ! cp = &buf[8]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->referer = cp; ! continue; ! } ! break; ! case 9: ! if ( memcmp( buf, "Range-If", 8 ) == 0 || ! memcmp( buf, "If-Range", 8 ) == 0 || ! strncasecmp( buf, "Range-If", 8 ) == 0 || ! strncasecmp( buf, "If-Range", 8 ) == 0) ! { ! cp = &buf[9]; ! hc->range_if = tdate_parse( cp ); ! if ( hc->range_if == (time_t) -1 ) ! syslog( LOG_DEBUG, "unparsable time: %.80s", cp ); ! continue; ! } ! break; ! case 11: ! if ( 'c' == tolower(buf[0]) && ! (memcmp( buf, "Connection", 10 ) == 0 || ! strncasecmp( buf, "Connection", 10 ) == 0)) ! { ! cp = &buf[11]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! if ( (memcmp(cp, "Keep-Alive", 10 ) == 0) || ! (strncasecmp( cp, "Keep-Alive", 10 ) == 0 )) ! hc->keep_alive = 1; ! continue; ! } ! else if ( memcmp( buf, "User-Agent", 10 ) == 0 || ! strncasecmp( buf, "User-Agent", 10 ) == 0) ! { ! cp = &buf[11]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->useragent = cp; ! continue; ! } ! break; ! case 13: ! if ( memcmp( buf, "Content-Type", 12 ) == 0 || ! strncasecmp( buf, "Content-Type", 12 ) == 0) ! { ! cp = &buf[13]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->contenttype = cp; ! continue; ! } ! break; ! case 14: ! if ( memcmp( buf, "Authorization", 13 ) == 0 || ! strncasecmp( buf, "Authorization", 13 ) == 0) ! { ! cp = &buf[14]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->authorization = cp; ! continue; ! } ! break; ! case 15: ! if ( memcmp( buf, "Content-Length", 14 ) == 0 || ! strncasecmp( buf, "Content-Length", 14 ) == 0) ! { ! cp = &buf[15]; ! hc->contentlength = atol( cp ); ! continue; ! } ! break; ! case 16: ! if ( 'e' == tolower(buf[7]) && ! (memcmp( buf, "Accept-Encoding", 15 ) == 0 || ! strncasecmp( buf, "Accept-Encoding", 15 ) == 0)) ! { ! cp = &buf[16]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! if ( hc->accepte[0] != '\0' ) ! { ! if ( strlen( hc->accepte ) > 5000 ) ! { ! syslog( ! LOG_ERR, "%.80s way too much Accept-Encoding: data", ! httpd_ntoa( &hc->client_addr ) ); ! continue; ! } ! httpd_realloc_str( ! &hc->accepte, &hc->maxaccepte, ! strlen( hc->accepte ) + 2 + strlen( cp ) ); ! (void) strcat( hc->accepte, ", " ); ! } ! else ! httpd_realloc_str( ! &hc->accepte, &hc->maxaccepte, strlen( cp ) ); ! (void) strcat( hc->accepte, cp ); ! continue; ! } ! else if ( memcmp( buf, "Accept-Language", 15 ) == 0 || ! strncasecmp( buf, "Accept-Language", 15 ) == 0) ! { ! cp = &buf[16]; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; ! hc->acceptl = cp; ! continue; ! } ! break; ! case 18: ! if ( memcmp( buf, "If-Modified-Since", 17 ) == 0 || ! strncasecmp( buf, "If-Modified-Since", 17 ) == 0) ! { ! cp = &buf[18]; ! hc->if_modified_since = tdate_parse( cp ); ! if ( hc->if_modified_since == (time_t) -1 ) ! syslog( LOG_DEBUG, "unparsable time: %.80s", cp ); ! continue; ! } ! break; ! default: ! break; ! } ! } #ifdef LOG_UNKNOWN_HEADERS ! fprintf(stderr, "unknown request header: %.80s", buf); ! if ( strncasecmp( buf, "Accept-Charset:", 15 ) == 0 || strncasecmp( buf, "Accept-Language:", 16 ) == 0 || strncasecmp( buf, "Agent:", 6 ) == 0 || strncasecmp( buf, "Cache-Control:", 14 ) == 0 || *************** *** 2237,2245 **** */ /* Copy original filename to expanded filename. */ httpd_realloc_str( ! &hc->expnfilename, &hc->maxexpnfilename, strlen( hc->origfilename ) ); ! (void) strcpy( hc->expnfilename, hc->origfilename ); /* Tilde mapping. */ if ( hc->expnfilename[0] == '~' ) --- 2493,2502 ---- */ /* Copy original filename to expanded filename. */ + ofnlen = strlen(hc->origfilename); httpd_realloc_str( ! &hc->expnfilename, &hc->maxexpnfilename, ofnlen ); ! memcpy( hc->expnfilename, hc->origfilename, ofnlen+1 ); /* Tilde mapping. */ if ( hc->expnfilename[0] == '~' ) *************** *** 2271,2292 **** /* Expand all symbolic links in the filename. This also gives us ** any trailing non-existing components, for pathinfo. */ ! cp = expand_symlinks( hc->expnfilename, &pi, hc->hs->no_symlink, hc->tildemapped ); if ( cp == (char*) 0 ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); return -1; } ! httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, strlen( cp ) ); ! (void) strcpy( hc->expnfilename, cp ); ! httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, strlen( pi ) ); ! (void) strcpy( hc->pathinfo, pi ); /* Remove pathinfo stuff from the original filename too. */ if ( hc->pathinfo[0] != '\0' ) { int i; ! i = strlen( hc->origfilename ) - strlen( hc->pathinfo ); if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 ) hc->origfilename[i - 1] = '\0'; } --- 2528,2551 ---- /* Expand all symbolic links in the filename. This also gives us ** any trailing non-existing components, for pathinfo. */ ! cp = expand_symlinks( hc, hc->expnfilename, &pi, hc->hs->no_symlink, hc->tildemapped ); if ( cp == (char*) 0 ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); return -1; } ! cplen = strlen(cp); ! httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, cplen ); ! memcpy( hc->expnfilename, cp, cplen+1 ); ! pilen = strlen(pi); ! httpd_realloc_str( &hc->pathinfo, &hc->maxpathinfo, pilen ); ! memcpy( hc->pathinfo, pi, pilen+1 ); /* Remove pathinfo stuff from the original filename too. */ if ( hc->pathinfo[0] != '\0' ) { int i; ! i = ofnlen - pilen ; if ( i > 0 && strcmp( &hc->origfilename[i], hc->pathinfo ) == 0 ) hc->origfilename[i - 1] = '\0'; } *************** *** 2330,2342 **** static char* ! bufgets( httpd_conn* hc ) { int i; char c; for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) { c = hc->read_buf[hc->checked_idx]; if ( c == '\n' || c == '\r' ) { --- 2589,2603 ---- static char* ! bufgets( httpd_conn* hc, int *buflen ) { int i; char c; + int blen = 0; for ( i = hc->checked_idx; hc->checked_idx < hc->read_idx; ++hc->checked_idx ) { + blen++; c = hc->read_buf[hc->checked_idx]; if ( c == '\n' || c == '\r' ) { *************** *** 2348,2362 **** hc->read_buf[hc->checked_idx] = '\0'; ++hc->checked_idx; } return &(hc->read_buf[i]); } } return (char*) 0; } static void ! de_dotdot( char* file ) { char* cp; char* cp2; --- 2609,2625 ---- hc->read_buf[hc->checked_idx] = '\0'; ++hc->checked_idx; } + *buflen = blen; return &(hc->read_buf[i]); } } + *buflen = blen; return (char*) 0; } static void ! de_dotdot( char* file, int *newlen ) { char* cp; char* cp2; *************** *** 2390,2408 **** break; *cp2 = '\0'; } - } void ! httpd_close_conn( httpd_conn* hc, struct timeval* nowP ) ! { make_log_entry( hc, nowP ); if ( hc->file_address != (char*) 0 ) { ! mmc_unmap( hc->file_address, &(hc->sb), nowP ); hc->file_address = (char*) 0; } if ( hc->conn_fd >= 0 ) { (void) close( hc->conn_fd ); --- 2653,2679 ---- break; *cp2 = '\0'; } + *newlen = l; + } void ! httpd_release_conn( httpd_conn* hc, struct timeval* nowP ) ! { make_log_entry( hc, nowP ); if ( hc->file_address != (char*) 0 ) { ! /* hykim: mmc opt1 */ ! mmc_unmap( &(hc->map), hc->file_address, &(hc->sb), nowP ); hc->file_address = (char*) 0; } + } + + + void + httpd_close_conn( httpd_conn* hc, struct timeval* nowP ) + { if ( hc->conn_fd >= 0 ) { (void) close( hc->conn_fd ); *************** *** 2451,2461 **** /* qsort comparison routine - declared old-style on purpose, for portability. */ static int ! ext_compare( a, b ) ! struct mime_entry* a; ! struct mime_entry* b; { ! return strcmp( a->ext, b->ext ); } static void --- 2722,2732 ---- /* qsort comparison routine - declared old-style on purpose, for portability. */ static int ! ext_compare( const void *a, const void *b ) { ! const struct mime_entry *am = (const struct mime_entry *)a; ! const struct mime_entry *bm = (const struct mime_entry *)b; ! return strcmp( am->ext, bm->ext ); } static void *************** *** 2464,2475 **** /* Sort the tables so we can do binary search. */ qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare ); qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare ); } /* Figure out MIME encodings and type based on the filename. Multiple ** encodings are separated by semicolons. */ ! static void figure_mime( httpd_conn* hc ) { int i, j, k, l; --- 2735,2749 ---- /* Sort the tables so we can do binary search. */ qsort( enc_tab, n_enc_tab, sizeof(*enc_tab), ext_compare ); qsort( typ_tab, n_typ_tab, sizeof(*typ_tab), ext_compare ); + + /* Calculate the length of the EXPOSED_SERVER_OK string */ + server_ok_len = strlen(EXPOSED_SERVER_OK); } /* Figure out MIME encodings and type based on the filename. Multiple ** encodings are separated by semicolons. */ ! void figure_mime( httpd_conn* hc ) { int i, j, k, l; *************** *** 2576,2586 **** /* qsort comparison routine - declared old-style on purpose, for portability. */ static int ! name_compare( a, b ) ! char** a; ! char** b; { ! return strcmp( *a, *b ); } --- 2850,2860 ---- /* qsort comparison routine - declared old-style on purpose, for portability. */ static int ! name_compare( const void *a, const void *b ) { ! const char **ac = (const char **)a; ! const char **bc = (const char **)b; ! return strcmp( *ac, *bc ); } *************** *** 2612,2617 **** --- 2886,2892 ---- time_t now; char* timestr; ClientData client_data; + char modbuf[40]; dirp = opendir( hc->expnfilename ); if ( dirp == (DIR*) 0 ) *************** *** 2621,2627 **** return -1; } ! send_mime( hc, 200, ok200title, "", "", "text/html", -1, hc->sb.st_mtime ); if ( hc->method == METHOD_HEAD ) closedir( dirp ); else if ( hc->method == METHOD_GET ) --- 2896,2903 ---- return -1; } ! time_rfc1123( modbuf, sizeof(modbuf), hc->sb.st_mtime ); ! send_mime( hc, 200, ok200title, "", "", "text/html", -1, modbuf, NULL, 0 ); if ( hc->method == METHOD_HEAD ) closedir( dirp ); else if ( hc->method == METHOD_GET ) *************** *** 3144,3150 **** ( cp == headers || *(cp-1) == '\n' ) ) { cp += 7; ! cp += strspn( cp, " \t" ); status = atoi( cp ); } if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 && --- 3420,3427 ---- ( cp == headers || *(cp-1) == '\n' ) ) { cp += 7; ! while (isspace(*cp) && ('\0' != *cp)) ! cp++; status = atoi( cp ); } if ( ( cp = strstr( headers, "Location:" ) ) != (char*) 0 && *************** *** 3445,3450 **** --- 3722,3741 ---- char* cp; char* pi; + #if defined(__i386) && defined(__FreeBSD__) + static int numreqs = 0; + static uint64_t systime = 0; + + numreqs++; + if (0 == systime) + systime = rdtsc(); + if (0 == (numreqs & 16383)) { + uint64_t newtm = rdtsc(); + fprintf(stderr, "Ticks for last request set: %llu\n", newtm - systime); + systime = newtm; + } + #endif + expnlen = strlen( hc->expnfilename ); if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD && *************** *** 3456,3462 **** } /* Stat the file. */ ! if ( stat( hc->expnfilename, &hc->sb ) < 0 ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); return -1; --- 3747,3753 ---- } /* Stat the file. */ ! if ( !hc->sb_inited && (stat( hc->expnfilename, &hc->sb ) < 0 )) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); return -1; *************** *** 3557,3563 **** /* Got an index file. Expand symlinks again. More pathinfo means ** something went wrong. */ ! cp = expand_symlinks( indexname, &pi, hc->hs->no_symlink, hc->tildemapped ); if ( cp == (char*) 0 || pi[0] != '\0' ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); --- 3848,3854 ---- /* Got an index file. Expand symlinks again. More pathinfo means ** something went wrong. */ ! cp = expand_symlinks( hc, indexname, &pi, hc->hs->no_symlink, hc->tildemapped ); if ( cp == (char*) 0 || pi[0] != '\0' ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); *************** *** 3668,3700 **** ( hc->end_byte_loc == -1 || hc->end_byte_loc >= hc->sb.st_size ) ) hc->end_byte_loc = hc->sb.st_size - 1; - figure_mime( hc ); - if ( hc->method == METHOD_HEAD ) { send_mime( hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, ! hc->sb.st_mtime ); } else if ( hc->if_modified_since != (time_t) -1 && hc->if_modified_since >= hc->sb.st_mtime ) { hc->method = METHOD_HEAD; send_mime( hc, 304, err304title, hc->encodings, "", hc->type, hc->sb.st_size, ! hc->sb.st_mtime ); } else { ! hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP ); if ( hc->file_address == (char*) 0 ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); return -1; } send_mime( hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, ! hc->sb.st_mtime ); } return 0; --- 3959,4003 ---- ( hc->end_byte_loc == -1 || hc->end_byte_loc >= hc->sb.st_size ) ) hc->end_byte_loc = hc->sb.st_size - 1; if ( hc->method == METHOD_HEAD ) { + char modbuf[40]; + time_rfc1123( modbuf, sizeof(modbuf), hc->sb.st_mtime); + figure_mime( hc ); send_mime( hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, ! modbuf, NULL, 0); } else if ( hc->if_modified_since != (time_t) -1 && hc->if_modified_since >= hc->sb.st_mtime ) { + /* NOTE: We should eventually use the cached modbuf here as well + * if possible. */ + char modbuf[40]; + time_rfc1123( modbuf, sizeof(modbuf), hc->sb.st_mtime); hc->method = METHOD_HEAD; + figure_mime( hc ); send_mime( hc, 304, err304title, hc->encodings, "", hc->type, hc->sb.st_size, ! modbuf, NULL, 0 ); } else { ! char *mtime; ! hc->file_address = mmc_map( &(hc->map), hc->expnfilename, hc, nowP, &mtime, (hc->sb_inited) ? &(hc->fn_hash) : NULL ); if ( hc->file_address == (char*) 0 ) { httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl ); return -1; } + #ifdef USE_SENDFILE + hc->file_fd = *((int *) hc->file_address); + #endif + if ('\0' == hc->type[0]) /* use conventional type, etc. */ + figure_mime( hc ); send_mime( hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size, ! mtime, hc->fileInfo, hc->fileInfoLen ); } return 0; Index: libhttpd.h =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/libhttpd.h,v retrieving revision 1.1.1.2 retrieving revision 1.3 diff -c -r1.1.1.2 -r1.3 *** libhttpd.h 2002/02/05 22:52:52 1.1.1.2 --- libhttpd.h 2002/09/24 15:22:49 1.3 *************** *** 83,95 **** int no_empty_referers; } httpd_server; /* A connection. */ typedef struct { int initialized; httpd_server* hs; httpd_sockaddr client_addr; char* read_buf; ! int read_size, read_idx, checked_idx; int checked_state; int method; int status; --- 83,98 ---- int no_empty_referers; } httpd_server; + /* hykim: mmc opt1 */ + struct MapStruct; + /* A connection. */ typedef struct { int initialized; httpd_server* hs; httpd_sockaddr client_addr; char* read_buf; ! int read_size, read_idx, checked_idx, next_req_idx; int checked_state; int method; int status; *************** *** 134,143 **** --- 137,156 ---- int tildemapped; /* this connection got tilde-mapped */ off_t init_byte_loc, end_byte_loc; int keep_alive; + int kept_alive; + int forbid_keep_alive; int should_linger; struct stat sb; + int sb_inited; int conn_fd; char* file_address; + #ifdef USE_SENDFILE + int file_fd; + uint32_t fn_hash; + #endif + char *fileInfo; + int fileInfoLen; + struct MapStruct *map; } httpd_conn; /* Methods. */ *************** *** 236,241 **** --- 249,258 ---- */ extern void httpd_destroy_conn( httpd_conn* hc ); + /* Call this to (re-)initialize the fields of a connection struct */ + extern void httpd_init_conn( httpd_conn* hc ); + + extern void httpd_init_pconn( httpd_conn* hc ); /* Send an error message back to the client. */ extern void httpd_send_err( *************** *** 253,259 **** extern char* httpd_method_str( int method ); /* Reallocate a string. */ ! extern void httpd_realloc_str( char** strP, int* maxsizeP, int size ); /* Format a network socket to a string representation. */ extern char* httpd_ntoa( httpd_sockaddr* saP ); --- 270,280 ---- extern char* httpd_method_str( int method ); /* Reallocate a string. */ ! #define httpd_realloc_str(str,max,sz) { \ ! if (((sz) > *(max)) || (*(max) == 0)) \ ! httpd_realloc_str_real((str), (max), (sz)); \ ! } ! extern void httpd_realloc_str_real( char** strP, int* maxsizeP, int size ); /* Format a network socket to a string representation. */ extern char* httpd_ntoa( httpd_sockaddr* saP ); *************** *** 266,270 **** --- 287,302 ---- /* Generate debugging statistics syslog message. */ extern void httpd_logstats( long secs ); + + /* Handle date caching */ + void fill_nowbuf( time_t ); + void time_rfc1123 ( char *buf, int sz, time_t tm ); + + /* Function to release a connection */ + void httpd_release_conn( httpd_conn* hc, struct timeval* nowP ); + + /* Get the mime info */ + void figure_mime( httpd_conn* hc ); + #endif /* _LIBHTTPD_H_ */ Index: mmc.c =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/mmc.c,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -c -r1.1.1.1 -r1.3 *** mmc.c 2001/12/10 19:23:47 1.1.1.1 --- mmc.c 2002/09/24 15:22:49 1.3 *************** *** 32,44 **** --- 32,59 ---- #include #include #include + #include + #include + #include + #include + #include #ifdef HAVE_MMAP #include #endif /* HAVE_MMAP */ #include "mmc.h" + #include "libhttpd.h" + #ifndef __FreeBSD__ + #undef USE_SENDFILE + #endif + + #if defined(HAVE_KQUEUE) && defined(USE_SENDFILE) + #define USE_KQUEUE_VNODE 1 + #include "fdwatch.h" + #endif + /* Defines. */ #ifndef DEFAULT_EXPIRE_AGE *************** *** 66,89 **** time_t ctime; int refcount; time_t reftime; void* addr; unsigned int hash; int hash_idx; struct MapStruct* next; } Map; - /* Globals. */ static Map* maps = (Map*) 0; static Map* free_maps = (Map*) 0; ! static int alloc_count = 0, map_count = 0, free_count = 0; static Map** hash_table = (Map**) 0; static int hash_size; static unsigned int hash_mask; static time_t expire_age = DEFAULT_EXPIRE_AGE; - /* Forwards. */ static void really_unmap( Map** mm ); static int check_hash_size( void ); --- 81,127 ---- time_t ctime; int refcount; time_t reftime; + #ifdef USE_SENDFILE + int fd; + #ifdef USE_KQUEUE_VNODE + char *filename; /* Use this to implement a stat cache */ + uint32_t fn_hash; /* Save the full 32-bit hash for faster compares */ + struct stat sb; + int sbvalidity; /* this is set to 0 when first entered + * 1 after first confirmed (since there is a race + * between when the statbuf is entered and when it is + * watched), and -1 when invalidated */ + TAILQ_ENTRY(MapStruct) statnext; /* hash table entry */ + #endif + #endif void* addr; unsigned int hash; int hash_idx; + char mtime[40]; /* RFC1123 fmt of the Last-Modified time */ + + /* NOTE: Do not cache fileInfo if encodings are in use! */ + char *fileType; /* hold hc->type; not malloc'ed */ + char fileInfo[500]; /* hold type, lmt, len, etc. here */ + int fileInfoLen;/* length of fileInfo */ + struct MapStruct* next; + TAILQ_ENTRY(MapStruct) lruList; /* LRU list of entries with refcount 0 */ } Map; /* Globals. */ static Map* maps = (Map*) 0; static Map* free_maps = (Map*) 0; ! static int alloc_count = 0, map_count = 0, lru_count = 0, free_count = 0; static Map** hash_table = (Map**) 0; static int hash_size; static unsigned int hash_mask; static time_t expire_age = DEFAULT_EXPIRE_AGE; + int mmc_maxmaps = 40000; /* can be set with -m; should default to some + * fraction of the sysctl value. Oh well. */ + /* LRU list of entries with refcount 0 */ + static TAILQ_HEAD(, MapStruct) mapLRU = TAILQ_HEAD_INITIALIZER(mapLRU); /* Forwards. */ static void really_unmap( Map** mm ); static int check_hash_size( void ); *************** *** 91,108 **** static Map* find_hash( ino_t ino, dev_t dev, off_t size, time_t ctime ); static unsigned int hash( ino_t ino, dev_t dev, off_t size, time_t ctime ); void* ! mmc_map( char* filename, struct stat* sbP, struct timeval* nowP ) { time_t now; struct stat sb; Map* m; int fd; /* Stat the file, if necessary. */ ! if ( sbP != (struct stat*) 0 ) ! sb = *sbP; else { if ( stat( filename, &sb ) != 0 ) --- 129,157 ---- static Map* find_hash( ino_t ino, dev_t dev, off_t size, time_t ctime ); static unsigned int hash( ino_t ino, dev_t dev, off_t size, time_t ctime ); + #ifdef USE_KQUEUE_VNODE + /* stat hashing */ + static uint32_t mmc_stathash( const char *filename ); + static void mmc_statadd( Map *map ); + static void mmc_statdel( Map *map ); + #endif void* ! mmc_map( Map **mapp, char* filename, httpd_conn *hc, struct timeval* nowP, char **mtime, uint32_t *fnhashP ) { time_t now; struct stat sb; Map* m; int fd; + #ifdef DEBUG_MMC + fprintf(stderr, __FUNCTION__ ":%d map_count %d lru_count %d\n", + __LINE__, map_count, lru_count); + #endif + /* Stat the file, if necessary. */ ! if ( hc != NULL ) ! sb = hc->sb; else { if ( stat( filename, &sb ) != 0 ) *************** *** 128,139 **** if ( m != (Map*) 0 ) { /* Yep. Just return the existing map */ ++m->refcount; m->reftime = now; return m->addr; } ! /* Open the file. */ fd = open( filename, O_RDONLY ); if ( fd < 0 ) { --- 177,228 ---- if ( m != (Map*) 0 ) { /* Yep. Just return the existing map */ + + if (0 == m->refcount) { + /* remove it from the LRU list */ + TAILQ_REMOVE(&mapLRU, m, lruList); + lru_count--; + } ++m->refcount; + /* hykim: mmc opt1 */ + *mapp = m; + m->reftime = now; + *mtime = m->mtime; + + hc->type = m->fileType; + hc->fileInfo = m->fileInfo; + hc->fileInfoLen = m->fileInfoLen; + + #ifdef DEBUG_MMC + fprintf(stderr, __FUNCTION__ ":%d map_count %d lru_count %d\n", + __LINE__, map_count, lru_count); + #endif + + #ifdef USE_SENDFILE + return (&m->fd); + #else return m->addr; + #endif } ! /* We are going to map a new file, so first consider unmapping an old ! * one if we don't have any more available to us. */ ! if ((map_count >= mmc_maxmaps) && (lru_count > 0)) { ! Map *last; ! ! last = TAILQ_FIRST(&mapLRU); ! TAILQ_REMOVE(&mapLRU, last, lruList); ! lru_count--; ! really_unmap(&last); ! ! #ifdef DEBUG_MMC ! fprintf(stderr, __FUNCTION__ ":%d map_count %d lru_count %d\n", ! __LINE__, map_count, lru_count); ! #endif ! } ! ! /* Open the file. */ fd = open( filename, O_RDONLY ); if ( fd < 0 ) { *************** *** 168,173 **** --- 257,300 ---- m->refcount = 1; m->reftime = now; + #ifdef USE_KQUEUE_VNODE + if (NULL == (m->filename = strdup(filename))) { + fprintf(stderr, "Out of memory.\n"); + exit(1); + } + if (NULL != fnhashP) + m->fn_hash = *fnhashP; + else + m->fn_hash = mmc_stathash(filename); + + m->sb = sb; + m->sbvalidity = 0; /* Initially set to 0, reconfirm on 1st use */ + #endif + /* No need to initialize lruList since we are clearly not on the list */ + time_rfc1123(m->mtime, sizeof(m->mtime), sb.st_mtime); + + figure_mime(hc); + + if (('\0' == hc->encodings[0]) && (sb.st_size <= (off_t)INT_MAX)) { + char type[200]; + m->fileType = hc->type; + snprintf(type, sizeof(type), hc->type, hc->hs->charset); + m->fileInfoLen = snprintf(m->fileInfo, sizeof(m->fileInfo), + "Content-Type: %s\r\n" + "Last-Modified: %s" + "Content-Length: %d\r\n", + type, m->mtime, (int)sb.st_size); + if (m->fileInfoLen > sizeof(m->fileInfo)) { /* slow path */ + *m->fileInfo = '\0'; + m->fileInfoLen = 0; + } + } + else { /* treat this in the slow path to avoid mallocing encodings later */ + m->fileType = ""; + *m->fileInfo = '\0'; + m->fileInfoLen = 0; + } + /* Avoid doing anything for zero-length files; some systems don't like ** to mmap them, other systems dislike mallocing zero bytes. */ *************** *** 175,181 **** m->addr = (void*) 1; /* arbitrary non-NULL address */ else { ! #ifdef HAVE_MMAP /* Map the file into memory. */ m->addr = mmap( 0, m->size, PROT_READ, MAP_SHARED, fd, 0 ); if ( m->addr == (void*) -1 ) --- 302,313 ---- m->addr = (void*) 1; /* arbitrary non-NULL address */ else { ! #ifdef USE_SENDFILE ! m->fd = fd; ! #ifdef USE_KQUEUE_VNODE ! fdwatch_add_fd(fd, m, FDW_VNODE); ! #endif ! #elif defined(HAVE_MMAP) /* Map the file into memory. */ m->addr = mmap( 0, m->size, PROT_READ, MAP_SHARED, fd, 0 ); if ( m->addr == (void*) -1 ) *************** *** 207,214 **** } #endif /* HAVE_MMAP */ } (void) close( fd ); ! /* Put the Map into the hash table. */ if ( add_hash( m ) < 0 ) { --- 339,347 ---- } #endif /* HAVE_MMAP */ } + #ifndef USE_SENDFILE (void) close( fd ); ! #endif /* !USE_SENDFILE */ /* Put the Map into the hash table. */ if ( add_hash( m ) < 0 ) { *************** *** 217,238 **** --alloc_count; return (void*) 0; } /* Put the Map on the active list. */ m->next = maps; maps = m; ++map_count; /* And return the address. */ return m->addr; } void ! mmc_unmap( void* addr, struct stat* sbP, struct timeval* nowP ) { Map* m = (Map*) 0; ! /* Find the Map entry for this address. First try a hash. */ if ( sbP != (struct stat*) 0 ) { --- 350,400 ---- --alloc_count; return (void*) 0; } + + #ifdef USE_KQUEUE_VNODE + mmc_statadd(m); + #endif /* Put the Map on the active list. */ m->next = maps; maps = m; ++map_count; + *mtime = m->mtime; + + *mapp = m; + #ifdef DEBUG_MMC + fprintf(stderr, __FUNCTION__ ":%d map_count %d lru_count %d\n", + __LINE__, map_count, lru_count); + #endif + + #ifdef USE_SENDFILE + return (&m->fd); + #else /* And return the address. */ return m->addr; + #endif } void ! mmc_unmap( Map **mapp, void* addr, struct stat* sbP, struct timeval* nowP ) { Map* m = (Map*) 0; ! #ifdef DEBUG_MMC ! fprintf(stderr, __FUNCTION__ ":%d map_count %d lru_count %d\n", ! __LINE__, map_count, lru_count); ! #endif ! ! ! /* hykim: mmc opt1 */ ! if(*mapp != NULL) ! { ! /* fprintf(stderr, "mmc_unmap: mapp != NULL\n");*/ ! m = *mapp; ! } ! else ! { /* Find the Map entry for this address. First try a hash. */ if ( sbP != (struct stat*) 0 ) { *************** *** 245,262 **** --- 407,440 ---- for ( m = maps; m != (Map*) 0; m = m->next ) if ( m->addr == addr ) break; + } + if ( m == (Map*) 0 ) syslog( LOG_ERR, "mmc_unmap failed to find entry!" ); else if ( m->refcount <= 0 ) syslog( LOG_ERR, "mmc_unmap found zero or negative refcount!" ); else { + /* hykim: mmc opt1 */ + *mapp = NULL; + --m->refcount; if ( nowP != (struct timeval*) 0 ) m->reftime = nowP->tv_sec; else m->reftime = time( (time_t*) 0 ); + + if (0 == m->refcount) { + /* insert into LRU list */ + TAILQ_INSERT_TAIL(&mapLRU, m, lruList); + lru_count++; } + } + #ifdef DEBUG_MMC + fprintf(stderr, __FUNCTION__ ":%d map_count %d lru_count %d\n", + __LINE__, map_count, lru_count); + #endif + } *************** *** 267,272 **** --- 445,452 ---- Map** mm; Map* m; + return; /* This function is no longer needed since we now replace */ + /* Get the current time, if necessary. */ if ( nowP != (struct timeval*) 0 ) now = nowP->tv_sec; *************** *** 277,284 **** for ( mm = &maps; *mm != (Map*) 0; ) { m = *mm; ! if ( m->refcount == 0 && now - m->reftime >= expire_age ) really_unmap( mm ); else mm = &(*mm)->next; } --- 457,467 ---- for ( mm = &maps; *mm != (Map*) 0; ) { m = *mm; ! if ( m->refcount == 0 && now - m->reftime >= expire_age ) { ! TAILQ_REMOVE(&mapLRU, m, lruList); ! lru_count--; really_unmap( mm ); + } else mm = &(*mm)->next; } *************** *** 309,315 **** m = *mm; if ( m->size != 0 ) { ! #ifdef HAVE_MMAP if ( munmap( m->addr, m->size ) < 0 ) syslog( LOG_ERR, "munmap - %m" ); #else /* HAVE_MMAP */ --- 492,507 ---- m = *mm; if ( m->size != 0 ) { ! #ifdef USE_SENDFILE ! #ifdef USE_KQUEUE_VNODE ! mmc_statdel(m); ! if (m->filename != NULL) ! free( m->filename); ! m->sbvalidity = -1; ! fdwatch_del_fd(m->fd); ! #endif ! close(m->fd); ! #elif defined(HAVE_MMAP) if ( munmap( m->addr, m->size ) < 0 ) syslog( LOG_ERR, "munmap - %m" ); #else /* HAVE_MMAP */ *************** *** 334,341 **** { Map* m; ! while ( maps != (Map*) 0 ) really_unmap( &maps ); while ( free_maps != (Map*) 0 ) { m = free_maps; --- 526,540 ---- { Map* m; ! return; /* this cleanup function is unnecessary */ ! ! while ( maps != (Map*) 0 ) { ! if (0 == maps->refcount) { ! TAILQ_REMOVE(&mapLRU, maps, lruList); ! lru_count--; ! } really_unmap( &maps ); + } while ( free_maps != (Map*) 0 ) { m = free_maps; *************** *** 463,465 **** --- 662,875 ---- if ( map_count + free_count != alloc_count ) syslog( LOG_ERR, "map counts don't add up!" ); } + + #ifdef USE_KQUEUE_VNODE + + /* The below code is derived from CRC code that is + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + static const u_int32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + #define NUMSTATHASHBINS 32768 + + static TAILQ_HEAD(MapStatHash, MapStruct) statHashBins[NUMSTATHASHBINS]; + + static + uint32_t mmc_stathash( const char *filename ) + { + uint32_t csum = 0; + uint32_t len = 0; + const char *tmpptr; + + #define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + + for (tmpptr = filename; *tmpptr != '\0'; tmpptr++, len++) { + COMPUTE(csum, *tmpptr); + } + + /* Include the length of the file. */ + for (; len != 0; len >>= 8) { + COMPUTE(csum, len & 0xff); + } + + /* At this point, csum includes the POSIX 1003.2 (CRC) checksum. */ + return(csum); + } + + static void + mmc_statadd( Map *map ) + { + uint32_t hashidx; + + hashidx = (map->fn_hash) & (NUMSTATHASHBINS-1) ; + TAILQ_INSERT_HEAD(&statHashBins[hashidx], map, statnext); + } + + static void + mmc_statdel( Map *map ) + { + uint32_t hashidx; + + hashidx = (map->fn_hash) & (NUMSTATHASHBINS-1) ; + TAILQ_REMOVE(&statHashBins[hashidx], map, statnext); + } + + int + mmc_statlookup( const char *filename, struct stat *sbP, uint32_t *hashP ) + { + uint32_t csum; + uint32_t hashidx; + Map *map; + + csum = mmc_stathash(filename); + if (NULL != hashP) + *hashP = csum; /* save aside for later use if desired */ + + hashidx = csum & (NUMSTATHASHBINS-1) ; + TAILQ_FOREACH(map, &statHashBins[hashidx], statnext) { + if ((csum == map->fn_hash) && !strcmp(filename, map->filename)) { + /* There is a hash entry for this file */ + assert(-1 != map->sbvalidity); + if (0 == map->sbvalidity) { + struct stat sb2; + if ((0 != stat(filename, &sb2)) || + (sb2.st_ino != map->sb.st_ino) || + (sb2.st_dev != map->sb.st_dev) || + (sb2.st_ctime != map->sb.st_ctime)) { + Map *mapold = map; + /* Something changed about access to file */ + map->sbvalidity = -1; + map = TAILQ_PREV(map, MapStatHash, statnext); + TAILQ_REMOVE(&statHashBins[hashidx], mapold, statnext); + continue; /* go to next FOREACH entry */ + } + map->sbvalidity = 1; + } + + /* Now it's safe to return this */ + *sbP = map->sb; + return(1); + } + } + return(0); + } + + void + mmc_statinval( void *mapPtr ) + { + Map *modFile = (Map *)mapPtr; + Map *map; + int hashidx; + + #if 0 + fprintf(stderr, "mmc_statinval: file %s\n", modFile->filename); + #endif + + if (!fdwatch_check_fd(modFile->fd)) /* something went wrong */ + return; + + #if 0 + fprintf(stderr, "mmc_statinval after check: file %s\n", modFile->filename); + #endif + + modFile->sbvalidity = -1; + hashidx = modFile->fn_hash & (NUMSTATHASHBINS-1) ; + TAILQ_FOREACH(map, &statHashBins[hashidx], statnext) { + if ((modFile->fn_hash == map->fn_hash) && + !strcmp(modFile->filename, map->filename)) { + TAILQ_REMOVE(&statHashBins[hashidx], map, statnext); + return; + } + } + + return; + } + + #endif + Index: mmc.h =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/mmc.h,v retrieving revision 1.1.1.1 retrieving revision 1.3 diff -c -r1.1.1.1 -r1.3 *** mmc.h 2001/12/10 19:23:47 1.1.1.1 --- mmc.h 2002/09/24 15:22:50 1.3 *************** *** 28,44 **** #ifndef _MMC_H_ #define _MMC_H_ /* Returns an mmap()ed area for the given file, or (void*) 0 on errors. ** If you have a stat buffer on the file, pass it in, otherwise pass 0. ** Same for the current time. */ ! extern void* mmc_map( char* filename, struct stat* sbP, struct timeval* nowP ); /* Done with an mmap()ed area that was returned by mmc_map(). ** If you have a stat buffer on the file, pass it in, otherwise pass 0. ** Same for the current time. */ ! extern void mmc_unmap( void* addr, struct stat* sbP, struct timeval* nowP ); /* Clean up the mmc package, freeing any unused storage. ** This should be called periodically, say every five minutes. --- 28,52 ---- #ifndef _MMC_H_ #define _MMC_H_ + #include + #include "libhttpd.h" + + struct MapStruct; + /* Returns an mmap()ed area for the given file, or (void*) 0 on errors. ** If you have a stat buffer on the file, pass it in, otherwise pass 0. ** Same for the current time. */ ! extern void* mmc_map( struct MapStruct **mapp, ! char* filename, httpd_conn *hc, struct timeval* nowP, ! char** mtime, uint32_t* fnhashP); /* Done with an mmap()ed area that was returned by mmc_map(). ** If you have a stat buffer on the file, pass it in, otherwise pass 0. ** Same for the current time. */ ! extern void mmc_unmap( struct MapStruct **mapp, ! void* addr, struct stat* sbP, struct timeval* nowP ); /* Clean up the mmc package, freeing any unused storage. ** This should be called periodically, say every five minutes. *************** *** 51,55 **** --- 59,71 ---- /* Generate debugging statistics syslog message. */ extern void mmc_logstats( long secs ); + + /* maximum mapped files */ + extern int mmc_maxmaps; + + /* New functions for stat caching */ + extern int mmc_statlookup( const char *filename, struct stat *sbP, + uint32_t *hashP ); + extern void mmc_statinval ( void *mapPtr ); #endif /* _MMC_H_ */ Index: tdate_parse.c =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/tdate_parse.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -c -r1.1.1.1 -r1.2 *** tdate_parse.c 2001/12/10 19:23:47 1.1.1.1 --- tdate_parse.c 2002/09/24 15:22:50 1.2 *************** *** 60,70 **** } static int ! strlong_compare( v1, v2 ) ! char* v1; ! char* v2; { ! return strcmp( ((struct strlong*) v1)->s, ((struct strlong*) v2)->s ); } --- 60,71 ---- } static int ! strlong_compare( const void *v1v, const void *v2v ) { ! const char* v1 = (const char *)v1v; ! const char* v2 = (const char *)v2v; ! return strcmp( ((const struct strlong*) v1)->s, ! ((const struct strlong*) v2)->s ); } Index: thttpd.c =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/thttpd.c,v retrieving revision 1.1.1.2 retrieving revision 1.3 diff -c -r1.1.1.2 -r1.3 *** thttpd.c 2002/02/05 22:52:54 1.1.1.2 --- thttpd.c 2002/09/24 15:22:50 1.3 *************** *** 63,68 **** --- 63,71 ---- #define SHUT_WR 1 #endif + #ifndef __FreeBSD__ + #undef USE_SENDFILE + #endif static char* argv0; static int debug; *************** *** 112,120 **** --- 115,128 ---- off_t bytes_to_send; } connecttab; static connecttab* connects; + static int* free_connects; + static int fc_ptr; static int numconnects, maxconnects; static int httpd_conn_count; + #define FC_INSERT(cnum) { free_connects[--fc_ptr] = cnum; } + #define FC_REMOVE() free_connects[fc_ptr++] + /* The connection states. */ #define CNST_FREE 0 #define CNST_READING 1 *************** *** 144,149 **** --- 152,158 ---- static void shut_down( void ); static int handle_newconnect( struct timeval* tvP, int listen_fd ); static void handle_read( connecttab* c, struct timeval* tvP ); + static int handle_read_pconn( connecttab* c, struct timeval* tvP ); static void handle_send( connecttab* c, struct timeval* tvP ); static void handle_linger( connecttab* c, struct timeval* tvP ); static int check_throttles( connecttab* c ); *************** *** 240,245 **** --- 249,257 ---- /* Handle command-line arguments. */ parse_args( argc, argv ); + /* Initialize date cache */ + fill_nowbuf( time(NULL) ); + /* Check port number. */ if ( port <= 0 ) { *************** *** 518,528 **** --- 530,548 ---- syslog( LOG_CRIT, "out of memory allocating a connecttab" ); exit( 1 ); } + free_connects = NEW( int, maxconnects ); + if ( free_connects == (int*) 0 ) + { + syslog( LOG_CRIT, "out of memory allocating a connecttab free array" ); + exit( 1 ); + } for ( cnum = 0; cnum < maxconnects; ++cnum ) { connects[cnum].conn_state = CNST_FREE; connects[cnum].hc = (httpd_conn*) 0; + free_connects[cnum] = cnum; } + fc_ptr = 0; numconnects = 0; httpd_conn_count = 0; *************** *** 538,545 **** --- 558,567 ---- (void) gettimeofday( &tv, (struct timezone*) 0 ); while ( ( ! terminate ) || numconnects > 0 ) { + time_t oldtvs = tv.tv_sec; /* Do the fd watch. */ num_ready = fdwatch( tmr_mstimeout( &tv ) ); + if ( num_ready < 0 ) { if ( errno == EINTR ) *************** *** 548,553 **** --- 570,577 ---- exit( 1 ); } (void) gettimeofday( &tv, (struct timezone*) 0 ); + if (tv.tv_sec != oldtvs) + fill_nowbuf( tv.tv_sec ); if ( num_ready == 0 ) { /* No fd's are ready - run the timers. */ *************** *** 580,586 **** /* Find the connections that need servicing. */ for ( ridx = 0; ridx < num_ready; ++ridx ) { ! c = (connecttab*) fdwatch_get_client_data( ridx ); if ( c == (connecttab*) 0 ) continue; hc = c->hc; --- 604,624 ---- /* Find the connections that need servicing. */ for ( ridx = 0; ridx < num_ready; ++ridx ) { ! int fdstate; ! void *cdata; ! cdata = fdwatch_get_client_data( ridx, &fdstate ); ! ! #if defined(USE_SENDFILE) && defined(HAVE_KQUEUE) ! if ( FDW_VNODE == fdstate ) { ! if ( cdata != NULL ) ! mmc_statinval(cdata); ! continue; /* go to next fd. 'continue' minimizes patch size */ ! } ! ! /* Otherwise, it's a read or write */ ! #endif ! ! c = (connecttab*) cdata; if ( c == (connecttab*) 0 ) continue; hc = c->hc; *************** *** 669,674 **** --- 707,722 ---- ++argn; port = atoi( argv[argn] ); } + else if ( strcmp( argv[argn], "-m" ) == 0 && argn + 1 < argc ) + { + ++argn; + mmc_maxmaps = atoi( argv[argn] ); + } + else if ( strcmp( argv[argn], "-w" ) == 0 && argn + 1 < argc ) + { + ++argn; + sendLowWater = atoi( argv[argn] ); + } else if ( strcmp( argv[argn], "-d" ) == 0 && argn + 1 < argc ) { ++argn; *************** *** 1249,1261 **** free( (void*) throttles ); } static int handle_newconnect( struct timeval* tvP, int listen_fd ) { int cnum; connecttab* c; - ClientData client_data; /* This loops until the accept() fails, trying to start new ** connections as fast as possible so we don't overrun the --- 1297,1330 ---- free( (void*) throttles ); } + static void + init_conntab_entry ( connecttab* c, struct timeval* tvP ) + { + ClientData client_data; + client_data.p = c; + + c->conn_state = CNST_READING; + c->idle_read_timer = tmr_create( + tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L, + 0 ); + if ( c->idle_read_timer == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" ); + exit( 1 ); + } + c->idle_send_timer = (Timer*) 0; + c->wakeup_timer = (Timer*) 0; + c->linger_timer = (Timer*) 0; + c->bytes_sent = 0; + c->numtnums = 0; + fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ ); + } static int handle_newconnect( struct timeval* tvP, int listen_fd ) { int cnum; connecttab* c; /* This loops until the accept() fails, trying to start new ** connections as fast as possible so we don't overrun the *************** *** 1274,1283 **** tmr_run( tvP ); return 0; } /* Find a free connection entry. */ ! for ( cnum = 0; cnum < maxconnects; ++cnum ) ! if ( connects[cnum].conn_state == CNST_FREE ) ! break; c = &connects[cnum]; /* Make the httpd_conn if necessary. */ if ( c->hc == (httpd_conn*) 0 ) --- 1343,1363 ---- tmr_run( tvP ); return 0; } + /* Find a free connection entry. */ ! cnum = FC_REMOVE(); ! if (cnum >= maxconnects) ! { ! fprintf(stderr, "%s %d cnum >= maxconnects. Buffer overrun.\n", __FUNCTION__, __LINE__); ! exit(1); ! } ! if ( connects[cnum].conn_state != CNST_FREE ) ! { ! syslog( LOG_WARNING, "connects table corrupt!" ); ! fprintf(stderr, "LOWEST_FREE_ERROR!\n"); ! exit(1); ! } ! c = &connects[cnum]; /* Make the httpd_conn if necessary. */ if ( c->hc == (httpd_conn*) 0 ) *************** *** 1297,1329 **** { case GC_FAIL: case GC_NO_MORE: return 1; } - c->conn_state = CNST_READING; ++numconnects; - client_data.p = c; - c->idle_read_timer = tmr_create( - tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L, - 0 ); - if ( c->idle_read_timer == (Timer*) 0 ) - { - syslog( LOG_CRIT, "tmr_create(idle_read_connection) failed" ); - exit( 1 ); - } - c->idle_send_timer = (Timer*) 0; - c->wakeup_timer = (Timer*) 0; - c->linger_timer = (Timer*) 0; - c->bytes_sent = 0; - c->numtnums = 0; - /* Set the connection file descriptor to no-delay mode. */ httpd_set_ndelay( c->hc->conn_fd ); ! ! fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ ); ++stats_connections; if ( numconnects > stats_simultaneous ) stats_simultaneous = numconnects; } } --- 1377,1397 ---- { case GC_FAIL: case GC_NO_MORE: + FC_INSERT(cnum); return 1; } ++numconnects; /* Set the connection file descriptor to no-delay mode. */ + #ifndef __FreeBSD__ /* NOTE: In FreeBSD, conn gets ndelay from listen sock */ httpd_set_ndelay( c->hc->conn_fd ); ! #endif ++stats_connections; if ( numconnects > stats_simultaneous ) stats_simultaneous = numconnects; + + init_conntab_entry ( c, tvP ); + } } *************** *** 1338,1351 **** /* Is there room in our buffer to read more bytes? */ if ( hc->read_idx >= hc->read_size ) { ! if ( hc->read_size > 5000 ) { ! httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); ! clear_connection( c, tvP ); ! return; } - httpd_realloc_str( - &hc->read_buf, &hc->read_size, hc->read_size + 1000 ); } /* Read some more bytes. */ --- 1406,1442 ---- /* Is there room in our buffer to read more bytes? */ if ( hc->read_idx >= hc->read_size ) { ! if ( hc->read_size > 5000 && hc->next_req_idx > 0 ! && hc->read_idx > hc->next_req_idx ) { ! /* Move the unprocessed portion of the read buffer ! * to the front. ! */ ! (void) memcpy( hc->read_buf, &hc->read_buf[hc->next_req_idx], ! hc->read_idx - hc->next_req_idx ); ! ! hc->checked_idx = hc->checked_idx - hc->next_req_idx; ! hc->read_idx = hc->read_idx - hc->next_req_idx; ! hc->next_req_idx = 0; ! ! if ( hc->read_idx >= hc->read_size ) ! { ! printf( "handle_read: hc->read_idx >= hc->read_size after buffer relocation\n" ); ! exit(1); ! } ! } ! else if ( hc->read_size > 5000 ) ! { ! httpd_send_err( hc, 400, httpd_err400title, "", ! httpd_err400form, "" ); ! clear_connection( c, tvP ); ! return; ! } ! else ! { ! httpd_realloc_str(&hc->read_buf, &hc->read_size, ! hc->read_size + 1000 ); } } /* Read some more bytes. */ *************** *** 1387,1392 **** --- 1478,1486 ---- } /* Yes. Try parsing and resolving it. */ + hc->sb_inited = 0; + hc->fileInfo = NULL; + hc->fileInfoLen = 0; if ( httpd_parse_request( hc ) < 0 ) { clear_connection( c, tvP ); *************** *** 1454,1459 **** --- 1548,1695 ---- fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE ); } + + static int + handle_read_pconn( connecttab* c, struct timeval* tvP ) + { + /* handle_read_pconn is same as handle_read except that + * it does not read from the socket and does not + * generate error responses to avoid recursion. + * Eventually handle_read_pconn and handle_read should + * be merged. + */ + /* This function is called from really_clear_connection. + * We cannot call clear_connection here since that may force + * the server to recurse forever. + * If any error occurs, we send no reponse and simply close the connection. + */ + int i; + int sz; + ClientData client_data; + httpd_conn* hc = c->hc; + + /* Do we have a complete request yet? */ + switch ( httpd_got_request( hc ) ) + { + case GR_NO_REQUEST: + return 0; + case GR_BAD_REQUEST: + + /* Only for debugging */ + printf("%s %d bad request\n", __FUNCTION__, __LINE__); + for(i = 0; i < hc->read_idx; i++) { + if(hc->read_buf[i] == '\0') + hc->read_buf[i] = '\n'; + } + hc->read_buf[hc->read_idx] = '\0'; + printf("hc->read_buf: %d %d %d\n %s\n", hc->read_idx, hc->next_req_idx, hc->checked_idx, hc->read_buf); + printf("hc->read_buf[hc->next_req_idx]:\n %s\n", &hc->read_buf[hc->next_req_idx]); + exit(1); + + return -1; + } + + /* Yes. Try parsing and resolving it. */ + hc->sb_inited = 0; + hc->fileInfo = NULL; + hc->fileInfoLen = 0; + if ( httpd_parse_request( hc ) < 0 ) + { + /* Only for debugging */ + printf("%s %d parse_request returns -1 \n", __FUNCTION__, __LINE__); + exit(1); + + return -1; + } + + /* Check the throttle table */ + if ( ! check_throttles( c ) ) + { + #ifdef DEBUG + printf("%s %d check_throttles returns -1 \n", __FUNCTION__, __LINE__); + #endif + return -1; + } + + /* Start the connection going. */ + if ( httpd_start_request( hc, tvP ) < 0 ) + { + /* Something went wrong. Close down the connection. */ + #ifdef DEBUG + printf("%s %d httpd_start_request returns -1 \n", + __FUNCTION__, __LINE__); + #endif + return -1; + } + + #ifdef DEBUG + printf("%s %d File %s\n Stat %d\n" + "Sent %qd To send %qd\n", + __FUNCTION__, __LINE__, hc->expnfilename, hc->sb.st_size, + hc->bytes_sent, hc->bytes_to_send); + #endif + /* Fill in bytes_to_send. */ + if ( hc->got_range ) + { + c->bytes_sent = hc->init_byte_loc; + c->bytes_to_send = hc->end_byte_loc + 1; + } + else + c->bytes_to_send = hc->bytes_to_send; + + /* Check if it's already handled. */ + if ( hc->file_address == (char*) 0 ) + { + /* No file address means someone else is handling it. */ + #ifdef DEBUG + printf("%s %d hc->file_address == 0 \n", __FUNCTION__, __LINE__); + #endif + return -1; + } + if ( c->bytes_sent >= c->bytes_to_send ) + { + /* If the file size is zero, the response header generated + * by send_mime is transmitted by clear_connection. + * So we have to send the response... + */ + /* There's nothing to send. */ + if (hc->responselen > 0) + httpd_write_response(hc); + + printf("%s %d File %s\n" + "Sent qd, To send %qd\n", + __FUNCTION__, __LINE__, hc->expnfilename, + c->bytes_sent, c->bytes_to_send); + printf("DIE\n"); + exit(1); + + return -1; + } + + #ifdef DEBUG + printf("%s %d Conntab bytes to send %d\n", __FUNCTION__, __LINE__, + c->bytes_to_send); + #endif + + /* Cool, we have a valid connection and a file to send to it. */ + c->conn_state = CNST_SENDING; + c->started_at = tvP->tv_sec; + c->wouldblock_delay = 0; + client_data.p = c; + c->idle_read_timer = (Timer*) 0; + c->idle_send_timer = tmr_create(tvP, idle_send_connection, + client_data, IDLE_SEND_TIMELIMIT * 1000L, + 0 ); + if ( c->idle_send_timer == (Timer*) 0 ) + { + syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" ); + exit( 1 ); + } + + /* fd is cleared in really_clear_connection. */ + fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE ); + return 1; + } static void handle_send( connecttab* c, struct timeval* tvP ) *************** *** 1467,1478 **** --- 1703,1750 ---- if ( hc->responselen == 0 ) { /* No, just write the file. */ + #ifdef USE_SENDFILE + off_t sbytes; + + sz = sendfile( + hc->file_fd, hc->conn_fd, c->bytes_sent, + MIN( c->bytes_to_send - c->bytes_sent, c->limit ), + NULL, &sbytes, 0 ); + /* fprintf(stderr, "1477 Return from sendfile: %d %d\n", sz, sbytes); */ + if (sz == -1 && errno == EAGAIN) + sz = sbytes > 0 ? sbytes : -1; + else if (sz >= 0) + sz = sbytes; + #else sz = write( hc->conn_fd, &(hc->file_address[c->bytes_sent]), MIN( c->bytes_to_send - c->bytes_sent, c->limit ) ); + #endif } else { + #ifdef USE_SENDFILE + struct sf_hdtr sf; + struct iovec iv; + off_t sbytes; + + iv.iov_base = hc->response; + iv.iov_len = hc->responselen; + sf.headers = &iv; + sf.hdr_cnt = 1; + sf.trailers = NULL; + sf.trl_cnt = 0; + sz = sendfile( + hc->file_fd, hc->conn_fd, c->bytes_sent, + MIN( hc->responselen + c->bytes_to_send - c->bytes_sent, + c->limit ), + &sf, &sbytes, 0 ); + /* fprintf(stderr, "1505 Return from sendfile: %d %d\n", sz, sbytes); */ + if (sz == -1 && errno == EAGAIN) + sz = sbytes > 0 ? sbytes : -1; + else if (sz >= 0) + sz = sbytes; + #else /* Yes. We'll combine headers and file into a single writev(), ** hoping that this generates a single packet. */ *************** *** 1483,1488 **** --- 1755,1761 ---- iv[1].iov_base = &(hc->file_address[c->bytes_sent]); iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit ); sz = writev( hc->conn_fd, iv, 2 ); + #endif } if ( sz == 0 || *************** *** 1680,1686 **** ClientData client_data; /* If we haven't actually sent the buffered response yet, do so now. */ ! httpd_write_response( c->hc ); if ( c->idle_read_timer != (Timer*) 0 ) { --- 1953,1962 ---- ClientData client_data; /* If we haven't actually sent the buffered response yet, do so now. */ ! if ( c->hc->responselen > 0 ) { ! httpd_write_response( c->hc ); ! httpd_set_ndelay( c->hc->conn_fd ); /* may have been cleared above */ ! } if ( c->idle_read_timer != (Timer*) 0 ) { *************** *** 1734,1741 **** static void really_clear_connection( connecttab* c, struct timeval* tvP ) { stats_bytes += c->bytes_to_send; ! fdwatch_del_fd( c->hc->conn_fd ); httpd_close_conn( c->hc, tvP ); clear_throttles( c, tvP ); if ( c->linger_timer != (Timer*) 0 ) --- 2010,2090 ---- static void really_clear_connection( connecttab* c, struct timeval* tvP ) { + int cnum; + stats_bytes += c->bytes_to_send; ! httpd_release_conn( c->hc, tvP ); ! fdwatch_del_fd( c->hc->conn_fd ); /* must delete it since it may be ! * on queue as a write */ ! ! /* hykim: This is how p-conn is currently kept alive. */ ! if ( c->hc->kept_alive ) ! { ! /* If the previous request is bad, kept_alive flag should never have ! * been set. ! */ ! /* At this point, the read buffer may already have another full/partial ! * request. ! */ ! if( c->hc->read_idx > c->hc->next_req_idx) ! { ! #ifdef DEBUG ! printf("%s %d pre init_pconn %d %d %d\n", __FUNCTION__, __LINE__, ! c->hc->read_idx, c->hc->next_req_idx, c->hc->checked_idx); ! #endif ! httpd_init_pconn( c->hc ); ! #ifdef DEBUG ! printf("%s %d fd %d\n", __FUNCTION__, __LINE__, c->hc->conn_fd); ! #endif ! /* (Pipelined pconn) */ ! /* Assume the read buffer has another full request. ! */ ! /* This may well break request/reponse sequence under some circumstances... ! */ ! c->bytes_sent = 0; ! c->numtnums = 0; ! switch( handle_read_pconn( c, tvP ) ) ! { ! case -1: ! /* An error occurred. Really close the connection. */ ! #ifdef DEBUG ! printf("%s %d handle_read_pconn returns -1 %s\n", ! __FUNCTION__, __LINE__, c->hc->expnfilename); ! #endif ! break; ! case 0: ! /* The read buffer has a partial request. Keep reading. */ ! #ifdef DEBUG ! printf("%s %d continue reading\n", __FUNCTION__, __LINE__); ! #endif ! init_conntab_entry( c, tvP ); ! return; ! case 1: ! /* Another response is queued. Everything is in order. */ ! #ifdef DEBUG ! printf("%s %d pipelined request\n", __FUNCTION__, __LINE__); ! #endif ! return; ! } ! } ! else ! { ! /* We need to read more from the connection. (Non-pipelined pconn) */ ! ! if (c->hc->read_idx != c->hc->next_req_idx) ! { ! /* Only for debugging. */ ! printf("%s %d c->hc->read_idx != c->hc->next_req_idx for non-pipelined pconn\n", ! __FUNCTION__, __LINE__); ! exit(1); ! } ! httpd_init_conn (c->hc); ! init_conntab_entry( c, tvP ); ! return; ! } ! } ! ! /* Otherwise, this was a real close! */ httpd_close_conn( c->hc, tvP ); clear_throttles( c, tvP ); if ( c->linger_timer != (Timer*) 0 ) *************** *** 1745,1750 **** --- 2094,2102 ---- } c->conn_state = CNST_FREE; --numconnects; + + cnum = c - connects; + FC_INSERT(cnum); } Index: timers.c =================================================================== RCS file: /net/nevada1/cvs/arch/perftools_thttpd/timers.c,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -c -r1.1.1.1 -r1.2 *** timers.c 2001/12/10 19:23:47 1.1.1.1 --- timers.c 2002/09/24 15:22:51 1.2 *************** *** 34,41 **** #include "timers.h" ! #define HASH_SIZE 67 static Timer* timers[HASH_SIZE]; static Timer* free_timers; static int alloc_count, active_count, free_count; --- 34,42 ---- #include "timers.h" ! #define HASH_SIZE 64 static Timer* timers[HASH_SIZE]; + static Timer* timertails[HASH_SIZE]; static Timer* free_timers; static int alloc_count, active_count, free_count; *************** *** 53,59 **** */ return ( (unsigned int) t->time.tv_sec ^ ! (unsigned int) t->time.tv_usec ) % HASH_SIZE; } --- 54,60 ---- */ return ( (unsigned int) t->time.tv_sec ^ ! (unsigned int) t->time.tv_usec ) & (HASH_SIZE-1); } *************** *** 62,81 **** { int h = t->hash; register Timer* t2; register Timer* t2prev; t2 = timers[h]; if ( t2 == (Timer*) 0 ) { /* The list is empty. */ ! timers[h] = t; t->prev = t->next = (Timer*) 0; } else { ! if ( t->time.tv_sec < t2->time.tv_sec || ! ( t->time.tv_sec == t2->time.tv_sec && ! t->time.tv_usec <= t2->time.tv_usec ) ) { /* The new timer goes at the head of the list. */ timers[h] = t; --- 63,93 ---- { int h = t->hash; register Timer* t2; + register Timer* t3; register Timer* t2prev; t2 = timers[h]; + t3 = timertails[h]; if ( t2 == (Timer*) 0 ) { /* The list is empty. */ ! timers[h] = timertails[h] = t; t->prev = t->next = (Timer*) 0; } else { ! if ((t->time.tv_sec > t3->time.tv_sec) || ! ((t->time.tv_sec == t3->time.tv_sec) && ! (t->time.tv_usec >= t3->time.tv_usec))) { ! /* The new timer goes at the tail of the list */ ! timertails[h] = t; ! t->next = (Timer*) 0; ! t->prev = t3; ! t3->next = t; ! } ! else if ( t->time.tv_sec < t2->time.tv_sec || ! ( t->time.tv_sec == t2->time.tv_sec && ! t->time.tv_usec <= t2->time.tv_usec ) ) { /* The new timer goes at the head of the list. */ timers[h] = t; *************** *** 105,110 **** --- 117,123 ---- t2prev->next = t; t->prev = t2prev; t->next = (Timer*) 0; + timertails[h] = t; } } } *************** *** 121,126 **** --- 134,141 ---- t->prev->next = t->next; if ( t->next != (Timer*) 0 ) t->next->prev = t->prev; + else + timertails[h] = t->prev; } *************** *** 142,148 **** int h; for ( h = 0; h < HASH_SIZE; ++h ) ! timers[h] = (Timer*) 0; free_timers = (Timer*) 0; alloc_count = active_count = free_count = 0; } --- 157,163 ---- int h; for ( h = 0; h < HASH_SIZE; ++h ) ! timers[h] = timertails[h] = (Timer*) 0; free_timers = (Timer*) 0; alloc_count = active_count = free_count = 0; }