Commits

Bryan Fink  committed c510338

stickynotes proxying to a jiak resource running on riak cluster, instead of running a jiak resource that connects to the riak cluster

  • Participants
  • Parent commits 5f2baae

Comments (0)

Files changed (10)

File demo/stickynotes/deps/riak

-../../../

File demo/stickynotes/deps/webmachine

+../../../deps/webmachine

File demo/stickynotes/riak-config.erlenv

 {riak_hostname, "127.0.0.1"}.
 {add_paths, ["demo/stickynotes/ebin"]}.
 
+{jiak_name, "jiak"}.
+{riak_web_ip, "127.0.0.1"}.
+{riak_web_port, 9000}.
+{riak_web_logdir, "priv/weblogs"}.
+{jiak_buckets, [notes, groups]}. %% just to get atoms loaded in node
+

File demo/stickynotes/src/jiak_proxy.erl

+%% @author Bryan Fink
+%% @doc jiak_proxy is intended to be a simple webmachine resource
+%%      for proxying Webmachine requests to Jiak.
+%%
+%%      Note: this is not a production-quality proxy resource.
+%%      For something that will be used in a real application, it
+%%      is recommended that ibrowse be used instead of the inets
+%%      http client.
+-module(jiak_proxy).
+-export([init/1,
+         service_available/2]).
+-include_lib("webmachine/include/webmachine.hrl").
+
+init(Config) -> {ok, Config}.
+
+%% request to jiak is made in service_available, such that
+%% if jiak isn't up, we return 503 Service Unavailable, as expected
+service_available(RP, C={_ExternalPath, JiakPath}) ->
+    %% point path at jiak server
+    Path = lists:append(
+             [JiakPath,
+              wrq:disp_path(RP),
+              case wrq:req_qs(RP) of
+                  [] -> [];
+                  Qs -> [$?|mochiweb_util:urlencode(Qs)]
+              end]),
+
+    %% translate webmachine details to ibrowse details
+    Headers = clean_request_headers(
+                mochiweb_headers:to_list(wrq:req_headers(RP))),
+    Method = wm_to_http_method(wrq:method(RP)),
+    Req = case wrq:req_body(RP) of
+              Empty when Empty==undefined;
+                         Empty==[];
+                         Empty==<<>> ->
+                  {Path, Headers};
+              B ->
+                  {Path, Headers,
+                   wrq:get_req_header("content-type", RP), B}
+          end,
+    case http:request(Method, Req, [{autoredirect, false}], []) of
+        {ok, {{_, Status, _}, JiakHeaders, RespBody}} ->
+            RespHeaders = fix_location(JiakHeaders, C),
+
+            %% stop resource processing here and return whatever
+            %% jiak wanted to return
+            {{halt, Status},
+             wrq:set_resp_headers(RespHeaders,
+                                  wrq:set_resp_body(RespBody, RP)),
+             C};
+        _ ->
+            {false, RP, C}
+    end.
+
+%% ibrowse will recalculate Host and Content-Length headers,
+%% and will muck them up if they're manually specified
+clean_request_headers(Headers) ->
+    [{if is_atom(K) -> atom_to_list(K);
+         true -> K end,
+      V} || {K,V} <- Headers, K /= 'Host', K /= 'Content-Length'].
+
+%% webmachine expresses method as all-caps string or atom,
+%% while ibrowse uses all-lowercase atom
+wm_to_http_method(Method) when is_list(Method) ->
+    list_to_atom(string:to_lower(Method));
+wm_to_http_method(Method) when is_atom(Method) ->
+    wm_to_http_method(atom_to_list(Method)).
+
+%% jiak returns a fully-qualified URI in Location -
+%% hack off the jiak host, and drop in this proxy host
+fix_location([], _) -> [];
+fix_location([{"Location", JiakDataPath}|Rest],
+             {ExternalPath, JiakPath}) ->
+    DataPath = lists:nthtail(length(JiakPath), JiakDataPath),
+    [{"Location", ExternalPath++DataPath}|Rest];
+fix_location([H|T], C) ->
+    [H|fix_location(T, C)].

File demo/stickynotes/src/notes.erl

 after_write(_Key, JiakObject, ReqData, Context) ->
     spawn(fun() ->
                   [[_, GroupKey, _]] = jiak_object:links(JiakObject, groups),
-                  {ok, C} = jiak:client_connect(
-                              stickynotes:get_app_env(riak_ip),
-                              stickynotes:get_app_env(riak_port),
-                              stickynotes:get_app_env(riak_cookie)),
+                  {ok, C} = jiak:local_client(),
                   {ok, G} = C:get(groups, GroupKey, 2),
                   Key = Context:get_prop(key),
                   C:put(jiak_object:add_link(G, notes, Key, <<"note">>), 2)

File demo/stickynotes/src/stickynotes.app

   ]},
   {registered, []},
   {mod, {stickynotes_app, []}},
-  {env, [{riak_ip, "127.0.0.1"},
-         {riak_port, 9000},
-         {riak_cookie, stickynotes_cookie}]},
+  {env, []},
   {applications, [kernel, stdlib, crypto]}]}.

File demo/stickynotes/src/stickynotes.erl

     stickynotes_deps:ensure(),
     ensure_started(crypto),
     ensure_started(webmachine),
+    ensure_started(inets),
+    http:set_option(max_keep_alive_length, 0),
     application:start(stickynotes).
 
 %% @spec stop() -> ok
 %% @doc Stop the stickynotes server.
 stop() ->
     Res = application:stop(stickynotes),
+    application:stop(inets),
     application:stop(webmachine),
     application:stop(crypto),
     Res.

File demo/stickynotes/src/stickynotes_sup.erl

 %% @spec init([]) -> SupervisorTree
 %% @doc supervisor callback.
 init([]) ->
-    setup_buckets(),
     Ip = case os:getenv("WEBMACHINE_IP") of false -> "0.0.0.0"; Any -> Any end,   
-    JiakOptions = [{jiak_name, "jiak"},
-                   {jiak_buckets, [notes,groups]}
-                   |[{K, stickynotes:get_app_env(K)}
-                     || K <- [riak_ip, riak_port, riak_cookie]]],
-    Dispatch = [{["jiak",bucket], jiak_resource,
-                 [{key_type, container}|JiakOptions]},
-                {["jiak",bucket,key], jiak_resource,
-                 [{key_type, item}|JiakOptions]},
-                {["jiak",bucket,key,'*'], jaywalker_resource,
-                 JiakOptions},
+    {ok, RiakConfig} = file:consult("riak-config.erlenv"),
+    External = lists:flatten(
+                 io_lib:format("http://~s:~b/jiak/", [Ip, 8000])),
+    Internal = lists:flatten(
+                 io_lib:format("http://~s:~b/jiak/",
+                               [proplists:get_value(riak_web_ip, RiakConfig),
+                                proplists:get_value(riak_web_port, RiakConfig)])),
+    Dispatch = [{["jiak",'*'], jiak_proxy, {External, Internal}},
                 {['*'], stickynotes_resource, ["priv/www/"]}],
     WebConfig = [
 		 {ip, Ip},
 	   permanent, 5000, worker, dynamic},
     Processes = [Web],
     {ok, {{one_for_one, 10, 10}, Processes}}.
-
-%% @spec setup_buckets() -> ok
-%% @doc notes and groups buckets need to have their linkfuns set
-%%      before Jiak link walking will work properly on them
-setup_buckets() ->
-    case jiak:client_connect(
-           stickynotes:get_app_env(riak_ip),
-           stickynotes:get_app_env(riak_port),
-           stickynotes:get_app_env(riak_cookie)) of
-        {ok, C} ->
-            C:set_bucket(notes, jiak:default_jiak_bucket_props()),
-            C:set_bucket(groups, jiak:default_jiak_bucket_props());
-        Error ->
-            error_logger:error_msg(
-              "Unable to connect to riak cluster at ~p:~p~n"
-              "Error: ~p", [Error])
-    end.

File demo/stickynotes/start-dev.sh

 #!/bin/sh
 cd `dirname $0`
-exec erl -name stickynotes@127.0.0.1 -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin $PWD/deps/*/deps/*/deps/*/ebin -boot start_sasl -s reloader -s stickynotes
+exec erl -name stickynotes@127.0.0.1 -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s reloader -s stickynotes

File demo/stickynotes/start.sh

 #!/bin/sh
 cd `dirname $0`
-exec erl -name stickynotes@127.0.0.1 -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s stickynotes
+exec erl -name stickynotes@127.0.0.1 -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s stickynotes