2-生成Http請求報文-BuildRequest
阿新 • • 發佈:2019-01-07
- webtest工作的第二步是生成http請求報文,對應函式
BuildRequest(const char *url);
。 - Http請求報文的格式請參見我的部落格“HTTP權威指南”的“3-HTTP Messages”一文,您也可以直接閱讀RFC2616。
BuildRequest(const char *url);
主要有以下7個步驟:
- 檢測URL的有效性。URL的語法為
<scheme>://<host>:<port>/
(1). scheme代表使用的協議(http/ftp等等):如果沒有提供proxy server,那麼只支援http協議。協議的名稱不區分大小寫,我們用函式IgnoreCaseMatch(url, "http://")
(2). 查詢子串“://”,若未找到退出程式。
(3). 解析“:/”:必須要以‘/’結尾。
(4). 判斷URL長度是否合法。 - 如果URL有效,初始化host、request陣列:分別用來存放host name、請求報文。
- 填充method欄位。
(1). 預設為GET
方法。
(2). 函式void StrCopy(char *dest, int *dest_index, const char *src)
:由於本程式只有在填充request時需要strcpy
函式,所以特殊化。該函式每次填充字元時,從指定的索引開始,不復制src的結尾‘\0’字元。 - 填充request-url欄位。
(1). 當有proxy server時:不解析<host>:<port>
(2). 當沒有proxy server時:解析。其中port可有可無,和‘:’保持一致。將‘/’之後的url寫入request。 - 填充version欄位:http 1.1.
- 填充header欄位。
(1). 當沒有proxy時:寫入Host
header。
(2). 當有proxy且force_reload = 1時:寫入Pragma: no-cache\r\n
- 填充空行、結尾字元:”\r\n\0”。
- 檢測URL的有效性。URL的語法為
- 輔助的字串處理函式。
// Copy string pointed to by src to string pointed to by dest start at dest_index.
// Not including the terminating null byte ('\0')
void StrCopy(char *dest, int *dest_index, const char *src)
{
// Since we know our dest(request/host) must be large enough,
// so we don't need check size.
for(; *src; ++src) // Not including the terminating null byte ('\0')
{
*(dest + (*dest_index)++) = *src;
}
}
// Return the index at which the substr begin in src, if match.
// -1 means not find.
int FindFirstSubstring(const char *src, const char *substr)
{
int length = strlen(src);
for(int index = 0; index < length; ++index)
{
if(*(src + index) == *substr) // Match the begin character, continue match.
{
for(const char *find = src + index; *substr && *find; ++substr, ++find)
{
if(*substr != *find) // Not match, return -1.
{
return FAIL;
}
}
return index; // Match success.
}
}
return FAIL; // Not match.
}
// Return 0 if match, -1 otherwise.
int IgnoreCaseMatch(const char *large, const char *small)
{
// For different case of the same character: lowercase > uppercase.
// For the same case of different character: 'a' < 'z', 'A' < 'Z'.
for(; *small; ++large, ++small)
{
// If *small is a letter, either lowercase or uppercase.
if(('A' <= *small && *small <= 'Z') || ('a' <= *small && *small <= 'z'))
{
// The difference between the upper and lower case for the same character
int differ = 'a' - 'A';
if(!(*large == *small // Match exactly
|| *large == *small - differ // Match uppercase for lowercase letter
|| *large == *small + differ)) // Match lowercase for uppercase letter
{
return FAIL; // Not match.
}
}
else if(*large != *small) // *small is not letter, so must match exactly.
{
return FAIL;
}
}
return SUCCESS; // All match.
}
// Return the index of the first occurrence of ch in str: -1 if not find.
int FirstOccurIndex(const char *str, const char ch)
{
for(int index = 0; *str; ++str, ++index)
{
if(*str == ch)
{
return index;
}
}
return FAIL;
}
- void BuildRequest(const char *url)完整原始碼:
void BuildRequest(const char *url) // Step 2. Build http request message.
{
//printf("URL = %s\n", url);
// Step 1. Check whether url is legal, URL syntax: `<scheme>://<host>:<port>/`
if(proxy_host == NULL) // Check <scheme> supplied by user.
{
// Scheme names are case-insensitive.
if(IgnoreCaseMatch(url, "http://") != 0)
{
fprintf(stderr, "\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
exit(BAD_PARAMETER);
}
}
int first_colon = FindFirstSubstring(url, "://"); // The index of the first ':' appears in url.
if(first_colon == FAIL) // If "://" can't be found in url.
{
fprintf(stderr, "\n%s: is not a valid URL.\n", url);
exit(BAD_PARAMETER);
}
int url_host_begin = first_colon + 3; // Index where host name start: `://host:port/
// Index where `host:port` end, i.e., the index of '/' in the whole url.
int url_port_end = FirstOccurIndex(url + url_host_begin, '/') + url_host_begin;
// Host name must end with '/'
if(url_port_end == url_host_begin -1)
{
fprintf(stderr,"\nInvalid URL - host name must ends with '/'.\n");
exit(BAD_PARAMETER);
}
if(strlen(url) > MAX_URL_SIZE) // If url is too long.
{
fprintf(stderr,"URL is too long.\n");
exit(BAD_PARAMETER);
}
// Format of a request message:
// <method> <request-URL> <version>"\r\n"
// <headers>"\r\n"
// "\r\n"
// <entity-body>
// Step 2. Initialize(zero) host and request array.
bzero(host, MAX_HOST_NAME_SIZE);
bzero(request, MAX_REQUEST_SIZE);
// Step 3. Fill <method> field.
switch(method)
{
default:
case METHOD_GET:
StrCopy(request, &request_index, "GET");
break;
case METHOD_HEAD:
StrCopy(request, &request_index, "HEAD");
break;
case METHOD_OPTIONS:
StrCopy(request, &request_index, "OPTIONS");
break;
case METHOD_TRACE:
StrCopy(request, &request_index, "TRACE");
break;
}
// Add the space between <method> and <request-URL>
*(request + (request_index++)) = ' ';
// Step 4. Fill <request-URL> field
if(proxy_host == NULL) // No proxy, parse host-name and port-number.
{
int second_colon = FirstOccurIndex(url + url_host_begin, ':') + url_host_begin;
int url_host_end = url_port_end; // If port number doesn't exist.
// If port number exist.
if(second_colon != url_host_begin -1 && second_colon < url_port_end)
{
// Get port number:
int port = 0;
for(int index = second_colon + 1; index < url_port_end; ++index)
{
port = port * 10 + *(url + index) - '0';
}
proxy_port = (port ? port : 80);
//printf("proxy_port = %d\n", proxy_port);
url_host_end = second_colon;
}
// Get host name.
int dest_index = 0, src_index = url_host_begin;
for(; src_index < url_host_end; ++dest_index, ++src_index)
{
host[dest_index] = *(url + src_index);
}
host[dest_index] = 0; // End host name.
StrCopy(request, &request_index, url + url_port_end); // Fill <request-URL>
}
else
{
StrCopy(request, &request_index, url); // Fill <request-URL>
}
// Step 5. Fill <version> and this line's CRLF
StrCopy(request, &request_index, " HTTP/1.1\r\n");
// Step 6. Fill <headers>
StrCopy(request, &request_index, "User-Agent: WebTest-Xiang Gao\r\n");
if(proxy_host == NULL) // Without proxy server, fill <Host:> header.
{
StrCopy(request, &request_index, "Host: ");
StrCopy(request, &request_index, host);
StrCopy(request, &request_index, "\r\n");
}
if(force_reload && proxy_host != NULL)
{
StrCopy(request, &request_index, "Pragma: no-cache\r\n");
}
StrCopy(request, &request_index, "Connection: close\r\n");
// Step 7. Add an empty line after all headers and add '\0' to end request array.
StrCopy(request, &request_index, "\r\n\0");
//printf("%s", request);
}