1d540f44创建于 2021年9月23日历史提交
/*
 * Copyright (c) 2021 Huawei Technologies Co.,Ltd.
 *
 * openGauss is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * http_common.cpp
 *    http request common utility function
 *
 * IDENTIFICATION
 *    src/gausskernel/security/tde_key_management/http_common.cpp
 *
 * -------------------------------------------------------------------------
 */
 
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "tde_key_management/http_common.h"
#include "securec.h"
#include "utils/elog.h"

HttpErrCode HttpCommon::http_request(HttpReqMsg* http_req_msg, HttpConfig *http_config, AdvStrList **http_res_list)
{
    CURL *sender = NULL;
    struct curl_slist *http_header = NULL;
    HttpErrCode ret = TDE_HTTP_SUCCEED;

    curl_global_init(CURL_GLOBAL_ALL);
    sender = curl_easy_init();
    if (sender == NULL) {
        ereport(ERROR, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("failed to init curl")));
        return TDE_CURL_ERR;
    }
    ret = set_http_config(sender, http_config);
    if (ret != TDE_HTTP_SUCCEED) {
        curl_easy_cleanup(sender);
        return ret;
    }
    ret = set_http_reqline(sender, http_req_msg->method, http_req_msg->url);
    if (ret != TDE_HTTP_SUCCEED) {
        curl_easy_cleanup(sender);
        return ret;
    }
    ret = set_http_reqheader(sender, http_req_msg->header_list, http_header);
    curl_slist_free_all(http_header);
    if (ret != TDE_HTTP_SUCCEED) {
        curl_easy_cleanup(sender);
        return ret;
    }
    ret = set_http_reqbody(sender, http_req_msg->body);
    if (ret != TDE_HTTP_SUCCEED) {
        curl_easy_cleanup(sender);
        return ret;
    }
    ret = get_http_resmsg(sender, http_config->res_part, http_res_list);
    curl_easy_cleanup(sender);
    if (ret != TDE_HTTP_SUCCEED) {
        return ret;
    }
    curl_global_cleanup();
    return TDE_HTTP_SUCCEED;
}

HttpErrCode HttpCommon::set_http_config(CURL *http_obj, HttpConfig *http_config)
{
    CURLcode curl_ret = CURLE_OK;
    const int connect_timeout = 10;

    curl_ret = curl_easy_setopt(http_obj, CURLOPT_TIMEOUT, http_config->timeout);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_CONNECTTIMEOUT, connect_timeout);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_SSL_VERIFYPEER, false);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_SSL_VERIFYHOST, false);
    check_curl_ret(curl_ret);
    return TDE_HTTP_SUCCEED;
}

void HttpCommon::check_curl_ret(CURLcode curl_ret)
{
    if (curl_ret != CURLE_OK) {
        ereport(ERROR, (errcode(ERRCODE_CANNOT_CONNECT_NOW), 
            errmsg("curl error. err code: '%lu', err msg: '%s.", (long unsigned int)curl_ret, 
                curl_easy_strerror(curl_ret))));
    }
    return;
}

HttpErrCode HttpCommon::set_http_reqline(CURL *http_obj, HttpMethod method, const char *url)
{
    CURLcode curl_ret = CURLE_OK;

    switch (method) {
        case HTTP_POST:
            curl_ret = curl_easy_setopt(http_obj, CURLOPT_HTTPPOST, 1);
            break;
        case HTTP_GET:
            curl_ret = curl_easy_setopt(http_obj, CURLOPT_HTTPGET, 1);
            break;
        default:
            break;
    }
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_URL, url);
    check_curl_ret(curl_ret);
    return TDE_HTTP_SUCCEED;
}

HttpErrCode HttpCommon::set_http_reqheader(CURL *http_obj, const char *http_heaser_list[],
    struct curl_slist* ret_header_list)
{
    struct curl_slist *tmp_http_header = NULL;
    CURLcode curl_ret = CURLE_OK;

    for (size_t i = 0; http_heaser_list[i] != NULL; i++) {
        tmp_http_header = curl_slist_append(tmp_http_header, http_heaser_list[i]);
        if (tmp_http_header == NULL) {
            ereport(ERROR, (errcode(ERRCODE_CANNOT_CONNECT_NOW), 
                errmsg("failed to set http header: '%s'", http_heaser_list[i])));
            return TDE_CURL_ERR;
        }
    }
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_HTTPHEADER, tmp_http_header);
    check_curl_ret(curl_ret);
    ret_header_list = tmp_http_header;
    return TDE_HTTP_SUCCEED;
}

HttpErrCode HttpCommon::set_http_reqbody(CURL *http_obj, const char *http_body)
{
    CURLcode curl_ret = curl_easy_setopt(http_obj, CURLOPT_POSTFIELDS, http_body);
    check_curl_ret(curl_ret);
    return TDE_HTTP_SUCCEED;
}

HttpErrCode HttpCommon::get_http_resmsg(CURL *http_obj, HttpResListType res_part, AdvStrList **response_msg)
{
    HttpResList http_res_list = {"", res_part, HTTP_RESLINE, NULL, 0, 0, false};
    const int status_code = 300;
    long http_status_code = 0;
    CURLcode curl_ret = CURLE_OK;

    http_res_list.str_list = malloc_advstr_list();
    if (http_res_list.str_list == NULL) {
        ereport(ERROR, (errcode(ERRCODE_CANNOT_CONNECT_NOW), errmsg("failed to malloc memory")));
        return TDE_MALLOC_MEM_ERR;
    }
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_HEADER, 1);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_WRITEFUNCTION, dump_http_response_msg_callback);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_setopt(http_obj, CURLOPT_WRITEDATA, (void *) &http_res_list);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_perform(http_obj);
    check_curl_ret(curl_ret);
    curl_ret = curl_easy_getinfo(http_obj, CURLINFO_RESPONSE_CODE, &http_status_code);
    check_curl_ret(curl_ret);
    if (http_status_code > status_code) {
        ereport(ERROR, (errmodule(MOD_SEC_TDE), errcode(ERRCODE_CANNOT_CONNECT_NOW), 
            errmsg("curl error code is %d", (int)http_status_code), errdetail("N/A"), 
            errcause("http request status error"), erraction("check curl retrun code status")));
    }
    *response_msg = http_res_list.str_list;
    return TDE_HTTP_SUCCEED;
}

size_t HttpCommon::dump_http_response_msg_callback(void *tmp_cur_res_str, size_t chr_size, size_t cur_res_size, 
    void *tmp_http_res_list)
{
    bool is_need_dump = false;
    const int loop_num = 2;
    HttpResList *http_res_list = NULL;

    if (tmp_cur_res_str == NULL || tmp_http_res_list == NULL) {
        return 0;
    }
    http_res_list = (HttpResList *)tmp_http_res_list;
    if (http_res_list->is_stop_dump) {
        return chr_size * cur_res_size;
    }
    const char *cur_res_str = (const char *)tmp_cur_res_str;
    if (http_res_list->filter_res_type == HTTP_MSG) {
        is_need_dump = true;
    } else {
        switch (http_res_list->cur_pos) {
            case HTTP_RESLINE:
                if (http_res_list->filter_res_type == HTTP_RESLINE) {
                    is_need_dump = true;
                    http_res_list->is_stop_dump = true;
                }
                if (http_res_list->loop_cnt == loop_num) {
                    http_res_list->cur_pos = HTTP_RESHEADER;
                }
                http_res_list->loop_cnt++;
                break;
            case HTTP_RESHEADER:
                if (http_res_list->filter_res_type == HTTP_RESHEADER) {
                    is_need_dump = true;
                }
                if (strlen(cur_res_str) == strlen("\r\n") && cur_res_str[0] == '\r' && cur_res_str[1] == '\n') {
                    http_res_list->cur_pos = HTTP_RESBODY;
                }
                break;
            case HTTP_RESBODY:
                if (http_res_list->filter_res_type == HTTP_RESBODY) {
                    is_need_dump = true;
                }
                http_res_list->cur_pos = HTTP_RESLINE;
                break;
            default:
                break;
        }
    }
    if (is_need_dump) {
        tde_append_node(http_res_list->str_list, cur_res_str);
    }
    return chr_size * cur_res_size;
}