PageRenderTime 143ms CodeModel.GetById 78ms app.highlight 34ms RepoModel.GetById 16ms app.codeStats 0ms

/examples/https/https_store.erl

http://github.com/basho/mochiweb
Erlang | 146 lines | 100 code | 14 blank | 32 comment | 0 complexity | 521193db19fe9785003a035b1505f91e MD5 | raw file
  1
  2%% Trivial web storage app. It's available over both HTTP (port 8442)
  3%% and HTTPS (port 8443). You use a PUT to store items, a GET to
  4%% retrieve them and DELETE to delete them. The HTTP POST method is
  5%% invalid for this application. Example (using HTTPS transport):
  6%%
  7%%   $ curl -k --verbose https://localhost:8443/flintstones
  8%%   ...
  9%%   404 Not Found
 10%%   ...
 11%%   $ echo -e "Fred\nWilma\nBarney" |
 12%%           curl -k --verbose https://localhost:8443/flintstones \
 13%%                -X PUT -H "Content-Type: text/plain" --data-binary @-
 14%%   ...
 15%%   201 Created
 16%%   ...
 17%%   $ curl -k --verbose https://localhost:8443/flintstones
 18%%   ...
 19%%   Fred
 20%%   Wilma
 21%%   Barney
 22%%   ...
 23%%   $ curl -k --verbose https://localhost:8443/flintstones -X DELETE
 24%%   ...
 25%%   200 OK
 26%%   ...
 27%%   $ curl -k --verbose https://localhost:8443/flintstones
 28%%   ...
 29%%   404 Not Found
 30%%   ...
 31%%
 32%% All submitted data is stored in memory (in an ets table). Could be
 33%% useful for ad-hoc testing.
 34
 35-module(https_store).
 36
 37-export([start/0,
 38         stop/0,
 39         dispatch/1,
 40         loop/1
 41         ]).
 42
 43-define(HTTP_OPTS, [
 44            {loop, {?MODULE, dispatch}},
 45            {port, 8442},
 46            {name, http_8442}
 47            ]).
 48
 49-define(HTTPS_OPTS, [
 50            {loop, {?MODULE, dispatch}},
 51            {port, 8443},
 52            {name, https_8443},
 53            {ssl, true},
 54            {ssl_opts, [
 55                {certfile, "server_cert.pem"},
 56                {keyfile, "server_key.pem"}]}
 57            ]).
 58
 59-record(sd, {http, https}).
 60-record(resource, {type, data}).
 61
 62start() ->
 63    {ok, Http} = mochiweb_http:start(?HTTP_OPTS),
 64    {ok, Https} = mochiweb_http:start(?HTTPS_OPTS),
 65    SD = #sd{http=Http, https=Https},
 66    Pid = spawn_link(fun() ->
 67                             ets:new(?MODULE, [named_table]),
 68                             loop(SD)
 69                     end),
 70    register(http_store, Pid),
 71    ok.
 72
 73stop() ->
 74    http_store ! stop,
 75    ok.
 76
 77dispatch(Req) ->
 78    case Req:get(method) of
 79        'GET' ->
 80            get_resource(Req);
 81        'PUT' ->
 82            put_resource(Req);
 83        'DELETE' ->
 84            delete_resource(Req);
 85        _ ->
 86            Headers = [{"Allow", "GET,PUT,DELETE"}],
 87            Req:respond({405, Headers, "405 Method Not Allowed\r\n"})
 88    end.
 89
 90get_resource(Req) ->
 91    Path = Req:get(path),
 92    case ets:lookup(?MODULE, Path) of
 93        [{Path, #resource{type=Type, data=Data}}] ->
 94            Req:ok({Type, Data});
 95        [] ->
 96            Req:respond({404, [], "404 Not Found\r\n"})
 97    end.
 98
 99put_resource(Req) ->
100    ContentType = case Req:get_header_value("Content-Type") of
101        undefined ->
102            "application/octet-stream";
103        S ->
104            S
105    end,
106    Resource = #resource{type=ContentType, data=Req:recv_body()},
107    http_store ! {self(), {put, Req:get(path), Resource}},
108    Pid = whereis(http_store),
109    receive
110        {Pid, created} ->
111            Req:respond({201, [], "201 Created\r\n"});
112        {Pid, updated} ->
113            Req:respond({200, [], "200 OK\r\n"})
114    end.
115
116delete_resource(Req) ->
117    http_store ! {self(), {delete, Req:get(path)}},
118    Pid = whereis(http_store),
119    receive
120        {Pid, ok} ->
121            Req:respond({200, [], "200 OK\r\n"})
122    end.
123
124loop(#sd{http=Http, https=Https} = SD) ->
125    receive
126        stop ->
127            ok = mochiweb_http:stop(Http),
128            ok = mochiweb_http:stop(Https),
129            exit(normal);
130        {From, {put, Key, Val}} ->
131            Exists = ets:member(?MODULE, Key),
132            ets:insert(?MODULE, {Key, Val}),
133            case Exists of
134                true ->
135                    From ! {self(), updated};
136                false ->
137                    From ! {self(), created}
138            end;
139        {From, {delete, Key}} ->
140            ets:delete(?MODULE, Key),
141            From ! {self(), ok};
142        _ ->
143            ignore
144    end,
145    ?MODULE:loop(SD).
146