Subversion Repositories SE.SVN

Rev

Details | Last modification | View Log | RSS feed

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