Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,6 @@ When any of the three request functions (`http_get`, `http_post`, `http_delete`)

Once a response is received, it gets stored in the `_http_response` table. By monitoring this table, you can keep track of response statuses and messages.

A request whose headers contain a carriage return or line feed is not sent. It gets an `ERROR` response naming the header, since libcurl terminates headers with CRLF and an embedded one would let the header smuggle extra headers or a body into the request.

> [!IMPORTANT]
> Inserting directly into `net.http_request_queue` won't wake the worker, you must use the request functions. Rows inserted directly are only processed the next time the worker wakes up.
> We do it this way to avoid polling the `net.http_request_queue` table, which would pollute `pg_stat_statements` and cause unnecessary activity from the worker.
Expand Down
30 changes: 3 additions & 27 deletions src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,7 @@ static size_t body_cb(void *contents, size_t size, size_t nmemb, void *userp) {
return realsize;
}

// A header with a CR or LF in it can inject extra headers or a body into the request, since libcurl
// ends every header with CRLF. Such requests are not sent. The message only names the header, the
// value may hold credentials.
static char *crlf_header_rejection(const char *hdr) {
size_t bad = strcspn(hdr, "\r\n");
if (hdr[bad] == '\0') return NULL;

size_t name_len = strcspn(hdr, ":");
if (name_len > bad) name_len = bad;

return psprintf("header \"%.*s\" contains a carriage return or line feed", (int)name_len, hdr);
}

static struct curl_slist *pg_text_array_to_slist(ArrayType *array, struct curl_slist *headers,
char **rejected_reason) {
static struct curl_slist *pg_text_array_to_slist(ArrayType *array, struct curl_slist *headers) {
ArrayIterator iterator;
Datum value;
bool isnull;
Expand All @@ -50,17 +36,7 @@ static struct curl_slist *pg_text_array_to_slist(ArrayType *array, struct curl_s
}

hdr = TextDatumGetCString(value);

char *reason = crlf_header_rejection(hdr);
if (reason) {
if (*rejected_reason == NULL)
*rejected_reason = reason;
else
pfree(reason);
} else {
EREPORT_CURL_SLIST_APPEND(headers, hdr);
}

EREPORT_CURL_SLIST_APPEND(headers, hdr);
pfree(hdr);
}
array_free_iterator(iterator);
Expand Down Expand Up @@ -91,7 +67,7 @@ void init_curl_handle(CurlHandle *handle, RequestQueueRow row) {
ArrayType *pgHeaders = DatumGetArrayTypeP(row.headersBin.value);
struct curl_slist *request_headers = NULL;

request_headers = pg_text_array_to_slist(pgHeaders, request_headers, &handle->rejected_reason);
request_headers = pg_text_array_to_slist(pgHeaders, request_headers);

EREPORT_CURL_SLIST_APPEND(request_headers, "User-Agent: pg_net/" EXTVERSION);

Expand Down
120 changes: 0 additions & 120 deletions test/test_http_header_crlf.py

This file was deleted.

Loading