在第一部分, 已经了解大概, 接下来, 从源码层次来理解一下吧.
althttpd.c
关于具体源码, 由于 C
语言, 所以打算从宏定义/静态变量/静态函数入手, 再从 main
函数进行流程解读. 对于文件头部一些注释, 暂且不单独翻译, 大部分同 md
文档一致 .
宏定义
DEFAULT_PORT
http
服务默认监听端口;MAX_CONTENT_LENGTH
http
请求内容最大长度(报文主体);MAX_CPU
每秒CPU
周期最大值.
静态变量
zRoot
站点根目录, 定义static char *zRoot = 0
;zTmpNam
临时文件名称, 定义static char *zTmpNam = 0
;zTmpNamBuf
临时文件名缓冲区, 定义static char zTmpNamBuf[500]
;zProtocol
浏览器(客户端)使用的协议, 定义static char *zProtocol = 0
;zMethod
客户端请求类型, 定义static char *zMethod = 0
;zScript
脚本路径, 定义static char *zScript = 0
;zRealScript
实际脚本路径, 如果没有, 将追加/index.html
, 定义static char *zRealScript = 0
;zHome
包含脚本的根目录, 定义static char *zHome = 0
;zQueryString
查询字符串, 定义static char *zQueryString = 0
;zFile
执行脚本文件名称, 定义static char *zFile = 0
;lenFile
执行脚本文件名称长度, 定义static int lenFile = 0
;zDir
包含脚本当前目录, 定义static char *zDir = 0
;zPathInfo
脚本路径的部分, 定义static char *zPathInfo = 0
;zAgent
客户端类型, 定义static char *zAgent = 0
;zServerName
站点域名, 定义static char *zServerName = 0
;zServerPort
站点端口, 定义static char *zServerPort = 0
;zCookie
客户端请求携带的cookie
, 定义static char *zCookie = 0
;zHttpHost
请求主机, 定义static char *zHttpHost = 0
;zRealPort
守护模式运行实际TCP
端口, 定义static char *zRealPort = 0
;zRemoteAddr
客户端的IP
地址, 定义static char *zRemoteAddr = 0
;zReferer
当前请求的来源请求, 定义static char *zReferer = 0
;zAccept
请求接收类型, 定义static char *zAccept = 0
;zAcceptEncoding
请求接收编码, 压缩或默认, 定义static char *zAcceptEncoding =0
;zContentLength
请求主体长度, 定义static char *zContentLength = 0
;zContentType
请求主体类型, 定义static char *zContentType = 0
;zQuerySuffix
查询参数后缀(主要是第一个?
后面的字符串), 定义static char *zQuerySuffix = 0
;zAuthType
请求认证类型, 基础或摘要认证, 定义static char *zAuthType = 0
;zAuthArg
请求认证参数, 定义static char *zAuthArg = 0
;zRemoteUser
通过认证模块发送的用户, 定义static char *zRemoteUser = 0
;zIfNoneMatch
未匹配的请求头值, 定义static char *zIfNoneMatch= 0
;zIfModifiedSince
未变更的请求头值, 定义static char *zIfModifiedSince=0
;nIn
接收字符数量, 定义static int nIn = 0
;nOut
输出字符数量, 定义static int nOut = 0
;zReplyStatus
响应状态, 定义static char zReplyStatus[4]
;statusSent
是否已发送响应状态, 定义static int statusSent = 0
;zLogFile
日志文件, 定义static char *zLogFile = 0
;debugFlag
调试标识, 定义static int debugFlag = 0
;beginTime
当前进程启动时间, 定义static struct timeval beginTime
;closeConnection
是否已向连接发送关闭响应, 定义static int closeConnection = 0
;nRequest
进程中请求的数量, 定义static int nRequest = 0
;omitLog
忽略日志标识, 定义static int omitLog = 0
;useHttps
使用https
标识, 定义static int useHttps = 0
;zHttp
http
类型,http
或https
, 定义static char *zHttp = "http"
;useTimeout
使用超时标识, 定义static int useTimeout = 1
;standalone
独立服务运行标识, 定义static int standalone = 0
;ipv6Only
仅使用IPv6
模式标识, 定义static int ipv6Only = 0
;ipv4Only
仅使用IPv4
模式标识, 定义static int ipv4Only = 0
;priorSelf
当前进程占用资源情况, 定义static struct rusage priorSelf
;priorChild
子进程占用资源情况, 定义static struct rusage priorChild
;mxAge
缓存控制最大存活时间, 定义static int mxAge = 120
;default_path
默认环境目录, 定义static char *default_path = "/bin:/usr/bin"
;zScgi
SCGI
环境变量值, 定义static char *zScgi = 0
;rangeStart
请求主体的开始, 定义static int rangeStart = 0
;rangeEnd
请求主体的结束, 定义static int rangeEnd = 0
;maxCpu
每秒最大CPU
时间, 定义static int maxCpu = MAX_CPU
;cgienv
CGI
环境变量:
static struct {
char *zEnvName; // 变量名称
char **pzEnvValue; // 变量值
} cgienv[] = {
{
"CONTENT_LENGTH", &zContentLength }, /* Must be first for SCGI */
{
"AUTH_TYPE", &zAuthType },
{
"AUTH_CONTENT", &zAuthArg },
{
"CONTENT_TYPE", &zContentType },
{
"DOCUMENT_ROOT", &zHome },
{
"HTTP_ACCEPT", &zAccept },
{
"HTTP_ACCEPT_ENCODING", &zAcceptEncoding },
{
"HTTP_COOKIE", &zCookie },
{
"HTTP_HOST", &zHttpHost },
{
"HTTP_IF_MODIFIED_SINCE", &zIfModifiedSince },
{
"HTTP_IF_NONE_MATCH", &zIfNoneMatch },
{
"HTTP_REFERER", &zReferer },
{
"HTTP_USER_AGENT", &zAgent },
{
"PATH", &default_path },
{
"PATH_INFO", &zPathInfo },
{
"QUERY_STRING", &zQueryString },
{
"REMOTE_ADDR", &zRemoteAddr },
{
"REQUEST_METHOD", &zMethod },
{
"REQUEST_URI", &zScript },
{
"REMOTE_USER", &zRemoteUser },
{
"SCGI", &zScgi },
{
"SCRIPT_DIRECTORY", &zDir },
{
"SCRIPT_FILENAME", &zFile },
{
"SCRIPT_NAME", &zRealScript },
{
"SERVER_NAME", &zServerName },
{
"SERVER_PORT", &zServerPort },
{
"SERVER_PROTOCOL", &zProtocol },
}
静态函数
Escape
规避双引号干扰, 原型static char *Escape(char *z)
;
static char *Escape(char *z){
size_t i, j; // 循环计数控制循环变量, j为叠加双引号时的计数
size_t n; // 双引号计数变量
char c; // 循环中当前字符临时变量
char *zOut; // 加工处理后的字符串
/**
* 截断字符串, 如果出现双引号的情况, 或越界退出
* 如果出现双引号, 那么退出循环时, c为双引号, 继续后续处理
* 反之, c为越界获取, 异常字符, 和0对等(待加强理解, 未申明空间为0?), 直接返回当前传入字符串即可
*/
for(i=0; (c=z[i])!=0 && c!='"'; i++){
}
if( c==0 ) return z;
// 已经检测到有个双引号, 所以这里初始值为1
n = 1;
// 统计双引号出现次数 从上次循环截断时继续往后查找, 处理非常漂亮
for(i++; (c=z[i])!=0; i++){
if( c=='"' ) n++; }
// 动态分配空间(从堆中分配) i为当前字符总量, n为需要增加双引号的数量, 1为字符串终止符\0
zOut = malloc( i+n+1 );
if( zOut==0 ) return "";
for(i=j=0; (c=z[i])!=0; i++){
zOut[j++] = c;
// 双引号叠加
if( c=='"' ) zOut[j++] = c;
}
// 补充终止符
zOut[j] = 0;
return zOut;
}
tvms
将时间转化为微秒数, 主要参考((long long int)p->tv_sec)*1000000
将秒转为微秒, 然后加上当时的微秒数即可, 原型static long long int tvms(struct timeval *p)
;MakeLogEntry
创建日志入口, 如果连接关闭, 就结束这个进程, 反之, 正常返回. 原型static void MakeLogEntry(int exitCode, int lineNum)
;
static void MakeLogEntry(int exitCode, int lineNum){
FILE *log; // 日志文件指针
// 如果指定了临时文件名, 这里需做删除处理
if( zTmpNam ) unlink(zTmpNam);
// 存在日志文件名称, 且不忽略日志
if( zLogFile && !omitLog ){
struct timeval now; // 当前时间 秒/微秒
struct tm *pTm; // 可分解时间类型
struct rusage self, children; // 资源利用统计结构 当前进程 和子进程
int waitStatus; // 等待子进程状态描述
char *zRM = zRemoteUser ? zRemoteUser : "";
char *zFilename; // 保存处理后日志文件名
size_t sz; // 保存 strftime 返回
char zDate[200]; // 格式化后的时间(指定形式)
char zExpLogFile[500]; // 导出日志文件的名称
// 转化宏定义 zScript/zRealScript/zRemoteAddr/zHttpHost/zReferer/zAgent if 0, then ""
if( zScript==0 ) zScript = "";
// ...
if( zAgent==0 ) zAgent = "";
gettimeofday(&now, 0); // 获取当前时间
pTm = localtime(&now.tv_sec); // 将当前时间秒数转为本地可分解时间类型
// 格式化时间, 根据指定格式处理可分解时间转存最大长度到指定字符串
// strftime(target, max_length, format, tm_struct)
// 如果产生的字符串小于 max_length 个字符(包括空结束字符), 则会返回复制到 target 中的字符总数(不包括空结束字符), 否则返回零
strftime(zDate, sizeof(zDate), "%Y-%m-%d %H:%M:%S", pTm);
// 进行时间格式化处理, 如果其中包含有时间形式
sz = strftime