mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-19 14:15:21 -06:00
251 lines
10 KiB
C++
251 lines
10 KiB
C++
/******************************************************************************
|
|
* $Id: gdalhttp.cpp 27044 2014-03-16 23:41:27Z rouault $
|
|
*
|
|
* Project: WMS Client Driver
|
|
* Purpose: Implementation of Dataset and RasterBand classes for WMS
|
|
* and other similar services.
|
|
* Author: Adam Nowacki, nowak@xpam.de
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2007, Adam Nowacki
|
|
* Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "wmsdriver.h"
|
|
|
|
void CPLHTTPSetOptions(CURL *http_handle, char** papszOptions);
|
|
|
|
/* CURLINFO_RESPONSE_CODE was known as CURLINFO_HTTP_CODE in libcurl 7.10.7 and earlier */
|
|
#if LIBCURL_VERSION_NUM < 0x070a07
|
|
#define CURLINFO_RESPONSE_CODE CURLINFO_HTTP_CODE
|
|
#endif
|
|
|
|
static size_t CPLHTTPWriteFunc(void *buffer, size_t count, size_t nmemb, void *req) {
|
|
CPLHTTPRequest *psRequest = reinterpret_cast<CPLHTTPRequest *>(req);
|
|
size_t size = count * nmemb;
|
|
|
|
if (size == 0) return 0;
|
|
|
|
const size_t required_size = psRequest->nDataLen + size + 1;
|
|
if (required_size > psRequest->nDataAlloc) {
|
|
size_t new_size = required_size * 2;
|
|
if (new_size < 512) new_size = 512;
|
|
psRequest->nDataAlloc = new_size;
|
|
GByte * pabyNewData = reinterpret_cast<GByte *>(VSIRealloc(psRequest->pabyData, new_size));
|
|
if (pabyNewData == NULL) {
|
|
VSIFree(psRequest->pabyData);
|
|
psRequest->pabyData = NULL;
|
|
psRequest->pszError = CPLStrdup(CPLString().Printf("Out of memory allocating %u bytes for HTTP data buffer.", static_cast<int>(new_size)));
|
|
psRequest->nDataAlloc = 0;
|
|
psRequest->nDataLen = 0;
|
|
return 0;
|
|
}
|
|
psRequest->pabyData = pabyNewData;
|
|
}
|
|
memcpy(psRequest->pabyData + psRequest->nDataLen, buffer, size);
|
|
psRequest->nDataLen += size;
|
|
psRequest->pabyData[psRequest->nDataLen] = 0;
|
|
return nmemb;
|
|
}
|
|
|
|
void CPLHTTPInitializeRequest(CPLHTTPRequest *psRequest, const char *pszURL, const char *const *papszOptions) {
|
|
psRequest->pszURL = CPLStrdup(pszURL);
|
|
psRequest->papszOptions = CSLDuplicate(const_cast<char **>(papszOptions));
|
|
psRequest->nStatus = 0;
|
|
psRequest->pszContentType = 0;
|
|
psRequest->pszError = 0;
|
|
psRequest->pabyData = 0;
|
|
psRequest->nDataLen = 0;
|
|
psRequest->nDataAlloc = 0;
|
|
psRequest->m_curl_handle = 0;
|
|
psRequest->m_headers = 0;
|
|
psRequest->m_curl_error = 0;
|
|
|
|
psRequest->m_curl_handle = curl_easy_init();
|
|
if (psRequest->m_curl_handle == NULL) {
|
|
CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPInitializeRequest(): Unable to create CURL handle.");
|
|
}
|
|
|
|
char** papszOptionsDup = CSLDuplicate(const_cast<char **>(psRequest->papszOptions));
|
|
|
|
/* Set User-Agent */
|
|
const char *pszUserAgent = CSLFetchNameValue(papszOptionsDup, "USERAGENT");
|
|
if (pszUserAgent == NULL)
|
|
papszOptionsDup = CSLAddNameValue(papszOptionsDup, "USERAGENT",
|
|
"GDAL WMS driver (http://www.gdal.org/frmt_wms.html)");
|
|
|
|
/* Set URL */
|
|
curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_URL, psRequest->pszURL);
|
|
|
|
/* Set Headers (copied&pasted from cpl_http.cpp, but unused by callers of CPLHTTPInitializeRequest) .*/
|
|
const char *headers = CSLFetchNameValue(const_cast<char **>(psRequest->papszOptions), "HEADERS");
|
|
if (headers != NULL) {
|
|
psRequest->m_headers = curl_slist_append(psRequest->m_headers, headers);
|
|
curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_HTTPHEADER, psRequest->m_headers);
|
|
}
|
|
|
|
curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_WRITEDATA, psRequest);
|
|
curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_WRITEFUNCTION, CPLHTTPWriteFunc);
|
|
|
|
psRequest->m_curl_error = reinterpret_cast<char *>(CPLMalloc(CURL_ERROR_SIZE + 1));
|
|
psRequest->m_curl_error[0] = '\0';
|
|
curl_easy_setopt(psRequest->m_curl_handle, CURLOPT_ERRORBUFFER, psRequest->m_curl_error);
|
|
|
|
CPLHTTPSetOptions(psRequest->m_curl_handle, papszOptionsDup);
|
|
|
|
CSLDestroy(papszOptionsDup);
|
|
}
|
|
|
|
void CPLHTTPCleanupRequest(CPLHTTPRequest *psRequest) {
|
|
if (psRequest->m_curl_handle) {
|
|
curl_easy_cleanup(psRequest->m_curl_handle);
|
|
psRequest->m_curl_handle = 0;
|
|
}
|
|
if (psRequest->m_headers) {
|
|
curl_slist_free_all(psRequest->m_headers);
|
|
psRequest->m_headers = 0;
|
|
}
|
|
if (psRequest->m_curl_error) {
|
|
CPLFree(psRequest->m_curl_error);
|
|
psRequest->m_curl_error = 0;
|
|
}
|
|
|
|
if (psRequest->pszContentType) {
|
|
CPLFree(psRequest->pszContentType);
|
|
psRequest->pszContentType = 0;
|
|
}
|
|
if (psRequest->pszError) {
|
|
CPLFree(psRequest->pszError);
|
|
psRequest->pszError = 0;
|
|
}
|
|
if (psRequest->pabyData) {
|
|
CPLFree(psRequest->pabyData);
|
|
psRequest->pabyData = 0;
|
|
psRequest->nDataLen = 0;
|
|
psRequest->nDataAlloc = 0;
|
|
}
|
|
if (psRequest->papszOptions) {
|
|
CSLDestroy(psRequest->papszOptions);
|
|
psRequest->papszOptions = 0;
|
|
}
|
|
if (psRequest->pszURL) {
|
|
CPLFree(psRequest->pszURL);
|
|
psRequest->pszURL = 0;
|
|
}
|
|
}
|
|
|
|
CPLErr CPLHTTPFetchMulti(CPLHTTPRequest *pasRequest, int nRequestCount, const char *const *papszOptions) {
|
|
CPLErr ret = CE_None;
|
|
CURLM *curl_multi = 0;
|
|
int still_running;
|
|
int max_conn;
|
|
int i, conn_i;
|
|
|
|
const char *max_conn_opt = CSLFetchNameValue(const_cast<char **>(papszOptions), "MAXCONN");
|
|
if (max_conn_opt && (max_conn_opt[0] != '\0')) {
|
|
max_conn = MAX(1, MIN(atoi(max_conn_opt), 1000));
|
|
} else {
|
|
max_conn = 5;
|
|
}
|
|
|
|
curl_multi = curl_multi_init();
|
|
if (curl_multi == NULL) {
|
|
CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPFetchMulti(): Unable to create CURL multi-handle.");
|
|
}
|
|
|
|
// add at most max_conn requests
|
|
for (conn_i = 0; conn_i < MIN(nRequestCount, max_conn); ++conn_i) {
|
|
CPLHTTPRequest *const psRequest = &pasRequest[conn_i];
|
|
CPLDebug("HTTP", "Requesting [%d/%d] %s", conn_i + 1, nRequestCount, pasRequest[conn_i].pszURL);
|
|
curl_multi_add_handle(curl_multi, psRequest->m_curl_handle);
|
|
}
|
|
|
|
while (curl_multi_perform(curl_multi, &still_running) == CURLM_CALL_MULTI_PERFORM);
|
|
while (still_running || (conn_i != nRequestCount)) {
|
|
struct timeval timeout;
|
|
fd_set fdread, fdwrite, fdexcep;
|
|
int maxfd;
|
|
CURLMsg *msg;
|
|
int msgs_in_queue;
|
|
|
|
do {
|
|
msg = curl_multi_info_read(curl_multi, &msgs_in_queue);
|
|
if (msg != NULL) {
|
|
if (msg->msg == CURLMSG_DONE) { // transfer completed, check if we have more waiting and add them
|
|
if (conn_i < nRequestCount) {
|
|
CPLHTTPRequest *const psRequest = &pasRequest[conn_i];
|
|
CPLDebug("HTTP", "Requesting [%d/%d] %s", conn_i + 1, nRequestCount, pasRequest[conn_i].pszURL);
|
|
curl_multi_add_handle(curl_multi, psRequest->m_curl_handle);
|
|
++conn_i;
|
|
}
|
|
}
|
|
}
|
|
} while (msg != NULL);
|
|
FD_ZERO(&fdread);
|
|
FD_ZERO(&fdwrite);
|
|
FD_ZERO(&fdexcep);
|
|
curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd);
|
|
if( maxfd >= 0 )
|
|
{
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 100000;
|
|
select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout);
|
|
}
|
|
while (curl_multi_perform(curl_multi, &still_running) == CURLM_CALL_MULTI_PERFORM);
|
|
}
|
|
|
|
if (conn_i != nRequestCount) { // something gone really really wrong
|
|
CPLError(CE_Fatal, CPLE_AppDefined, "CPLHTTPFetchMulti(): conn_i != nRequestCount, this should never happen ...");
|
|
}
|
|
for (i = 0; i < nRequestCount; ++i) {
|
|
CPLHTTPRequest *const psRequest = &pasRequest[i];
|
|
|
|
long response_code = 0;
|
|
curl_easy_getinfo(psRequest->m_curl_handle, CURLINFO_RESPONSE_CODE, &response_code);
|
|
psRequest->nStatus = response_code;
|
|
|
|
char *content_type = 0;
|
|
curl_easy_getinfo(psRequest->m_curl_handle, CURLINFO_CONTENT_TYPE, &content_type);
|
|
if (content_type) psRequest->pszContentType = CPLStrdup(content_type);
|
|
|
|
if ((psRequest->pszError == NULL) && (psRequest->m_curl_error != NULL) && (psRequest->m_curl_error[0] != '\0')) {
|
|
psRequest->pszError = CPLStrdup(psRequest->m_curl_error);
|
|
}
|
|
|
|
/* In the case of a file:// URL, curl will return a status == 0, so if there's no */
|
|
/* error returned, patch the status code to be 200, as it would be for http:// */
|
|
if (strncmp(psRequest->pszURL, "file://", 7) == 0 && psRequest->nStatus == 0 &&
|
|
psRequest->pszError == NULL)
|
|
{
|
|
psRequest->nStatus = 200;
|
|
}
|
|
|
|
CPLDebug("HTTP", "Request [%d] %s : status = %d, content type = %s, error = %s",
|
|
i, psRequest->pszURL, psRequest->nStatus,
|
|
(psRequest->pszContentType) ? psRequest->pszContentType : "(null)",
|
|
(psRequest->pszError) ? psRequest->pszError : "(null)");
|
|
|
|
curl_multi_remove_handle(curl_multi, pasRequest[i].m_curl_handle);
|
|
}
|
|
curl_multi_cleanup(curl_multi);
|
|
|
|
return ret;
|
|
}
|