Skip to content

Commit

Permalink
Merge pull request pramsey#44 from pramsey/setopt
Browse files Browse the repository at this point in the history
Add http_set_curlopt facility for setting strange curl options.
  • Loading branch information
pramsey authored Jun 21, 2017
2 parents 54da5c8 + db2a100 commit 05f4c13
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 21 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,30 @@ By default a 5 second timeout is set for the completion of a request. If a diff

http.timeout_msec = 200

## CURL Options

Select [CURL options](https://curl.haxx.se/libcurl/c/curl_easy_setopt.html) are available to set using the `http_set_curlopt(curlopt VARCHAR, value varchar)` function.

* [CURLOPT_PROXY](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY.html)
* [CURLOPT_PRE_PROXY](https://curl.haxx.se/libcurl/c/CURLOPT_PRE_PROXY.html)
* [CURLOPT_PROXYPORT](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYPORT.html)
* [CURLOPT_PROXYUSERPWD](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERPWD.html)
* [CURLOPT_PROXYUSERNAME](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYUSERNAME.html)
* [CURLOPT_PROXYPASSWORD](https://curl.haxx.se/libcurl/c/CURLOPT_PROXYPASSWORD.html)
* [CURLOPT_TLSAUTH_USERNAME](https://curl.haxx.se/libcurl/c/CURLOPT_TLSAUTH_USERNAME.html)
* [CURLOPT_TLSAUTH_PASSWORD](https://curl.haxx.se/libcurl/c/CURLOPT_TLSAUTH_PASSWORD.html)
* [CURLOPT_PROXY_TLSAUTH_USERNAME](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY_TLSAUTH_USERNAME.html)
* [CURLOPT_PROXY_TLSAUTH_PASSWORD](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY_TLSAUTH_PASSWORD.html)
* [CURLOPT_TLSAUTH_TYPE](https://curl.haxx.se/libcurl/c/CURLOPT_TLSAUTH_TYPE.html)
* [CURLOPT_PROXY_TLSAUTH_TYPE](https://curl.haxx.se/libcurl/c/CURLOPT_PROXY_TLSAUTH_TYPE.html)
* [CURLOPT_CAINFO](https://curl.haxx.se/libcurl/c/CURLOPT_CAINFO.html)

For example,

SELECT http_set_curlopt('CURLOPT_PROXYPORT', '12345');

Will set the proxy port option for the lifetime of the database connection. You can reset all CURL options to their defaults using the `http_reset_curlopt()` function.

## Functions

* `http_header(field VARCHAR, value VARCHAR)` returns `http_header`
Expand All @@ -195,6 +219,8 @@ By default a 5 second timeout is set for the completion of a request. If a diff
* `http_put(uri VARCHAR, content VARCHAR, content_type VARCHAR)` returns `http_response`
* `http_delete(uri VARCHAR)` returns `http_resonse`
* `http_head(uri VARCHAR)` returns `http_resonse`
* `http_set_curlopt(curlopt VARCHAR, value varchar) returns `boolean`
* `http_reset_curlopt() returns `boolean`
* `urlencode(string VARCHAR)` returns `text`

## Installation
Expand Down
10 changes: 10 additions & 0 deletions http--1.2.sql
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ CREATE TYPE http_request AS (
content VARCHAR
);

CREATE OR REPLACE FUNCTION http_set_curlopt (curlopt VARCHAR, value VARCHAR)
RETURNS boolean
AS 'MODULE_PATHNAME', 'http_set_curlopt'
LANGUAGE 'c';

CREATE OR REPLACE FUNCTION http_reset_curlopt ()
RETURNS boolean
AS 'MODULE_PATHNAME', 'http_reset_curlopt'
LANGUAGE 'c';

CREATE OR REPLACE FUNCTION http_header (field VARCHAR, value VARCHAR)
RETURNS http_header
AS $$ SELECT $1, $2 $$
Expand Down
182 changes: 161 additions & 21 deletions http.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
/* Constants */
#define HTTP_VERSION "1.1"
#define HTTP_ENCODING "gzip"
#define CURL_MIN_VERSION 0x071400 /* 7.20.0 */

/* System */
#include <regex.h>
Expand Down Expand Up @@ -97,6 +98,45 @@ enum {
HEADER_VALUE = 1
} http_header_type;

typedef enum {
CURLOPT_STRING,
CURLOPT_LONG
} http_curlopt_type;

/* CURLOPT string/enum value mapping */
typedef struct {
char *curlopt_str;
CURLoption curlopt;
http_curlopt_type curlopt_type;
bool superuser_only;
} http_curlopt;

/* CURLOPT values we allow user to set at run-time */
/* Be careful adding these, as they can be a security risk */
static http_curlopt settable_curlopts[] = {
{ "CURLOPT_CAINFO", CURLOPT_CAINFO, CURLOPT_STRING, false },
#if LIBCURL_VERSION_NUM >= 0x070e01 /* 7.14.1 */
{ "CURLOPT_PROXY", CURLOPT_PROXY, CURLOPT_STRING, false },
{ "CURLOPT_PROXYPORT", CURLOPT_PROXYPORT, CURLOPT_LONG, false },
#endif
#if LIBCURL_VERSION_NUM >= 0x071301 /* 7.19.1 */
{ "CURLOPT_PROXYUSERNAME", CURLOPT_PROXYUSERNAME, CURLOPT_STRING, false },
{ "CURLOPT_PROXYPASSWORD", CURLOPT_PROXYPASSWORD, CURLOPT_STRING, false },
#endif
#if LIBCURL_VERSION_NUM >= 0x071504 /* 7.21.4 */
{ "CURLOPT_TLSAUTH_USERNAME", CURLOPT_TLSAUTH_USERNAME, CURLOPT_STRING, false },
{ "CURLOPT_TLSAUTH_PASSWORD", CURLOPT_TLSAUTH_PASSWORD, CURLOPT_STRING, false },
{ "CURLOPT_TLSAUTH_TYPE", CURLOPT_TLSAUTH_TYPE, CURLOPT_STRING, false },
#endif
#if LIBCURL_VERSION_NUM >= 0x073400 /* 7.52.0 */
{ "CURLOPT_PRE_PROXY", CURLOPT_PRE_PROXY, CURLOPT_STRING, false },
{ "CURLOPT_PROXY_CAINFO", CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPT_STRING, false },
{ "CURLOPT_PROXY_TLSAUTH_USERNAME", CURLOPT_PROXY_TLSAUTH_USERNAME, CURLOPT_STRING, false },
{ "CURLOPT_PROXY_TLSAUTH_PASSWORD", CURLOPT_PROXY_TLSAUTH_PASSWORD, CURLOPT_STRING, false },
{ "CURLOPT_PROXY_TLSAUTH_TYPE", CURLOPT_PROXY_TLSAUTH_TYPE, CURLOPT_STRING, false },
#endif
{ NULL, 0 } /* Array null terminator */
};


/* Function signatures */
Expand Down Expand Up @@ -181,7 +221,7 @@ http_readback(void *buffer, size_t size, size_t nitems, void *instream)
}

static void
http_error(CURLcode err, char *error_buffer)
http_error(CURLcode err, const char *error_buffer)
{
if ( strlen(error_buffer) > 0 )
ereport(ERROR, (errmsg("%s", error_buffer)));
Expand Down Expand Up @@ -463,6 +503,122 @@ header_string_to_array(StringInfo si)
return construct_array(arr_elems, arr_nelems, elem_type, elem_len, elem_byval, elem_align);
}

/* Check/log version info */
static void
http_check_curl_version(const curl_version_info_data *version_info)
{
elog(DEBUG2, "pgsql-http: curl version %s", version_info->version);
elog(DEBUG2, "pgsql-http: curl version number 0x%x", version_info->version_num);
elog(DEBUG2, "pgsql-http: ssl version %s", version_info->ssl_version);

if ( version_info->version_num < CURL_MIN_VERSION )
{
elog(ERROR, "pgsql-http requires Curl version 0.7.20 or higher");
}
}

/* Check/create the global CURL* handle */
static CURL*
http_get_handle()
{
CURL *handle = g_http_handle;

/* Initialize the global handle if needed */
if (!handle)
handle = curl_easy_init();

if (!handle)
ereport(ERROR, (errmsg("Unable to initialize CURL")));

g_http_handle = handle;
return handle;
}


/**
* User-defined Curl option reset.
*/
Datum http_reset_curlopt(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(http_reset_curlopt);
Datum http_reset_curlopt(PG_FUNCTION_ARGS)
{
/* Set up global HTTP handle */
g_http_handle = http_get_handle();
curl_easy_reset(g_http_handle);
PG_RETURN_BOOL(true);
}

/**
* User-defined Curl option handling.
*/
Datum http_set_curlopt(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(http_set_curlopt);
Datum http_set_curlopt(PG_FUNCTION_ARGS)
{
int i = 0;
char *curlopt, *value;
text *curlopt_txt, *value_txt;

/* Version check */
http_check_curl_version(curl_version_info(CURLVERSION_NOW));

/* We cannot handle null arguments */
if ( PG_ARGISNULL(0) || PG_ARGISNULL(1) )
PG_RETURN_BOOL(false);

/* Set up global HTTP handle */
g_http_handle = http_get_handle();

/* Read arguments */
curlopt_txt = PG_GETARG_TEXT_P(0);
value_txt = PG_GETARG_TEXT_P(1);
curlopt = text_to_cstring(curlopt_txt);
value = text_to_cstring(value_txt);

while (1)
{
http_curlopt opt = settable_curlopts[i++];
if (!opt.curlopt_str) /* Terminate at end of array */
break;
if (strcasecmp(opt.curlopt_str, curlopt) == 0)
{
CURLcode err;
char http_error_buffer[CURL_ERROR_SIZE];

/* Argument is a string */
if (opt.curlopt_type == CURLOPT_STRING)
{
err = curl_easy_setopt(g_http_handle, opt.curlopt, value);
elog(DEBUG2, "set '%s' to value '%s', return value = %d", opt.curlopt_str, value, err);
}
/* Argument is a long */
else if (opt.curlopt_type == CURLOPT_LONG)
{
long value_long = strtol(value, NULL, 10);
if ( errno == EINVAL || errno == ERANGE )
elog(ERROR, "invalid integer provided for '%s'", opt.curlopt_str);

err = curl_easy_setopt(g_http_handle, opt.curlopt, value_long);
elog(DEBUG2, "set '%s' to value '%ld', return value = %d", opt.curlopt_str, value_long, err);
}
else
{
elog(ERROR, "invalid curlopt_type");
}

if ( err != CURLE_OK )
{
http_error(err, http_error_buffer);
PG_RETURN_BOOL(false);
}
PG_RETURN_BOOL(true);
}
}
elog(ERROR, "curl option '%s' is not available for run-time configuration", curlopt);
PG_RETURN_BOOL(false);
}


/**
* Master HTTP request function, takes in an http_request tuple and outputs
* an http_response tuple.
Expand Down Expand Up @@ -501,20 +657,8 @@ Datum http_request(PG_FUNCTION_ARGS)
/* Output */
HeapTuple tuple_out;


/* Version check */
curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
elog(DEBUG2, "pgsql-http: curl version %s", version_info->version);
elog(DEBUG2, "pgsql-http: curl version number 0x%x", version_info->version_num);
elog(DEBUG2, "pgsql-http: ssl version %s", version_info->ssl_version);

/* 0x071400 == 7.20.0, each hex digit is for one version level (0xMMmmpp) */
if ( version_info->version_num < 0x071400 )
{
elog(ERROR, "pgsql-http requires Curl version 0.7.20 or higher");
PG_RETURN_NULL();
}

http_check_curl_version(curl_version_info(CURLVERSION_NOW));

/* We cannot handle a null request */
if ( ! PG_ARGISNULL(0) )
Expand Down Expand Up @@ -564,12 +708,8 @@ Datum http_request(PG_FUNCTION_ARGS)
elog(DEBUG2, "pgsql-http: method '%s'", method_str);
pfree(method_str);

/* Initialize the global handle if needed */
if (!g_http_handle)
g_http_handle = curl_easy_init();

if ( !g_http_handle )
ereport(ERROR, (errmsg("Unable to initialize CURL")));
/* Set up global HTTP handle */
g_http_handle = http_get_handle();

/* Set the target URL */
CURL_SETOPT(g_http_handle, CURLOPT_URL, uri);
Expand Down Expand Up @@ -639,7 +779,7 @@ Datum http_request(PG_FUNCTION_ARGS)
if ( method == HTTP_POST || method == HTTP_PUT )
{
text *content_text;
size_t content_size;
long content_size;
char *content_type;
char buffer[1024];

Expand Down

0 comments on commit 05f4c13

Please sign in to comment.