|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <curl/curl.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_lib.h>
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
#include <apr_buckets.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "md_http.h"
|
|
Packit |
90a5c9 |
#include "md_log.h"
|
|
Packit |
90a5c9 |
#include "md_curl.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**************************************************************************************************/
|
|
Packit |
90a5c9 |
/* md_http curl implementation */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t curl_status(int curl_code)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
switch (curl_code) {
|
|
Packit |
90a5c9 |
case CURLE_OK: return APR_SUCCESS;
|
|
Packit |
90a5c9 |
case CURLE_UNSUPPORTED_PROTOCOL: return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
case CURLE_NOT_BUILT_IN: return APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
case CURLE_URL_MALFORMAT: return APR_EINVAL;
|
|
Packit |
90a5c9 |
case CURLE_COULDNT_RESOLVE_PROXY:return APR_ECONNREFUSED;
|
|
Packit |
90a5c9 |
case CURLE_COULDNT_RESOLVE_HOST: return APR_ECONNREFUSED;
|
|
Packit |
90a5c9 |
case CURLE_COULDNT_CONNECT: return APR_ECONNREFUSED;
|
|
Packit |
90a5c9 |
case CURLE_REMOTE_ACCESS_DENIED: return APR_EACCES;
|
|
Packit |
90a5c9 |
case CURLE_OUT_OF_MEMORY: return APR_ENOMEM;
|
|
Packit |
90a5c9 |
case CURLE_OPERATION_TIMEDOUT: return APR_TIMEUP;
|
|
Packit |
90a5c9 |
case CURLE_SSL_CONNECT_ERROR: return APR_ECONNABORTED;
|
|
Packit |
90a5c9 |
case CURLE_AGAIN: return APR_EAGAIN;
|
|
Packit |
90a5c9 |
default: return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static size_t req_data_cb(void *data, size_t len, size_t nmemb, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket_brigade *body = baton;
|
|
Packit |
90a5c9 |
size_t blen, read_len = 0, max_len = len * nmemb;
|
|
Packit |
90a5c9 |
const char *bdata;
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (body && !APR_BRIGADE_EMPTY(body) && max_len > 0) {
|
|
Packit |
90a5c9 |
b = APR_BRIGADE_FIRST(body);
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_METADATA(b)) {
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_EOS(b)) {
|
|
Packit |
90a5c9 |
body = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
rv = apr_bucket_read(b, &bdata, &blen, APR_BLOCK_READ);
|
|
Packit |
90a5c9 |
if (rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
if (blen > max_len) {
|
|
Packit |
90a5c9 |
apr_bucket_split(b, max_len);
|
|
Packit |
90a5c9 |
blen = max_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
memcpy(data, bdata, blen);
|
|
Packit |
90a5c9 |
read_len += blen;
|
|
Packit |
90a5c9 |
max_len -= blen;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
body = NULL;
|
|
Packit |
90a5c9 |
if (!APR_STATUS_IS_EOF(rv)) {
|
|
Packit |
90a5c9 |
/* everything beside EOF is an error */
|
|
Packit |
90a5c9 |
read_len = CURL_READFUNC_ABORT;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_bucket_delete(b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return read_len;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static size_t resp_data_cb(void *data, size_t len, size_t nmemb, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_http_response_t *res = baton;
|
|
Packit |
90a5c9 |
size_t blen = len * nmemb;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (res->body) {
|
|
Packit |
90a5c9 |
if (res->req->resp_limit) {
|
|
Packit |
90a5c9 |
apr_off_t body_len = 0;
|
|
Packit |
90a5c9 |
apr_brigade_length(res->body, 0, &body_len);
|
|
Packit |
90a5c9 |
if (body_len + (apr_off_t)len > res->req->resp_limit) {
|
|
Packit |
90a5c9 |
return 0; /* signal curl failure */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
rv = apr_brigade_write(res->body, NULL, NULL, (const char *)data, blen);
|
|
Packit |
90a5c9 |
if (rv != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* returning anything != blen will make CURL fail this */
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return blen;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static size_t header_cb(void *buffer, size_t elen, size_t nmemb, void *baton)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
md_http_response_t *res = baton;
|
|
Packit |
90a5c9 |
size_t len, clen = elen * nmemb;
|
|
Packit |
90a5c9 |
const char *name = NULL, *value = "", *b = buffer;
|
|
Packit |
90a5c9 |
apr_size_t i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
len = (clen && b[clen-1] == '\n')? clen-1 : clen;
|
|
Packit |
90a5c9 |
len = (len && b[len-1] == '\r')? len-1 : len;
|
|
Packit |
90a5c9 |
for (i = 0; i < len; ++i) {
|
|
Packit |
90a5c9 |
if (b[i] == ':') {
|
|
Packit |
90a5c9 |
name = apr_pstrndup(res->req->pool, b, i);
|
|
Packit |
90a5c9 |
++i;
|
|
Packit |
90a5c9 |
while (i < len && b[i] == ' ') {
|
|
Packit |
90a5c9 |
++i;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (i < len) {
|
|
Packit |
90a5c9 |
value = apr_pstrndup(res->req->pool, b+i, len - i);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (name != NULL) {
|
|
Packit |
90a5c9 |
apr_table_add(res->headers, name, value);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return clen;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t curl_init(md_http_request_t *req)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
CURL *curl = curl_easy_init();
|
|
Packit |
90a5c9 |
if (!curl) {
|
|
Packit |
90a5c9 |
return APR_EGENERAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_cb);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_HEADERDATA, NULL);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_READFUNCTION, req_data_cb);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_READDATA, NULL);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, resp_data_cb);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
req->internals = curl;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
md_http_request_t *req;
|
|
Packit |
90a5c9 |
struct curl_slist *hdrs;
|
|
Packit |
90a5c9 |
apr_status_t rv;
|
|
Packit |
90a5c9 |
} curlify_hdrs_ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int curlify_headers(void *baton, const char *key, const char *value)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
curlify_hdrs_ctx *ctx = baton;
|
|
Packit |
90a5c9 |
const char *s;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (strchr(key, '\r') || strchr(key, '\n')
|
|
Packit |
90a5c9 |
|| strchr(value, '\r') || strchr(value, '\n')) {
|
|
Packit |
90a5c9 |
ctx->rv = APR_EINVAL;
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
s = apr_psprintf(ctx->req->pool, "%s: %s", key, value);
|
|
Packit |
90a5c9 |
ctx->hdrs = curl_slist_append(ctx->hdrs, s);
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t curl_perform(md_http_request_t *req)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
CURLcode curle;
|
|
Packit |
90a5c9 |
md_http_response_t *res;
|
|
Packit |
90a5c9 |
CURL *curl;
|
|
Packit |
90a5c9 |
struct curl_slist *req_hdrs = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = curl_init(req);
|
|
Packit |
90a5c9 |
curl = req->internals;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
res = apr_pcalloc(req->pool, sizeof(*res));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
res->req = req;
|
|
Packit |
90a5c9 |
res->rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
res->status = 400;
|
|
Packit |
90a5c9 |
res->headers = apr_table_make(req->pool, 5);
|
|
Packit |
90a5c9 |
res->body = apr_brigade_create(req->pool, req->bucket_alloc);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_URL, req->url);
|
|
Packit |
90a5c9 |
if (!apr_strnatcasecmp("GET", req->method)) {
|
|
Packit |
90a5c9 |
/* nop */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!apr_strnatcasecmp("HEAD", req->method)) {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!apr_strnatcasecmp("POST", req->method)) {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, req->method);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_HEADERDATA, res);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_READDATA, req->body);
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_WRITEDATA, res);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (req->user_agent) {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_USERAGENT, req->user_agent);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (req->proxy_url) {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_PROXY, req->proxy_url);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!apr_is_empty_table(req->headers)) {
|
|
Packit |
90a5c9 |
curlify_hdrs_ctx ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ctx.req = req;
|
|
Packit |
90a5c9 |
ctx.hdrs = NULL;
|
|
Packit |
90a5c9 |
ctx.rv = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_table_do(curlify_headers, &ctx, req->headers, NULL);
|
|
Packit |
90a5c9 |
req_hdrs = ctx.hdrs;
|
|
Packit |
90a5c9 |
if (ctx.rv == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_hdrs);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, req->pool,
|
|
Packit |
90a5c9 |
"request %ld --> %s %s", req->id, req->method, req->url);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (md_log_is_level(req->pool, MD_LOG_TRACE3)) {
|
|
Packit |
90a5c9 |
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
curle = curl_easy_perform(curl);
|
|
Packit |
90a5c9 |
res->rv = curl_status(curle);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == res->rv) {
|
|
Packit |
90a5c9 |
long l;
|
|
Packit |
90a5c9 |
res->rv = curl_status(curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &l);;
|
|
Packit |
90a5c9 |
if (APR_SUCCESS == res->rv) {
|
|
Packit |
90a5c9 |
res->status = (int)l;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, res->rv, req->pool,
|
|
Packit |
90a5c9 |
"request %ld <-- %d", req->id, res->status);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, res->rv, req->pool,
|
|
Packit |
90a5c9 |
"request %ld failed(%d): %s", req->id, curle,
|
|
Packit |
90a5c9 |
curl_easy_strerror(curle));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (req->cb) {
|
|
Packit |
90a5c9 |
res->rv = req->cb(res);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = res->rv;
|
|
Packit |
90a5c9 |
md_http_req_destroy(req);
|
|
Packit |
90a5c9 |
if (req_hdrs) {
|
|
Packit |
90a5c9 |
curl_slist_free_all(req_hdrs);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int initialized;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t md_curl_init(void) {
|
|
Packit |
90a5c9 |
if (!initialized) {
|
|
Packit |
90a5c9 |
initialized = 1;
|
|
Packit |
90a5c9 |
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void curl_req_cleanup(md_http_request_t *req)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (req->internals) {
|
|
Packit |
90a5c9 |
curl_easy_cleanup(req->internals);
|
|
Packit |
90a5c9 |
req->internals = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static md_http_impl_t impl = {
|
|
Packit |
90a5c9 |
md_curl_init,
|
|
Packit |
90a5c9 |
curl_req_cleanup,
|
|
Packit |
90a5c9 |
curl_perform
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
md_http_impl_t * md_curl_get_impl(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* trigger early global curl init, before we are down a rabbit hole */
|
|
Packit |
90a5c9 |
(void)p;
|
|
Packit |
90a5c9 |
md_curl_init();
|
|
Packit |
90a5c9 |
return &impl;
|
|
Packit |
90a5c9 |
}
|