size_t CurlHttpClient::ReadBody(char* ptr, size_t size, size_t nmemb, void* userdata) { CurlReadCallbackContext* context = reinterpret_cast<CurlReadCallbackContext*>(userdata); if(context == nullptr) { return 0; } const CurlHttpClient* client = context->m_client; if(!client->IsRequestProcessingEnabled()) { return 0; } HttpRequest* request = context->m_request; std::shared_ptr<Aws::IOStream> ioStream = request->GetContentBody(); if (ioStream != nullptr && size * nmemb) { size_t amountToRead = size * nmemb; ioStream->read(ptr, amountToRead); size_t amountRead = static_cast<size_t>(ioStream->gcount()); auto& sentHandler = request->GetDataSentEventHandler(); if (sentHandler) { sentHandler(request, static_cast<long long>(amountRead)); } return amountRead; } return 0; }
bool WinSyncHttpClient::StreamPayloadToRequest(const HttpRequest& request, void* hHttpRequest) const { bool success = true; auto payloadStream = request.GetContentBody(); if(payloadStream) { auto startingPos = payloadStream->tellg(); char streamBuffer[ HTTP_REQUEST_WRITE_BUFFER_LENGTH ]; bool done = false; while(success && !done) { payloadStream->read(streamBuffer, HTTP_REQUEST_WRITE_BUFFER_LENGTH); std::streamsize bytesRead = payloadStream->gcount(); success = !payloadStream->bad(); uint64_t bytesWritten = 0; if (bytesRead > 0) { bytesWritten = DoWriteData(hHttpRequest, streamBuffer, bytesRead); if (!bytesWritten) { success = false; } } auto& sentHandler = request.GetDataSentEventHandler(); if (sentHandler) { sentHandler(&request, (long long)bytesWritten); } if(!payloadStream->good()) { done = true; } success = success && IsRequestProcessingEnabled(); } payloadStream->clear(); payloadStream->seekg(startingPos, payloadStream->beg); } if(success) { success = DoReceiveResponse(hHttpRequest); } return success; }
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; }