示例#1
0
文件: t_timer.c 项目: cpaasch/iperf3
int 
main(int argc, char **argv)
{
    struct Timer *tp;

    flag = 0;
    tp = tmr_create((struct timeval*) 0, timer_proc, JunkClientData, 3000000, 0);

    sleep(2);

    tmr_run((struct timeval*) 0);
    if (flag)
    {
	printf("timer should not have expired\n");
	exit(-1);
    }
    sleep(1);

    tmr_run((struct timeval*) 0);
    if (!flag)
    {
	printf("timer should have expired\n");
	exit(-2);
    }

    tmr_destroy();
    exit(0);
}
示例#2
0
static void
clear_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;
	fdwatch_del_fd( c->hc->conn_fd );
	fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
	/* Make sure we are still in no-delay mode. */
	httpd_set_ndelay( c->hc->conn_fd );
	client_data.p = c;
	c->linger_timer = tmr_create(
	    tvP, linger_clear_connection, client_data, LINGER_TIME * 1000L, 0 );
	if ( c->linger_timer == (Timer*) 0 )
	    {
	    syslog( LOG_CRIT, "tmr_create(linger_clear_connection) failed" );
	    exit( 1 );
	    }
	}
    else
	really_clear_connection( c, tvP );
    }
示例#3
0
/// Create a timer.
/// \param[in]     timer_def     timer object referenced with \ref osTimer.
/// \param[in]     type          osTimerOnce for one-shot or osTimerPeriodic for periodic behavior.
/// \param[in]     argument      argument to the timer call back function.
/// \return timer ID for reference by other functions or NULL in case of error.
/// \note MUST REMAIN UNCHANGED: \b osTimerCreate shall be consistent in every CMSIS-RTOS.
osTimerId osTimerCreate (const osTimerDef_t *timer_def, os_timer_type type, void *argument)
{
	osTimerId tmr;
	(void) argument;
	tmr = tmr_create();
	if (tmr)
	{
		tmr->period = type;
		tmr->state  = timer_def->ptimer;
	}
	return tmr;
}
示例#4
0
static void clear_connection(struct connect_s *conn, struct timeval *tv)
{
  ClientData client_data;

  if (conn->wakeup_timer != NULL)
    {
      tmr_cancel(conn->wakeup_timer);
      conn->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 rateis
   * 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 (conn->conn_state == CNST_LINGERING)
    {
      /* If we were already lingering, shut down for real */

      tmr_cancel(conn->linger_timer);
      conn->linger_timer      = NULL;
      conn->hc->should_linger = false;
    }
  else if (conn->hc->should_linger)
    {
      fdwatch_del_fd(fw, conn->hc->conn_fd);
      conn->conn_state = CNST_LINGERING;
      fdwatch_add_fd(fw, conn->hc->conn_fd, conn);
      client_data.p = conn;

      conn->linger_timer = tmr_create(tv, linger_clear_connection, client_data,
                                      CONFIG_THTTPD_LINGER_MSEC, 0);
      if (conn->linger_timer != NULL)
        {
          return;
        }

      nerr("ERROR: tmr_create(linger_clear_connection) failed\n");
    }

  /* Either we are done lingering, we shouldn't linger, or we failed to setup the linger */

  really_clear_connection(conn);
}
示例#5
0
timer_hdl_t timer_start(timer_handler_t *thfunc, time_t duration,
                        timer_arg_t arg)
{
timer_t *tmr;
timer_hdl_t ret = NONE_TIMER;
   tmr = tmr_create(duration + systime,  thfunc);
   if (tmr != NULL)
   {
       ret = tmr->hdl = tmrhdl ++;
       tmr->arg = arg;
       tmr_insert(tmr);
   }
   return ret;
}
示例#6
0
void
httpd_conn_sleep( httpd_conn *hc, int seconds )
    {
    connecttab* c = hc->conn;
    ClientData client_data;

    if ( c->wakeup_timer != (Timer*) 0 )
	{
	tmr_cancel( c->wakeup_timer );
	c->wakeup_timer = 0;
	}
    fdwatch_del_fd( hc->conn_fd );
    c->conn_state = CNST_SLEEPING;
    client_data.p = c;
    c->wakeup_timer = tmr_create(
        0, httpd_conn_wakeup, client_data, seconds * 1000, 0 );
    }
struct Stream* stream_create(unsigned short int port, int stream_id,
        struct Receiver* receiver)
{
    struct Stream* cls = (struct Stream*) calloc(1, sizeof(struct Stream));
    const int requested_buffer_len = 16*1024*1024;
    cls->port = port;
    cls->buffer_len = requested_buffer_len;
    cls->stream_id = stream_id;
    cls->receiver = receiver;
    cls->tmr_memcpy = tmr_create();
    if ((cls->socket_handle = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        fprintf(stderr, "Cannot create socket.\n");
        return cls;
    }
    fcntl(cls->socket_handle, F_SETFL, O_NONBLOCK);
    setsockopt(cls->socket_handle, SOL_SOCKET, SO_RCVBUF,
            &cls->buffer_len, sizeof(int));
    uint32_t int_size = (uint32_t) sizeof(int);
    getsockopt(cls->socket_handle, SOL_SOCKET, SO_RCVBUF,
            &cls->buffer_len, &int_size);
    if (int_size != (uint32_t) sizeof(int))
    {
        fprintf(stderr, "Error at line %d\n", __LINE__);
        exit(1);
    }
    if ((cls->buffer_len / 2) < requested_buffer_len)
    {
        printf("Requested socket buffer of %d bytes; actual size is %d bytes\n",
                requested_buffer_len, cls->buffer_len / 2);
    }
    struct sockaddr_in myaddr;
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(port);
    if (bind(cls->socket_handle, (struct sockaddr*)&myaddr, sizeof(myaddr)) < 0)
    {
        fprintf(stderr, "Bind failed.\n");
        return cls;
    }
    cls->socket_buffer = (uchar*) malloc(cls->buffer_len);
    memset(cls->socket_buffer, 0, cls->buffer_len);
    return cls;
}
示例#8
0
static void
clear_connection( connecttab* c, struct timeval* tvP )
    {
    ClientData client_data;

    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->conn_state == CNST_LINGERING )
	{
	/* If we were already lingering, shut down for real. */
	tmr_cancel( c->linger_timer );
	c->linger_timer = (Timer*) 0;
	c->hc->should_linger = 0;
	}
    if ( c->hc->should_linger )
	{
	if ( c->conn_state != CNST_PAUSING && c->conn_state != CNST_SLEEPING )
	    fdwatch_del_fd( c->hc->conn_fd );
	c->conn_state = CNST_LINGERING;
	shutdown( c->hc->conn_fd, SHUT_WR );
	fdwatch_add_fd( c->hc->conn_fd, c, FDW_READ );
	client_data.p = c;
	c->linger_timer = tmr_create(
	    tvP, linger_clear_connection, client_data, LINGER_TIME, 0 );
	}
    else
	really_clear_connection( c, tvP );
    }
示例#9
0
文件: timer.c 项目: antontest/c
/*********************************************************
 ******************    Main Function    ******************
 *********************************************************/
int main(int agrc, char *agrv[])
{
    int rt = 0; /* return value of function main */

    tmr_arg_t arg;
    tmr_arg_t arg1;
    tmr_t *tmr = NULL;
    tmr = tmr_create();

    arg.wait = 800;
    arg.arg = "hi tmr 1";
    arg.cb = hi;
    arg.cnt = 10;

    arg1.wait = 2000;
    arg1.arg = "hi tmr 2";
    arg1.cb = hi;
    arg1.cnt = 5;

    tmr->start(tmr, &arg);
    tmr->start(tmr, &arg1);
    sleep(10);
    tmr->destroy(tmr);
    
    return 0;

    timer *t;
    t = timer_start(hi, "hi", 100);
    t->start(t);
    printf("timer state1: %s\n", t->get_state_str(t));
    sleep(1);
    t->pause(t);
    t->set_interval(t, 1000);
    printf("timer run times: %d\n", t->get_runtimes(t));
    printf("timer state: %s\n", t->get_state_str(t));
    sleep(1);
    t->resume(t);
    sleep(2);
    t->destroy(t);

    return rt;
}
示例#10
0
static void
start_socket( int url_num, int cnum, struct timeval* nowP )
    {
    ClientData client_data;
    int flags;
    int sip_num;

    /* Start filling in the connection slot. */
    connections[cnum].url_num = url_num;
    connections[cnum].started_at = *nowP;
    client_data.i = cnum;
    connections[cnum].did_connect = 0;
    connections[cnum].did_response = 0;
    connections[cnum].idle_timer = tmr_create(
	nowP, idle_connection, client_data, idle_secs * 1000L, 0 );
    connections[cnum].wakeup_timer = (Timer*) 0;
    connections[cnum].content_length = -1;
    connections[cnum].bytes = 0;
    connections[cnum].checksum = 0;
    connections[cnum].http_status = -1;

    /* Make a socket. */
    connections[cnum].conn_fd = socket(
	urls[url_num].sock_family, urls[url_num].sock_type,
	urls[url_num].sock_protocol );
    if ( connections[cnum].conn_fd < 0 )
	{
	perror( urls[url_num].url_str );
	return;
	}

    /* Set the file descriptor to no-delay mode. */
    flags = fcntl( connections[cnum].conn_fd, F_GETFL, 0 );
    if ( flags == -1 )
	{
	perror( urls[url_num].url_str );
	(void) close( connections[cnum].conn_fd );
	return;
	}
    if ( fcntl( connections[cnum].conn_fd, F_SETFL, flags | O_NDELAY ) < 0 ) 
	{
	perror( urls[url_num].url_str );
	(void) close( connections[cnum].conn_fd );
	return;
	}

    if ( num_sips > 0 )
	{
	/* Try a random source IP address. */
	sip_num = ( (unsigned long) random() ) % ( (unsigned int) num_sips );
	if ( bind(
		 connections[cnum].conn_fd,
		 (struct sockaddr*) &sips[sip_num].sa,
	         sizeof(sips[sip_num].sa) ) < 0 )
	    {
	    perror( "binding local address" );
	    (void) close( connections[cnum].conn_fd );
	    return;
	    }
	}

    /* Connect to the host. */
    connections[cnum].sa_len = urls[url_num].sa_len;
    (void) memmove(
	(void*) &connections[cnum].sa, (void*) &urls[url_num].sa,
	urls[url_num].sa_len );
    connections[cnum].connect_at = *nowP;
    if ( connect(
	     connections[cnum].conn_fd,
	     (struct sockaddr*) &connections[cnum].sa,
	     connections[cnum].sa_len ) < 0 )
	{
	if ( errno == EINPROGRESS )
	    {
	    connections[cnum].conn_state = CNST_CONNECTING;
	    return;
	    }
	else
	    {
	    perror( urls[url_num].url_str );
	    (void) close( connections[cnum].conn_fd );
	    return;
	    }
	}

    /* Connect succeeded instantly, so handle it now. */
    (void) gettimeofday( nowP, (struct timezone*) 0 );
    handle_connect( cnum, nowP, 0 );
    }
示例#11
0
int
main( int argc, char** argv )
    {
    int argn;
    int start;
#define START_NONE 0
#define START_PARALLEL 1
#define START_RATE 2
    int start_parallel = -1, start_rate = -1;
    int end;
#define END_NONE 0
#define END_FETCHES 1
#define END_SECONDS 2
    int end_fetches = -1, end_seconds = -1;
    int cnum;
    char* url_file;
    char* sip_file;
#ifdef RLIMIT_NOFILE
    struct rlimit limits;
#endif /* RLIMIT_NOFILE */
    fd_set rfdset;
    fd_set wfdset;
    struct timeval now;
    int i, r;

    max_connections = 64 - RESERVED_FDS;	/* a guess */
#ifdef RLIMIT_NOFILE
    /* Try and increase the limit on # of files to the maximum. */
    if ( getrlimit( RLIMIT_NOFILE, &limits ) == 0 )
	{
	if ( limits.rlim_cur != limits.rlim_max )
	    {
	    if ( limits.rlim_max == RLIM_INFINITY )
		limits.rlim_cur = 8192;		/* arbitrary */
	    else if ( limits.rlim_max > limits.rlim_cur )
		limits.rlim_cur = limits.rlim_max;
	    (void) setrlimit( RLIMIT_NOFILE, &limits );
	    }
	max_connections = limits.rlim_cur - RESERVED_FDS;
	}
#endif /* RLIMIT_NOFILE */

    /* Parse args. */
    argv0 = argv[0];
    argn = 1;
    do_checksum = do_throttle = do_verbose = do_jitter = do_proxy = 0;
    throttle = THROTTLE;
    sip_file = (char*) 0;
    idle_secs = IDLE_SECS;
    start = START_NONE;
    end = END_NONE;
    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
	{
	if ( strncmp( argv[argn], "-checksum", strlen( argv[argn] ) ) == 0 )
	    do_checksum = 1;
	else if ( strncmp( argv[argn], "-throttle", strlen( argv[argn] ) ) == 0 )
	    do_throttle = 1;
	else if ( strncmp( argv[argn], "-Throttle", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    do_throttle = 1;
	    throttle = atoi( argv[++argn] ) / 10.0;
	    }
	else if ( strncmp( argv[argn], "-verbose", strlen( argv[argn] ) ) == 0 )
	    do_verbose = 1;
	else if ( strncmp( argv[argn], "-timeout", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    idle_secs = atoi( argv[++argn] );
	else if ( strncmp( argv[argn], "-jitter", strlen( argv[argn] ) ) == 0 )
	    do_jitter = 1;
	else if ( strncmp( argv[argn], "-parallel", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    start = START_PARALLEL;
	    start_parallel = atoi( argv[++argn] );
	    if ( start_parallel < 1 )
		{
		(void) fprintf(
		    stderr, "%s: parallel must be at least 1\n", argv0 );
		exit( 1 );
		}
	    if ( start_parallel > max_connections )
		{
		(void) fprintf(
		    stderr, "%s: parallel may be at most %d\n", argv0, max_connections );
		exit( 1 );
		}
	    }
	else if ( strncmp( argv[argn], "-rate", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    start = START_RATE;
	    start_rate = atoi( argv[++argn] );
	    if ( start_rate < 1 )
		{
		(void) fprintf(
		    stderr, "%s: rate must be at least 1\n", argv0 );
		exit( 1 );
		}
	    if ( start_rate > 1000 )
		{
		(void) fprintf(
		    stderr, "%s: rate may be at most 1000\n", argv0 );
		exit( 1 );
		}
	    }
	else if ( strncmp( argv[argn], "-fetches", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    end = END_FETCHES;
	    end_fetches = atoi( argv[++argn] );
	    if ( end_fetches < 1 )
		{
		(void) fprintf(
		    stderr, "%s: fetches must be at least 1\n", argv0 );
		exit( 1 );
		}
	    }
	else if ( strncmp( argv[argn], "-seconds", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    end = END_SECONDS;
	    end_seconds = atoi( argv[++argn] );
	    if ( end_seconds < 1 )
		{
		(void) fprintf(
		    stderr, "%s: seconds must be at least 1\n", argv0 );
		exit( 1 );
		}
	    }
	else if ( strncmp( argv[argn], "-sip", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    sip_file = argv[++argn];
#ifdef USE_SSL
	else if ( strncmp( argv[argn], "-cipher", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    cipher = argv[++argn];
	    if ( strcasecmp( cipher, "fastsec" ) == 0 )
		cipher = "RC4-MD5";
	    else if ( strcasecmp( cipher, "highsec" ) == 0 )
		cipher = "DES-CBC3-SHA";
	    else if ( strcasecmp( cipher, "paranoid" ) == 0 )
		cipher = "AES256-SHA";
	    }
#endif /* USE_SSL */
	else if ( strncmp( argv[argn], "-proxy", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
	    {
	    char* colon;
	    do_proxy = 1;
	    proxy_hostname = argv[++argn];
	    colon = strchr( proxy_hostname, ':' );
	    if ( colon == (char*) 0 )
		proxy_port = 80;
	    else
		{
		proxy_port = (unsigned short) atoi( colon + 1 );
		*colon = '\0';
		}
	    }
	else
	    usage();
	++argn;
	}
    if ( argn + 1 != argc )
	usage();
    if ( start == START_NONE || end == END_NONE )
	usage();
    if ( do_jitter && start != START_RATE )
	usage();
    url_file = argv[argn];

    /* Read in and parse the URLs. */
	printf("Loading url file...");
    read_url_file( url_file );
	printf("Total %d urls loaded.\n", num_urls);

    /* Read in the source IP file, if specified. */
    if ( sip_file != (char*) 0 )
	read_sip_file( sip_file );

    /* Initialize the connections table. */
    if ( start == START_PARALLEL )
	max_connections = start_parallel;
    connections = (connection*) malloc_check(
	max_connections * sizeof(connection) );
    for ( cnum = 0; cnum < max_connections; ++cnum )
	connections[cnum].conn_state = CNST_FREE;
    num_connections = max_parallel = 0;

    /* Initialize the HTTP status-code histogram. */
    for ( i = 0; i < 1000; ++i )
	http_status_counts[i] = 0;

    /* Initialize the statistics. */
    fetches_started = 0;
    connects_completed = 0;
    responses_completed = 0;
    fetches_completed = 0;
    total_bytes = 0;
    total_connect_usecs = 0;
    max_connect_usecs = 0;
    min_connect_usecs = 1000000000L;
    total_response_usecs = 0;
    max_response_usecs = 0;
    min_response_usecs = 1000000000L;
    total_timeouts = 0;
    total_badbytes = 0;
    total_badchecksums = 0;

    /* Initialize the random number generator. */
#ifdef HAVE_SRANDOMDEV
    srandomdev();
#else
    srandom( (int) time( (time_t*) 0 ) ^ getpid() );
#endif

    /* Initialize the rest. */
    tmr_init();
    (void) gettimeofday( &now, (struct timezone*) 0 );
    start_at = now;
    if ( do_verbose )
	(void) tmr_create(
	    &now, progress_report, JunkClientData, PROGRESS_SECS * 1000L, 1 );
    if ( start == START_RATE )
	{
	start_interval = 1000L / start_rate;
	if ( do_jitter )
	    {
	    low_interval = start_interval * 9 / 10;
	    high_interval = start_interval * 11 / 10;
	    range_interval = high_interval - low_interval + 1;
	    }
	(void) tmr_create(
	    &now, start_timer, JunkClientData, start_interval, ! do_jitter );
	}
    if ( end == END_SECONDS )
	(void) tmr_create(
	    &now, end_timer, JunkClientData, end_seconds * 1000L, 0 );
    (void) signal( SIGPIPE, SIG_IGN );

    /* Main loop. */
    for (;;)
	{
	if ( end == END_FETCHES && fetches_completed >= end_fetches )
	    finish( &now );

	if ( start == START_PARALLEL )
	    {
	    /* See if we need to start any new connections; but at most 10. */
	    for ( i = 0;
		  i < 10 &&
		    num_connections < start_parallel &&
		    ( end != END_FETCHES || fetches_started < end_fetches );
		  ++i )
		{
		start_connection( &now );
		(void) gettimeofday( &now, (struct timezone*) 0 );
		tmr_run( &now );
		}
	    }

	/* Build the fdsets. */
	FD_ZERO( &rfdset );
	FD_ZERO( &wfdset );
	for ( cnum = 0; cnum < max_connections; ++cnum )
	    switch ( connections[cnum].conn_state )
		{
		case CNST_CONNECTING:
		FD_SET( connections[cnum].conn_fd, &wfdset );
		break;
		case CNST_HEADERS:
		case CNST_READING:
		FD_SET( connections[cnum].conn_fd, &rfdset );
		break;
		}
	r = select(
	    FD_SETSIZE, &rfdset, &wfdset, (fd_set*) 0, tmr_timeout( &now ) );
	if ( r < 0 )
	    {
	    perror( "select" );
	    exit( 1 );
	    }
	(void) gettimeofday( &now, (struct timezone*) 0 );

	/* Service them. */
	for ( cnum = 0; cnum < max_connections; ++cnum )
	    switch ( connections[cnum].conn_state )
		{
		case CNST_CONNECTING:
		if ( FD_ISSET( connections[cnum].conn_fd, &wfdset ) )
		    handle_connect( cnum, &now, 1 );
		break;
		case CNST_HEADERS:
		case CNST_READING:
		if ( FD_ISSET( connections[cnum].conn_fd, &rfdset ) )
		    handle_read( cnum, &now );
		break;
		}
	/* And run the timers. */
	tmr_run( &now );
	}

    /* NOT_REACHED */
    }
示例#12
0
static void
handle_read( int cnum, struct timeval* nowP )
    {
    char buf[30000];	/* must be larger than throttle / 2 */
    int bytes_to_read, bytes_read, bytes_handled;
    float elapsed;
    ClientData client_data;
    register long checksum;

    tmr_reset( nowP, connections[cnum].idle_timer );

    if ( do_throttle )
	bytes_to_read = throttle / 2.0;
    else
	bytes_to_read = sizeof(buf);
    if ( ! connections[cnum].did_response )
	{
	connections[cnum].did_response = 1;
	connections[cnum].response_at = *nowP;
	}
#ifdef USE_SSL
    if ( urls[connections[cnum].url_num].protocol == PROTO_HTTPS )
	bytes_read = SSL_read( connections[cnum].ssl, buf, bytes_to_read );
    else
	bytes_read = read( connections[cnum].conn_fd, buf, bytes_to_read );
#else
    bytes_read = read( connections[cnum].conn_fd, buf, bytes_to_read );
#endif
    if ( bytes_read <= 0 )
	{
	close_connection( cnum );
	return;
	}

    for ( bytes_handled = 0; bytes_handled < bytes_read; )
	{
	switch ( connections[cnum].conn_state )
	    {
	    case CNST_HEADERS:
	    /* State machine to read until we reach the file part.  Looks for
	    ** Content-Length header too.
	    */
	    for ( ; bytes_handled < bytes_read && connections[cnum].conn_state == CNST_HEADERS; ++bytes_handled )
		{
		switch ( connections[cnum].header_state )
		    {

		    case HDST_LINE1_PROTOCOL:
		    switch ( buf[bytes_handled] )
			{
			case ' ': case '\t':
			connections[cnum].header_state = HDST_LINE1_WHITESPACE;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			}
		    break;

		    case HDST_LINE1_WHITESPACE:
		    switch ( buf[bytes_handled] )
			{
			case ' ': case '\t':
			break;
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
			connections[cnum].http_status =
			    buf[bytes_handled] - '0';
			connections[cnum].header_state = HDST_LINE1_STATUS;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_LINE1_STATUS:
		    switch ( buf[bytes_handled] )
			{
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
			connections[cnum].http_status =
			    connections[cnum].http_status * 10 +
			    buf[bytes_handled] - '0';
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_BOL:
		    switch ( buf[bytes_handled] )
			{
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			case 'C': case 'c':
			connections[cnum].header_state = HDST_C;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_TEXT:
		    switch ( buf[bytes_handled] )
			{
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			break;
			}
		    break;

		    case HDST_LF:
		    switch ( buf[bytes_handled] )
			{
			case '\n':
			connections[cnum].conn_state = CNST_READING;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			case 'C': case 'c':
			connections[cnum].header_state = HDST_C;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CR:
		    switch ( buf[bytes_handled] )
			{
			case '\n':
			connections[cnum].header_state = HDST_CRLF;
			break;
			case '\r':
			connections[cnum].conn_state = CNST_READING;
			break;
			case 'C': case 'c':
			connections[cnum].header_state = HDST_C;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CRLF:
		    switch ( buf[bytes_handled] )
			{
			case '\n':
			connections[cnum].conn_state = CNST_READING;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CRLFCR;
			break;
			case 'C': case 'c':
			connections[cnum].header_state = HDST_C;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CRLFCR:
		    switch ( buf[bytes_handled] )
			{
			case '\n': case '\r':
			connections[cnum].conn_state = CNST_READING;
			break;
			case 'C': case 'c':
			connections[cnum].header_state = HDST_C;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_C:
		    switch ( buf[bytes_handled] )
			{
			case 'O': case 'o':
			connections[cnum].header_state = HDST_CO;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CO:
		    switch ( buf[bytes_handled] )
			{
			case 'N': case 'n':
			connections[cnum].header_state = HDST_CON;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CON:
		    switch ( buf[bytes_handled] )
			{
			case 'T': case 't':
			connections[cnum].header_state = HDST_CONT;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONT:
		    switch ( buf[bytes_handled] )
			{
			case 'E': case 'e':
			connections[cnum].header_state = HDST_CONTE;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTE:
		    switch ( buf[bytes_handled] )
			{
			case 'N': case 'n':
			connections[cnum].header_state = HDST_CONTEN;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTEN:
		    switch ( buf[bytes_handled] )
			{
			case 'T': case 't':
			connections[cnum].header_state = HDST_CONTENT;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT:
		    switch ( buf[bytes_handled] )
			{
			case '-':
			connections[cnum].header_state = HDST_CONTENT_;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_:
		    switch ( buf[bytes_handled] )
			{
			case 'L': case 'l':
			connections[cnum].header_state = HDST_CONTENT_L;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_L:
		    switch ( buf[bytes_handled] )
			{
			case 'E': case 'e':
			connections[cnum].header_state = HDST_CONTENT_LE;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LE:
		    switch ( buf[bytes_handled] )
			{
			case 'N': case 'n':
			connections[cnum].header_state = HDST_CONTENT_LEN;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LEN:
		    switch ( buf[bytes_handled] )
			{
			case 'G': case 'g':
			connections[cnum].header_state = HDST_CONTENT_LENG;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LENG:
		    switch ( buf[bytes_handled] )
			{
			case 'T': case 't':
			connections[cnum].header_state = HDST_CONTENT_LENGT;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LENGT:
		    switch ( buf[bytes_handled] )
			{
			case 'H': case 'h':
			connections[cnum].header_state = HDST_CONTENT_LENGTH;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LENGTH:
		    switch ( buf[bytes_handled] )
			{
			case ':':
			connections[cnum].header_state = HDST_CONTENT_LENGTH_COLON;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LENGTH_COLON:
		    switch ( buf[bytes_handled] )
			{
			case ' ': case '\t':
			connections[cnum].header_state = HDST_CONTENT_LENGTH_COLON_WHITESPACE;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LENGTH_COLON_WHITESPACE:
		    switch ( buf[bytes_handled] )
			{
			case ' ': case '\t':
			break;
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
			connections[cnum].content_length = buf[bytes_handled] - '0';
			connections[cnum].header_state = HDST_CONTENT_LENGTH_COLON_WHITESPACE_NUM;
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    case HDST_CONTENT_LENGTH_COLON_WHITESPACE_NUM:
		    switch ( buf[bytes_handled] )
			{
			case '0': case '1': case '2': case '3': case '4':
			case '5': case '6': case '7': case '8': case '9':
			connections[cnum].content_length =
			    connections[cnum].content_length * 10 +
			    buf[bytes_handled] - '0';
			break;
			case '\n':
			connections[cnum].header_state = HDST_LF;
			break;
			case '\r':
			connections[cnum].header_state = HDST_CR;
			break;
			default:
			connections[cnum].header_state = HDST_TEXT;
			break;
			}
		    break;

		    }
		}
	    break;

	    case CNST_READING:
	    connections[cnum].bytes += bytes_read - bytes_handled;
	    if ( do_throttle )
		{
		/* Check if we're reading too fast. */
		elapsed = delta_timeval( &connections[cnum].started_at, nowP ) / 1000000.0;
		if ( elapsed > 0.01 && connections[cnum].bytes / elapsed > throttle )
		    {
		    connections[cnum].conn_state  = CNST_PAUSING;
		    client_data.i = cnum;
		    connections[cnum].wakeup_timer = tmr_create(
			nowP, wakeup_connection, client_data, 1000L, 0 );
		    }
		}
	    if ( do_checksum )
		{
		checksum = connections[cnum].checksum;
		for ( ; bytes_handled < bytes_read; ++bytes_handled )
		    {
		    if ( checksum & 1 )
			checksum = ( checksum >> 1 ) + 0x8000;
		    else
			checksum >>= 1;
		    checksum += buf[bytes_handled];
		    checksum &= 0xffff;
		    }
		connections[cnum].checksum = checksum;
		}
	    else
		bytes_handled = bytes_read;

	    if ( connections[cnum].content_length != -1 &&
		 connections[cnum].bytes >= connections[cnum].content_length )
		{
		close_connection( cnum );
		return;
		}

	    break;
	    }
示例#13
0
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
    ** 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];
	/* Make the httpd_conn if necessary. */
	if ( c->hc == (httpd_conn*) 0 )
	    {
	    c->hc = NEW( httpd_conn, 1 );
	    if ( c->hc == (httpd_conn*) 0 )
		{
		syslog( LOG_CRIT, "out of memory allocating an httpd_conn" );
		exit( 1 );
		}
	    c->hc->initialized = 0;
	    ++httpd_conn_count;
	    }

	/* Get the connection. */
	switch ( httpd_get_conn( hs, listen_fd, c->hc ) )
	    {
	    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;
	}
    }
int thttpd_main(int argc, char **argv)
#endif
{
  int num_ready;
  int cnum;
  FAR struct connect_s *conn;
  FAR httpd_conn *hc;
  httpd_sockaddr sa;
  struct timeval tv;
#ifdef CONFIG_THTTPD_DIR
  int ret;
#endif

  nvdbg("THTTPD started\n");

  /* Setup host address */

#ifdef  CONFIG_NET_IPv6
#  error "IPv6 support not yet implemented"
#else
  sa.sin_family      = AF_INET;
  sa.sin_port        = HTONS(CONFIG_THTTPD_PORT);
  sa.sin_addr.s_addr = HTONL(CONFIG_THTTPD_IPADDR);
#endif

  /* Initialize the fdwatch package to handle all of the configured
   * socket descriptors
   */

  fw = fdwatch_initialize(CONFIG_NSOCKET_DESCRIPTORS);
  if (!fw)
    {
      ndbg("fdwatch initialization failure\n");
      exit(1);
    }

  /* Switch directories again if requested */

#ifdef CONFIG_THTTPD_DATADIR
  if (chdir(CONFIG_THTTPD_DATADIR) < 0)
    {
      ndbg("chdir to %s: %d\n", CONFIG_THTTPD_DATADIR, errno);
      exit(1);
    }
#endif

  /* Initialize the timer package */

  tmr_init();

  /* Initialize the HTTP layer */

  nvdbg("Calling httpd_initialize()\n");
  hs = httpd_initialize(&sa);
  if (!hs)
    {
      ndbg("httpd_initialize() failed\n");
      exit(1);
    }

  /* Set up the occasional timer */

  if (tmr_create(NULL, occasional, JunkClientData, CONFIG_THTTPD_OCCASIONAL_MSEC * 1000L, 1) == NULL)
    {
      ndbg("tmr_create(occasional) failed\n");
      exit(1);
    }

  /* Set up the idle timer */

  if (tmr_create(NULL, idle, JunkClientData, 5 * 1000L, 1) == NULL)
    {
      ndbg("tmr_create(idle) failed\n");
      exit(1);

    }

  /* Initialize our connections table */

  connects = NEW(struct connect_s, AVAILABLE_FDS);
  if (connects == NULL)
    {
      ndbg("Out of memory allocating a struct connect_s\n");
      exit(1);
    }

  for (cnum = 0; cnum < AVAILABLE_FDS; ++cnum)
    {
      connects[cnum].conn_state  = CNST_FREE;
      connects[cnum].next        = &connects[cnum + 1];
      connects[cnum].hc          = NULL;
    }

  connects[AVAILABLE_FDS-1].next = NULL;      /* End of link list */
  free_connections               = connects;  /* Beginning of the link list */

  if (hs != NULL)
    {
      if (hs->listen_fd != -1)
        {
          fdwatch_add_fd(fw, hs->listen_fd, NULL);
        }
    }

  /* Main loop */

  nvdbg("Entering the main loop\n");
  (void)gettimeofday(&tv, NULL);
  for (;;)
    {
      /* Do the fd watch */

      num_ready = fdwatch(fw, tmr_mstimeout(&tv));
      if (num_ready < 0)
        {
          if (errno == EINTR || errno == EAGAIN)
            {
              /* Not errors... try again */

              continue;
            }

          ndbg("fdwatch failed: %d\n", errno);
          exit(1);
        }

      (void)gettimeofday(&tv, NULL);

      if (num_ready == 0)
        {
          /* No fd's are ready - run the timers */

          tmr_run(&tv);
          continue;
        }

      /* Is it a new connection? */

      if (fdwatch_check_fd(fw, hs->listen_fd))
        {
          if (!handle_newconnect(&tv, hs->listen_fd))
            {
              /* Go around the loop and do another fdwatch, rather than
               * dropping through and processing existing connections.  New
               * connections always get priority.
               */

              continue;
            }
        }

      /* Find the connections that need servicing */

      while ((conn = (struct connect_s*)fdwatch_get_next_client_data(fw)) != (struct connect_s*)-1)
        {
          if (conn)
            {
              hc = conn->hc;
              if (fdwatch_check_fd(fw, hc->conn_fd))
                {
                  nvdbg("Handle conn_state %d\n", conn->conn_state);
                  switch (conn->conn_state)
                    {
                      case CNST_READING:
                        {
                          handle_read(conn, &tv);

                          /* If a GET request was received and a file is ready to
                           * be sent, then fall through to send the file.
                           */

                          if (conn->conn_state != CNST_SENDING)
                            {
                              break;
                            }
                        }

                      case CNST_SENDING:
                        {
                          /* Send a file -- this really should be performed on a
                           * separate thread to keep the serve from locking up during
                           * the write.
                           */

                          handle_send(conn, &tv);
                        }
                        break;

                      case CNST_LINGERING:
                        {
                          /* Linger close the connection */

                          handle_linger(conn, &tv);
                        }
                        break;
                    }
                }
            }
        }
      tmr_run(&tv);
    }

  /* The main loop terminated */

  shut_down();
  ndbg("Exiting\n");
  exit(0);
}
示例#15
0
static void
handle_read( connecttab* c, struct timeval* tvP )
    {
    int sz;
    ClientData client_data;
    httpd_conn* hc = c->hc;

    /* 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. */
    sz = read(
	hc->conn_fd, &(hc->read_buf[hc->read_idx]),
	hc->read_size - hc->read_idx );
    /* Ignore EWOULDBLOCK errors.  At first glance you would think that
    ** connections returned by fdwatch as readable should never give an
    ** EWOULDBLOCK; however, this apparently can happen if a packet gets
    ** garbled.
    */
    if ( sz == 0 || ( sz < 0 && ( errno != EWOULDBLOCK ) ) )
	{
	httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
	clear_connection( c, tvP );
	return;
	}
    hc->read_idx += sz;

    /* Do we have a complete request yet? */
    switch ( httpd_got_request( hc ) )
	{
	case GR_NO_REQUEST:
	return;
	case GR_BAD_REQUEST:
	httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
	clear_connection( c, tvP );
	return;
	}

    /* Yes.  Try parsing and resolving it. */
    if ( httpd_parse_request( hc ) < 0 )
	{
	clear_connection( c, tvP );
	return;
	}

    /* Check the throttle table */
    if ( ! check_throttles( c ) )
	{
	httpd_send_err(
	    hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
	clear_connection( c, tvP );
	return;
	}

    /* Start the connection going. */
    if ( httpd_start_request( hc, tvP ) < 0 )
	{
	/* Something went wrong.  Close down the connection. */
	clear_connection( c, tvP );
	return;
	}

    /* 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. */
	c->bytes_sent = hc->bytes_sent;
	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;
    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 );
    if ( c->idle_send_timer == (Timer*) 0 )
	{
	syslog( LOG_CRIT, "tmr_create(idle_send_connection) failed" );
	exit( 1 );
	}

    fdwatch_del_fd( hc->conn_fd );
    fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
    }
示例#16
0
static void
handle_send( connecttab* c, struct timeval* tvP )
    {
    size_t max_bytes;
    int sz, coast;
    ClientData client_data;
    time_t elapsed;
    httpd_conn* hc = c->hc;
    int tind;

    max_bytes = 1000000000L;

    /* Do we need to write the headers first? */
    if ( hc->responselen == 0 )
	{
	/* No, just write the file. */
	sz = write(
	    hc->conn_fd, &(hc->body_data[c->next_byte_index]),
	    MIN( c->end_byte_index - c->next_byte_index, max_bytes ) );
	}
    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 = hc->response;
	iv[0].iov_len = hc->responselen;
	iv[1].iov_base = &(hc->body_data[c->next_byte_index]);
	iv[1].iov_len = MIN( c->end_byte_index - c->next_byte_index, max_bytes );
	sz = writev( hc->conn_fd, iv, 2 );
	}

    if ( sz < 0 && errno == EINTR )
	return;

    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;
	fdwatch_del_fd( hc->conn_fd );
	client_data.p = c;
	c->wakeup_timer = tmr_create(
	    tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
	if ( c->wakeup_timer == (Timer*) 0 )
	    {
	    return;
	    }
	return;
	}

    if ( sz < 0 )
	{
	/* Something went wrong, close this connection.
	*/
	clear_connection( c, tvP );
	return;
	}

    /* Ok, we wrote something. */
    c->active_at = tvP->tv_sec;
    /* Was this a headers + file writev()? */
    if ( hc->responselen > 0 )
	{
	/* Yes; did we write only part of the headers? */
	if ( sz < hc->responselen )
	    {
	    /* Yes; move the unwritten part to the front of the buffer. */
	    int newlen = hc->responselen - sz;
	    (void) memmove( hc->response, &(hc->response[sz]), newlen );
	    hc->responselen = newlen;
	    sz = 0;
	    }
	else
	    {
	    /* Nope, we wrote the full headers, so adjust accordingly. */
	    sz -= hc->responselen;
	    hc->responselen = 0;
	    }
	}
    /* And update how much of the file we wrote. */
    c->next_byte_index += sz;
    c->hc->bytes_sent += sz;

    /* Are we done? */
    if ( c->next_byte_index >= c->end_byte_index )
	{
	/* This connection is finished! */
	finish_connection( c, tvP );
	return;
	}

    /* Tune the (blockheaded) wouldblock delay. */
    if ( c->wouldblock_delay > MIN_WOULDBLOCK_DELAY )
	c->wouldblock_delay -= MIN_WOULDBLOCK_DELAY;

    /* (No check on min_limit here, that only controls connection startups.) */
    }
示例#17
0
int
thttpd_run(void)
    {
    char* cp;
    struct passwd* pwd;
    uid_t uid = 32767;
    gid_t gid = 32767;
    int num_ready;
    int cnum;
    connecttab* c;
    httpd_conn* hc;
    httpd_sockaddr sa4;
    httpd_sockaddr sa6;
    int gotv4, gotv6;
    struct timeval tv;
    
    cp = getenv( "GHTTPPORT" );
    if ( cp )
	port = atoi( cp );
    if( port == 0 )
	port = 9999;

    /* Read zone info now, in case we chroot(). */
    tzset();

    /* Look up hostname now, in case we chroot(). */
    lookup_hostname( &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6 );
    if ( ! ( gotv4 || gotv6 ) )
	{
	memset(&sa4, 0, sizeof sa4);
	gotv4 = 1;
	}

    /* Initialize the fdwatch package.  Have to do this before chroot,
    ** if /dev/poll is used.
    */
    max_connects = fdwatch_get_nfiles();
    if ( max_connects < 0 )
	{
	return;
	}
    max_connects -= SPARE_FDS;

    /* Set up to catch signals. */
#ifdef HAVE_SIGSET
    (void) sigset( SIGPIPE, SIG_IGN );          /* get EPIPE instead */
#else /* HAVE_SIGSET */
    (void) signal( SIGPIPE, SIG_IGN );          /* get EPIPE instead */
#endif /* HAVE_SIGSET */

    /* Initialize the timer package. */
    tmr_init();

    /* Initialize the HTTP layer.  Got to do this before giving up root,
    ** so that we can bind to a privileged port.
    */
    hs = httpd_initialize(
	hostname,
	gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
	port, cgi_pattern, cgi_limit, charset, p3p, max_age, "/", no_log, 
	no_symlink_check, do_vhost, do_global_passwd, url_pattern,
	local_pattern, no_empty_referers );
    if ( hs == (httpd_server*) 0 )
	exit( 1 );

    /* Set up the occasional timer. */
    if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 )
	{
	return;
	}
    /* Set up the idle timer. */
    if ( tmr_create( (struct timeval*) 0, idle, JunkClientData, 5 * 1000L, 1 ) == (Timer*) 0 )
	{
	return;
	}
    start_time = stats_time = time( (time_t*) 0 );
    stats_connections = 0;
    stats_bytes = 0;
    stats_simultaneous = 0;

    /* Initialize our connections table. */
    connects = NEW( connecttab, max_connects );
    if ( connects == (connecttab*) 0 )
	{
	return;
	}
    for ( cnum = 0; cnum < max_connects; ++cnum )
	{
	connects[cnum].conn_state = CNST_FREE;
	connects[cnum].next_free_connect = cnum + 1;
	connects[cnum].hc = (httpd_conn*) 0;
	}
    connects[max_connects - 1].next_free_connect = -1;	/* end of link list */
    first_free_connect = 0;
    num_connects = 0;
    httpd_conn_count = 0;

    if ( hs != (httpd_server*) 0 )
	{
	if ( hs->listen4_fd != -1 )
	    fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
	if ( hs->listen6_fd != -1 )
	    fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
	}

    /* Main loop. */
    (void) gettimeofday( &tv, (struct timezone*) 0 );
    while ( ( ! terminate ) || num_connects > 0 )
	{

	/* Do the fd watch. */
	num_ready = fdwatch( tmr_mstimeout( &tv ) );
	if ( num_ready < 0 )
	    {
	    if ( errno == EINTR || errno == EAGAIN )
		continue;       /* try again */
	    return;
	    }
	(void) gettimeofday( &tv, (struct timezone*) 0 );

	if ( num_ready == 0 )
	    {
	    /* No fd's are ready - run the timers. */
	    tmr_run( &tv );
	    continue;
	    }

	/* Is it a new connection? */
	if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 &&
	     fdwatch_check_fd( hs->listen6_fd ) )
	    {
	    if ( handle_newconnect( &tv, hs->listen6_fd ) )
		/* Go around the loop and do another fdwatch, rather than
		** dropping through and processing existing connections.
		** New connections always get priority.
		*/
		continue;
	    }
	if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 &&
	     fdwatch_check_fd( hs->listen4_fd ) )
	    {
	    if ( handle_newconnect( &tv, hs->listen4_fd ) )
		/* Go around the loop and do another fdwatch, rather than
		** dropping through and processing existing connections.
		** New connections always get priority.
		*/
		continue;
	    }

	/* Find the connections that need servicing. */
	while ( ( c = (connecttab*) fdwatch_get_next_client_data() ) != (connecttab*) -1 )
	    {
	    if ( c == (connecttab*) 0 )
		continue;
	    hc = c->hc;
	    if ( ! fdwatch_check_fd( hc->conn_fd ) )
		/* Something went wrong. */
		clear_connection( c, &tv );
	    else
		switch ( c->conn_state )
		    {
		    case CNST_READING: handle_read( c, &tv ); break;
		    case CNST_SENDING: handle_send( c, &tv ); break;
		    case CNST_LINGERING: handle_linger( c, &tv ); break;
		    }
	    }
	tmr_run( &tv );
	}

    /* The main loop terminated. */
    shut_down();
    return 0;
    }
示例#18
0
int
main( int argc, char** argv )
    {
    char* cp;
    struct passwd* pwd;
    uid_t uid;
    gid_t gid;
    char cwd[MAXPATHLEN];
    FILE* logfp;
    int num_ready;
    int cnum, ridx;
    connecttab* c;
    httpd_conn* hc;
    httpd_sockaddr sa4;
    httpd_sockaddr sa6;
    int gotv4, gotv6;
    struct timeval tv;

    argv0 = argv[0];

    cp = strrchr( argv0, '/' );
    if ( cp != (char*) 0 )
	++cp;
    else
	cp = argv0;
    openlog( cp, LOG_NDELAY|LOG_PID, LOG_FACILITY );

    /* Handle command-line arguments. */
    parse_args( argc, argv );

    /* Check port number. */
    if ( port <= 0 )
	{
	syslog( LOG_CRIT, "illegal port number" );
	(void) fprintf( stderr, "%s: illegal port number\n", argv0 );
	exit( 1 );
	}

    /* Read zone info now, in case we chroot(). */
    tzset();

    /* Look up hostname now, in case we chroot(). */
    lookup_hostname( &sa4, sizeof(sa4), &gotv4, &sa6, sizeof(sa6), &gotv6 );
    if ( ! ( gotv4 || gotv6 ) )
	{
	syslog( LOG_ERR, "can't find any valid address" );
	(void) fprintf( stderr, "%s: can't find any valid address\n", argv0 );
	exit( 1 );
	}

    /* Throttle file. */
    numthrottles = 0;
    maxthrottles = 0;
    throttles = (throttletab*) 0;
    if ( throttlefile != (char*) 0 )
	read_throttlefile( throttlefile );

    /* Log file. */
    if ( logfile != (char*) 0 )
	{
	if ( strcmp( logfile, "/dev/null" ) == 0 )
	    {
	    no_log = 1;
	    logfp = (FILE*) 0;
	    }
	else
	    {
	    logfp = fopen( logfile, "a" );
	    if ( logfp == (FILE*) 0 )
		{
		syslog( LOG_CRIT, "%.80s - %m", logfile );
		perror( logfile );
		exit( 1 );
		}
	    (void) fcntl( fileno( logfp ), F_SETFD, 1 );
	    }
	}
    else
	logfp = (FILE*) 0;

    /* Figure out uid/gid from user. */
    pwd = getpwnam( user );
    if ( pwd == (struct passwd*) 0 )
	{
	syslog( LOG_CRIT, "unknown user - '%.80s'", user );
	(void) fprintf( stderr, "%s: unknown user - '%s'\n", argv0, user );
	exit( 1 );
	}
    uid = pwd->pw_uid;
    gid = pwd->pw_gid;

    /* Switch directories if requested. */
    if ( dir != (char*) 0 )
	{
	if ( chdir( dir ) < 0 )
	    {
	    syslog( LOG_CRIT, "chdir - %m" );
	    perror( "chdir" );
	    exit( 1 );
	    }
	}
#ifdef USE_USER_DIR
    else if ( getuid() == 0 )
	{
	/* No explicit directory was specified, we're root, and the
	** USE_USER_DIR option is set - switch to the specified user's
	** home dir.
	*/
	if ( chdir( pwd->pw_dir ) < 0 )
	    {
	    syslog( LOG_CRIT, "chdir - %m" );
	    perror( "chdir" );
	    exit( 1 );
	    }
	}
#endif /* USE_USER_DIR */

    /* Get current directory. */
    (void) getcwd( cwd, sizeof(cwd) - 1 );
    if ( cwd[strlen( cwd ) - 1] != '/' )
	(void) strcat( cwd, "/" );

    if ( ! debug )
	{
	/* We're not going to use stdin stdout or stderr from here on, so close
	** them to save file descriptors.
	*/
	(void) fclose( stdin );
	(void) fclose( stdout );
	(void) fclose( stderr );

	/* Daemonize - make ourselves a subprocess. */
#ifdef HAVE_DAEMON
	if ( daemon( 1, 1 ) < 0 )
	    {
	    syslog( LOG_CRIT, "daemon - %m" );
	    exit( 1 );
	    }
#else /* HAVE_DAEMON */
	switch ( fork() )
	    {
	    case 0:
	    break;
	    case -1:
	    syslog( LOG_CRIT, "fork - %m" );
	    exit( 1 );
	    default:
	    exit( 0 );
	    }
#ifdef HAVE_SETSID
        (void) setsid();
#endif /* HAVE_SETSID */
#endif /* HAVE_DAEMON */
	}
    else
	{
	/* Even if we don't daemonize, we still want to disown our parent
	** process.
	*/
#ifdef HAVE_SETSID
        (void) setsid();
#endif /* HAVE_SETSID */
	}

    if ( pidfile != (char*) 0 )
	{
	/* Write the PID file. */
	FILE* pidfp = fopen( pidfile, "w" );
	if ( pidfp == (FILE*) 0 )
	    {
	    syslog( LOG_CRIT, "%.80s - %m", pidfile );
	    exit( 1 );
	    }
	(void) fprintf( pidfp, "%d\n", (int) getpid() );
	(void) fclose( pidfp );
	}

    /* Chroot if requested. */
    if ( do_chroot )
	{
	if ( chroot( cwd ) < 0 )
	    {
	    syslog( LOG_CRIT, "chroot - %m" );
	    perror( "chroot" );
	    exit( 1 );
	    }
	(void) strcpy( cwd, "/" );
	/* Always chdir to / after a chroot. */
	if ( chdir( cwd ) < 0 )
	    {
	    syslog( LOG_CRIT, "chroot chdir - %m" );
	    perror( "chroot chdir" );
	    exit( 1 );
	    }
	}

    /* Set up to catch signals. */
    (void) signal( SIGTERM, handle_term );
    (void) signal( SIGINT, handle_term );
    (void) signal( SIGPIPE, SIG_IGN );          /* get EPIPE instead */
    (void) signal( SIGHUP, handle_hup );
    got_usr1 = 0;
    (void) signal( SIGUSR1, handle_usr1 );
    (void) signal( SIGUSR2, handle_usr2 );

    /* Initialize the timer package. */
    tmr_init();

    /* Initialize the HTTP layer.  Got to do this before giving up root,
    ** so that we can bind to a privileged port.
    */
    hs = httpd_initialize(
	hostname,
	gotv4 ? &sa4 : (httpd_sockaddr*) 0, gotv6 ? &sa6 : (httpd_sockaddr*) 0,
	port, cgi_pattern, charset, cwd, no_log, logfp, no_symlink, do_vhost,
	do_global_passwd, url_pattern, local_pattern, no_empty_referers );
    if ( hs == (httpd_server*) 0 )
	exit( 1 );

    /* Set up the occasional timer. */
    if ( tmr_create( (struct timeval*) 0, occasional, JunkClientData, OCCASIONAL_TIME * 1000L, 1 ) == (Timer*) 0 )
	{
	syslog( LOG_CRIT, "tmr_create(occasional) failed" );
	exit( 1 );
	}
    if ( numthrottles > 0 )
	{
	/* Set up the throttles timer. */
	if ( tmr_create( (struct timeval*) 0, update_throttles, JunkClientData, THROTTLE_TIME * 1000L, 1 ) == (Timer*) 0 )
	    {
	    syslog( LOG_CRIT, "tmr_create(update_throttles) failed" );
	    exit( 1 );
	    }
	}
#ifdef STATS_TIME
    /* Set up the stats timer. */
    if ( tmr_create( (struct timeval*) 0, show_stats, JunkClientData, STATS_TIME * 1000L, 1 ) == (Timer*) 0 )
	{
	syslog( LOG_CRIT, "tmr_create(show_stats) failed" );
	exit( 1 );
	}
#endif /* STATS_TIME */
    start_time = stats_time = time( (time_t*) 0 );
    stats_connections = stats_bytes = 0L;
    stats_simultaneous = 0;

    /* If we're root, try to become someone else. */
    if ( getuid() == 0 )
	{
	/* Set aux groups to null. */
	if ( setgroups( 0, (const gid_t*) 0 ) < 0 )
	    {
	    syslog( LOG_CRIT, "setgroups - %m" );
	    exit( 1 );
	    }
	/* Set primary group. */
	if ( setgid( gid ) < 0 )
	    {
	    syslog( LOG_CRIT, "setgid - %m" );
	    exit( 1 );
	    }
	/* Try setting aux groups correctly - not critical if this fails. */
	if ( initgroups( user, gid ) < 0 )
	    syslog( LOG_WARNING, "initgroups - %m" );
#ifdef HAVE_SETLOGIN
	/* Set login name. */
        (void) setlogin( user );
#endif /* HAVE_SETLOGIN */
	/* Set uid. */
	if ( setuid( uid ) < 0 )
	    {
	    syslog( LOG_CRIT, "setuid - %m" );
	    exit( 1 );
	    }
	/* Check for unnecessary security exposure. */
	if ( ! do_chroot )
	    syslog(
		LOG_CRIT,
		"started as root without requesting chroot(), warning only" );
	}

    /* Initialize our connections table. */
    maxconnects = fdwatch_get_nfiles();
    if ( maxconnects < 0 )
	{
	syslog( LOG_CRIT, "fdwatch initialization failure" );
	exit( 1 );
	}
    maxconnects -= SPARE_FDS;
    connects = NEW( connecttab, maxconnects );
    if ( connects == (connecttab*) 0 )
	{
	syslog( LOG_CRIT, "out of memory allocating a connecttab" );
	exit( 1 );
	}
    for ( cnum = 0; cnum < maxconnects; ++cnum )
	{
	connects[cnum].conn_state = CNST_FREE;
	connects[cnum].hc = (httpd_conn*) 0;
	}
    numconnects = 0;
    httpd_conn_count = 0;

    if ( hs != (httpd_server*) 0 )
	{
	if ( hs->listen4_fd != -1 )
	    fdwatch_add_fd( hs->listen4_fd, (void*) 0, FDW_READ );
	if ( hs->listen6_fd != -1 )
	    fdwatch_add_fd( hs->listen6_fd, (void*) 0, FDW_READ );
	}

    /* Main loop. */
    (void) gettimeofday( &tv, (struct timezone*) 0 );
    while ( ( ! terminate ) || numconnects > 0 )
	{
	/* Do the fd watch. */
	num_ready = fdwatch( tmr_mstimeout( &tv ) );
	if ( num_ready < 0 )
	    {
	    if ( errno == EINTR )
		continue;       /* try again */
	    syslog( LOG_ERR, "fdwatch - %m" );
	    exit( 1 );
	    }
	(void) gettimeofday( &tv, (struct timezone*) 0 );
	if ( num_ready == 0 )
	    {
	    /* No fd's are ready - run the timers. */
	    tmr_run( &tv );
	    continue;
	    }

	/* Is it a new connection? */
	if ( hs != (httpd_server*) 0 && hs->listen6_fd != -1 && 
	     fdwatch_check_fd( hs->listen6_fd ) )
	    {
	    if ( handle_newconnect( &tv, hs->listen6_fd ) )
		/* Go around the loop and do another fdwatch, rather than
		** dropping through and processing existing connections.
		** New connections always get priority.
		*/
		continue;
	    }
	if ( hs != (httpd_server*) 0 && hs->listen4_fd != -1 && 
	     fdwatch_check_fd( hs->listen4_fd ) )
	    {
	    if ( handle_newconnect( &tv, hs->listen4_fd ) )
		/* Go around the loop and do another fdwatch, rather than
		** dropping through and processing existing connections.
		** New connections always get priority.
		*/
		continue;
	    }

	/* 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;
	    if ( c->conn_state == CNST_READING &&
		 fdwatch_check_fd( hc->conn_fd ) )
		handle_read( c, &tv );
	    else if ( c->conn_state == CNST_SENDING &&
		 fdwatch_check_fd( hc->conn_fd ) )
		handle_send( c, &tv );
	    else if ( c->conn_state == CNST_LINGERING &&
		 fdwatch_check_fd( hc->conn_fd ) )
		handle_linger( c, &tv );
	    }
	tmr_run( &tv );

	if ( got_usr1 && ! terminate )
	    {
	    terminate = 1;
	    if ( hs != (httpd_server*) 0 )
		{
		httpd_terminate( hs );
		hs = (httpd_server*) 0;
		}
	    }
	}

    /* The main loop terminated. */
    shut_down();
    syslog( LOG_NOTICE, "exiting" );
    closelog();
    exit( 0 );
    }
示例#19
0
static void
handle_send( connecttab* c, struct timeval* tvP )
    {
    int sz, coast;
    ClientData client_data;
    time_t elapsed;
    httpd_conn* hc = c->hc;

    /* Do we need to write the headers first? */
    if ( hc->responselen == 0 )
	{
	/* No, just write the file. */
	sz = write(
	    hc->conn_fd, &(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 = hc->response;
	iv[0].iov_len = hc->responselen;
	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 );
	}

    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;
	fdwatch_del_fd( hc->conn_fd );
	client_data.p = c;
	c->wakeup_timer = tmr_create(
	    tvP, wakeup_connection, client_data, c->wouldblock_delay, 0 );
	if ( c->wakeup_timer == (Timer*) 0 )
	    {
	    syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
	    exit( 1 );
	    }
	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.
	**
	** And ECONNRESET isn't interesting either.
	*/
	if ( errno != EPIPE && errno != EINVAL && errno != ECONNRESET )
	    syslog( LOG_ERR, "write - %m sending %.80s", hc->encodedurl );
	clear_connection( c, tvP );
	return;
	}

    /* Ok, we wrote something. */
    tmr_reset( tvP, c->idle_send_timer );
    /* Was this a headers + file writev()? */
    if ( hc->responselen > 0 )
	{
	/* Yes; did we write only part of the headers? */
	if ( sz < hc->responselen )
	    {
	    /* Yes; move the unwritten part to the front of the buffer. */
	    int newlen = hc->responselen - sz;
	    (void) memcpy( hc->response, &(hc->response[sz]), newlen );
	    hc->responselen = newlen;
	    sz = 0;
	    }
	else
	    {
	    /* Nope, we wrote the full headers, so adjust accordingly. */
	    sz -= hc->responselen;
	    hc->responselen = 0;
	    }
	}
    /* And update how much of the file we wrote. */
    c->bytes_sent += sz;
    c->hc->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;

    /* If we're throttling, check if we're sending too fast. */
    if ( c->limit != THROTTLE_NOLIMIT )
	{
	elapsed = tvP->tv_sec - c->started_at;
	if ( elapsed == 0 || c->hc->bytes_sent / elapsed > c->limit )
	    {
	    c->conn_state = CNST_PAUSING;
	    fdwatch_del_fd( hc->conn_fd );
	    /* 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->hc->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 );
	    if ( c->wakeup_timer == (Timer*) 0 )
		{
		syslog( LOG_CRIT, "tmr_create(wakeup_connection) failed" );
		exit( 1 );
		}
	    }
	}
    }