Roxen.git / server / etc / modules / HTTPClient.pmod

version» Context lines:

Roxen.git/server/etc/modules/HTTPClient.pmod:30:   //! });   //!   //! args->on_failure(lambda (HTTPClient.Result resp) {   //! werror("Failed request for %O (%d %s)\n", resp->url,   //! resp->status, resp->status_description);   //! });   //!   //! HTTPClient.async_get("http://domain.com", args);   //! @endcode    - #define HTTP_CLIENT_DEBUG + // #define HTTP_CLIENT_DEBUG      #ifdef HTTP_CLIENT_DEBUG   # define TRACE(X...)werror("%s:%d: %s",basename(__FILE__),__LINE__,sprintf(X))   #else   # define TRACE(X...)0   #endif      protected constant DEFAULT_MAXTIME = 60;      
Roxen.git/server/etc/modules/HTTPClient.pmod:146:    }    };       qr = s->async_do_method_url(http_method, uri,    args->variables,    args->data,    args->headers,    0, // headers received callback    cb, // ok callback    cb, // fail callback -  ({})); +  args->extra_args || ({}));       if (!query_has_maxtime()) { -  TRACE("No maxtime in Protocols.HTTP.Query. Set external max timeout\n"); +  TRACE("No maxtime in Protocols.HTTP.Query. Set external max timeout: %O\n", +  s->maxtime || DEFAULT_MAXTIME);    co_maxtime = call_out(lambda () {    TRACE("Timeout callback: %O\n", qr);       res = Failure(([    "status" : 504,    "status_desc" : "Gateway timeout",    "host" : qr->con->host,    "headers" : qr->con->headers,    "url" : qr->url_requested    ]));
Roxen.git/server/etc/modules/HTTPClient.pmod:241:       //! POST data    void|string|mapping data;       //! Callback to call on successful async request    function(Result:void) on_success;       //! Callback to call on failed async request    function(Result:void) on_failure;    +  //! Extra arguments that will end up in the @[Result] object +  array(mixed) extra_args; +     //! If @[args] is given the indices that match any of this object's    //! members will set those object members to the value of the    //! corresponding mapping member.    protected void create(void|mapping(string:mixed) args)    {    if (args) { -  foreach (args; string key; mixed val) { +  foreach (args; string key; mixed value) {    if (has_index(this, key)) { -  this[key] = val; +  if ((< "variables", "headers" >)[key]) { +  // Cast all values to string +  value = mkmapping(indices(value), map(values(value), +  lambda (mixed s) { +  return (string) s; +  }));    } -  +  +  this[key] = value;    } -  +  else { +  error("Unknown argument %O!\n", key);    }    }    } -  +  } + }         //! HTTP result class. Consider internal.   //!   //! @seealso   //! @[Success], @[Failure]   class Result   {    //! Internal result mapping    protected mapping result;
Roxen.git/server/etc/modules/HTTPClient.pmod:300:    {    return result->status_desc;    }       //! Returns the requested URL    public string `url()    {    return result->url;    }    +  //! Extra arguments set in the @[Arguments] object. +  public array(mixed) `extra_args() +  { +  return result->extra_args; +  } +     //! @ignore    protected void create(mapping _result)    {    //TRACE("this_program(%O)\n", _result->headers);    result = _result;    }    //! @endignore   }      
Roxen.git/server/etc/modules/HTTPClient.pmod:322:   //! @[Concurrent.Future()->on_success()] callback registered on the returned   //! @[Concurrent.Future] object from @[get_url()], @[post_url()],   //! @[delete_url()], @[put_url()] or @[do_method()].   class Success   {    inherit Result;       public bool `ok() { return true; }       //! The response body, i.e the content of the requested URL -  public string `data() { return result->data; } +  public string `data() +  { +  string data = result->data;    -  +  if (content_encoding && content_encoding == "gzip") { +  data = Gz.uncompress(data[10..<8], true); +  } +  +  return data; +  } +     //! Returns the value of the @tt{content-length@} header.    public int `length()    {    return headers && (int)headers["content-length"];    }       //! Returns the content type of the requested document    public string `content_type()    {    if (string ct = (headers && headers["content-type"])) {    sscanf (ct, "%s;", ct);    return ct;    }    }    -  //! Returns the content encoding of the requested document, if given by the +  //! Returns the charset of the requested document, if given by the    //! response headers. -  public string `content_encoding() +  public string `charset()    {    if (string ce = (headers && headers["content-type"])) {    if (sscanf (ce, "%*s;%*scharset=%s", ce) == 3) {    if (ce[0] == '"' || ce[0] == '\'') {    ce = ce[1..<1];    }    return ce;    }    }    } -  +  +  //! Returns the content encoding of the response if set by the remote server. +  public string `content_encoding() +  { +  if (string ce = (headers && headers["content-encoding"])) { +  return ce;    } -  +  } + }         //! A class representing a failed request. An instance of   //! this class will be given as argument to the   //! @[Concurrent.Future()->on_failure()] callback registered on the returned   //! @[Concurrent.Future] object from @[get_url()], @[post_url()],   //! @[delete_url()], @[put_url()] or @[do_method()].   class Failure   {    inherit Result;
Roxen.git/server/etc/modules/HTTPClient.pmod:394:    {    if (stringp(url)) {    url = Standards.URI(url);    }       // Due to a bug in Protocols.HTTP.Session which is fixed in Pike 8.1    // but not yet in 8.0. (2016-05-20)    if (!extra_headers || !extra_headers->host || !extra_headers->Host) {    extra_headers = extra_headers || ([]);    -  if (url->scheme == "http" && url->port != 80) { -  extra_headers->host = url->host + ":" + url->port; +  TRACE("Set host in headers: %O\n", url); +  +  if (url->scheme == "http") { +  extra_headers->host = url->host; +  if (url->port != 80) { +  extra_headers->host += ":" + url->port;    } -  else if (url->scheme == "https" && url->port != 443) { -  extra_headers->host = url->host + ":" + url->port; +     } -  +  else if (url->scheme == "https") { +  extra_headers->host = url->host; +  if (url->port != 443) { +  extra_headers->host += ":" + url->port; +  } +  }       if (!sizeof(extra_headers)) {    extra_headers = 0;    }       TRACE("Host header set?: %O\n", extra_headers);    }    -  +  if (upper_case(method) == "POST") { +  TRACE("Got post request: %O, %O, %O, %O\n", +  url, query_variables, data, extra_headers); +  // In Pike < 8.1 the content-length header isn't auotmatically added so +  // we have to take care of that explicitly +  bool has_content_len = false; +  bool has_content_type = false; +  +  if (extra_headers) { +  array(string) lc_headers = map(indices(extra_headers), lower_case); +  +  if (has_value(lc_headers, "content-length")) { +  has_content_len = true; +  } +  +  if (has_value(lc_headers, "content-type")) { +  has_content_type = true; +  } +  } +  +  if (!has_content_len) { +  mapping(string:string) qvars = url->get_query_variables(); +  data = data||""; +  +  if (qvars && sizeof(qvars)) { +  if (!query_variables) { +  query_variables = qvars; +  } +  else { +  query_variables |= qvars; +  } +  } +  +  if (sizeof(data) && query_variables) { +  data += "&" + Protocols.HTTP.http_encode_query(query_variables); +  } +  else if (query_variables) { +  data = Protocols.HTTP.http_encode_query(query_variables); +  } +  +  if (!extra_headers) { +  extra_headers = ([]); +  } +  +  extra_headers["Content-Length"] = (string) sizeof(data); +  +  if (!has_content_type) { +  extra_headers["Content-Type"] = "application/x-www-form-urlencoded"; +  } +  +  query_variables = 0; +  } +  } +  +  TRACE("Request: %O\n", url); +     return ::async_do_method_url(method, url, query_variables, data,    extra_headers, callback_headers_ok,    callback_data_ok, callback_fail,    callback_arguments);    }          class Request    {    inherit parent::Request;    -  +  protected void set_extra_args_in_result(mapping(string:mixed) r) +  { +  if (extra_callback_arguments && sizeof(extra_callback_arguments) > 1) { +  r->extra_args = extra_callback_arguments[1..]; +  } +  } +     protected void async_fail(SessionQuery q)    {    TRACE("fail q: %O -> %O\n", q, ::url_requested);       mapping ret = ([    "status" : q->status,    "status_desc" : q->status_desc,    "host" : q->host,    "headers" : copy_value(q->headers),    "url" : ::url_requested    ]);       TRACE("Ret: %O\n", ret);    -  +  set_extra_args_in_result(ret); +     // clear callbacks for possible garbation of this Request object    con->set_callbacks(0, 0);       function fc = fail_callback;    set_callbacks(0, 0, 0); // drop all references    extra_callback_arguments = 0;       if (fc) {    fc(Failure(ret));    }
Roxen.git/server/etc/modules/HTTPClient.pmod:471:    follow_redirects--;    do_async(prepare_method("GET", loc));    return;    }    }       // clear callbacks for possible garbation of this Request object    con->set_callbacks(0, 0);       if (data_callback) { -  con->timed_async_fetch(async_data, async_fail); // start data downloading +  con->async_fetch(async_data); // start data downloading    }    else {    extra_callback_arguments = 0; // to allow garb    }    }          protected void async_data()    {    mapping ret = ([    "host" : con->host,    "status" : con->status,    "status_desc" : con->status_desc,    "headers" : copy_value(con->headers),    "data" : con->data(),    "url" : ::url_requested    ]);    -  +  set_extra_args_in_result(ret); +     // clear callbacks for possible garbation of this Request object    con->set_callbacks(0, 0);       if (data_callback) {    data_callback(Success(ret));    }       extra_callback_arguments = 0;    }