Permalink
Fetching contributors…
Cannot retrieve contributors at this time
114 lines (108 sloc) 4.63 KB
%% JSON-RPC for Cowboy
%%---------------------------------------------------------------------------
%% @author Erik Timan <dev@timan.info>
%% @author Tony Garnock-Jones <tonygarnockjones@gmail.com>
%% @author LShift Ltd. <query@lshift.net>
%% @copyright 2007-2010, 2011, 2012 Tony Garnock-Jones and 2007-2010 LShift Ltd.
%% @license
%%
%% Permission is hereby granted, free of charge, to any person
%% obtaining a copy of this software and associated documentation
%% files (the "Software"), to deal in the Software without
%% restriction, including without limitation the rights to use, copy,
%% modify, merge, publish, distribute, sublicense, and/or sell copies
%% of the Software, and to permit persons to whom the Software is
%% furnished to do so, subject to the following conditions:
%%
%% The above copyright notice and this permission notice shall be
%% included in all copies or substantial portions of the Software.
%%
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
%% SOFTWARE.
%%---------------------------------------------------------------------------
%%
%% @reference the <a href="http://github.com/extend/cowboy/">Cowboy github page</a>
%%
%% @doc Support for serving JSON-RPC via Cowboy.
%%
%% Familiarity with writing Cowboy applications is assumed.
%%
%% == Basic Usage ==
%%
%% <ul>
%% <li>Register your JSON-RPC services as usual.</li>
%% <li>Decide on your `AliasPrefix' (see {@link rfc4627_jsonrpc_http:invoke_service_method/4}).</li>
%% <li>When a Cowboy request arrives at your application, call {@link handle/2} with your `AliasPrefix' and the request.</li>
%% </ul>
%%
%% It's as simple as that - if the request's URI path matches the
%% `AliasPrefix', it will be decoded and the JSON-RPC service it names
%% will be invoked.
-module(rfc4627_jsonrpc_cowboy).
-export([handle/2]).
normalize(X) when is_atom(X) ->
string:to_lower(atom_to_list(X));
normalize(X) when is_binary(X) ->
string:to_lower(binary_to_list(X));
normalize(X) when is_list(X) ->
string:to_lower(X).
%% @spec (string(), #http_req{}) -> no_match | {ok, #http_req{}}
%%
%% @doc If the request matches `AliasPrefix', the corresponding
%% JSON-RPC service is invoked, and an `{ok, #http_req{}}' is returned;
%% otherwise, `no_match' is returned.
%%
%% Call this function from your Cowboy HTTP handler's `handle'
%% function, as follows:
%%
%% ```
%% Req2 = case rfc4627_jsonrpc_cowboy:handle("/rpc", Req) of
%% no_match ->
%% handle_non_jsonrpc_request(Req);
%% {ok, Reponse} ->
%% Response
%% end
%% '''
%%
%% where `handle_non_jsonrpc_request' does the obvious thing for
%% non-JSON-RPC requests.
handle(AliasPrefix, Req) ->
{BinaryPath, _} = cowboy_req:path(Req),
Path = binary_to_list(BinaryPath),
{QSVals, _} = cowboy_req:qs_vals(Req),
QueryObj = {obj, [{binary_to_list(K), V} || {K,V} <- QSVals]},
{Hdrs, _} = cowboy_req:headers(Req),
HeaderObj = {obj, [{normalize(K), V} || {K,V} <- Hdrs]},
{{PeerAddr, _Port}, _} = cowboy_req:peer(Req),
Peer = list_to_binary(inet_parse:ntoa(PeerAddr)),
{Method, _} = cowboy_req:method(Req),
RequestInfo = {obj, [{"http_method", Method},
{"http_query_parameters", QueryObj},
{"http_headers", HeaderObj},
{"remote_peername", Peer},
{"scheme", <<"http">>}]},
{ok, Body, _} = cowboy_req:body(Req),
case rfc4627_jsonrpc_http:invoke_service_method(AliasPrefix,
Path,
RequestInfo,
Body) of
no_match ->
no_match;
{ok, ResultEnc, ResponseInfo} ->
{obj, ResponseHeaderFields} =
rfc4627:get_field(ResponseInfo, "http_headers", {obj, []}),
StatusCode =
rfc4627:get_field(ResponseInfo, "http_status_code", 200),
Headers = [{list_to_binary(K), V} || {K,V} <- ResponseHeaderFields],
RespType = [{<<"Content-Type">>, rfc4627:mime_type()}],
cowboy_req:reply(StatusCode,
Headers ++ RespType,
ResultEnc,
Req)
end.