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