| 1 | /* |
| 2 | * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"). |
| 5 | * You may not use this file except in compliance with the License. |
| 6 | * A copy of the License is located at |
| 7 | * |
| 8 | * http://aws.amazon.com/apache2.0 |
| 9 | * |
| 10 | * or in the "license" file accompanying this file. This file is distributed |
| 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| 12 | * express or implied. See the License for the specific language governing |
| 13 | * permissions and limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | #include <aws/core/http/curl/CurlHandleContainer.h> |
| 17 | #include <aws/core/utils/logging/LogMacros.h> |
| 18 | |
| 19 | #include <algorithm> |
| 20 | |
| 21 | using namespace Aws::Utils::Logging; |
| 22 | using namespace Aws::Http; |
| 23 | |
| 24 | static const char* CURL_HANDLE_CONTAINER_TAG = "CurlHandleContainer" ; |
| 25 | |
| 26 | |
| 27 | CurlHandleContainer::CurlHandleContainer(unsigned maxSize, long httpRequestTimeout, long connectTimeout, bool enableTcpKeepAlive, |
| 28 | unsigned long tcpKeepAliveIntervalMs, long lowSpeedTime, unsigned long lowSpeedLimit) : |
| 29 | m_maxPoolSize(maxSize), m_httpRequestTimeout(httpRequestTimeout), m_connectTimeout(connectTimeout), m_enableTcpKeepAlive(enableTcpKeepAlive), |
| 30 | m_tcpKeepAliveIntervalMs(tcpKeepAliveIntervalMs), m_lowSpeedTime(lowSpeedTime), m_lowSpeedLimit(lowSpeedLimit), m_poolSize(0) |
| 31 | { |
| 32 | AWS_LOGSTREAM_INFO(CURL_HANDLE_CONTAINER_TAG, "Initializing CurlHandleContainer with size " << maxSize); |
| 33 | } |
| 34 | |
| 35 | CurlHandleContainer::~CurlHandleContainer() |
| 36 | { |
| 37 | AWS_LOGSTREAM_INFO(CURL_HANDLE_CONTAINER_TAG, "Cleaning up CurlHandleContainer." ); |
| 38 | for (CURL* handle : m_handleContainer.ShutdownAndWait(m_poolSize)) |
| 39 | { |
| 40 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "Cleaning up " << handle); |
| 41 | curl_easy_cleanup(handle); |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | CURL* CurlHandleContainer::AcquireCurlHandle() |
| 46 | { |
| 47 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "Attempting to acquire curl connection." ); |
| 48 | |
| 49 | if(!m_handleContainer.HasResourcesAvailable()) |
| 50 | { |
| 51 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "No current connections available in pool. Attempting to create new connections." ); |
| 52 | CheckAndGrowPool(); |
| 53 | } |
| 54 | |
| 55 | CURL* handle = m_handleContainer.Acquire(); |
| 56 | AWS_LOGSTREAM_INFO(CURL_HANDLE_CONTAINER_TAG, "Connection has been released. Continuing." ); |
| 57 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "Returning connection handle " << handle); |
| 58 | return handle; |
| 59 | } |
| 60 | |
| 61 | void CurlHandleContainer::ReleaseCurlHandle(CURL* handle) |
| 62 | { |
| 63 | if (handle) |
| 64 | { |
| 65 | curl_easy_reset(handle); |
| 66 | SetDefaultOptionsOnHandle(handle); |
| 67 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "Releasing curl handle " << handle); |
| 68 | m_handleContainer.Release(handle); |
| 69 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "Notified waiting threads." ); |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | void CurlHandleContainer::DestroyCurlHandle(CURL* handle) |
| 74 | { |
| 75 | if (!handle) |
| 76 | { |
| 77 | return; |
| 78 | } |
| 79 | |
| 80 | curl_easy_cleanup(handle); |
| 81 | { |
| 82 | std::lock_guard<std::mutex> locker(m_containerLock); |
| 83 | m_poolSize--; |
| 84 | } |
| 85 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "Destroy curl handle: " << handle << " and decrease pool size by 1." ); |
| 86 | } |
| 87 | |
| 88 | bool CurlHandleContainer::CheckAndGrowPool() |
| 89 | { |
| 90 | std::lock_guard<std::mutex> locker(m_containerLock); |
| 91 | if (m_poolSize < m_maxPoolSize) |
| 92 | { |
| 93 | unsigned multiplier = m_poolSize > 0 ? m_poolSize : 1; |
| 94 | unsigned amountToAdd = (std::min)(multiplier * 2, m_maxPoolSize - m_poolSize); |
| 95 | AWS_LOGSTREAM_DEBUG(CURL_HANDLE_CONTAINER_TAG, "attempting to grow pool size by " << amountToAdd); |
| 96 | |
| 97 | unsigned actuallyAdded = 0; |
| 98 | for (unsigned i = 0; i < amountToAdd; ++i) |
| 99 | { |
| 100 | CURL* curlHandle = curl_easy_init(); |
| 101 | |
| 102 | if (curlHandle) |
| 103 | { |
| 104 | SetDefaultOptionsOnHandle(curlHandle); |
| 105 | m_handleContainer.Release(curlHandle); |
| 106 | ++actuallyAdded; |
| 107 | } |
| 108 | else |
| 109 | { |
| 110 | AWS_LOGSTREAM_ERROR(CURL_HANDLE_CONTAINER_TAG, "curl_easy_init failed to allocate." ); |
| 111 | break; |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | AWS_LOGSTREAM_INFO(CURL_HANDLE_CONTAINER_TAG, "Pool grown by " << actuallyAdded); |
| 116 | m_poolSize += actuallyAdded; |
| 117 | |
| 118 | return actuallyAdded > 0; |
| 119 | } |
| 120 | |
| 121 | AWS_LOGSTREAM_INFO(CURL_HANDLE_CONTAINER_TAG, "Pool cannot be grown any further, already at max size." ); |
| 122 | |
| 123 | return false; |
| 124 | } |
| 125 | |
| 126 | void CurlHandleContainer::SetDefaultOptionsOnHandle(CURL* handle) |
| 127 | { |
| 128 | //for timeouts to work in a multi-threaded context, |
| 129 | //always turn signals off. This also forces dns queries to |
| 130 | //not be included in the timeout calculations. |
| 131 | curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L); |
| 132 | curl_easy_setopt(handle, CURLOPT_TIMEOUT_MS, m_httpRequestTimeout); |
| 133 | curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT_MS, m_connectTimeout); |
| 134 | curl_easy_setopt(handle, CURLOPT_LOW_SPEED_LIMIT, m_lowSpeedLimit); |
| 135 | curl_easy_setopt(handle, CURLOPT_LOW_SPEED_TIME, m_lowSpeedTime < 1000 ? (m_lowSpeedTime == 0 ? 0 : 1) : m_lowSpeedTime / 1000); |
| 136 | curl_easy_setopt(handle, CURLOPT_TCP_KEEPALIVE, m_enableTcpKeepAlive ? 1L : 0L); |
| 137 | curl_easy_setopt(handle, CURLOPT_TCP_KEEPINTVL, m_tcpKeepAliveIntervalMs); |
| 138 | curl_easy_setopt(handle, CURLOPT_TCP_KEEPIDLE, m_tcpKeepAliveIntervalMs); |
| 139 | #ifdef CURL_HAS_H2 |
| 140 | curl_easy_setopt(handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); |
| 141 | #endif |
| 142 | } |
| 143 | |