最后的冲刺.
althttpd.c
静态函数
ProcessOneRequest
请求处理过程, 原型void ProcessOneRequest(int forceClose)
;
void ProcessOneRequest(int forceClose){
int i, j, j0;
char *z; // 格式化字符串空间
struct stat statbuf; // 获取到的文件信息
FILE *in; // 从 CGI 脚本中读取到的文件描述符
#ifdef LOG_HEADER
FILE *hdrLog = 0; // 记录请求头, 如果定义了 LOG_HEADER 宏
#endif
char zLine[1000]; // 保存输入行或格式化名称的缓冲区
// 切换 HTTP 根目录
if( chdir(zRoot[0] ? zRoot : "/")!=0 ){
char zBuf[1000];
// getcwd 将当前工作目录的绝对路径复制到参数 buffer 所指的内存空间中
Malfunction(190, "cannot chdir to [%s] from [%s]", zRoot, getcwd(zBuf,999));
}
nRequest++; // request 请求数量统计
// 必须在15s内接受到一个完整的头信息
signal(SIGALRM, Timeout); // 时钟信号 在Linux系统下, 每一个进程都有惟一的一个定时器, 该定时器提供了以秒为单位的定时功能. 在定时器设置的超时时间到达后, 调用alarm的进程将收到SIGALRM信号
signal(SIGSEGV, Timeout); // 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据
signal(SIGPIPE, Timeout); // 管道破裂信号量
signal(SIGXCPU, Timeout); // 超过CPU时间资源限制
if( useTimeout ) alarm(15); // 设置定时器
// 从标准输入中读取 请求的第一行, 然后解析出请求类型, 脚本和协议, 如果读取失败, 直接 exit
if( fgets(zLine,sizeof(zLine),stdin)==0 )exit(0);
gettimeofday(&beginTime, 0);
omitLog = 0;
nIn += strlen(zLine); // 计算有效字符, 不含结束符
/* Parse the first line of the HTTP request */
zMethod = StrDup(GetFirstElement(zLine,&z));
zRealScript = zScript = StrDup(GetFirstElement(z,&z));
zProtocol = StrDup(GetFirstElement(z,&z));
if( zProtocol==0 || strncmp(zProtocol,"HTTP/",5)!=0 || strlen(zProtocol)!=8 ){
StartResponse("400 Bad Request");
nOut += printf(
"Content-type: text/plain; charset=utf-8\r\n"
"\r\n"
"This server does not understand the requested protocol\n"
);
MakeLogEntry(0, 200); // 请求协议异常
exit(0);
}
if( zScript[0]!='/' ) NotFound(210);
while( zScript[1]=='/' ){
// 移除 头部 连续 /
zScript++;
zRealScript++;
}
if( forceClose ){
// 强制关闭连接条件
closeConnection = 1;
}else if( zProtocol[5]<'1' || zProtocol[7]<'1' ){
// 未看懂
closeConnection = 1;
}
// 只支持 GET/POST/HEAD请求的简单服务
if( strcmp(zMethod,"GET")!=0 && strcmp(zMethod,"POST")!=0
&& strcmp(zMethod,"HEAD")!=0 ){
StartResponse("501 Not Implemented");
nOut += printf(
"Content-type: text/plain; charset=utf-8\r\n"
"\r\n"
"The %s method is not implemented on this server.\n",
zMethod);
MakeLogEntry(0, 220);
exit(0);
}
// 打开请求头记录文件 如果设置了的话
#ifdef LOG_HEADER
if( zLogFile
&& strstr(zScript,"FullHeaderLog")!=0
&& strlen(zLogFile)<sizeof(zLine)-50
){
sprintf(zLine, "%s-hdr", zLogFile);
hdrLog = fopen(zLine, "wb");
}
#endif
// 从标准输入获取的第一行中可选字段
zCookie = 0;
zAuthType = 0;
zRemoteUser = 0;
zReferer = 0;
zIfNoneMatch = 0;
zIfModifiedSince = 0;
rangeEnd = 0;
while( fgets(zLine,sizeof(zLine),stdin) ){
char *zFieldName;
char *zVal;
#ifdef LOG_HEADER
if( hdrLog ) fprintf(hdrLog, "%s", zLine); // 请求头日志
#endif
nIn += strlen(zLine);
zFieldName = GetFirstElement(zLine,&zVal);
if( zFieldName==0 || *zFieldName==0 ) break;
RemoveNewline(zVal);
if( strcasecmp(zFieldName,"User-Agent:")==0 ){
zAgent = StrDup(zVal);
}else if( strcasecmp(zFieldName,"Accept:")==0 ){
zAccept = StrDup(zVal);
}else if( strcasecmp(zFieldName,"Accept-Encoding:")==0 ){
zAcceptEncoding = StrDup(zVal);
}else if( strcasecmp(zFieldName,"Content-length:")==0 ){
zContentLength = StrDup(zVal);
}else if( strcasecmp(zFieldName,"Content-type:")==0 ){
zContentType = StrDup(zVal);
}else if( strcasecmp(zFieldName,"Referer:")==0 ){
zReferer = StrDup(zVal);
// 指定 Referer
if( strstr(zVal, "devids.net/")!=0 ){
zReferer = "devids.net.smut";
Forbidden(230);
}
}else if( strcasecmp(zFieldName,"Cookie:")==0 ){
zCookie = StrAppend(zCookie,"; ",zVal); // 多 cookie, 所以采用 append 模式
}else if( strcasecmp(zFieldName,"Connection:")==0 ){
if( strcasecmp(zVal,"close")==0 ){
closeConnection = 1;
}else if( !forceClose && strcasecmp(zVal, "keep-alive")==0 ){
closeConnection = 0;
}
}else if( strcasecmp(zFieldName,"Host:")==0 ){
int inSquare = 0;
char c;
if( sanitizeString(zVal) )Forbidden(240); // 非法主机地址
zHttpHost = StrDup(zVal);
zServerPort = zServerName = StrDup(zHttpHost);
while( zServerPort && (c = *zServerPort)!=0
&& (c!=':' || inSquare) ){
if( c=='[' ) inSquare = 1;
if( c==']' ) inSquare = 0;
zServerPort++;
}
if( zServerPort && *zServerPort ){
*zServerPort = 0;
zServerPort++;
}
if( zRealPort ){
zServerPort = StrDup(zRealPort);
}
}else if(