void SetOptCodeForHttpMethod(CURL* requestHandle, const HttpRequest& request) { switch (request.GetMethod()) { case HttpMethod::HTTP_GET: curl_easy_setopt(requestHandle, CURLOPT_HTTPGET, 1L); break; case HttpMethod::HTTP_POST: if (!request.HasHeader(Aws::Http::CONTENT_LENGTH_HEADER)|| request.GetHeaderValue(Aws::Http::CONTENT_LENGTH_HEADER) == "0") { curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "POST"); } else { curl_easy_setopt(requestHandle, CURLOPT_POST, 1L); } break; case HttpMethod::HTTP_PUT: if (!request.HasHeader(Aws::Http::CONTENT_LENGTH_HEADER) || request.GetHeaderValue(Aws::Http::CONTENT_LENGTH_HEADER) == "0") { curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "PUT"); } else { curl_easy_setopt(requestHandle, CURLOPT_PUT, 1L); } break; case HttpMethod::HTTP_HEAD: curl_easy_setopt(requestHandle, CURLOPT_HTTPGET, 1L); curl_easy_setopt(requestHandle, CURLOPT_NOBODY, 1L); break; case HttpMethod::HTTP_PATCH: if (!request.HasHeader(Aws::Http::CONTENT_LENGTH_HEADER)|| request.GetHeaderValue(Aws::Http::CONTENT_LENGTH_HEADER) == "0") { curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "PATCH"); } else { curl_easy_setopt(requestHandle, CURLOPT_POST, 1L); curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "PATCH"); } break; case HttpMethod::HTTP_DELETE: curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "DELETE"); //curl_easy_setopt(requestHandle, CURLOPT_NOBODY, 1L); break; default: assert(0); curl_easy_setopt(requestHandle, CURLOPT_CUSTOMREQUEST, "GET"); break; } }
std::shared_ptr<HttpResponse> CurlHttpClient::MakeRequest(HttpRequest& request, Aws::Utils::RateLimits::RateLimiterInterface* readLimiter, Aws::Utils::RateLimits::RateLimiterInterface* writeLimiter) const { //handle uri encoding at last second. Otherwise, the signer and the http layer will mismatch. URI uri = request.GetUri(); uri.SetPath(URI::URLEncodePath(uri.GetPath())); Aws::String url = uri.GetURIString(); AWS_LOGSTREAM_TRACE(CURL_HTTP_CLIENT_TAG, "Making request to " << url); struct curl_slist* headers = NULL; if (writeLimiter != nullptr) { writeLimiter->ApplyAndPayForCost(request.GetSize()); } Aws::StringStream headerStream; HeaderValueCollection requestHeaders = request.GetHeaders(); AWS_LOG_TRACE(CURL_HTTP_CLIENT_TAG, "Including headers:"); for (auto& requestHeader : requestHeaders) { headerStream.str(""); headerStream << requestHeader.first << ": " << requestHeader.second; Aws::String headerString = headerStream.str(); AWS_LOGSTREAM_TRACE(CURL_HTTP_CLIENT_TAG, headerString); headers = curl_slist_append(headers, headerString.c_str()); } headers = curl_slist_append(headers, "transfer-encoding:"); if (!request.HasHeader(Http::CONTENT_LENGTH_HEADER)) { headers = curl_slist_append(headers, "content-length:"); } if (!request.HasHeader(Http::CONTENT_TYPE_HEADER)) { headers = curl_slist_append(headers, "content-type:"); } std::shared_ptr<HttpResponse> response(nullptr); CURL* connectionHandle = m_curlHandleContainer.AcquireCurlHandle(); if (connectionHandle) { AWS_LOGSTREAM_DEBUG(CURL_HTTP_CLIENT_TAG, "Obtained connection handle " << connectionHandle); if (headers) { curl_easy_setopt(connectionHandle, CURLOPT_HTTPHEADER, headers); } response = Aws::MakeShared<StandardHttpResponse>(CURL_HTTP_CLIENT_TAG, request); CurlWriteCallbackContext writeContext(this, &request, response.get(), readLimiter); CurlReadCallbackContext readContext(this, &request); SetOptCodeForHttpMethod(connectionHandle, request); curl_easy_setopt(connectionHandle, CURLOPT_URL, url.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_WRITEFUNCTION, &CurlHttpClient::WriteData); curl_easy_setopt(connectionHandle, CURLOPT_WRITEDATA, &writeContext); curl_easy_setopt(connectionHandle, CURLOPT_HEADERFUNCTION, &CurlHttpClient::WriteHeader); curl_easy_setopt(connectionHandle, CURLOPT_HEADERDATA, response.get()); //we only want to override the default path if someone has explicitly told us to. if(!m_caPath.empty()) { curl_easy_setopt(connectionHandle, CURLOPT_CAPATH, m_caPath.c_str()); } // only set by android test builds because the emulator is missing a cert needed for aws services #ifdef TEST_CERT_PATH curl_easy_setopt(connectionHandle, CURLOPT_CAPATH, TEST_CERT_PATH); #endif // TEST_CERT_PATH if (m_verifySSL) { curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYPEER, 1L); curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYHOST, 2L); #if LIBCURL_VERSION_MAJOR >= 7 #if LIBCURL_VERSION_MINOR >= 34 curl_easy_setopt(connectionHandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); #endif //LIBCURL_VERSION_MINOR #endif //LIBCURL_VERSION_MAJOR } else { curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(connectionHandle, CURLOPT_SSL_VERIFYHOST, 0L); } if (m_allowRedirects) { curl_easy_setopt(connectionHandle, CURLOPT_FOLLOWLOCATION, 1L); } else { curl_easy_setopt(connectionHandle, CURLOPT_FOLLOWLOCATION, 0L); } //curl_easy_setopt(connectionHandle, CURLOPT_VERBOSE, 1); //curl_easy_setopt(connectionHandle, CURLOPT_DEBUGFUNCTION, CurlDebugCallback); if (m_isUsingProxy) { curl_easy_setopt(connectionHandle, CURLOPT_PROXY, m_proxyHost.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_PROXYPORT, (long) m_proxyPort); curl_easy_setopt(connectionHandle, CURLOPT_PROXYUSERNAME, m_proxyUserName.c_str()); curl_easy_setopt(connectionHandle, CURLOPT_PROXYPASSWORD, m_proxyPassword.c_str()); } if (request.GetContentBody()) { curl_easy_setopt(connectionHandle, CURLOPT_READFUNCTION, &CurlHttpClient::ReadBody); curl_easy_setopt(connectionHandle, CURLOPT_READDATA, &readContext); } CURLcode curlResponseCode = curl_easy_perform(connectionHandle); if (curlResponseCode != CURLE_OK) { response = nullptr; AWS_LOGSTREAM_ERROR(CURL_HTTP_CLIENT_TAG, "Curl returned error code " << curlResponseCode); } else { long responseCode; curl_easy_getinfo(connectionHandle, CURLINFO_RESPONSE_CODE, &responseCode); response->SetResponseCode(static_cast<HttpResponseCode>(responseCode)); AWS_LOGSTREAM_DEBUG(CURL_HTTP_CLIENT_TAG, "Returned http response code " << responseCode); char* contentType = nullptr; curl_easy_getinfo(connectionHandle, CURLINFO_CONTENT_TYPE, &contentType); if (contentType) { response->SetContentType(contentType); AWS_LOGSTREAM_DEBUG(CURL_HTTP_CLIENT_TAG, "Returned content type " << contentType); } AWS_LOGSTREAM_DEBUG(CURL_HTTP_CLIENT_TAG, "Releasing curl handle " << connectionHandle); } m_curlHandleContainer.ReleaseCurlHandle(connectionHandle); //go ahead and flush the response body stream if(response) { response->GetResponseBody().flush(); } } if (headers) { curl_slist_free_all(headers); } return response; }