📄 thttpd.c.bak
字号:
{ /* Nuke comments. */ cp = strchr( buf, '#' ); if ( cp != (char*) 0 ) *cp = '\0'; /* Nuke trailing whitespace. */ len = strlen( buf ); while ( len > 0 && ( buf[len-1] == ' ' || buf[len-1] == '\t' || buf[len-1] == '\n' || buf[len-1] == '\r' ) ) buf[--len] = '\0'; /* Ignore empty lines. */ if ( len == 0 ) continue; /* Parse line. */ if ( sscanf( buf, " %[^ \t] %ld", pattern, &limit ) != 2 || limit <= 0 ) { syslog( LOG_CRIT, "unparsable line in %.80s - %.80s", throttlefile, buf ); (void) fprintf( stderr, "unparsable line in %.80s - %.80s\n", throttlefile, buf ); continue; } /* Nuke any leading slashes in pattern. */ if ( pattern[0] == '/' ) (void) strcpy( pattern, &pattern[1] ); while ( ( cp = strstr( pattern, "|/" ) ) != (char*) 0 ) (void) strcpy( cp + 1, cp + 2 ); /* Check for room in throttles. */ if ( numthrottles >= maxthrottles ) { if ( maxthrottles == 0 ) { maxthrottles = 100; /* arbitrary */ throttles = NEW( throttletab, maxthrottles ); } else { maxthrottles *= 2; throttles = RENEW( throttles, throttletab, maxthrottles ); } if ( throttles == (throttletab*) 0 ) { syslog( LOG_CRIT, "out of memory" ); (void) fprintf( stderr, "out of memory\n" ); exit( 1 ); } } /* Add to table. */ throttles[numthrottles].pattern = strdup( pattern ); if ( throttles[numthrottles].pattern == (char*) 0 ) { syslog( LOG_CRIT, "out of memory" ); (void) fprintf( stderr, "out of memory\n" ); exit( 1 ); } throttles[numthrottles].limit = limit; throttles[numthrottles].rate = 0; throttles[numthrottles].last_avg = tv.tv_sec; throttles[numthrottles].bytes_since_avg = 0; throttles[numthrottles].num_sending = 0; ++numthrottles; } (void) fclose( fp ); }static voidshut_down( void ) { int cnum; struct timeval tv;#ifdef STATS_TIME show_stats( (ClientData) 0, (struct timeval*) 0 );#endif /* STATS_TIME */ (void) gettimeofday( &tv, (struct timezone*) 0 ); for ( cnum = 0; cnum < maxconnects; ++cnum ) { if ( connects[cnum].conn_state != CNST_FREE ) httpd_close_conn( &connects[cnum].hc, &tv ); httpd_destroy_conn( &connects[cnum].hc ); } if ( hs != (httpd_server*) 0 ) { httpd_terminate( hs ); hs = (httpd_server*) 0; } mmc_destroy(); tmr_destroy(); free( (void*) connects ); }static inthandle_newconnect( struct timeval* tvP ) { 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 ** listen queue. */ for (;;) { /* Is there room in the connection table? */ if ( numconnects >= maxconnects ) { /* Out of connection slots. Run the timers, then the ** existing connections, and maybe we'll free up a slot ** by the time we get back here. **/ syslog( LOG_WARNING, "too many connections!" ); 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]; /* Get the connection. */ switch ( httpd_get_conn( hs, &connects[cnum].hc ) ) { case GC_FAIL: case GC_NO_MORE: return 1; } c->conn_state = CNST_READING; recompute_fdsets = 1; ++numconnects; client_data.p = c; c->idle_read_timer = tmr_create( tvP, idle_read_connection, client_data, IDLE_READ_TIMELIMIT * 1000L, 0 ); c->idle_send_timer = (Timer*) 0; c->wakeup_timer = (Timer*) 0; c->linger_timer = (Timer*) 0; c->bytes_sent = 0; /* Set the connection file descriptor to no-delay mode. */ if ( fcntl( c->hc.conn_fd, F_SETFL, O_NDELAY ) < 0 ) syslog( LOG_ERR, "fcntl O_NDELAY - %m" );#ifdef STATS_TIME ++stats_connections; if ( numconnects > stats_simultaneous ) stats_simultaneous = numconnects;#endif /* STATS_TIME */ } }static voidhandle_read( connecttab* c, struct timeval* tvP ) { int sz; ClientData client_data; /* Is there room in our buffer to read more bytes? */ if ( c->hc.read_idx >= sizeof(c->hc.read_buf) ) { httpd_send_err( &c->hc, 400, httpd_err400title, httpd_err400form, "" ); clear_connection( c, tvP ); return; } /* Read some more bytes. */ sz = read( c->hc.conn_fd, &(c->hc.read_buf[c->hc.read_idx]), sizeof(c->hc.read_buf) - c->hc.read_idx ); if ( sz <= 0 ) { httpd_send_err( &c->hc, 400, httpd_err400title, httpd_err400form, "" ); clear_connection( c, tvP ); return; } c->hc.read_idx += sz; /* Do we have a complete request yet? */ switch ( httpd_got_request( &c->hc ) ) { case GR_NO_REQUEST: return; case GR_BAD_REQUEST: httpd_send_err( &c->hc, 400, httpd_err400title, httpd_err400form, "" ); clear_connection( c, tvP ); return; } /* Yes. Try parsing it. */ if ( httpd_parse_request( &c->hc ) < 0 ) { clear_connection( c, tvP ); return; } /* Check the throttle table */ if ( ! check_throttles( c ) ) { httpd_send_err( &c->hc, 503, httpd_err503title, httpd_err503form, c->hc.encodedurl ); clear_connection( c, tvP ); return; } /* Start the connection going. */ if ( httpd_start_request( &c->hc ) < 0 ) { /* Something went wrong. Close down the connection. */ clear_connection( c, tvP ); return; } /* Fill in bytes_to_send. */ if ( c->hc.got_range ) { c->bytes_sent = c->hc.init_byte_loc; c->bytes_to_send = c->hc.end_byte_loc + 1; } else c->bytes_to_send = c->hc.bytes; /* Check if it's already handled. */ if ( c->hc.file_address == (char*) 0 ) { /* No file address means someone else is handling it. */ c->bytes_sent = c->hc.bytes; clear_connection( c, tvP ); return; } if ( c->bytes_sent >= c->bytes_to_send ) { /* There's nothing to send. */ clear_connection( c, tvP ); return; } /* Cool, we have a valid connection and a file to send to it. */ c->conn_state = CNST_SENDING; recompute_fdsets = 1; c->started_at = tvP->tv_sec; c->wouldblock_delay = 0; client_data.p = c; tmr_cancel( c->idle_read_timer ); c->idle_read_timer = (Timer*) 0; c->idle_send_timer = tmr_create( tvP, idle_send_connection, client_data, IDLE_SEND_TIMELIMIT * 1000L, 0 ); }static voidhandle_send( connecttab* c, struct timeval* tvP ) { int sz, coast; ClientData client_data; time_t elapsed; /* Do we need to write the headers first? */ if ( c->hc.responselen == 0 ) { /* No, just write the file. */ sz = write( c->hc.conn_fd, &c->hc.file_address[c->bytes_sent], MIN( c->bytes_to_send - c->bytes_sent, c->limit ) ); } else { /* Yes. We'll combine headers and file into a single writev(), ** hoping that this generates a single packet. */ struct iovec iv[2]; iv[0].iov_base = c->hc.response; iv[0].iov_len = c->hc.responselen; iv[1].iov_base = &c->hc.file_address[c->bytes_sent]; iv[1].iov_len = MIN( c->bytes_to_send - c->bytes_sent, c->limit ); sz = writev( c->hc.conn_fd, iv, 2 ); } if ( sz == 0 || ( sz < 0 && ( errno == EWOULDBLOCK || errno == EAGAIN ) ) ) { /* This shouldn't happen, but some kernels, e.g. ** SunOS 4.1.x, are broken and select() says that ** O_NDELAY sockets are always writable even when ** they're actually not. ** ** Current workaround is to block sending on this ** socket for a brief adaptively-tuned period. ** Fortunately we already have all the necessary ** blocking code, for use with throttling. */ c->wouldblock_delay += MIN_WOULDBLOCK_DELAY; c->conn_state = CNST_PAUSING; recompute_fdsets = 1; client_data.p = c; c->wakeup_timer = tmr_create( tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 ); return; } if ( sz < 0 ) { /* Something went wrong, close this connection. ** ** If it's just an EPIPE, don't bother logging, that ** just means the client hung up on us. ** ** On some systems, write() occasionally gives an EINVAL. ** Dunno why, something to do with the socket going ** bad. Anyway, we don't log those either. */ if ( errno != EPIPE && errno != EINVAL ) syslog( LOG_ERR, "write - %m" ); clear_connection( c, tvP ); return; } /* Ok, we wrote something. */ tmr_reset( tvP, c->idle_send_timer ); /* Was this a headers + file writev()? */ if ( c->hc.responselen > 0 ) { /* Yes; did we write only part of the headers? */ if ( sz < c->hc.responselen ) { /* Yes; move the unwritten part to the front of the buffer. */ int newlen = c->hc.responselen - sz; (void) memcpy( c->hc.response, &(c->hc.response[sz]), newlen ); c->hc.responselen = newlen; sz = 0; } else { /* Nope, we wrote the full headers, so adjust accordingly. */ sz -= c->hc.responselen; c->hc.responselen = 0; } } /* And update how much of the file we wrote. */ c->bytes_sent += sz; /* Are we done? */ if ( c->bytes_sent >= c->bytes_to_send ) { /* This conection is finished! */ clear_connection( c, tvP ); return; } /* Tune the (blockheaded) wouldblock delay. */ if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY ) c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY; /* Check if we're sending faster than the throttle. */ elapsed = tvP->tv_sec - c->started_at; if ( elapsed == 0 || c->bytes_sent / elapsed > c->limit ) { c->conn_state = CNST_PAUSING; recompute_fdsets = 1; /* When should we send the next c->limit bytes ** to get back on schedule? If less than a second ** (integer math rounding), use 1/8 second. */ coast = ( c->bytes_sent + c->limit ) / c->limit - elapsed; client_data.p = c; c->wakeup_timer = tmr_create( tvP, wakeup_connection, client_data, coast ? ( coast * 1000L ) : 125L, 0 ); } }static voidhandle_linger( connecttab* c, struct timeval* tvP ) { char buf[1024]; int r; /* In lingering-close mode we just read and ignore bytes. An error ** or EOF ends things, otherwise we go until a timeout. */ r = read( c->hc.conn_fd, buf, sizeof(buf) ); if ( r <= 0 ) really_clear_connection( c, tvP ); }static intcheck_throttles( connecttab* c ) { int tnum; c->numtnums = 0; c->limit = 1234567890L; for ( tnum = 0; tnum < numthrottles && c->numtnums < MAXTHROTTLENUMS; ++tnum ) if ( match( throttles[tnum].pattern, c->hc.expnfilename ) ) { c->tnums[c->numtnums++] = tnum; /* If we're way over the limit, don't even start. */ if ( throttles[tnum].rate > throttles[tnum].limit * 3 / 2 ) return 0; ++throttles[tnum].num_sending; c->limit = MIN( c->limit, throttles[tnum].limit / throttles[tnum].num_sending ); } return 1; }static voidclear_throttles( connecttab* c, struct timeval* tvP ) { int i, tnum; time_t elapsed; for ( i = 0; i < c->numtnums; ++i ) { tnum = c->tnums[i]; --throttles[tnum].num_sending; throttles[tnum].bytes_since_avg += c->bytes_sent; elapsed = tvP->tv_sec - throttles[tnum].last_avg; if ( elapsed >= THROTTLE_TIME ) { throttles[tnum].rate = ( throttles[tnum].rate + throttles[tnum].bytes_since_avg / elapsed ) / 2; throttles[tnum].bytes_since_avg = 0; } } }static voidclear_connection( connecttab* c, struct timeval* tvP ) { 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 ) { tmr_cancel( c->idle_read_timer ); c->idle_read_timer = 0; } if ( c->idle_send_timer != (Timer*) 0 ) { tmr_cancel( c->idle_send_timer ); c->idle_send_timer = 0; } if ( c->wakeup_timer != (Timer*) 0 ) { tmr_cancel( c->wakeup_timer ); c->wakeup_timer = 0; } /* This is our version of Apache's lingering_close() routine, which is ** their version of the often-broken SO_LINGER socket option. For why ** this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html ** What we do is delay the actual closing for a few seconds, while reading ** any bytes that come over the connection. However, we don't want to do ** this unless it's necessary, because it ties up a connection slot and ** file descriptor which means our maximum connection-handling rate ** is lower. So, elsewhere we set a flag when we detect the few ** circumstances that make a lingering close necessary. If the flag ** isn't set we do the real close now. */ if ( c->hc.should_linger ) { c->conn_state = CNST_LINGERING; recompute_fdsets = 1; client_data.p = c; c->linger_timer = tmr_create( tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 ); } else really_clear_connection( c, tvP ); }static voidreally_clear_connection( connecttab* c, struct timeval* tvP ) { httpd_close_conn( &c->hc, tvP ); clear_throttles( c, tvP ); if ( c->linger_timer != (Timer*) 0 ) { tmr_cancel( c->linger_timer ); c->linger_timer = 0; } c->conn_state = CNST_FREE; recompute_fdsets = 1; --numconnects; }static voididle_read_connection( ClientData client_data, struct timeval* nowP ) { connecttab* c; c = (connecttab*) client_data.p; c->idle_read_timer = (Timer*) 0; if ( c->conn_state != CNST_FREE ) { syslog( LOG_NOTICE, "%.80s connection timed out reading", inet_ntoa( c->hc.client_addr ) ); httpd_send_err( &c->hc, 408, httpd_err408title, httpd_err408form, "" ); clear_connection( c, nowP ); } }static voididle_send_connection( ClientData client_data, struct timeval* nowP ) { connecttab* c; c = (connecttab*) client_data.p; c->idle_send_timer = (Timer*) 0; if ( c->conn_state != CNST_FREE ) { syslog( LOG_NOTICE, "%.80s connection timed out sending", inet_ntoa( c->hc.client_addr ) ); clear_connection( c, nowP ); } }static voidwakeup_connection( ClientData client_data, struct timeval* nowP ) { connecttab* c; c = (connecttab*) client_data.p; c->wakeup_timer = (Timer*) 0; if ( c->conn_state == CNST_PAUSING ) { c->conn_state = CNST_SENDING; recompute_fdsets = 1; } }static voidlinger_clear_connection( ClientData client_data, struct timeval* nowP ) { connecttab* c; c = (connecttab*) client_data.p; c->linger_timer = (Timer*) 0; really_clear_connection( c, nowP ); }static voidoccasional( ClientData client_data, struct timeval* nowP ) { mmc_cleanup( nowP ); tmr_cleanup(); }#ifdef STATS_TIMEstatic voidshow_stats( ClientData client_data, struct timeval* nowP ) { int am, fm, at, ft; mmc_stats( &am, &fm ); tmr_stats( &at, &ft ); syslog( LOG_INFO, "%d seconds, %d connections, %d simultaneous, %d maps, %d timers", STATS_TIME, stats_connections, stats_simultaneous, am, at ); stats_connections = stats_simultaneous = 0; }#endif /* STATS_TIME */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -