Commit ac887eed authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

CURLOPT_PROXYHEADER: set headers for proxy-only

Includes docs and new test cases: 1525, 1526 and 1527

Co-written-by: Vijay Panghal
parent 42937f87
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -1541,6 +1541,33 @@ Pass a NULL to this to reset back to no custom headers.

The most commonly replaced headers have "shortcuts" in the options
\fICURLOPT_COOKIE\fP, \fICURLOPT_USERAGENT\fP and \fICURLOPT_REFERER\fP.

Starting in 7.36.0, libcurl offers an alternative option that sets or replaces
headers only for requests that are sent to a proxy:
\fICURLOPT_PROXYHEADER\fP. If \fICURLOPT_PROXYHEADER\fP is not used at all by
an application, the \fICURLOPT_HTTPHEADER headers\fP will be used for proxy
requests as well!
.IP CURLOPT_PROXYHEADER
Pass a pointer to a linked list of HTTP headers to pass in your HTTP request
sent to a proxy. The rules for this list is identical to the
\fICURLOPT_HTTPHEADER\fP option's.

The headers set with this option is only ever used in requests sent to a
proxy.

As a special quirk to stay backwards compatible with the libcurl versions
released before this option existed, all headers set with
\fICURLOPT_HTTPHEADER\fP will also be used for proxies unless you set one or
more headers (or even just NULL) with \fICURLOPT_PROXYHEADER\fP.

The first line in a request (containing the method, usually a GET or POST) is
not a header and cannot be replaced using this option. Only the lines
following the request-line are headers. Adding this method line in this list
of headers will only cause your request to send an invalid header.

Pass a NULL to this to reset back to no custom headers.

This option was added in libcurl 7.36.0.
.IP CURLOPT_HTTP200ALIASES
Pass a pointer to a linked list of aliases to be treated as valid HTTP 200
responses.  Some servers respond with a custom header response line.  For
+1 −0
Original line number Diff line number Diff line
@@ -439,6 +439,7 @@ CURLOPT_PROGRESSFUNCTION 7.1 7.32.0
CURLOPT_PROTOCOLS               7.19.4
CURLOPT_PROXY                   7.1
CURLOPT_PROXYAUTH               7.10.7
CURLOPT_PROXYHEADER             7.36.0
CURLOPT_PROXYPASSWORD           7.19.1
CURLOPT_PROXYPORT               7.1
CURLOPT_PROXYTYPE               7.10
+7 −2
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -903,7 +903,8 @@ typedef enum {
  /* Set cookie in request: */
  CINIT(COOKIE, OBJECTPOINT, 22),

  /* This points to a linked list of headers, struct curl_slist kind */
  /* This points to a linked list of headers, struct curl_slist kind. This
     list is also used for RTSP (in spite of its name) */
  CINIT(HTTPHEADER, OBJECTPOINT, 23),

  /* This points to a linked list of post entries, struct curl_httppost */
@@ -1581,6 +1582,10 @@ typedef enum {
   * Expect: 100-continue header before sending the data anyway. */
  CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227),

  /* This points to a linked list of headers used for proxy requests only,
     struct curl_slist kind */
  CINIT(PROXYHEADER, OBJECTPOINT, 228),

  CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

+58 −26
Original line number Diff line number Diff line
@@ -169,10 +169,12 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
 *
 * Returns a pointer to the first matching header or NULL if none matched.
 */
char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader)
char *Curl_checkheaders(const struct connectdata *conn,
                        const char *thisheader)
{
  struct curl_slist *head;
  size_t thislen = strlen(thisheader);
  struct SessionHandle *data = conn->data;

  for(head = data->set.headers;head; head=head->next) {
    if(Curl_raw_nequal(head->data, thisheader, thislen))
@@ -181,6 +183,32 @@ char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader)
  return NULL;
}

/*
 * checkProxyHeaders() checks the linked list of custom proxy headers
 * if proxy headers are not available, then it will lookup into http header
 * link list
 *
 * It takes a connectdata struct as input instead of the SessionHandle simply
 * to know if this is a proxy request or not, as it then might check a
 * different header list.
 *
 */
char *Curl_checkProxyheaders(const struct connectdata *conn,
                             const char *thisheader)
{
  struct curl_slist *head;
  size_t thislen = strlen(thisheader);
  struct SessionHandle *data = conn->data;

  for(head = (conn->bits.proxy && data->set.proxyheaders)?
        data->set.proxyheaders:data->set.headers;
      head; head=head->next) {
    if(Curl_raw_nequal(head->data, thisheader, thislen))
      return head->data;
  }
  return NULL;
}

/*
 * Strip off leading and trailing whitespace from the value in the
 * given HTTP header line and return a strdupped copy. Returns NULL in
@@ -584,9 +612,9 @@ output_auth_headers(struct connectdata *conn,
  if(authstatus->picked == CURLAUTH_BASIC) {
    /* Basic */
    if((proxy && conn->bits.proxy_user_passwd &&
       !Curl_checkheaders(data, "Proxy-authorization:")) ||
        !Curl_checkProxyheaders(conn, "Proxy-authorization:")) ||
       (!proxy && conn->bits.user_passwd &&
       !Curl_checkheaders(data, "Authorization:"))) {
        !Curl_checkheaders(conn, "Authorization:"))) {
      auth="Basic";
      result = http_output_basic(conn, proxy);
      if(result)
@@ -1501,7 +1529,7 @@ static CURLcode expect100(struct SessionHandle *data,
    /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
       100-continue to the headers which actually speeds up post operations
       (as there is one packet coming back from the web server) */
    ptr = Curl_checkheaders(data, "Expect:");
    ptr = Curl_checkheaders(conn, "Expect:");
    if(ptr) {
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
@@ -1517,10 +1545,13 @@ static CURLcode expect100(struct SessionHandle *data,
}

CURLcode Curl_add_custom_headers(struct connectdata *conn,
                                 bool is_proxy,
                                 Curl_send_buffer *req_buffer)
{
  char *ptr;
  struct curl_slist *headers=conn->data->set.headers;
  struct curl_slist *headers=
    (is_proxy && conn->data->set.proxyheaders)?
    conn->data->set.proxyheaders:conn->data->set.headers;

  while(headers) {
    ptr = strchr(headers->data, ':');
@@ -1736,7 +1767,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     it might have been used in the proxy connect, but if we have got a header
     with the user-agent string specified, we erase the previously made string
     here. */
  if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
  if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
    free(conn->allocptr.uagent);
    conn->allocptr.uagent=NULL;
  }
@@ -1757,7 +1788,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    conn->bits.authneg = FALSE;

  Curl_safefree(conn->allocptr.ref);
  if(data->change.referer && !Curl_checkheaders(data, "Referer:")) {
  if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) {
    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
    if(!conn->allocptr.ref)
      return CURLE_OUT_OF_MEMORY;
@@ -1765,10 +1796,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
  else
    conn->allocptr.ref = NULL;

  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:"))
  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:"))
    addcookies = data->set.str[STRING_COOKIE];

  if(!Curl_checkheaders(data, "Accept-Encoding:") &&
  if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
     data->set.str[STRING_ENCODING]) {
    Curl_safefree(conn->allocptr.accept_encoding);
    conn->allocptr.accept_encoding =
@@ -1780,13 +1811,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
#ifdef HAVE_LIBZ
  /* we only consider transfer-encoding magic if libz support is built-in */

  if(!Curl_checkheaders(data, "TE:") && data->set.http_transfer_encoding) {
  if(!Curl_checkheaders(conn, "TE:") &&
     data->set.http_transfer_encoding) {
    /* When we are to insert a TE: header in the request, we must also insert
       TE in a Connection: header, so we need to merge the custom provided
       Connection: header and prevent the original to get sent. Note that if
       the user has inserted his/hers own TE: header we don't do this magic
       but then assume that the user will handle it all! */
    char *cptr = Curl_checkheaders(data, "Connection:");
    char *cptr = Curl_checkheaders(conn, "Connection:");
#define TE_HEADER "TE: gzip\r\n"

    Curl_safefree(conn->allocptr.te);
@@ -1804,7 +1836,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    /* In HTTP2 forbids Transfer-Encoding: chunked */
    ptr = NULL;
  else {
    ptr = Curl_checkheaders(data, "Transfer-Encoding:");
    ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
    if(ptr) {
      /* Some kind of TE is requested, check if 'chunked' is chosen */
      data->req.upload_chunky =
@@ -1838,7 +1870,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)

  Curl_safefree(conn->allocptr.host);

  ptr = Curl_checkheaders(data, "Host:");
  ptr = Curl_checkheaders(conn, "Host:");
  if(ptr && (!data->state.this_is_a_follow ||
             Curl_raw_equal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
@@ -1983,13 +2015,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    /* we must build the whole post sequence first, so that we have a size of
       the whole transfer before we start to send it */
    result = Curl_getformdata(data, &http->sendit, data->set.httppost,
                              Curl_checkheaders(data, "Content-Type:"),
                              Curl_checkheaders(conn, "Content-Type:"),
                              &http->postsize);
    if(result)
      return result;
  }

  http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
  http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n";

  if(( (HTTPREQ_POST == httpreq) ||
       (HTTPREQ_POST_FORM == httpreq) ||
@@ -2069,7 +2101,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     * ones if any such are specified.
     */
    if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
       !Curl_checkheaders(data, "Range:")) {
       !Curl_checkheaders(conn, "Range:")) {
      /* if a line like this was already allocated, free the previous one */
      if(conn->allocptr.rangeline)
        free(conn->allocptr.rangeline);
@@ -2077,7 +2109,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
                                         data->state.range);
    }
    else if((httpreq != HTTPREQ_GET) &&
            !Curl_checkheaders(data, "Content-Range:")) {
            !Curl_checkheaders(conn, "Content-Range:")) {

      /* if a line like this was already allocated, free the previous one */
      if(conn->allocptr.rangeline)
@@ -2179,7 +2211,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
                     conn->allocptr.ref:"" /* Referer: <data> */,
                     (conn->bits.httpproxy &&
                      !conn->bits.tunnel_proxy &&
                      !Curl_checkheaders(data, "Proxy-Connection:"))?
                      !Curl_checkProxyheaders(conn, "Proxy-Connection:"))?
                     "Proxy-Connection: Keep-Alive\r\n":"",
                     te
      );
@@ -2264,7 +2296,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
      return result;
  }

  result = Curl_add_custom_headers(conn, req_buffer);
  result = Curl_add_custom_headers(conn, FALSE, req_buffer);
  if(result)
    return result;

@@ -2314,7 +2346,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    http->sending = HTTPSEND_BODY;

    if(!data->req.upload_chunky &&
       !Curl_checkheaders(data, "Content-Length:")) {
       !Curl_checkheaders(conn, "Content-Length:")) {
      /* only add Content-Length if not uploading chunked */
      result = Curl_add_bufferf(req_buffer,
                                "Content-Length: %" CURL_FORMAT_CURL_OFF_T
@@ -2386,7 +2418,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
      postsize = data->set.infilesize;

    if((postsize != -1) && !data->req.upload_chunky &&
       !Curl_checkheaders(data, "Content-Length:")) {
       !Curl_checkheaders(conn, "Content-Length:")) {
      /* only add Content-Length if not uploading chunked */
      result = Curl_add_bufferf(req_buffer,
                                "Content-Length: %" CURL_FORMAT_CURL_OFF_T
@@ -2438,7 +2470,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       we don't upload data chunked, as RFC2616 forbids us to set both
       kinds of headers (Transfer-Encoding: chunked and Content-Length) */
    if((postsize != -1) && !data->req.upload_chunky &&
       !Curl_checkheaders(data, "Content-Length:")) {
       !Curl_checkheaders(conn, "Content-Length:")) {
      /* we allow replacing this header if not during auth negotiation,
         although it isn't very wise to actually set your own */
      result = Curl_add_bufferf(req_buffer,
@@ -2448,7 +2480,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
        return result;
    }

    if(!Curl_checkheaders(data, "Content-Type:")) {
    if(!Curl_checkheaders(conn, "Content-Type:")) {
      result = Curl_add_bufferf(req_buffer,
                                "Content-Type: application/"
                                "x-www-form-urlencoded\r\n");
@@ -2460,7 +2492,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       the somewhat bigger ones we allow the app to disable it. Just make
       sure that the expect100header is always set to the preferred value
       here. */
    ptr = Curl_checkheaders(data, "Expect:");
    ptr = Curl_checkheaders(conn, "Expect:");
    if(ptr) {
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
+7 −2
Original line number Diff line number Diff line
@@ -39,9 +39,13 @@ extern const struct Curl_handler Curl_handler_https;
bool Curl_compareheader(const char *headerline,  /* line to check */
                        const char *header,   /* header keyword _with_ colon */
                        const char *content); /* content string to find */
char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader);

char *Curl_checkheaders(const struct connectdata *conn,
                        const char *thisheader);
char *Curl_copy_header_value(const char *header);

char *Curl_checkProxyheaders(const struct connectdata *conn,
                             const char *thisheader);
/* ------------------------------------------------------------------------- */
/*
 * The add_buffer series of functions are used to build one large memory chunk
@@ -67,6 +71,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
CURLcode Curl_add_timecondition(struct SessionHandle *data,
                                Curl_send_buffer *buf);
CURLcode Curl_add_custom_headers(struct connectdata *conn,
                                 bool is_connect,
                                 Curl_send_buffer *req_buffer);

/* protocol-specific functions set up to be called by the main engine */
Loading