经过前面几篇文章的讲述知道了Thttpd在处理用户的请求时的几个主要的函数的作用,下面将讲述处理请求的是主要流程。
当有文件需要进行处理的时候将对每个可以进行处理的文件描述符进行处理,可以进行处理的文件描述符的状态有可读,可写和异常处理。当文件描述符处于可读状态时表示服务器接收的用户的连接请求下面将要进行对用户的请求进行分析和处理。
处理流程
(1)判断读取的字节数是否大于设置的读取数据的空间,用于防治数组超界;如果超出设置的范围但是设置的读取数据的空间小于5000字节 则重新多分配1000字节的空间,但是设置的空间的字节数大于5000字节则会返回400错误网页退出函数。
(2)读取剩余未读取完的数据。如果读取的数据量为0表示读取完所有的用户请求数据表示已经处理完用户的请求了所以返回400错误网页并退出函数,如果读取的数据量小于0表示读取数据错误当错误的值为EINTR或者是EAGAIN或者是EWOULDBLOCK退出函数等待下一次继续读取,如果不是这些错误值则返回400错误并退出函数。
(3)设置当前读取的数据的索引值,更新当前处理的时间值。
(4)调用httpd_got_request函数检测读取到的用户数据的数据格式是否符合HTTP的规范,对于不符合规范的请求作出错误处理返回错误网页退出函数,对于符合的请求继续执行后面的处理过程。
(5)调用httpd_parse_request函数分析和设置用户请求的相关参数。对于请求存在错误的进行错误处理返回错误网页退出函数,对于正确的请求继续执行后面的过程。
(6)调用check_throttles函数设置请求限流相关的设置对于错误的数据进行错误处理返回503错误网页退出函数,对于正确的继续执行后面的过程。
(7)调用httpd_start_request函数对用户的请求进行处理并将请求的响应信息存储在回送数据缓存数组中,对于错误的进行错误处理返回错误网页并退出函数,对于正确的请求继续进行后面的处理。
(8)根据用户设置的发送数据的范围的请求设置发送数据的起始位置和终止位置。
(9)对于文件地址为0的进行关闭连接退出函数
(10)对于发送数据的范围错误的即开始位置大于终止位置的关闭连接退出函数。
(11)设置连接的状态为发送状态并设置发送相关的参数
(12)更新文件描述符的状态。
流程图
总结
用户的请求处理过程是真个Thttpd最为核心的部分主要是获取用户的请求数据和对用户的请求数据进行校验和获取并进行处理其中还有对于静态文件的内存映射和对有数据请求的CGI处理都在此过程中。这一部分是对程序进行优化和改进最为值得研究的一部分。
源程序
static void handle_read( connecttab* c, struct timeval* tvP )
{
int sz;
ClientData client_data;
httpd_conn* hc = c->hc;
#ifdef JI_DEBUG
char ji_debugbuf[601];
printf("connect fd is %d\n",hc->conn_fd);
printf("the read size is %d\n",hc->read_size);
#endif
/* Is there room in our buffer to read more bytes? */
/**对于读取数据的索引值大于数据存储空间时的处理*/
if ( hc->read_idx >= hc->read_size )
{
#ifdef JI_DEBUG
printf("The read size is to small\n");
#endif
/**对于读数据空间的大小大于5000字节时的处理*/
if ( hc->read_size > 5000 )
{
#ifdef JI_DEBUG
printf("The read size is to big\n");
#endif
httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
finish_connection( c, tvP );
return;
}
/**给此连接的读数据的数据空间增加1000字节*/
#ifdef JI_DEBUG
printf("The add read size 1000\n");
#endif
httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + 1000 );
}
#ifdef JI_DEBUG
printf("The read size is ok \n");
#endif
/* Read some more bytes. */
/**服务器从客户端读取数据hc->read_size - hc->read_idx个字节,并将数据存储至read_buf中*/
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? */
/**获取文件中的请求头HTTP/0.9 request 即第一行*/
switch ( httpd_got_request( hc ) )
{
#ifdef JI_DEBUG
printf("call httpd_got_request");
#endif
case GR_NO_REQUEST:
return;
case GR_BAD_REQUEST:
httpd_send_err( hc, 400, httpd_err400title, "", httpd_err400form, "" );
finish_connection( c, tvP );
return;
case GR_GOT_REQUEST:
#ifdef JI_DEBUG
strncpy(ji_debugbuf,hc->read_buf,600);
ji_debugbuf[600]=NULL;
printf("The buf is %s\n",ji_debugbuf);
#endif
}
#ifdef JI_DEBUG
printf("call httpd_got_request\n",ji_debugbuf);
#endif
/* Yes. Try parsing and resolving it. */
/**对请求头进行处理*/
if ( httpd_parse_request( hc ) < 0 )
{
finish_connection( c, tvP );
return;
}
/* Check the throttle table */
if ( ! check_throttles( c ) )
{
httpd_send_err(hc, 503, httpd_err503title, "", httpd_err503form, hc->encodedurl );
finish_connection( c, tvP );
return;
}
/* Start the connection going. */
/**这里为Http数据的处理和返回数据的生成,同时对错误的处理*/
if ( httpd_start_request( hc, tvP ) < 0 )
{
/* Something went wrong. Close down the connection. */
finish_connection( c, tvP );
return;
}
/* Fill in end_byte_index. */
/**对于定义range的处理*/
if ( hc->got_range )
{
/**设置发送数据的起始位置和结束位置 */
c->next_byte_index = hc->first_byte_index;
c->end_byte_index = hc->last_byte_index + 1;
}
/**对于需要发送的数据的处理*/
else if ( hc->bytes_to_send < 0 )
{
c->end_byte_index = 0;
}
/**对于正常的处理*/
else
{
c->end_byte_index = hc->bytes_to_send;
}
/* Check if it's already handled. */
/**对于文件的映射地址为0的处理*/
if ( hc->file_address == (char*) 0 )
{
/* No file address means someone else is handling it. */
int tind;
for ( tind = 0; tind < c->numtnums; ++tind )
{
throttles[c->tnums[tind]].bytes_since_avg += hc->bytes_sent;
}
c->next_byte_index = hc->bytes_sent;
finish_connection( c, tvP );
return;
}
/***/
if ( c->next_byte_index >= c->end_byte_index )
{
/* There's nothing to send. */
finish_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;
fdwatch_del_fd( hc->conn_fd );
fdwatch_add_fd( hc->conn_fd, c, FDW_WRITE );
}