static void idle(ClientData client_data, struct timeval *nowP) { int cnum; struct connect_s *conn; for (cnum = 0; cnum < AVAILABLE_FDS; ++cnum) { conn = &connects[cnum]; switch (conn->conn_state) { case CNST_READING: if (nowP->tv_sec - conn->active_at >= CONFIG_THTTPD_IDLE_READ_LIMIT_SEC) { ndbg("%s connection timed out reading\n", httpd_ntoa(&conn->hc->client_addr)); httpd_send_err(conn->hc, 408, httpd_err408title, "", httpd_err408form, ""); finish_connection(conn, nowP); } break; case CNST_SENDING: if (nowP->tv_sec - conn->active_at >= CONFIG_THTTPD_IDLE_SEND_LIMIT_SEC) { ndbg("%s connection timed out sending\n", httpd_ntoa(&conn->hc->client_addr)); clear_connection(conn, nowP); } break; } } }
static void idle( ClientData client_data, struct timeval* nowP ) { int cnum; connecttab* c; for ( cnum = 0; cnum < max_connects; ++cnum ) { c = &connects[cnum]; switch ( c->conn_state ) { case CNST_READING: if ( nowP->tv_sec - c->active_at >= IDLE_READ_TIMELIMIT ) { httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" ); finish_connection( c, nowP ); } break; case CNST_SENDING: case CNST_PAUSING: if ( nowP->tv_sec - c->active_at >= IDLE_SEND_TIMELIMIT ) { clear_connection( c, nowP ); } break; case CNST_SLEEPING:; } } }
int32 PoorManServer::_Listener(void* data) { PRINT(("The listener thread is working.\n")); int retval; thread_id tid; httpd_conn* hc; PoorManServer* s = static_cast<PoorManServer*>(data); while (s->fIsRunning) { hc = new httpd_conn; hc->initialized = 0; PRINT(("calling httpd_get_conn()\n")); retval = //accept(), blocked here httpd_get_conn(s->fHttpdServer, s->fHttpdServer->listen4_fd, hc); switch (retval) { case GC_OK: break; case GC_FAIL: httpd_destroy_conn(hc); delete hc; s->fIsRunning = false; return -1; case GC_NO_MORE: //should not happen, since we have a blocking socket httpd_destroy_conn(hc); continue; break; default: //shouldn't happen continue; break; } if (s->fCurConns > s->fMaxConns) { httpd_send_err(hc, 503, httpd_err503title, (char *)"", httpd_err503form, (char *)""); httpd_write_response(hc); continue; } tid = spawn_thread( PoorManServer::_Worker, "www connection", B_NORMAL_PRIORITY, static_cast<void*>(s) ); if (tid < B_OK) { continue; } /*We don't check the return code here. *As we can't kill a thread that doesn't receive the *httpd_conn, we simply let it die itself. */ send_data(tid, 512, &hc, sizeof(httpd_conn*)); atomic_add(&s->fCurConns, 1); resume_thread(tid); }//while return 0; }
static void idle_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_INFO, "%.80s connection timed out reading", httpd_ntoa( &c->hc->client_addr ) ); httpd_send_err( c->hc, 408, httpd_err408title, "", httpd_err408form, "" ); clear_connection( c, nowP ); } }
static void handle_read(struct connect_s *conn, struct timeval *tv) { ClientData client_data; httpd_conn *hc = conn->hc; off_t actual; int sz; /* Is there room in our buffer to read more bytes? */ if (hc->read_idx >= hc->read_size) { if (hc->read_size > CONFIG_THTTPD_MAXREALLOC) { BADREQUEST("MAXREALLOC"); goto errout_with_400; } httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + CONFIG_THTTPD_REALLOCINCR); } /* Read some more bytes */ sz = read(hc->conn_fd, &(hc->read_buf[hc->read_idx]), hc->read_size - hc->read_idx); if (sz == 0) { BADREQUEST("EOF"); goto errout_with_400; } if (sz < 0) { /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. 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 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { return; } ndbg("read(fd=%d) failed: %d\n", hc->conn_fd, errno); BADREQUEST("read"); goto errout_with_400; } hc->read_idx += sz; conn->active_at = tv->tv_sec; /* Do we have a complete request yet? */ switch (httpd_got_request(hc)) { case GR_NO_REQUEST: return; case GR_BAD_REQUEST: BADREQUEST("httpd_got_request"); goto errout_with_400; } /* Yes. Try parsing and resolving it */ if (httpd_parse_request(hc) < 0) { goto errout_with_connection; } /* Start the connection going */ if (httpd_start_request(hc, tv) < 0) { /* Something went wrong. Close down the connection */ goto errout_with_connection; } /* Set up the file offsets to read */ conn->eof = false; if (hc->got_range) { conn->offset = hc->range_start; conn->end_offset = hc->range_end + 1; } else { conn->offset = 0; if (hc->bytes_to_send < 0) { conn->end_offset = 0; } else { conn->end_offset = hc->bytes_to_send; } } /* Check if it's already handled */ if (hc->file_fd < 0) { /* No file descriptor means someone else is handling it */ conn->offset = hc->bytes_sent; goto errout_with_connection; } if (conn->offset >= conn->end_offset) { /* There's nothing to send */ goto errout_with_connection; } /* Seek to the offset of the next byte to send */ actual = lseek(hc->file_fd, conn->offset, SEEK_SET); if (actual != conn->offset) { ndbg("fseek to %d failed: offset=%d errno=%d\n", conn->offset, actual, errno); BADREQUEST("lseek"); goto errout_with_400; } /* We have a valid connection and a file to send to it */ conn->conn_state = CNST_SENDING; client_data.p = conn; fdwatch_del_fd(fw, hc->conn_fd); return; errout_with_400: BADREQUEST("errout"); httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, ""); errout_with_connection: finish_connection(conn, tv); return; }
static void handle_read( connecttab* c, struct timeval* tvP ) { int sz; 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, "" ); finish_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 ); if ( sz == 0 ) { httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); finish_connection( c, tvP ); return; } if ( sz < 0 ) { /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. 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 ( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK ) return; httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" ); finish_connection( c, tvP ); return; } hc->read_idx += sz; c->active_at = tvP->tv_sec; /* 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, "" ); finish_connection( c, tvP ); return; } /* Yes. Try parsing and resolving it. */ if ( httpd_parse_request( hc ) < 0 ) { finish_connection( c, tvP ); return; } /* Start the connection going. */ if ( httpd_start_request( hc, tvP ) < 0 ) { /* Something went wrong. Close down the connection. */ finish_connection( c, tvP ); return; } if ( c->conn_state == CNST_SLEEPING ) { return; } handle_read_2( c, tvP ); }
int32 PoorManServer::_Worker(void* data) { static const struct timeval kTimeVal = {60, 0}; PoorManServer* s = static_cast<PoorManServer*>(data); httpd_conn* hc; int retval; if (has_data(find_thread(NULL))) { thread_id sender; if (receive_data(&sender, &hc, sizeof(httpd_conn*)) != 512) goto cleanup; } else { // No need to go throught the whole cleanup, as we haven't open // nor allocated ht yet. atomic_add(&s->fCurConns, -1); return 0; } PRINT(("A worker thread starts to work.\n")); setsockopt(hc->conn_fd, SOL_SOCKET, SO_RCVTIMEO, &kTimeVal, sizeof(struct timeval)); retval = recv( hc->conn_fd, &(hc->read_buf[hc->read_idx]), hc->read_size - hc->read_idx, 0 ); if (retval < 0) goto cleanup; hc->read_idx += retval; switch(httpd_got_request(hc)) { case GR_GOT_REQUEST: break; case GR_BAD_REQUEST: httpd_send_err(hc, 400, httpd_err400title, (char *)"", httpd_err400form, (char *)""); httpd_write_response(hc);//fall through case GR_NO_REQUEST: //fall through default: //won't happen goto cleanup; break; } if (httpd_parse_request(hc) < 0) { httpd_write_response(hc); goto cleanup; } retval = httpd_start_request(hc, (struct timeval*)0); if (retval < 0) { httpd_write_response(hc); goto cleanup; } /*true means the connection is already handled *by the directory index generator in httpd_start_request(). */ if (hc->file_address == (char*) 0) { static_cast<PoorManApplication*>(be_app)->GetPoorManWindow()->SetHits( static_cast<PoorManApplication*>(be_app)->GetPoorManWindow()->GetHits() + 1 ); hc->conn_fd = -1; goto cleanup; } switch (hc->method) { case METHOD_GET: s->_HandleGet(hc); break; case METHOD_HEAD: s->_HandleHead(hc); break; case METHOD_POST: s->_HandlePost(hc); break; } cleanup: ; httpd_close_conn(hc, (struct timeval*)0); httpd_destroy_conn(hc); delete hc; atomic_add(&s->fCurConns, -1); return 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 ); }