|
|
|
|
|
|
|
|
|
|
#if defined(SOCIAL_REQUEST_DEBUG) || defined(SOCIAL_REQUEST_DATA_DEBUG) |
# define TRACE(X...) werror("%s:%d: %s",basename(__FILE__),__LINE__,sprintf(X)) |
#else |
# define TRACE(X...) 0 |
#endif |
|
|
constant API_URI = 0; |
|
|
|
|
protected constant ACCESS_TOKEN_PARAM_NAME = "access_token"; |
|
|
|
protected constant DECODE_UTF8 = 0; |
|
|
|
public int(0..1) utf8_decode = DECODE_UTF8; |
|
|
typedef function(mapping,Protocols.HTTP.Query:void) Callback; |
|
|
typedef mapping|Auth.Params ParamsArg; |
|
|
|
|
|
protected Auth.OAuth2.Client _auth; |
|
|
protected constant AuthClass = Auth.OAuth2.Client; |
|
protected mapping(string:string) default_headers = ([ |
"user-agent" : .USER_AGENT |
]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void create(string client_id, string client_secret, |
void|string redirect_uri, |
void|string|array(string)|multiset(string) scope) |
{ |
if (AuthClass) |
_auth = AuthClass(client_id, client_secret, redirect_uri, scope); |
} |
|
|
|
|
|
|
Auth.OAuth2.Client `auth() |
{ |
return _auth; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mapping(string:string|mapping) parse_canonical_url(string url) |
{ |
Standards.URI api_uri = Standards.URI(API_URI); |
Standards.URI this_url; |
|
|
|
|
if (catch(this_url = Standards.URI(url))) { |
this_url = Standards.URI("http://localhost" + url); |
} |
|
mapping params = this_url->get_query_variables() || ([]); |
|
foreach (indices(params), string k) { |
if (!sizeof(params[k])) |
m_delete(params, k); |
} |
|
string path = this_url->path; |
|
if (has_prefix(path, api_uri->path)) |
path -= api_uri->path; |
|
return ([ "path" : path, "params" : params ]); |
} |
|
|
|
|
|
|
|
|
mixed get(string api_method, void|ParamsArg params, void|Callback cb) |
{ |
return call(api_method, params, "GET", 0, cb); |
} |
|
|
|
|
|
|
|
|
|
|
mixed post(string api_method, void|ParamsArg params, void|string data, |
void|Callback cb) |
{ |
return call(api_method, params, "POST", data, cb); |
} |
|
|
|
|
|
|
|
|
mixed delete(string api_method, void|ParamsArg params, void|Callback cb) |
{ |
return call(api_method, params, "DELETE", 0, cb); |
} |
|
|
|
|
|
|
|
|
mixed put(string api_method, void|ParamsArg params, void|Callback cb) |
{ |
return call(api_method, params, "PUT", 0, cb); |
} |
|
|
|
|
|
|
|
|
mixed patch(string api_method, void|ParamsArg params, void|Callback cb) |
{ |
return call(api_method, params, "PATCH", 0, cb); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mixed call(string api_method, void|ParamsArg params, |
void|string http_method, void|string data, void|Callback cb) |
{ |
http_method = upper_case(http_method || "get"); |
Auth.Params p = Auth.Params(); |
p->add_mapping(default_params()); |
|
if (params) p += params; |
|
if (_auth && !_auth->is_expired()) { |
if (string a = _auth->access_token) { |
p += Auth.Param(ACCESS_TOKEN_PARAM_NAME, a); |
} |
} |
|
if (http_method == "POST") { |
if (!data) data = (string) p; |
params = 0; |
} |
else { |
params = (mapping) p; |
} |
|
|
Protocols.HTTP.Query req = Protocols.HTTP.Query(); |
|
#ifdef SOCIAL_REQUEST_DEBUG |
TRACE("\n> Request: %s %s?%s\n", http_method, api_method, (string) p); |
if (data) TRACE("> data: %s\n", data); |
#endif |
|
if (cb) { |
req->set_callbacks( |
lambda (Protocols.HTTP.Query qq, mixed ... args) { |
if (qq->status == 200) { |
cb(handle_response(qq), qq); |
} |
}, |
lambda (Protocols.HTTP.Query qq, mixed ... args) { |
cb(0, qq); |
} |
); |
|
Protocols.HTTP.do_async_method(http_method, api_method, params, |
default_headers, req, data); |
|
|
return 0; |
} |
|
req = Protocols.HTTP.do_method(http_method, api_method, params, |
default_headers, req, data); |
return req && handle_response(req); |
} |
|
private mixed handle_response(Protocols.HTTP.Query req) |
{ |
TRACE("Handle response: %O\n", req); |
|
if ((< 301, 302 >)[req->status]) |
return req->headers; |
|
#ifdef SOCIAL_REQUEST_DATA_DEBUG |
TRACE("Data: [%s]\n\n", req->data()||"(empty)"); |
#endif |
|
if (req->status != 200) { |
string d = req->data(); |
|
TRACE("Bad resp[%d]: %s\n\n%O\n", |
req->status, req->data(), req->headers); |
|
if (has_value(d, "error")) { |
mapping e; |
mixed err = catch { |
e = Standards.JSON.decode(d); |
}; |
|
if (e) { |
if (e->error) |
error("Error %d: %s. ", e->error->code, e->error->message); |
else if (e->meta && e->meta->code) |
error("Error %d: %s. ", e->meta->code, e->meta->error_message); |
} |
|
error("Error: %s", "Unknown"); |
} |
|
error("Bad status (%d) in HTTP response! ", req->status); |
} |
|
if (utf8_decode) { |
TRACE("Decode UTF8: %s\n", req->data()); |
return Standards.JSON.decode_utf8(unescape_forward_slashes(req->data())); |
} |
|
return Standards.JSON.decode(unescape_forward_slashes(req->data())); |
} |
|
|
|
|
string _sprintf(int t) |
{ |
return sprintf("%O(authorized:%O)", this_program, |
(_auth && !!_auth->access_token)); |
} |
|
|
|
|
protected string get_uri(string method) |
{ |
if (has_suffix(API_URI, "/")) { |
if (has_prefix(method, "/")) |
method = method[1..]; |
} |
else { |
if (!has_prefix(method, "/")) |
method = "/" + method; |
} |
|
|
if (search(method, "//") > -1) |
method = replace(method, "//", "/"); |
|
return API_URI + method; |
} |
|
|
|
|
|
protected string get_encoding(mapping h) |
{ |
if (h["content-type"]) { |
sscanf(h["content-type"], "%*scharset=%s", string s); |
return s && lower_case(String.trim_all_whites(s)) || ""; |
} |
|
return ""; |
} |
|
|
protected string unescape_forward_slashes(string s) |
{ |
return replace(s, "\\/", "/"); |
} |
|
|
protected mapping default_params() |
{ |
return ([]); |
} |
|
|
|
class Method |
{ |
|
|
|
|
|
|
protected constant METHOD_PATH = 0; |
|
|
protected void create() |
{ |
if (this_program == WebApi.Api.Method) |
error("This class can not be instantiated directly! "); |
} |
|
|
protected mixed _get(string s, void|ParamsArg p, void|Callback cb); |
|
|
protected mixed _put(string s, void|ParamsArg p, void|Callback cb); |
|
|
protected mixed _post(string s, void|ParamsArg p, void|string data, |
void|Callback cb); |
|
|
protected mixed _delete(string s, void|ParamsArg p, void|Callback cb); |
|
|
protected mixed _patch(string s, void|ParamsArg p, void|Callback cb); |
} |
|
|