struct _TSession { bool validRequest; /* Client has sent, and server has recognized, a valid HTTP request. This is false when the session is new. If and when the server reads the request from the client and finds it to be valid HTTP, it becomes true. */ TRequestInfo requestInfo; /* Some of the strings this references are in individually malloc'ed memory, but some are pointers into arbitrary other data structures that happen to live as long as the session. Some day, we will fix that. 'requestInfo' is valid only if 'validRequest' is true. */ uint32_t nbfileds; TList cookies; TList ranges; uint16_t status; /* Response status from handler. Zero means session is not ready for a response yet. This can mean that we ran a handler and it did not call ResponseStatus() to declare this fact. */ TString header; bool serverDeniesKeepalive; /* Server doesn't want keepalive for this session, regardless of what happens in the session. E.g. because the connection has already been kept alive long enough. */ bool responseStarted; /* Handler has at least started the response (i.e. called ResponseWriteStart()) */ struct _TConn * conn; httpVersion version; TTable request_headers; /* All the headers in the HTTP request. The key is the header name in lower case. The value is the verbatim value from the header. */ TTable response_headers; time_t date; bool chunkedwrite; bool chunkedwritemode; bool continueRequired; /* This client must receive 100 (continue) status before it will send more of the body of the request. */ };
之前在研究server.c文件内的processDataFromClient函数时第一次看到了TSession。由于每次收到一个数据包后都会调用processDataFromClient函数,所以我认为TSession是对一个请求的封装。大致浏览了下_TSession结构体的声明后,我觉得更应该是针对某个HTTP请求数据报文的封装。
typedef struct { TMethod method; const char * uri; /* This is NOT the URI. It is the pathname part of the URI. We really should fix that and put the pathname in another member. If the URI does not contain a pathname, this is "*". */ const char * query; /* The query part of the URI (stuff after '?'). NULL if none. */ const char * host; /* NOT the value of the host: header. Rather, the name of the target host (could be part of the host: value; could be from the URI). No port number. NULL if request does not specify a host name. */ const char * from; const char * useragent; const char * referer; const char * requestline; const char * user; /* Requesting user (from authorization: header). NULL if request doesn't specify or handler has not authenticated it. */ xmlrpc_uint16_t port; /* The port number from the URI, or default 80 if the URI doesn't specify a port. */ abyss_bool keepalive; } TRequestInfo;TRequestInfo结构体内有method方法(应该是HTTP Method,类似于GET、POST等)、uri、query、host(主机)、port(端口号)和useragent等信息。这些都是一个HTTP请求所必需包含的信息。_TSession还包括cookies和request_headers等数据。这些数据都表明_TSession是针对一个HTTP请求数据报文的封装。
简单过了一遍session.c文件,没有看到所谓的初始化函数。之前研究processDataFromClient函数时看到过RequestInit函数,但这个函数是在http.c文件中。个人觉得这个函数还是放在session.c文件中比较好,最起码从概念上说与session.c文件中的其他函数比较接近。
这个文件内的有一些函数基本上属于辅助函数,有点类似于面向对象语言里的GET函数。例如,SessionGetRequestInfo、SessionGetChannelInfo和SessionGetDefaultHandlerCtx。SessionLog函数的作用是将_TSession的一些主要属性信息输出至日志。
依据函数头部的注释可以获知此函数的用途是返回HTTP请求的body域内容。
此函数的作用是检查缓存区内是否还有剩余未处理的数据。
abyss_bool SessionRefillBuffer(TSession * const sessionP) { /*---------------------------------------------------------------------------- Get the next chunk of HTTP request body from the connection into the buffer. I.e. read data from the socket. -----------------------------------------------------------------------------*/ struct _TServer * const srvP = sessionP->conn->server->srvP; bool failed; failed = FALSE; /* initial value */ /* Reset our read buffer & flush data from previous reads. */ ConnReadInit(sessionP->conn); if (sessionP->continueRequired) failed = !HTTPWriteContinue(sessionP); if (!failed) { sessionP->continueRequired = FALSE; /* Read more network data into our buffer. If we encounter a timeout, exit immediately. We're very forgiving about the timeout here. We allow a full timeout per network read, which would allow somebody to keep a connection alive nearly indefinitely. But it's hard to do anything intelligent here without very complicated code. */ failed = !ConnRead(sessionP->conn, srvP->timeout); } return !failed; }函数头部的注释说明了这个函数的用途:从连接中读取HTTP请求中下一个数据块,并放入缓存区中。首先使用ConnReadInit函数重置内部接收数据缓存区。接着调用HTTPWriteContinue函数检查此连接是否还可以继续使用。最后会调用ConnRead函数进行实际的收取网络数据的操作。
bool HTTPWriteContinue(TSession * const sessionP) { char const continueStatus[] = "HTTP/1.1 100 continue\r\n\r\n"; /* This is a status line plus an end-of-headers empty line */ return ConnWrite(sessionP->conn, continueStatus, strlen(continueStatus)); }HTTPWriteContineu函数体显示它只是发送了一条HTTP消息给对端。也就是说,这个函数的用途不是用来监测连接是否还可以继续使用,而应该是通知对端请继续发送数据。如果这条消息发送成功了,说明连接还存在,因此可以继续调用ConnRead函数从连接中读取数据。
因此SessionRefillBuffer函数的用途是在某种情况下希望对方能继续将一个完整HTTP请求数据包发送完毕。