%% 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).