1. 程式人生 > >2-生成Http請求報文-BuildRequest

2-生成Http請求報文-BuildRequest

  • webtest工作的第二步是生成http請求報文,對應函式BuildRequest(const char *url);
  • Http請求報文的格式請參見我的部落格“HTTP權威指南”的“3-HTTP Messages”一文,您也可以直接閱讀RFC2616。
  • BuildRequest(const char *url);主要有以下7個步驟:
    1. 檢測URL的有效性。URL的語法為<scheme>://<host>:<port>/
      (1). scheme代表使用的協議(http/ftp等等):如果沒有提供proxy server,那麼只支援http協議。協議的名稱不區分大小寫,我們用函式IgnoreCaseMatch(url, "http://")
      進行匹配。
      (2). 查詢子串“://”,若未找到退出程式。
      (3). 解析“:/”:必須要以‘/’結尾。
      (4). 判斷URL長度是否合法。
    2. 如果URL有效,初始化host、request陣列:分別用來存放host name、請求報文。
    3. 填充method欄位。
      (1). 預設為GET方法。
      (2). 函式void StrCopy(char *dest, int *dest_index, const char *src):由於本程式只有在填充request時需要strcpy函式,所以特殊化。該函式每次填充字元時,從指定的索引開始,不復制src的結尾‘\0’字元。
    4. 填充request-url欄位。
      (1). 當有proxy server時:不解析<host>:<port>
      部分,將整個url寫入request陣列。
      (2). 當沒有proxy server時:解析。其中port可有可無,和‘:’保持一致。將‘/’之後的url寫入request。
    5. 填充version欄位:http 1.1.
    6. 填充header欄位。
      (1). 當沒有proxy時:寫入Host header。
      (2). 當有proxy且force_reload = 1時:寫入Pragma: no-cache\r\n
    7. 填充空行、結尾字元:”\r\n\0”。
  • 輔助的字串處理函式。
// 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);
}