std::shared_ptr CurlHttpClient::makeRequest()

in sdk/src/http/CurlHttpClient.cc [432:643]


std::shared_ptr<HttpResponse> CurlHttpClient::makeRequest(const std::shared_ptr<HttpRequest> &request)
{
    OSS_LOG(LogLevel::LogDebug, TAG, "request(%p) enter makeRequest", request.get());
    curl_slist *list = nullptr;
    auto& headers = request->Headers();
    for (const auto &p : headers) {
        if (p.second.empty())
            continue;
        std::string str = p.first;
        str.append(": ").append(p.second);
        list = curl_slist_append(list, str.c_str());
    }
    // Disable Expect: 100-continue
    list = curl_slist_append(list, "Expect:");

    auto response = std::make_shared<HttpResponse>(request);
    
    std::iostream::pos_type requestBodyPos = -1;
    if (request->Body() != nullptr) {
        requestBodyPos = request->Body()->tellg();
    }

    CURL * curl = curlContainer_->Acquire();

    OSS_LOG(LogLevel::LogDebug, TAG, "request(%p) acquire curl handle:%p", request.get(), curl);

    uint64_t initCRC64 = 0;
#ifdef ENABLE_OSS_TEST
    if (headers.find("oss-test-crc64") != headers.end()) {
        initCRC64 = std::strtoull(headers.at("oss-test-crc64").c_str(), nullptr, 10);
    }
#endif
    TransferState transferState = {
        this,
        curl,
        request.get(),
        response.get(),
        0, -1, 
        true, -1, 
        request->TransferProgress().Handler,
        request->TransferProgress().UserData,
        request->hasCheckCrc64(), initCRC64, initCRC64, 
        0, 0
    };

    if (request->hasHeader(Http::CONTENT_LENGTH)) {
        transferState.total = std::atoll(request->Header(Http::CONTENT_LENGTH).c_str());
    }

    std::string url = request->url().toString();
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    switch (request->method())
    {
    case Http::Method::Head:
        curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
        break;
    case Http::Method::Put:
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        break;
    case Http::Method::Post:
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        break;
    case Http::Method::Delete:
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
        break;
    case Http::Method::Get:
    default:
        break;
    }
    
    curl_easy_setopt(curl, CURLOPT_USERAGENT,userAgent_.c_str());

    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, &transferState);
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, recvHeaders);

    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &transferState);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, recvBody);

    curl_easy_setopt(curl, CURLOPT_READDATA, &transferState);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, sendBody);

    if (verifySSL_) {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
    }
    if(!caPath_.empty()) {
        curl_easy_setopt(curl, CURLOPT_CAPATH, caPath_.c_str());
    }
    if(!caFile_.empty()){
        curl_easy_setopt(curl, CURLOPT_CAINFO, caFile_.c_str());
    }

    if (!proxyHost_.empty()) {
        std::stringstream ss;
        ss << Http::SchemeToString(proxyScheme_) << "://" << proxyHost_;
        curl_easy_setopt(curl, CURLOPT_PROXY, ss.str().c_str());
        curl_easy_setopt(curl, CURLOPT_PROXYPORT, (long) proxyPort_);
        curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, proxyUserName_.c_str());
        curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, proxyPassword_.c_str());
    }

    if (!networkInterface_.empty()) {
        curl_easy_setopt(curl, CURLOPT_INTERFACE, networkInterface_.c_str());
    }

    //debug
    if (GetLogLevelInner() >= LogLevel::LogInfo) {
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, debugCallback);
    }

    //Error Buffer
    char errbuf[CURL_ERROR_SIZE];
    curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
    errbuf[0] = 0;

    //progress Callback
    curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
    curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &transferState);
    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);

    //Send bytes/sec 
    if (sendRateLimiter_ != nullptr) {
        transferState.sendSpeed = sendRateLimiter_->Rate();
        auto speed = static_cast<curl_off_t>(transferState.sendSpeed);
        speed = speed * 1024;
        curl_easy_setopt(curl, CURLOPT_MAX_SEND_SPEED_LARGE, speed);
    }

    //Recv bytes/sec 
    if (recvRateLimiter_ != nullptr) {
        transferState.recvSpeed = recvRateLimiter_->Rate();
        auto speed = static_cast<curl_off_t>(transferState.recvSpeed);
        speed = speed * 1024;
        curl_easy_setopt(curl, CURLOPT_MAX_RECV_SPEED_LARGE, speed);
    }

    CURLcode res = curl_easy_perform(curl);
    long response_code= 0;
    curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);

    if (res == CURLE_OK) {
        response->setStatusCode(response_code);
    } else {
        response->setStatusCode(res + ERROR_CURL_BASE);
        switch (res) {
        case 23: //CURLE_WRITE_ERROR
        {
            std::string msg(curl_easy_strerror(res));
            if (response->Body() == nullptr) {
                msg.append(". Caused by content is null.");
            }
            else if (response->Body()->bad()) {
                msg.append(". Caused by content is in bad state(Read/writing error on i/o operation).");
            }
            else if (response->Body()->fail()) {
                msg.append(". Caused by content is in fail state(Logical error on i/o operation).");
            }
            response->setStatusMsg(msg);
        }
            break;
        default:
        {
            std::string msg(curl_easy_strerror(res));
            msg.append(".").append(errbuf);
            response->setStatusMsg(msg);
        }
            break;
        };
    }
   
    switch (request->method())
    {
    case Http::Method::Put:
    case Http::Method::Post:
        request->setCrc64Result(transferState.sendCrc64Value);
        break;
    default:
        request->setCrc64Result(transferState.recvCrc64Value);
        break;
    }
    request->setTransferedBytes(transferState.transferred);

    curlContainer_->Release(curl, (res != CURLE_OK));

    curl_slist_free_all(list);

    auto & body = response->Body();
    if (body != nullptr) {
        body->flush();
        if (res != CURLE_OK && transferState.recvBodyPos != static_cast<std::streampos>(-1)) {
            OSS_LOG(LogLevel::LogDebug, TAG, "request(%p) setResponseBody, tellp:%lld, recvBodyPos:%lld",
                request.get(), body->tellp(), transferState.recvBodyPos);
            body->clear();
            body->seekp(transferState.recvBodyPos);
        }
    }
    else {
        response->addBody(std::make_shared<std::stringstream>());
    }

    if (requestBodyPos != static_cast<std::streampos>(-1)) {
        request->Body()->clear();
        request->Body()->seekg(requestBodyPos);
    }

    OSS_LOG(LogLevel::LogDebug, TAG, "request(%p) leave makeRequest, CURLcode:%d, ResponseCode:%d", 
        request.get(), res, response_code);

    return response;
}