bryan / wmexamples

Example resources for Webmachine (http://bitbucket.org/justin/webmachine). This project contains several sample webmachine resources that I've posted elsewhere at various times. This is a complete Webmachine app ready to be run and examined.

Clone this repository (size: 42.5 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/bryan/wmexamples/
commit 18: a6ddaedf483e
parent 17: 3796e75618aa
branch: default
tags: tip
point to new ibrowse-1.5 repo on github
Bryan Fink
8 months ago
wmexamples / src / couchdb_proxy.erl
    #   Introduced
1
bd68f4076442
%% @author Bryan Fink
2
bd68f4076442
%% @doc couchdb_proxy is intended to be a simple webmachine resource
3
bd68f4076442
%%      for proxying Webmachine requests to CouchDB.  In theory, it's
4
bd68f4076442
%%      general enough to be a simple proxy to most any other HTTP
5
bd68f4076442
%%      service, but I make no guarantees about assumption it makes that
6
bd68f4076442
%%      are CouchDB-specific.
7
bd68f4076442
%%
8
bd68f4076442
%%      Load this with a dispatch line like:
9
bd68f4076442
%%      {['*'], couchdb_proxy, {ExternalPath, CouchPath}}.
10
bd68f4076442
%%      Where:
11
bd68f4076442
%%        ExternalPath is the base path to this resource, like
12
bd68f4076442
%%          "http://localhost:8000/"
13
bd68f4076442
%%        CouchPath is the base path to your CouchDB server, like
14
bd68f4076442
%%          "http://localhost:5984/"
15
bd68f4076442
%%
16
bd68f4076442
%%      Another useful example might be:
17
bd68f4076442
%%        {["couch", '*'], couchdb_proxy,
18
bd68f4076442
%%         {"http://localhost:8000/couch/",
19
bd68f4076442
%%          "http://localhost:5984/"}}
20
bd68f4076442
%%      Which would redirect requests from
21
bd68f4076442
%%        http://localhost:8000/couch/DATABASE/KEY
22
bd68f4076442
%%      to
23
bd68f4076442
%%        http://localhost:5984/DATABASE/KEY
24
bd68f4076442
-module(couchdb_proxy).
25
bd68f4076442
-export([init/1,
26
bd68f4076442
         service_available/2]).
27
bd68f4076442
-include_lib("webmachine/include/webmachine.hrl").
28
bd68f4076442
29
bd68f4076442
init(Config) -> {ok, Config}.
30
bd68f4076442
31
bd68f4076442
%% request to couchdb is made in service_available, such that
32
bd68f4076442
%% if couchdb isn't up, we return 503 Service Unavailable, as expected
33
bd68f4076442
service_available(RP, C={_ExternalPath, CouchPath}) ->
34
bd68f4076442
    %% point path at couchdb server
35
bd68f4076442
    Path = lists:append(
36
bd68f4076442
             [CouchPath,
37
bd68f4076442
              wrq:disp_path(RP),
38
bd68f4076442
              case wrq:req_qs(RP) of
39
bd68f4076442
                  [] -> [];
40
bd68f4076442
                  Qs -> [$?|mochiweb_util:urlencode(Qs)]
41
bd68f4076442
              end]),
42
bd68f4076442
43
bd68f4076442
    %% translate webmachine details to ibrowse details
44
bd68f4076442
    Headers = clean_request_headers(
45
bd68f4076442
                mochiweb_headers:to_list(wrq:req_headers(RP))),
46
bd68f4076442
    Method = wm_to_ibrowse_method(wrq:method(RP)),
47
bd68f4076442
    ReqBody = case wrq:req_body(RP) of
48
bd68f4076442
                  undefined -> [];
49
bd68f4076442
                  B -> B
50
bd68f4076442
              end,
51
bd68f4076442
52
bd68f4076442
    case ibrowse:send_req(Path, Headers, Method, ReqBody) of
53
bd68f4076442
        {ok, Status, CouchHeaders, RespBody} ->
54
bd68f4076442
            RespHeaders = fix_location(CouchHeaders, C),
55
bd68f4076442
56
bd68f4076442
            %% stop resource processing here and return whatever
57
bd68f4076442
            %% couchdb wanted to return
58
bd68f4076442
            {{halt, list_to_integer(Status)},
59
bd68f4076442
             wrq:set_resp_headers(RespHeaders,
60
bd68f4076442
                                  wrq:set_resp_body(RespBody, RP)),
61
bd68f4076442
             C};
62
bd68f4076442
        _ ->
63
bd68f4076442
            {false, RP, C}
64
bd68f4076442
    end.
65
bd68f4076442
66
bd68f4076442
%% ibrowse will recalculate Host and Content-Length headers,
67
bd68f4076442
%% and will muck them up if they're manually specified
68
bd68f4076442
clean_request_headers(Headers) ->
69
bd68f4076442
    [{K,V} || {K,V} <- Headers,
70
bd68f4076442
              K /= 'Host', K /= 'Content-Length'].
71
bd68f4076442
72
bd68f4076442
%% webmachine expresses method as all-caps string or atom,
73
bd68f4076442
%% while ibrowse uses all-lowercase atom
74
bd68f4076442
wm_to_ibrowse_method(Method) when is_list(Method) ->
75
bd68f4076442
    list_to_atom(string:to_lower(Method));
76
bd68f4076442
wm_to_ibrowse_method(Method) when is_atom(Method) ->
77
bd68f4076442
    wm_to_ibrowse_method(atom_to_list(Method)).
78
bd68f4076442
79
bd68f4076442
%% couchdb returns a fully-qualified URI in Location -
80
bd68f4076442
%% hack off the couch host, and drop in this proxy host
81
bd68f4076442
fix_location([], _) -> [];
82
bd68f4076442
fix_location([{"Location", CouchDataPath}|Rest],
83
bd68f4076442
             {ExternalPath, CouchPath}) ->
84
bd68f4076442
    DataPath = lists:nthtail(length(CouchPath), CouchDataPath),
85
bd68f4076442
    [{"Location", ExternalPath++DataPath}|Rest];
86
bd68f4076442
fix_location([H|T], C) ->
87
bd68f4076442
    [H|fix_location(T, C)].