根据前面系列的介绍httpd_got_request函数检验请求的格式是否正确,httpd_parse_request检验请求的内容是否正确,httpd_start_request函数的主要作用是根据请求的做出具体的回应,主要使用的是really_start_request函数。
really_start_request函数
really_start_request函数是具体的对用户的请求进行处理的函数,下面将讲述really_start_request函数的处理流程。
(1)首先获取用户请求文件的文件属性,获取文件属性失败返回500错误退出函数。
(2)如果文件不具有其他用户的可读或可操作属性,返回403错误退出函数。
(3)当请求的路径是文件夹。
1)判断hc->pathinfo的值不为空返回404错误退出函数,如果hc->origfilename的值不""或者"."或者hc->origfilename的最后一个字符不为'/'返回302退出函数
2)在此目录下寻找默认文件列表中获取文件信息成功的文件,如果找到跳转到got_one处执行,如果没有找到
1>如果文件不具有其他用户的可读权限返回403错误退出函数。
2>安全认证失败的退出函数
3>检测是否是经过其他网页引用过来的并进行处理,如果检测失败返回-1
4>调用ls函数返回此目录下的文件信息
5>再次进行符号扩展,校验转换后的结果是失败的返回500错误退出函数,对于转换成功的但是文件的属性还是没有可读或者可执行权限的返回403错误退出函数。
(4)检测引用的连接是否正确对于不正确的退出啊函数,正确的继续执行后面的过程。
(5)对于CGI的路径不为空且文件具有其他用户可执行权限且此文件处于CGI文件路径中执行CGI过程然后退出程序。
(6)如果文件具有其他用户的可执行权限返回403错误退出函数
(7)如果路径信息不为空返回403错误退出函数。
(8)如果请求方式不为GET或者是HEAD返回501错误退出函数
(9)如果用户请求定义范围设置,但是最大的范围超界设置发送的最后一个字节在文件的末尾。
(10)设置文件的mime类型
(11)如果请求为HEAD方式的处理返回200网页并退出,如果是GET且设置了If-Modified-Since标志且此文件修改时间不大于此时间返回304退出函数。
(12)对于是GET方式但是没有If-Modified-Since标志的请求从映射文件中获取,如果映射文件中没有返回500错误并退出,如果有此文件返回200并退出。
原理图
源程序
static int really_start_request( httpd_conn* hc, struct timeval* nowP )
{
static char* indexname;
static size_t maxindexname = 0;
static const char* index_names[] = { INDEX_NAMES };
int i;
#ifdef AUTH_FILE
static char* dirname;
static size_t maxdirname = 0;
#endif /* AUTH_FILE */
size_t expnlen, indxlen;
char* cp;
char* pi;
expnlen = strlen( hc->expnfilename );
/* Stat the file. */
/**获取文件的状态,
* 并对于有错误的文件进行错误处理
* 对于正确的文件将文件的信息写入hc->sb中*/
if ( stat( hc->expnfilename, &hc->sb ) < 0 )
{
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
return -1;
}
/* Is it world-readable or world-executable? We check explicitly instead
** of just trying to open it, so that no one ever gets surprised by
** a file that's not set world-readable and yet somehow is
** readable by the HTTP server and therefore the *whole* world.
*/
/**对于文件模式为其他用户没有读和操作的权限的处理
* 返回错误信息
*/
if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
{
syslog(LOG_INFO,"%.80s URL \"%.80s\" resolves to a non world-readable file",httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(hc, 403, err403title, "",ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file that is not world-readable.\n" ),hc->encodedurl );
return -1;
}
/* Is it a directory? */
/**对于文件的类型为文件夹的处理*/
if ( S_ISDIR(hc->sb.st_mode) )
{
/* If there's pathinfo, it's just a non-existent file. */
/**对于路径信息的第一个字符不为空字符的处理
* 返回错误信息
*/
if ( hc->pathinfo[0] != '\0' )
{
httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl );
return -1;
}
/* Special handling for directory URLs that don't end in a slash.
** We send back an explicit redirect with the slash, because
** otherwise many clients can't build relative URLs properly.
*/
/**对于原始文件名不为空字符串且原始文件名不为.且原始文件的最后一个字符不为/的处理
*
*/
if ( strcmp( hc->origfilename, "" ) != 0 &&strcmp( hc->origfilename, "." ) != 0 &&hc->origfilename[strlen( hc->origfilename ) - 1] != '/' )
{
send_dirredirect( hc );
return -1;
}
/* Check for an index file. */
/**检测可用的index文件*/
for ( i = 0; i < sizeof(index_names) / sizeof(char*); ++i )
{
#ifdef JI_DEBUG
printf("check index file\n");
#endif
httpd_realloc_str(&indexname, &maxindexname,expnlen + 1 + strlen( index_names[i] ) );
/**设置indexname为hc->expnfilename*/
(void) strcpy( indexname, hc->expnfilename );
/**获取indexname的字符长度*/
indxlen = strlen( indexname );
/**对于indexname的字符长度为0或者是indexname的最后一个字符的不为/的处理
* 在indexname后面添加/字符
*/
if ( indxlen == 0 || indexname[indxlen - 1] != '/' )
{
(void) strcat( indexname, "/" );
}
/**对于idnexname的值为./的处理
* 设置indexname的第一个值为空字符
*/
if ( strcmp( indexname, "./" ) == 0 )
{
indexname[0] = '\0';
}
(void) strcat( indexname, index_names[i] );
if ( stat( indexname, &hc->sb ) >= 0 )
{
#ifdef JI_DEBUG
printf("goto _one\n");
#endif
goto got_one;
}
}
/* Nope, no index file, so it's an actual directory request. */
#ifdef GENERATE_INDEXES
/* Directories must be readable for indexing. */
/**对于文件的模式是其他用户不具有读属性的处理*/
if ( ! ( hc->sb.st_mode & S_IROTH ) )
{
syslog(
LOG_INFO,
"%.80s URL \"%.80s\" tried to index a directory with indexing disabled",
httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(
hc, 403, err403title, "",
ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a directory that has indexing disabled.\n" ),
hc->encodedurl );
return -1;
}
#ifdef AUTH_FILE
/* Check authorization for this directory. */
if ( auth_check( hc, hc->expnfilename ) == -1 )
{
return -1;
}
#endif /* AUTH_FILE */
/* Referrer check. */
if ( ! check_referrer( hc ) )
{
return -1;
}
/* Ok, generate an index. */
/**返回当前目录文件的相关信息*/
return ls( hc );
#else /* GENERATE_INDEXES */
syslog(LOG_INFO, "%.80s URL \"%.80s\" tried to index a directory",httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(hc, 403, err403title, "",ERROR_FORM( err403form, "The requested URL '%.80s' is a directory, and directory indexing is disabled on this server.\n" ),hc->encodedurl );
return -1;
#endif /* GENERATE_INDEXES */
got_one: ;
/* Got an index file. Expand symlinks again. More pathinfo means
** something went wrong.
*/
cp = expand_symlinks( indexname, &pi, hc->hs->no_symlink_check, hc->tildemapped );
if ( cp == (char*) 0 || pi[0] != '\0' )
{
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
return -1;
}
expnlen = strlen( cp );
httpd_realloc_str( &hc->expnfilename, &hc->maxexpnfilename, expnlen );
(void) strcpy( hc->expnfilename, cp );
/* Now, is the index version world-readable or world-executable? */
if ( ! ( hc->sb.st_mode & ( S_IROTH | S_IXOTH ) ) )
{
syslog(
LOG_INFO,
"%.80s URL \"%.80s\" resolves to a non-world-readable index file",
httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(
hc, 403, err403title, "",
ERROR_FORM( err403form, "The requested URL '%.80s' resolves to an index file that is not world-readable.\n" ),
hc->encodedurl );
return -1;
}
}
#ifdef JI_DEBUG
/**对于文件的处理*/
printf("This is file \n");
#endif
#ifdef AUTH_FILE
/* Check authorization for this directory. */
httpd_realloc_str( &dirname, &maxdirname, expnlen );
(void) strcpy( dirname, hc->expnfilename );
/**获取/字符在dirname字符串中最后出现的位置*/
cp = strrchr( dirname, '/' );
/**对于没有出现的处理设置dirname的值为.*/
if ( cp == (char*) 0 )
{
(void) strcpy( dirname, "." );
}
/**对于出现/字符的处理设置cp的值为0*/
else
{
*cp = '\0';
}
/**安全检测*/
if ( auth_check( hc, dirname ) == -1 )
{
return -1;
}
/* Check if the filename is the AUTH_FILE itself - that's verboten. */
if ( expnlen == sizeof(AUTH_FILE) - 1 )
{
if ( strcmp( hc->expnfilename, AUTH_FILE ) == 0 )
{
syslog(
LOG_NOTICE,
"%.80s URL \"%.80s\" tried to retrieve an auth file",
httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(
hc, 403, err403title, "",
ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
hc->encodedurl );
return -1;
}
}
else if ( expnlen >= sizeof(AUTH_FILE) &&strcmp( &(hc->expnfilename[expnlen - sizeof(AUTH_FILE) + 1]), AUTH_FILE ) == 0 &&hc->expnfilename[expnlen - sizeof(AUTH_FILE)] == '/' )
{
syslog(LOG_NOTICE,"%.80s URL \"%.80s\" tried to retrieve an auth file",httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(
hc, 403, err403title, "",
ERROR_FORM( err403form, "The requested URL '%.80s' is an authorization file, retrieving it is not permitted.\n" ),
hc->encodedurl );
return -1;
}
#endif /* AUTH_FILE */
/* Referrer check. */
if ( ! check_referrer( hc ) )
{
return -1;
}
/* Is it world-executable and in the CGI area? */
/**对于文件是cgi文件的处理*/
if ( hc->hs->cgi_pattern != (char*) 0 &&( hc->sb.st_mode & S_IXOTH ) &&match( hc->hs->cgi_pattern, hc->expnfilename ) )
{
return cgi( hc );
}
/* It's not CGI. If it's executable or there's pathinfo, someone's
** trying to either serve or run a non-CGI file as CGI. Either case
** is prohibited.
*/
/**对于文件的属性为其他用户具有操作属性的处理*/
if ( hc->sb.st_mode & S_IXOTH )
{
syslog(LOG_NOTICE, "%.80s URL \"%.80s\" is executable but isn't CGI",httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(hc, 403, err403title, "",ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file which is marked executable but is not a CGI file; retrieving it is forbidden.\n" ),hc->encodedurl );
return -1;
}
/**对于hc->pathinfo的值不为空字符的处理*/
if ( hc->pathinfo[0] != '\0' )
{
syslog(LOG_INFO, "%.80s URL \"%.80s\" has pathinfo but isn't CGI",httpd_ntoa( &hc->client_addr ), hc->encodedurl );
httpd_send_err(hc, 403, err403title, "",ERROR_FORM( err403form, "The requested URL '%.80s' resolves to a file plus CGI-style pathinfo, but the file is not a valid CGI file.\n" ),hc->encodedurl );
return -1;
}
/**对于请求方式不为GET且请求方式不为HEAD的处理*/
if ( hc->method != METHOD_GET && hc->method != METHOD_HEAD )
{
httpd_send_err(hc, 501, err501title, "", err501form, httpd_method_str( hc->method ) );
return -1;
}
/* Fill in last_byte_index, if necessary. */
if ( hc->got_range &&( hc->last_byte_index == -1 || hc->last_byte_index >= hc->sb.st_size ) )
{
hc->last_byte_index = hc->sb.st_size - 1;
}
/**指出mime类型*/
figure_mime( hc );
/**对于请求方式为HEAD的处理*/
if ( hc->method == METHOD_HEAD )
{
send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,hc->sb.st_mtime );
}
else if ( hc->if_modified_since != (time_t) -1 &&hc->if_modified_since >= hc->sb.st_mtime )
{
send_mime(hc, 304, err304title, hc->encodings, "", hc->type, (off_t) -1,hc->sb.st_mtime );
}
else
{
/**对于Get方式的处理,对文件进行内存映射*/
hc->file_address = mmc_map( hc->expnfilename, &(hc->sb), nowP );
if ( hc->file_address == (char*) 0 )
{
httpd_send_err( hc, 500, err500title, "", err500form, hc->encodedurl );
return -1;
}
/**发送mime类型*/
send_mime(hc, 200, ok200title, hc->encodings, "", hc->type, hc->sb.st_size,hc->sb.st_mtime );
}
return 0;
}