Merge lp:~eday/burrow/openstack_app into lp:~burrow-core/burrow/cactus
- openstack_app
- Merge into cactus
Status: | Merged |
---|---|
Approved by: | Eric Day |
Approved revision: | 9 |
Merged at revision: | 3 |
Proposed branch: | lp:~eday/burrow/openstack_app |
Merge into: | lp:~burrow-core/burrow/cactus |
Diff against target: |
1767 lines (+1509/-88) 19 files modified
.bzrignore (+3/-3) README (+5/-0) apps/burrow/src/burrow.app.src (+6/-8) apps/burrow/src/burrow.erl (+44/-2) apps/burrow/src/burrow_app.erl (+0/-32) apps/burrow/src/burrow_http.erl (+38/-0) apps/burrow/src/burrow_sup.erl (+0/-32) apps/openstack/example.com-cert.pem (+15/-0) apps/openstack/example.com-key.pem (+15/-0) apps/openstack/include/openstack_http.hrl (+125/-0) apps/openstack/include/openstack_logging.hrl (+19/-0) apps/openstack/src/openstack.app.src (+29/-0) apps/openstack/src/openstack.erl (+86/-0) apps/openstack/src/openstack_http.erl (+763/-0) apps/openstack/src/openstack_server.erl (+221/-0) apps/openstack/src/openstack_socket.erl (+127/-0) rebar.config (+6/-2) rel/reltool.config (+6/-8) start (+1/-1) |
To merge this branch: | bzr merge lp:~eday/burrow/openstack_app |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Burrow Core Team | Pending | ||
Review via email: mp+51173@code.launchpad.net |
Commit message
Description of the change
Per Vish's comment on the last MP, this is more of a FYI, but reviews appreciated if you have time. :) Also doing this so jenkins can merge it.
Adds an openstack application for modules not specific to burrow. So far this includes generic tcp/ssl socket wrapper, server, and HTTP protocol modules.
OpenStack Infra (hudson-openstack) wrote : | # |
OpenStack Infra (hudson-openstack) wrote : | # |
The attempt to merge lp:~eday/burrow/openstack_app into lp:burrow failed. Below is the output from the failed tests.
==> burrow (compile)
Compiled src/burrow.erl
Compiled src/burrow_http.erl
==> openstack (compile)
Compiled src/openstack_
Compiled src/openstack_
Compiled src/openstack.erl
src/openstack_
Compiled src/openstack_
==> rel (compile)
==> tmpChhJXr (compile)
==> burrow (eunit)
Compiled src/burrow_http.erl
Compiled src/burrow.erl
=INFO REPORT==== 28-Feb-
Now listening on port: {tcp,32123,[]}
=INFO REPORT==== 28-Feb-
application: burrow
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
Now listening on port: {tcp,32123,[]}
=INFO REPORT==== 28-Feb-
application: burrow
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
Test passed.
Cover analysis: /tmp/tmpChhJXr/
==> openstack (eunit)
Compiled src/openstack_
Compiled src/openstack.erl
Compiled src/openstack_
src/openstack_
src/openstack_
Compiled src/openstack_
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
Now listening on port: {tcp,32123,[]}
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
Now listening on port: {tcp,32123,[]}
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
Now listening on port: {tcp,32123,[]}
=ERROR REPORT==== 28-Feb-
Error during HTTP request: <<"GET * BAD\r\n">>
=INFO REPORT==== 28-Feb-
application: openstack
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
application: ssl
exited: stopped
type: temporary
=INFO REPORT==== 28-Feb-
Now listening on port: {tcp,32123,[]}
=INFO REPORT==== ...
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2011-02-23 01:59:49 +0000 |
3 | +++ .bzrignore 2011-02-28 07:20:04 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -apps/burrow/.eunit/ |
6 | -apps/burrow/doc |
7 | -apps/burrow/ebin |
8 | +apps/*/.eunit/ |
9 | +apps/*/doc |
10 | +apps/*/ebin |
11 | rel/burrow/ |
12 | |
13 | === modified file 'README' |
14 | --- README 2011-02-23 01:59:49 +0000 |
15 | +++ README 2011-02-28 07:20:04 +0000 |
16 | @@ -50,3 +50,8 @@ |
17 | For information on the mailing list, IRC, and contributing, see: |
18 | |
19 | http://wiki.openstack.org/HowToContribute |
20 | + |
21 | +For developers, if you would like to see if your binary operations |
22 | +are optimized, set the following environment variable and recompile: |
23 | + |
24 | +export ERL_COMPILER_OPTIONS=bin_opt_info |
25 | |
26 | === modified file 'apps/burrow/src/burrow.app.src' |
27 | --- apps/burrow/src/burrow.app.src 2011-02-23 01:59:49 +0000 |
28 | +++ apps/burrow/src/burrow.app.src 2011-02-28 07:20:04 +0000 |
29 | @@ -13,18 +13,16 @@ |
30 | % limitations under the License. |
31 | |
32 | {application, burrow, [ |
33 | - {description, "Burrow - OpenStack Queue Service"}, |
34 | + {description, "Burrow - OpenStack Message Queue"}, |
35 | {vsn, "2011.2"}, |
36 | {modules, [ |
37 | burrow, |
38 | - burrow_app, |
39 | - burrow_sup |
40 | - ]}, |
41 | + burrow_http]}, |
42 | {applications, [ |
43 | kernel, |
44 | - stdlib |
45 | - ]}, |
46 | + openstack, |
47 | + stdlib]}, |
48 | {registered, []}, |
49 | - {mod, {burrow_app, []}}, |
50 | - {env, []} |
51 | + {mod, {burrow, []}}, |
52 | + {env, [{http_ports, [8080]}]} |
53 | ]}. |
54 | |
55 | === modified file 'apps/burrow/src/burrow.erl' |
56 | --- apps/burrow/src/burrow.erl 2011-02-23 01:59:49 +0000 |
57 | +++ apps/burrow/src/burrow.erl 2011-02-28 07:20:04 +0000 |
58 | @@ -12,21 +12,34 @@ |
59 | % See the License for the specific language governing permissions and |
60 | % limitations under the License. |
61 | |
62 | -% @doc Main functions for Burrow. |
63 | +% @doc Main application module for Burrow. This contains callbacks for |
64 | +% both the application and root supervisor behaviours. |
65 | |
66 | -module(burrow). |
67 | |
68 | -export([start/0, stop/0, restart/0]). |
69 | |
70 | +-behaviour(application). |
71 | +-export([start/2, stop/1]). |
72 | + |
73 | +-behaviour(supervisor). |
74 | +-export([init/1]). |
75 | + |
76 | % @doc Start Burrow. |
77 | +% @spec () -> ok | {error, Reason} |
78 | start() -> |
79 | + openstack:start(), |
80 | application:start(?MODULE). |
81 | |
82 | % @doc Stop Burrow. |
83 | +% @spec () -> ok | {error, Reason} |
84 | stop() -> |
85 | - application:stop(?MODULE). |
86 | + Result = application:stop(?MODULE), |
87 | + openstack:stop(), |
88 | + Result. |
89 | |
90 | % @doc Restart Burrow. |
91 | +% @spec () -> ok | {error, Reason} |
92 | restart() -> |
93 | case stop() of |
94 | ok -> start(); |
95 | @@ -34,6 +47,33 @@ |
96 | end. |
97 | |
98 | % |
99 | +% Callbacks for the application behaviour. |
100 | +% |
101 | + |
102 | +% @doc Start callback for the application behaviour. This starts the root |
103 | +% supervisor process and all components of the server. See application(3erl) |
104 | +% for more details. |
105 | +start(_Type, _Args) -> |
106 | + Supervisor = supervisor:start_link({local, ?MODULE}, ?MODULE, []), |
107 | + burrow_http:start(), |
108 | + Supervisor. |
109 | + |
110 | +% @doc Stop callback for the application behaviour. See application(3erl) |
111 | +% for more details. |
112 | +stop(_State) -> |
113 | + ok. |
114 | + |
115 | +% |
116 | +% Callbacks for the supervisor behaviour. |
117 | +% |
118 | + |
119 | +% @doc Init callback for the supervisor behaviour. See supervisor(3erl) |
120 | +% for more details. |
121 | +init(_Args) -> |
122 | + {ok, {{one_for_one, 10, 10}, [ |
123 | + ]}}. |
124 | + |
125 | +% |
126 | % Test functions. |
127 | % |
128 | |
129 | @@ -41,6 +81,8 @@ |
130 | -include_lib("eunit/include/eunit.hrl"). |
131 | |
132 | application_test() -> |
133 | + ?assertEqual(ok, application:load(burrow)), |
134 | + ?assertEqual(ok, application:set_env(burrow, http_ports, [32123])), |
135 | ?assertEqual(ok, start()), |
136 | ?assertEqual(ok, restart()), |
137 | ?assertEqual(ok, stop()), |
138 | |
139 | === removed file 'apps/burrow/src/burrow_app.erl' |
140 | --- apps/burrow/src/burrow_app.erl 2011-02-23 01:59:49 +0000 |
141 | +++ apps/burrow/src/burrow_app.erl 1970-01-01 00:00:00 +0000 |
142 | @@ -1,32 +0,0 @@ |
143 | -% Copyright (C) 2011 OpenStack LLC. |
144 | -% |
145 | -% Licensed under the Apache License, Version 2.0 (the "License"); |
146 | -% you may not use this file except in compliance with the License. |
147 | -% You may obtain a copy of the License at |
148 | -% |
149 | -% http://www.apache.org/licenses/LICENSE-2.0 |
150 | -% |
151 | -% Unless required by applicable law or agreed to in writing, software |
152 | -% distributed under the License is distributed on an "AS IS" BASIS, |
153 | -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
154 | -% See the License for the specific language governing permissions and |
155 | -% limitations under the License. |
156 | - |
157 | -% @doc Application module for burrow. This contains callbacks for the |
158 | -% application behaviour. |
159 | - |
160 | --module(burrow_app). |
161 | - |
162 | --behaviour(application). |
163 | --export([start/2, stop/1]). |
164 | - |
165 | -% @doc Start callback for the application behaviour. This starts the root |
166 | -% supervisor process and all components of the server. See application(3erl) |
167 | -% for more details. |
168 | -start(_Type, _Args) -> |
169 | - burrow_sup:start_link(). |
170 | - |
171 | -% @doc Stop callback for the application behaviour. See application(3erl) |
172 | -% for more details. |
173 | -stop(_State) -> |
174 | - ok. |
175 | |
176 | === added file 'apps/burrow/src/burrow_http.erl' |
177 | --- apps/burrow/src/burrow_http.erl 1970-01-01 00:00:00 +0000 |
178 | +++ apps/burrow/src/burrow_http.erl 2011-02-28 07:20:04 +0000 |
179 | @@ -0,0 +1,38 @@ |
180 | +% Copyright (C) 2011 OpenStack LLC. |
181 | +% |
182 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
183 | +% you may not use this file except in compliance with the License. |
184 | +% You may obtain a copy of the License at |
185 | +% |
186 | +% http://www.apache.org/licenses/LICENSE-2.0 |
187 | +% |
188 | +% Unless required by applicable law or agreed to in writing, software |
189 | +% distributed under the License is distributed on an "AS IS" BASIS, |
190 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
191 | +% See the License for the specific language governing permissions and |
192 | +% limitations under the License. |
193 | + |
194 | +% @doc This is a HTTP interface into Burrow. This module is built off of |
195 | +% the generic HTTP server provided by openstack_http. |
196 | + |
197 | +-module(burrow_http). |
198 | + |
199 | +-export([start/0]). |
200 | + |
201 | +-include_lib("openstack/include/openstack_http.hrl"). |
202 | + |
203 | +% @doc Start up the HTTP server for the configured ports. |
204 | +% @spec () -> {ok, Child} | {error, Reason} |
205 | +start() -> |
206 | + {ok, Ports} = application:get_env(burrow, http_ports), |
207 | + {ok, Version} = application:get_key(burrow, vsn), |
208 | + ServerName = <<"Burrow/", (list_to_binary(Version))/binary>>, |
209 | + openstack_http:server(Ports, ServerName, fun handle_request/3, undefined). |
210 | + |
211 | +% @doc Callback to handle requests from the client. |
212 | +% @spec (Request::openstack_http:request(), |
213 | +% Response::openstack_http:response(), |
214 | +% State::term()) |
215 | +% -> {Response::openstack_http:response(), State::term()} |
216 | +handle_request(_Request, Response, State) -> |
217 | + {Response, State}. |
218 | |
219 | === removed file 'apps/burrow/src/burrow_sup.erl' |
220 | --- apps/burrow/src/burrow_sup.erl 2011-02-23 01:59:49 +0000 |
221 | +++ apps/burrow/src/burrow_sup.erl 1970-01-01 00:00:00 +0000 |
222 | @@ -1,32 +0,0 @@ |
223 | -% Copyright (C) 2011 OpenStack LLC. |
224 | -% |
225 | -% Licensed under the Apache License, Version 2.0 (the "License"); |
226 | -% you may not use this file except in compliance with the License. |
227 | -% You may obtain a copy of the License at |
228 | -% |
229 | -% http://www.apache.org/licenses/LICENSE-2.0 |
230 | -% |
231 | -% Unless required by applicable law or agreed to in writing, software |
232 | -% distributed under the License is distributed on an "AS IS" BASIS, |
233 | -% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
234 | -% See the License for the specific language governing permissions and |
235 | -% limitations under the License. |
236 | - |
237 | -% @doc Supervisor module for burrow. This contains callbacks for the root |
238 | -% supervisor behaviour. |
239 | - |
240 | --module(burrow_sup). |
241 | - |
242 | --export([start_link/0]). |
243 | - |
244 | --behaviour(supervisor). |
245 | --export([init/1]). |
246 | - |
247 | -% @doc Start and link to the supervisor process. |
248 | -start_link() -> |
249 | - supervisor:start_link({local, ?MODULE}, ?MODULE, []). |
250 | - |
251 | -% @doc Callback for the supervisor behaviour. See supervisor(3erl) for |
252 | -% more details. |
253 | -init([]) -> |
254 | - {ok, {{one_for_one, 5, 10}, []}}. |
255 | |
256 | === added directory 'apps/openstack' |
257 | === added file 'apps/openstack/example.com-cert.pem' |
258 | --- apps/openstack/example.com-cert.pem 1970-01-01 00:00:00 +0000 |
259 | +++ apps/openstack/example.com-cert.pem 2011-02-28 07:20:04 +0000 |
260 | @@ -0,0 +1,15 @@ |
261 | +-----BEGIN CERTIFICATE----- |
262 | +MIICXTCCAcYCCQDKsG7RfKYfUjANBgkqhkiG9w0BAQUFADBzMQswCQYDVQQGEwJV |
263 | +UzEPMA0GA1UECBMGT3JlZ29uMREwDwYDVQQHEwhQb3J0bGFuZDEUMBIGA1UEChML |
264 | +ZXhhbXBsZS5jb20xFDASBgNVBAsTC2V4YW1wbGUuY29tMRQwEgYDVQQDEwtleGFt |
265 | +cGxlLmNvbTAeFw0xMTAyMjQwMjU0MzNaFw0yMTAyMjEwMjU0MzNaMHMxCzAJBgNV |
266 | +BAYTAlVTMQ8wDQYDVQQIEwZPcmVnb24xETAPBgNVBAcTCFBvcnRsYW5kMRQwEgYD |
267 | +VQQKEwtleGFtcGxlLmNvbTEUMBIGA1UECxMLZXhhbXBsZS5jb20xFDASBgNVBAMT |
268 | +C2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCycD48lZj0 |
269 | +wzMBZsNT7qzwlKcOiQJaPvaL4kQiL/3SGegnsnjiHscuAe2cQID7aPRTqam0GDbb |
270 | +ULLA8c8RIxaK7k8eM0GKFOzmbqtiyPi3H1FaL+2V94tUFrRy/OX9IXbArLWFmlCN |
271 | +V3gIhOFT4HH0io2OFzwc62lFYtyoZKQIaQIDAQABMA0GCSqGSIb3DQEBBQUAA4GB |
272 | +AKYQNMfbwttDyv3h96SKa6ll0sEHzghO/HwpF0n6iIrly3xvpSoR39ydR2wAmmGH |
273 | +33oA5tF51SD7CplQDHyst6QWFjRzmNla5TfUfImJS0VqIQYQTneikpHsBopt5T5f |
274 | +MKn4t7AeUVMLGIU1w3DuIMe6OLm08gpwyb6XYqhLMvtp |
275 | +-----END CERTIFICATE----- |
276 | |
277 | === added file 'apps/openstack/example.com-key.pem' |
278 | --- apps/openstack/example.com-key.pem 1970-01-01 00:00:00 +0000 |
279 | +++ apps/openstack/example.com-key.pem 2011-02-28 07:20:04 +0000 |
280 | @@ -0,0 +1,15 @@ |
281 | +-----BEGIN RSA PRIVATE KEY----- |
282 | +MIICWwIBAAKBgQCycD48lZj0wzMBZsNT7qzwlKcOiQJaPvaL4kQiL/3SGegnsnji |
283 | +HscuAe2cQID7aPRTqam0GDbbULLA8c8RIxaK7k8eM0GKFOzmbqtiyPi3H1FaL+2V |
284 | +94tUFrRy/OX9IXbArLWFmlCNV3gIhOFT4HH0io2OFzwc62lFYtyoZKQIaQIDAQAB |
285 | +AoGAG4uyP94EJ7y+cffwLVPKBLWd+GN0JoRTwRJj3Dc2Yqm3KfGmMZn4u4R2rslS |
286 | +X/06wnONjAy04zRY8+ogQg/9maCdNYITJTh/hgIwv0p+El1KDRvolRbdKLX+PkB2 |
287 | +yCgQ2E1yh73sHYZUJP4pmxgMyRqnuxAHI3LTCMjQxyx8z4ECQQDqZx8Eer+b0RDX |
288 | +i5vD6p4+9j5bWMvPrDd2BTQJFQSmcnPq5urQcMjJXUpokQ8SBxJjojYtzzCIkiZT |
289 | +WGX4uGtZAkEAwuEV8vd+V7BOGE6T2V8MUQYaSbIXeSq0mWxpeG7z54UjYLHaxVKv |
290 | +3tuTakKFscBOPURRt+0GczmdOtkaliCzkQJAEduzaEL10Cg4cUOIpX99eOyHyavV |
291 | +M0g6nXo+AachSJEVtcLMBv9gvL6nyZmrnUL+qqqUGB9d4jnIQ7dLZYyfWQJAFCw4 |
292 | +mW4FJg6aBuobY+Ty4XBASGCnDWPeTgNq54yUsuKVRu0iOErW6brRB67bKAtADheE |
293 | +XgWrJUJRhEX0+pVoAQJAKFR1XG4VauNlsrxTSlDGEitew1sdw3/uzzsQ/XWxSgYF |
294 | +izvmXCV5e8IINSMSicjd+2/IQrCLGXNWA4Tt02bOqA== |
295 | +-----END RSA PRIVATE KEY----- |
296 | |
297 | === added directory 'apps/openstack/include' |
298 | === added file 'apps/openstack/include/openstack_http.hrl' |
299 | --- apps/openstack/include/openstack_http.hrl 1970-01-01 00:00:00 +0000 |
300 | +++ apps/openstack/include/openstack_http.hrl 2011-02-28 07:20:04 +0000 |
301 | @@ -0,0 +1,125 @@ |
302 | +% Copyright (C) 2011 OpenStack LLC. |
303 | +% |
304 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
305 | +% you may not use this file except in compliance with the License. |
306 | +% You may obtain a copy of the License at |
307 | +% |
308 | +% http://www.apache.org/licenses/LICENSE-2.0 |
309 | +% |
310 | +% Unless required by applicable law or agreed to in writing, software |
311 | +% distributed under the License is distributed on an "AS IS" BASIS, |
312 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
313 | +% See the License for the specific language governing permissions and |
314 | +% limitations under the License. |
315 | + |
316 | +% Data types used with the openstack_http module. |
317 | + |
318 | +% @type request() = #request{ |
319 | +% method = atom() | binary(), |
320 | +% path = binary(), |
321 | +% parameters = [parameter()], |
322 | +% version = {integer(), integer()}, |
323 | +% headers = [request_header()], |
324 | +% connection = undefined | close | keep_alive, |
325 | +% expect = undefined | continue, |
326 | +% length = integer(), |
327 | +% body = undefined | function()} |
328 | +% @type parameter() = {Name::binary(), Value::binary()} |
329 | +% @type request_header() = {Name::atom() | binary(), Value::binary()} |
330 | +-record(request, { |
331 | + method = 'GET', |
332 | + path = <<>>, |
333 | + parameters = [], |
334 | + version = {1, 1}, |
335 | + headers = [], |
336 | + connection, |
337 | + expect, |
338 | + length = 0, |
339 | + body}). |
340 | + |
341 | +% @type response() = #response{ |
342 | +% status = integer(), |
343 | +% headers = [response_header()], |
344 | +% connection = undefined | close | keep_alive, |
345 | +% chunked = true | false | allowed, |
346 | +% length = integer(), |
347 | +% body = undefined | binary() | function()} |
348 | +% @type response_header() = { |
349 | +% Name::atom() | binary(), |
350 | +% Value::integer() | list() | binary()} |
351 | +-record(response, { |
352 | + status = 200, |
353 | + headers = [], |
354 | + connection, |
355 | + chunked = false, |
356 | + length = 0, |
357 | + body}). |
358 | + |
359 | +% The built-in Erlang packet parser (written in C) parses HTTP natively |
360 | +% which is much faster than doing it in Erlang. It optimizes around common |
361 | +% method and header names by returning them as atoms instead of strings or |
362 | +% binaries. Common headers must be matched using atoms, but custom headers |
363 | +% must be matched using binaries. The following lists show what are atoms. |
364 | + |
365 | +% The following method names will be atoms, all others will be binaries: |
366 | +% OPTIONS |
367 | +% GET |
368 | +% HEAD |
369 | +% POST |
370 | +% PUT |
371 | +% DELETE |
372 | +% TRACE |
373 | + |
374 | +% The following header names will be atoms, all others will be binaries: |
375 | +% Cache-Control |
376 | +% Connection |
377 | +% Date |
378 | +% Pragma |
379 | +% Transfer-Encoding |
380 | +% Upgrade |
381 | +% Via |
382 | +% Accept |
383 | +% Accept-Charset |
384 | +% Accept-Encoding |
385 | +% Accept-Language |
386 | +% Authorization |
387 | +% From |
388 | +% Host |
389 | +% If-Modified-Since |
390 | +% If-Match |
391 | +% If-None-Match |
392 | +% If-Range |
393 | +% If-Unmodified-Since |
394 | +% Max-Forwards |
395 | +% Proxy-Authorization |
396 | +% Range |
397 | +% Referer |
398 | +% User-Agent |
399 | +% Age |
400 | +% Location |
401 | +% Proxy-Authenticate |
402 | +% Public |
403 | +% Retry-After |
404 | +% Server |
405 | +% Vary |
406 | +% Warning |
407 | +% Www-Authenticate |
408 | +% Allow |
409 | +% Content-Base |
410 | +% Content-Encoding |
411 | +% Content-Language |
412 | +% Content-Length |
413 | +% Content-Location |
414 | +% Content-Md5 |
415 | +% Content-Range |
416 | +% Content-Type |
417 | +% Etag |
418 | +% Expires |
419 | +% Last-Modified |
420 | +% Accept-Ranges |
421 | +% Set-Cookie |
422 | +% Set-Cookie2 |
423 | +% X-Forwarded-For |
424 | +% Cookie |
425 | +% Keep-Alive |
426 | +% Proxy-Connection |
427 | |
428 | === added file 'apps/openstack/include/openstack_logging.hrl' |
429 | --- apps/openstack/include/openstack_logging.hrl 1970-01-01 00:00:00 +0000 |
430 | +++ apps/openstack/include/openstack_logging.hrl 2011-02-28 07:20:04 +0000 |
431 | @@ -0,0 +1,19 @@ |
432 | +% Copyright (C) 2011 OpenStack LLC. |
433 | +% |
434 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
435 | +% you may not use this file except in compliance with the License. |
436 | +% You may obtain a copy of the License at |
437 | +% |
438 | +% http://www.apache.org/licenses/LICENSE-2.0 |
439 | +% |
440 | +% Unless required by applicable law or agreed to in writing, software |
441 | +% distributed under the License is distributed on an "AS IS" BASIS, |
442 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
443 | +% See the License for the specific language governing permissions and |
444 | +% limitations under the License. |
445 | + |
446 | +% Logging macros in case we want to switch or wrap these later. |
447 | + |
448 | +-define(ERROR, error_logger:error_msg). |
449 | +-define(WARNING, error_logger:warning_msg). |
450 | +-define(INFO, error_logger:info_msg). |
451 | |
452 | === added directory 'apps/openstack/src' |
453 | === added file 'apps/openstack/src/openstack.app.src' |
454 | --- apps/openstack/src/openstack.app.src 1970-01-01 00:00:00 +0000 |
455 | +++ apps/openstack/src/openstack.app.src 2011-02-28 07:20:04 +0000 |
456 | @@ -0,0 +1,29 @@ |
457 | +% Copyright (C) 2011 OpenStack LLC. |
458 | +% |
459 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
460 | +% you may not use this file except in compliance with the License. |
461 | +% You may obtain a copy of the License at |
462 | +% |
463 | +% http://www.apache.org/licenses/LICENSE-2.0 |
464 | +% |
465 | +% Unless required by applicable law or agreed to in writing, software |
466 | +% distributed under the License is distributed on an "AS IS" BASIS, |
467 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
468 | +% See the License for the specific language governing permissions and |
469 | +% limitations under the License. |
470 | + |
471 | +{application, openstack, [ |
472 | + {description, "OpenStack - Common OpenStack Modules"}, |
473 | + {vsn, "2011.2"}, |
474 | + {modules, [ |
475 | + openstack, |
476 | + openstack_http, |
477 | + openstack_server, |
478 | + openstack_socket]}, |
479 | + {applications, [ |
480 | + kernel, |
481 | + stdlib]}, |
482 | + {registered, []}, |
483 | + {mod, {openstack, []}}, |
484 | + {env, []} |
485 | +]}. |
486 | |
487 | === added file 'apps/openstack/src/openstack.erl' |
488 | --- apps/openstack/src/openstack.erl 1970-01-01 00:00:00 +0000 |
489 | +++ apps/openstack/src/openstack.erl 2011-02-28 07:20:04 +0000 |
490 | @@ -0,0 +1,86 @@ |
491 | +% Copyright (C) 2011 OpenStack LLC. |
492 | +% |
493 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
494 | +% you may not use this file except in compliance with the License. |
495 | +% You may obtain a copy of the License at |
496 | +% |
497 | +% http://www.apache.org/licenses/LICENSE-2.0 |
498 | +% |
499 | +% Unless required by applicable law or agreed to in writing, software |
500 | +% distributed under the License is distributed on an "AS IS" BASIS, |
501 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
502 | +% See the License for the specific language governing permissions and |
503 | +% limitations under the License. |
504 | + |
505 | +% @doc Main application module for OpenStack. This contains callbacks for |
506 | +% both the application and root supervisor behaviours. |
507 | + |
508 | +-module(openstack). |
509 | + |
510 | +-export([start/0, stop/0, restart/0]). |
511 | + |
512 | +-behaviour(application). |
513 | +-export([start/2, stop/1]). |
514 | + |
515 | +-behaviour(supervisor). |
516 | +-export([init/1]). |
517 | + |
518 | +% @doc Start OpenStack. |
519 | +% @spec () -> ok | {error, Reason} |
520 | +start() -> |
521 | + ssl:start(), |
522 | + application:start(?MODULE). |
523 | + |
524 | +% @doc Stop OpenStack. |
525 | +% @spec () -> ok | {error, Reason} |
526 | +stop() -> |
527 | + application:stop(?MODULE). |
528 | + |
529 | +% @doc Restart OpenStack. |
530 | +% @spec () -> ok | {error, Reason} |
531 | +restart() -> |
532 | + case stop() of |
533 | + ok -> start(); |
534 | + Error -> Error |
535 | + end. |
536 | + |
537 | +% |
538 | +% Callbacks for the application behaviour. |
539 | +% |
540 | + |
541 | +% @doc Start callback for the application behaviour. This starts the root |
542 | +% supervisor process and all components of the server. See application(3erl) |
543 | +% for more details. |
544 | +start(_Type, _Args) -> |
545 | + supervisor:start_link({local, ?MODULE}, ?MODULE, []). |
546 | + |
547 | +% @doc Stop callback for the application behaviour. See application(3erl) |
548 | +% for more details. |
549 | +stop(_State) -> |
550 | + ok. |
551 | + |
552 | +% |
553 | +% Callbacks for the supervisor behaviour. |
554 | +% |
555 | + |
556 | +% @doc Init callback for the supervisor behaviour. See supervisor(3erl) |
557 | +% for more details. |
558 | +init(_Args) -> |
559 | + {ok, {{one_for_one, 10, 10}, [ |
560 | + openstack_server:child_spec() |
561 | + ]}}. |
562 | + |
563 | +% |
564 | +% Test functions. |
565 | +% |
566 | + |
567 | +-ifdef(TEST). |
568 | +-include_lib("eunit/include/eunit.hrl"). |
569 | + |
570 | +application_test() -> |
571 | + ?assertEqual(ok, start()), |
572 | + ?assertEqual(ok, restart()), |
573 | + ?assertEqual(ok, stop()), |
574 | + ?assertEqual({error, {not_started, openstack}}, restart()). |
575 | + |
576 | +-endif. |
577 | |
578 | === added file 'apps/openstack/src/openstack_http.erl' |
579 | --- apps/openstack/src/openstack_http.erl 1970-01-01 00:00:00 +0000 |
580 | +++ apps/openstack/src/openstack_http.erl 2011-02-28 07:20:04 +0000 |
581 | @@ -0,0 +1,763 @@ |
582 | +% Copyright (C) 2011 OpenStack LLC. |
583 | +% |
584 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
585 | +% you may not use this file except in compliance with the License. |
586 | +% You may obtain a copy of the License at |
587 | +% |
588 | +% http://www.apache.org/licenses/LICENSE-2.0 |
589 | +% |
590 | +% Unless required by applicable law or agreed to in writing, software |
591 | +% distributed under the License is distributed on an "AS IS" BASIS, |
592 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
593 | +% See the License for the specific language governing permissions and |
594 | +% limitations under the License. |
595 | + |
596 | +% @doc HTTP protocol module that allows other modules to easily write |
597 | +% HTTP clients and servers. Currently only server functions are defined, |
598 | +% clients should use the HTTP client module provided by inets(3erl). |
599 | + |
600 | +-module(openstack_http). |
601 | + |
602 | +-export([server/4, get_request_header/2, set_response_headers/2]). |
603 | + |
604 | +% @headerfile "openstack_http.hrl" |
605 | +-include("openstack_http.hrl"). |
606 | +-include("openstack_logging.hrl"). |
607 | + |
608 | +% @type state() = #state{ |
609 | +% handle_request = function(), |
610 | +% callback_state = term(), |
611 | +% server_name = binary(), |
612 | +% socket = term()} |
613 | +-record(state, { |
614 | + handle_request, |
615 | + callback_state, |
616 | + server_name, |
617 | + socket}). |
618 | + |
619 | +% @doc Start up HTTP listener sockets for the given ports using the |
620 | +% openstack_server module. |
621 | +% @spec (Ports::openstack_server:ports(), |
622 | +% ServerName::binary(), |
623 | +% HandleRequest::function(), |
624 | +% CallbackState::term()) |
625 | +% -> {ok, Child} | {error, Reason} |
626 | +server(Ports, ServerName, HandleRequest, CallbackState) -> |
627 | + openstack_server:start(Ports, fun accept/2, #state{ |
628 | + handle_request = HandleRequest, |
629 | + callback_state = CallbackState, |
630 | + server_name = <<"OpenStack (", ServerName/binary, ")">>}). |
631 | + |
632 | +% @doc Callback function for when a new client is accepted. |
633 | +% @spec (Socket::term(), State::state()) -> ok |
634 | +accept(Socket, State) -> |
635 | + start_request(State#state{socket = Socket}). |
636 | + |
637 | +% @doc Start a new HTTP request. |
638 | +% @spec (State::state()) -> ok |
639 | +start_request(State = #state{socket = Socket}) -> |
640 | + case recv_request(Socket) of |
641 | + {ok, Request} -> |
642 | + handle_request(Request, State); |
643 | + {error, closed} -> |
644 | + ok; |
645 | + {error, {http_error, Reason}} -> |
646 | + ?WARNING("Error during HTTP request: ~p", [Reason]), |
647 | + openstack_socket:send(Socket, << |
648 | + "HTTP/1.1 400 Bad Request\r\n" |
649 | + "Date: ", |
650 | + (list_to_binary(httpd_util:rfc1123_date()))/binary, "\r\n" |
651 | + "Server: ", (State#state.server_name)/binary, "\r\n" |
652 | + "Content-Length: 0\r\n" |
653 | + "Connection: close\r\n\r\n">>), |
654 | + ok; |
655 | + Error -> |
656 | + ?WARNING("Error during HTTP request: ~p", [Error]), |
657 | + ok |
658 | + end. |
659 | + |
660 | +% @doc Pass the request off to the callback function and send the response. |
661 | +% @spec (Request::request(), State::state()) -> ok |
662 | +handle_request(Request, State = #state{ |
663 | + handle_request = HandleRequest, |
664 | + callback_state = CallbackState, |
665 | + server_name = ServerName, |
666 | + socket = Socket}) -> |
667 | + Response = #response{ |
668 | + headers = [ |
669 | + {'Date', list_to_binary(httpd_util:rfc1123_date())}, |
670 | + {'Server', ServerName}], |
671 | + connection = Request#request.connection, |
672 | + chunked = chunked_allowed(Request)}, |
673 | + {NewResponse, NewCallbackState} = |
674 | + HandleRequest(Request, Response, CallbackState), |
675 | + case send_response(NewResponse, Socket) of |
676 | + persist -> |
677 | + start_request(State#state{callback_state = NewCallbackState}); |
678 | + close -> |
679 | + ok; |
680 | + Error -> |
681 | + ?WARNING("Error during HTTP response: ~p", [Error]), |
682 | + ok |
683 | + end. |
684 | + |
685 | +% @doc Check to see if chunked transfer encoding is possible. |
686 | +% @spec (Request::request()) -> allowed | false |
687 | +chunked_allowed(#request{version = {1, 1}}) -> |
688 | + allowed; |
689 | +chunked_allowed(_Request) -> |
690 | + false. |
691 | + |
692 | +% @doc Setup the socket and request record for reading a request. |
693 | +% @spec (Socket::term()) |
694 | +% -> {ok, Request::request()} | {error | http_error, Reason} |
695 | +recv_request(Socket) -> |
696 | + openstack_socket:setopts(Socket, [{packet, http_bin}]), |
697 | + recv_request(Socket, #request{}). |
698 | + |
699 | +% @doc Parse the request line and headers for a HTTP request. This uses |
700 | +% the built-in Erlang HTTP parsing that is written in C. For full details |
701 | +% on the packet types, see the decode_packet documentation in erlang(3erl). |
702 | +% @spec (Socket::term(), Request::request()) |
703 | +% -> {ok, Request::request()} | {error | http_error, Reason} |
704 | +recv_request(Socket, Request) -> |
705 | + case openstack_socket:recv(Socket, 0) of |
706 | + {ok, {http_request, Method, URI, Version}} -> |
707 | + {Path, Parameters} = parse_uri(URI), |
708 | + NewRequest = parse_version(Version, Request), |
709 | + recv_request(Socket, NewRequest#request{ |
710 | + method = Method, |
711 | + path = Path, |
712 | + parameters = Parameters}); |
713 | + {ok, {http_header, _Number, Name, _Reserved, Value}} -> |
714 | + recv_request(Socket, parse_header({Name, Value}, Request)); |
715 | + {ok, http_eoh} -> |
716 | + openstack_socket:setopts(Socket, [{packet, raw}]), |
717 | + NewHeaders = lists:reverse(Request#request.headers), |
718 | + {ok, set_recv(Request#request{headers = NewHeaders}, Socket)}; |
719 | + Error -> |
720 | + Error |
721 | + end. |
722 | + |
723 | +% @doc Parse one of the URI formats given by the HTTP packet parsing. |
724 | +% @spec (URI::term()) -> {Path::binary, Parameters::[parameter()]} |
725 | +parse_uri('*') -> |
726 | + {'*', []}; |
727 | +parse_uri({abs_path, URI}) -> |
728 | + parse_path(URI, <<>>); |
729 | +parse_uri(URI) -> |
730 | + parse_path(URI, <<>>). |
731 | + |
732 | +% @doc Parse the path portion of the URI. |
733 | +% @spec (URI::binary(), Path::binary()) |
734 | +% -> {Path::binary, Parameters::[parameter()]} |
735 | +parse_path(<<>>, Path) -> |
736 | + {Path, []}; |
737 | +parse_path(<<$?, URI/binary>>, Path) -> |
738 | + parse_key(URI, Path, [], <<>>); |
739 | +parse_path(<<Character, URI/binary>>, Path) -> |
740 | + parse_path(URI, <<Path/binary, Character>>). |
741 | + |
742 | +% @doc Parse a key in the URI parameter list. |
743 | +% @spec (URI::binary(), |
744 | +% Path::binary(), |
745 | +% Parameters::[parameter()], |
746 | +% Key::binary()) |
747 | +% -> {Path::binary(), Parameters::[parameter()]} |
748 | +parse_key(<<>>, Path, Parameters, <<>>) -> |
749 | + {Path, Parameters}; |
750 | +parse_key(<<>>, Path, Parameters, Key) -> |
751 | + {Path, lists:reverse([{Key, <<>>} | Parameters])}; |
752 | +parse_key(<<$&, URI/binary>>, Path, Parameters, <<>>) -> |
753 | + parse_key(URI, Path, Parameters, <<>>); |
754 | +parse_key(<<$&, URI/binary>>, Path, Parameters, Key) -> |
755 | + parse_key(URI, Path, [{Key, <<>>} | Parameters], <<>>); |
756 | +parse_key(<<$=, URI/binary>>, Path, Parameters, Key) -> |
757 | + parse_value(URI, Path, [{Key, <<>>} | Parameters], <<>>); |
758 | +parse_key(<<Character, URI/binary>>, Path, Parameters, Key) -> |
759 | + parse_key(URI, Path, Parameters, <<Key/binary, Character>>). |
760 | + |
761 | +% @doc Parse a value in the URI parameter list. |
762 | +% @spec (URI::binary(), |
763 | +% Path::binary(), |
764 | +% Parameters::[parameter()], |
765 | +% Value::binary()) |
766 | +% -> {Path::binary, Parameters::[parameter()]} |
767 | +parse_value(<<>>, Path, Parameters, <<>>) -> |
768 | + {Path, Parameters}; |
769 | +parse_value(<<>>, Path, [{Key, <<>>} | Parameters], Value) -> |
770 | + {Path, lists:reverse([{Key, Value} | Parameters])}; |
771 | +parse_value(<<$&, URI/binary>>, Path, [{Key, <<>>} | Parameters], Value) -> |
772 | + parse_key(URI, Path, [{Key, Value} | Parameters], <<>>); |
773 | +parse_value(<<Character, URI/binary>>, Path, Parameters, Value) -> |
774 | + parse_value(URI, Path, Parameters, <<Value/binary, Character>>). |
775 | + |
776 | +% @doc Set any request parameters for the version. |
777 | +% @spec (Version::term(), Request::request()) -> Request::request() |
778 | +parse_version(Version = {1, 0}, Request) -> |
779 | + Request#request{version = Version, connection = close}; |
780 | +parse_version(Version, Request) -> |
781 | + Request#request{version = Version}. |
782 | + |
783 | +% @doc Set any request parameters for the header and save the header. |
784 | +% @spec (Header::request_header(), Request::request()) -> Request::request() |
785 | +parse_header(Header = {'Connection', Value}, Request) -> |
786 | + Connection = case string:to_lower(binary_to_list(Value)) of |
787 | + "close" -> close; |
788 | + "keep-alive" -> keep_alive; |
789 | + _Other -> Request#request.connection |
790 | + end, |
791 | + Request#request{ |
792 | + headers = [Header | Request#request.headers], |
793 | + connection = Connection}; |
794 | +parse_header(Header = {'Content-Length', Value}, Request) -> |
795 | + Request#request{ |
796 | + headers = [Header | Request#request.headers], |
797 | + length = list_to_integer(binary_to_list(Value))}; |
798 | +parse_header({<<"Expect">>, Value}, Request) -> |
799 | + Expect = case string:to_lower(binary_to_list(Value)) of |
800 | + "100-continue" -> continue; |
801 | + _Other -> Request#request.expect |
802 | + end, |
803 | + Request#request{ |
804 | + headers = [{<<"expect">>, Value} | Request#request.headers], |
805 | + expect = Expect}; |
806 | +parse_header(Header, Request) -> |
807 | + Request#request{headers = [Header | Request#request.headers]}. |
808 | + |
809 | +% @doc Set the body receive callback in a request record if needed. |
810 | +% @spec (Request::request(), Socket::term()) -> Request::request() |
811 | +set_recv(Request = #request{expect = Expect, length = Length}, Socket) |
812 | + when Length > 0 -> |
813 | + Request#request{body = fun(L) -> recv(L, Expect, Socket) end}; |
814 | +set_recv(Request, _Socket) -> |
815 | + Request. |
816 | + |
817 | +% @doc Receive a chunk of the request body. |
818 | +% @spec (Length::integer(), Expect::undefined | continue, Socket::term()) |
819 | +% -> {ok, Buffer, Recv} | {error, Reason} |
820 | +recv(Length, continue, Socket) -> |
821 | + case openstack_socket:send(Socket, "HTTP/1.1 100 Continue\r\n\r\n") of |
822 | + ok -> recv(Length, undefined, Socket); |
823 | + Error -> Error |
824 | + end; |
825 | +recv(Length, _Expect, Socket) -> |
826 | + case openstack_socket:recv(Socket, Length) of |
827 | + {ok, Buffer} -> {ok, Buffer, fun(L) -> recv(L, false, Socket) end}; |
828 | + Error -> Error |
829 | + end. |
830 | + |
831 | +% @doc Get a header value in a list of headers from a request record. |
832 | +% @spec (Name::atom() | binary(), Requst::request()) -> Value::binary() |
833 | +get_request_header(Name, #request{headers = Headers}) -> |
834 | + get_header(Name, Headers). |
835 | + |
836 | +% @doc Get a header value in a list of headers. |
837 | +% @spec (Name::atom() | binary(), Headers::[request_header()]) |
838 | +% -> Value::binary() |
839 | +get_header(_Name, []) -> |
840 | + undefined; |
841 | +get_header(Name, [{Name, Value} | _Headers]) -> |
842 | + Value; |
843 | +get_header(Name, [{Name2, Value} | Headers]) |
844 | + when is_binary(Name), is_binary(Name2) -> |
845 | + LowerName = string:to_lower(binary_to_list(Name)), |
846 | + LowerName2 = string:to_lower(binary_to_list(Name2)), |
847 | + case LowerName of |
848 | + LowerName2 -> Value; |
849 | + _Other -> get_header(LowerName, Headers) |
850 | + end; |
851 | +get_header(Name, [_Header | Headers]) -> |
852 | + get_header(Name, Headers). |
853 | + |
854 | +% @doc Add or replace a header in the response record. |
855 | +% @spec (Headers::[response_header()], Response::response()) |
856 | +% -> Response::response() |
857 | +set_response_headers([], Response) -> |
858 | + Response; |
859 | +set_response_headers([Header | Headers], |
860 | + Response = #response{headers = OldHeaders}) -> |
861 | + set_response_headers(Headers, |
862 | + Response#response{headers = set_header(Header, OldHeaders, [])}). |
863 | + |
864 | +% @doc Add or replace a header in the header list. |
865 | +% @spec (Header::response_header(), |
866 | +% Headers::[response_header()], |
867 | +% Skipped::[response_header()]) |
868 | +% -> Headers::[response_header()] |
869 | +set_header(Header, [], Skipped) -> |
870 | + lists:reverse(Skipped) ++ [Header]; |
871 | +set_header({Name, Value}, [{Name, _OldValue} | Headers], Skipped) -> |
872 | + lists:reverse(Skipped) ++ [{Name, Value}] ++ Headers; |
873 | +set_header(Header, [Skip | Headers], Skipped) -> |
874 | + set_header(Header, Headers, [Skip | Skipped]). |
875 | + |
876 | +% @doc Send a response based on the response record details. |
877 | +% @spec (Response::response(), Socket::term()) |
878 | +% -> persist | close | {error, Reason} |
879 | +send_response(Response = #response{status = Status}, Socket) -> |
880 | + pack_headers(<<"HTTP/1.1 ", (status_name(Status))/binary, "\r\n">>, |
881 | + Response#response.headers, |
882 | + Response, |
883 | + Socket). |
884 | + |
885 | +% @doc Pack all headers into the buffer. |
886 | +% @spec (Buffer::binary(), |
887 | +% Headers::[response_header()], |
888 | +% Response::response(), |
889 | +% Socket::term()) |
890 | +% -> persist | close | {error, Reason} |
891 | +pack_headers(Buffer, [], Response, Socket) -> |
892 | + pack_content_headers(Buffer, Response, Socket); |
893 | +pack_headers(Buffer, [Header | Headers], Response, Socket) -> |
894 | + pack_headers(pack_header(Buffer, Header), Headers, Response, Socket). |
895 | + |
896 | +% @doc Pack the header into the buffer, converting types as needed. |
897 | +% @spec (Buffer::binary(), Header::response_header()) -> Buffer::binary() |
898 | +pack_header(Buffer, {Name, Value}) when is_atom(Name) -> |
899 | + pack_header(Buffer, {atom_to_list(Name), Value}); |
900 | +pack_header(Buffer, {Name, Value}) when is_list(Name) -> |
901 | + pack_header(Buffer, {list_to_binary(Name), Value}); |
902 | +pack_header(Buffer, {Name, Value}) when is_integer(Value) -> |
903 | + pack_header(Buffer, {Name, integer_to_list(Value)}); |
904 | +pack_header(Buffer, {Name, Value}) when is_list(Value) -> |
905 | + pack_header(Buffer, {Name, list_to_binary(Value)}); |
906 | +pack_header(Buffer, {Name, Value}) -> |
907 | + <<Buffer/binary, Name/binary, ": ", Value/binary, "\r\n">>. |
908 | + |
909 | +% @doc Pack headers related to the response content. Specifically, this is |
910 | +% used to set the content-length or transfer-encoding headers. The order |
911 | +% of the clauses matter and is used to set priority of response options. |
912 | +% @spec (Buffer::binary(), Response::response(), Socket::term()) |
913 | +% -> persist | close | {error, Reason} |
914 | +pack_content_headers(Buffer, Response = #response{body = Body}, Socket) |
915 | + when is_binary(Body) -> |
916 | + pack_length_header(Buffer, Response#response{length = byte_size(Body)}, |
917 | + Socket); |
918 | +pack_content_headers(Buffer, Response = #response{length = Length}, Socket) |
919 | + when Length > 0 -> |
920 | + pack_length_header(Buffer, Response, Socket); |
921 | +pack_content_headers(Buffer, |
922 | + Response = #response{body = Body, chunked = allowed}, |
923 | + Socket) |
924 | + when is_function(Body) -> |
925 | + pack_connection_header(<<Buffer/binary, "Transfer-Encoding: chunked\r\n">>, |
926 | + Response#response{chunked = true}, Socket); |
927 | +pack_content_headers(Buffer, |
928 | + Response = #response{body = Body, chunked = false}, |
929 | + Socket) |
930 | + when is_function(Body) -> |
931 | + pack_connection_header(Buffer, Response#response{connection = close}, |
932 | + Socket); |
933 | +pack_content_headers(Buffer, Response, Socket) -> |
934 | + pack_length_header(Buffer, Response, Socket). |
935 | + |
936 | +% @doc Send a length header. |
937 | +% @spec (Buffer::binary(), Response::response(), Socket::term()) |
938 | +% -> persist | close | {error, Reason} |
939 | +pack_length_header(Buffer, Response = #response{length = Length}, Socket) -> |
940 | + pack_connection_header(<<Buffer/binary, "Content-Length: ", |
941 | + (list_to_binary(integer_to_list(Length)))/binary, "\r\n">>, |
942 | + Response, Socket). |
943 | + |
944 | +% @doc Send a connection header if needed. |
945 | +% @spec (Buffer::binary(), Response::response(), Socket::term()) |
946 | +% -> persist | close | {error, Reason} |
947 | +pack_connection_header(Buffer, Response = #response{connection = close}, |
948 | + Socket) -> |
949 | + send_body(<<Buffer/binary, "Connection: close\r\n">>, Response, Socket); |
950 | +pack_connection_header(Buffer, Response = #response{connection = keep_alive}, |
951 | + Socket) -> |
952 | + send_body(<<Buffer/binary, "Connection: Keep-Alive\r\n">>, Response, |
953 | + Socket); |
954 | +pack_connection_header(Buffer, Response, Socket) -> |
955 | + send_body(Buffer, Response, Socket). |
956 | + |
957 | +% @doc Send the headers and body, streaming from a callback if needed. |
958 | +% @spec (Headers::binary(), Response::response(), Socket::term()) |
959 | +% -> persist | close | {error, Reason} |
960 | +send_body(Headers, Response = #response{body = undefined}, Socket) -> |
961 | + end_response(<<Headers/binary, "\r\n">>, Response, Socket); |
962 | +send_body(Headers, Response = #response{body = Body}, Socket) |
963 | + when is_binary(Body) -> |
964 | + end_response(<<Headers/binary, "\r\n", Body/binary>>, Response, Socket); |
965 | +send_body(Headers, Response = #response{chunked = Chunked, body = Body}, Socket) |
966 | + when is_function(Body) -> |
967 | + case openstack_socket:send(Socket, <<Headers/binary, "\r\n">>) of |
968 | + ok -> |
969 | + case Body(fun (Buffer) -> send(Buffer, Chunked, Socket) end) of |
970 | + ok -> end_response(Response, Socket); |
971 | + Error -> Error |
972 | + end; |
973 | + Error -> |
974 | + Error |
975 | + end. |
976 | + |
977 | +% @doc Callback function for other modules to send a chunk of the body. |
978 | +% @spec (Buffer::binary(), Chunked::true | false | allowed, Socket::term()) |
979 | +% -> ok | {error, Reason} |
980 | +send(Buffer, _Chunked, _Socket) when byte_size(Buffer) == 0 -> |
981 | + ok; |
982 | +send(Buffer, true, Socket) -> |
983 | + openstack_socket:send(Socket, [ |
984 | + io_lib:format("~.16b\r\n", [byte_size(Buffer)]), |
985 | + Buffer, |
986 | + <<"\r\n">>]); |
987 | +send(Buffer, _Chunked, Socket) -> |
988 | + openstack_socket:send(Socket, Buffer). |
989 | + |
990 | +% @doc Write the buffer and end the response. |
991 | +% @spec (Buffer::binary(), Response::response(), Socket::term()) |
992 | +% -> persist | close | {error, Reason} |
993 | +end_response(Buffer, Response, Socket) -> |
994 | + case openstack_socket:send(Socket, Buffer) of |
995 | + ok -> end_response(Response); |
996 | + Error -> Error |
997 | + end. |
998 | + |
999 | +% @doc Write the last chunk if needed and end the response. |
1000 | +% @spec (Response::response(), Socket::term()) |
1001 | +% -> persist | close | {error, Reason} |
1002 | +end_response(Response = #response{chunked = true}, Socket) -> |
1003 | + case openstack_socket:send(Socket, <<"0\r\n\r\n">>) of |
1004 | + ok -> end_response(Response); |
1005 | + Error -> Error |
1006 | + end; |
1007 | +end_response(Response, _Socket) -> |
1008 | + end_response(Response). |
1009 | + |
1010 | +% @doc End the response. |
1011 | +% @spec (Response::response()) -> persist | close |
1012 | +end_response(#response{connection = close}) -> |
1013 | + close; |
1014 | +end_response(_Response) -> |
1015 | + persist. |
1016 | + |
1017 | +% @doc Get the status name for a status code. |
1018 | +% @spec (Statue::integer()) -> StatusName::binary() |
1019 | +status_name(100) -> <<"100 Continue">>; |
1020 | +status_name(101) -> <<"101 Switching Protocols">>; |
1021 | +status_name(200) -> <<"200 OK">>; |
1022 | +status_name(201) -> <<"201 Created">>; |
1023 | +status_name(202) -> <<"202 Accepted">>; |
1024 | +status_name(203) -> <<"203 Non-Authoritative Information">>; |
1025 | +status_name(204) -> <<"204 No Content">>; |
1026 | +status_name(205) -> <<"205 Reset Content">>; |
1027 | +status_name(206) -> <<"206 Partial Content">>; |
1028 | +status_name(300) -> <<"300 Multiple Choices">>; |
1029 | +status_name(301) -> <<"301 Moved Permanently">>; |
1030 | +status_name(302) -> <<"302 Found">>; |
1031 | +status_name(303) -> <<"303 See Other">>; |
1032 | +status_name(304) -> <<"304 Not Modified">>; |
1033 | +status_name(305) -> <<"305 Use Proxy">>; |
1034 | +status_name(307) -> <<"307 Temporary Redirect">>; |
1035 | +status_name(400) -> <<"400 Bad Request">>; |
1036 | +status_name(401) -> <<"401 Unauthorized">>; |
1037 | +status_name(402) -> <<"402 Payment Required">>; |
1038 | +status_name(403) -> <<"403 Forbidden">>; |
1039 | +status_name(404) -> <<"404 Not Found">>; |
1040 | +status_name(405) -> <<"405 Method Not Allowed">>; |
1041 | +status_name(406) -> <<"406 Not Acceptable">>; |
1042 | +status_name(407) -> <<"407 Proxy Authentication Required">>; |
1043 | +status_name(408) -> <<"408 Request Timeout">>; |
1044 | +status_name(409) -> <<"409 Conflict">>; |
1045 | +status_name(410) -> <<"410 Gone">>; |
1046 | +status_name(411) -> <<"411 Length Required">>; |
1047 | +status_name(412) -> <<"412 Precondition Failed">>; |
1048 | +status_name(413) -> <<"413 Request Entity Too Large">>; |
1049 | +status_name(414) -> <<"414 Request-URI Too Long">>; |
1050 | +status_name(415) -> <<"415 Unsupported Media Type">>; |
1051 | +status_name(416) -> <<"416 Requested Range Not Satisfiable">>; |
1052 | +status_name(417) -> <<"417 Expectation Failed">>; |
1053 | +status_name(500) -> <<"500 Internal Server Error">>; |
1054 | +status_name(501) -> <<"501 Not Implemented">>; |
1055 | +status_name(502) -> <<"502 Bad Gateway">>; |
1056 | +status_name(503) -> <<"503 Service Unavailable">>; |
1057 | +status_name(504) -> <<"504 Gateway Timeout">>; |
1058 | +status_name(505) -> <<"505 HTTP Version Not Supported">>; |
1059 | +status_name(Status) -> <<(list_to_binary(integer_to_list(Status)))/binary, |
1060 | + " Unknown">>. |
1061 | + |
1062 | +% |
1063 | +% Test functions. |
1064 | +% |
1065 | + |
1066 | +-ifdef(TEST). |
1067 | +-include_lib("eunit/include/eunit.hrl"). |
1068 | + |
1069 | +server_test_socket() -> |
1070 | + Options = [binary, {active, false}, {nodelay, true}], |
1071 | + {ok, Socket} = openstack_socket:connect(tcp, "localhost", 32123, Options), |
1072 | + Socket. |
1073 | + |
1074 | +server_test_request(Socket, Request, Length) -> |
1075 | + ?assertEqual(ok, openstack_socket:send(Socket, Request)), |
1076 | + case openstack_socket:recv(Socket, Length) of |
1077 | + {ok, Response} -> Response; |
1078 | + Error -> Error |
1079 | + end. |
1080 | + |
1081 | +-define(assertRequest(Socket, Request, Response), |
1082 | + ?assertEqual(Response, |
1083 | + server_test_request(Socket, Request, byte_size(Response)))). |
1084 | + |
1085 | +server_basic_test() -> |
1086 | + ?assertEqual(ok, openstack:start()), |
1087 | + ?assertEqual(ok, |
1088 | + server([32123], <<"test">>, fun server_basic_handle/3, undefined)), |
1089 | + Socket = server_test_socket(), |
1090 | + <<"HTTP/1.1 200 OK\r\n" |
1091 | + "Date: ", _Date:29/binary, "\r\n" |
1092 | + "Server: OpenStack (test)\r\n" |
1093 | + "Content-Length: 0\r\n" |
1094 | + "Connection: close\r\n\r\n">> = |
1095 | + server_test_request(Socket, <<"GET / HTTP/1.0\r\n\r\n">>, 0), |
1096 | + ?assertEqual({error, closed}, openstack_socket:recv(Socket, 0)), |
1097 | + ?assertEqual(ok, openstack:stop()). |
1098 | + |
1099 | +server_basic_handle(_Request, Response, State) -> |
1100 | + {Response, State}. |
1101 | + |
1102 | +server_connection_test() -> |
1103 | + ?assertEqual(ok, openstack:start()), |
1104 | + ?assertEqual(ok, |
1105 | + server([32123], <<"test">>, fun server_connection_handle/3, undefined)), |
1106 | + Socket = server_test_socket(), |
1107 | + ?assertRequest(Socket, |
1108 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1109 | + <<"HTTP/1.1 200 OK\r\n" |
1110 | + "Content-Length: 0\r\n\r\n">>), |
1111 | + ?assertRequest(Socket, |
1112 | + <<"GET / HTTP/1.1\r\n" |
1113 | + "Connection: keep-alive\r\n\r\n">>, |
1114 | + <<"HTTP/1.1 200 OK\r\n" |
1115 | + "Content-Length: 0\r\n" |
1116 | + "Connection: Keep-Alive\r\n\r\n">>), |
1117 | + ?assertRequest(Socket, |
1118 | + <<"GET / HTTP/1.1\r\n" |
1119 | + "Connection: other\r\n\r\n">>, |
1120 | + <<"HTTP/1.1 200 OK\r\n" |
1121 | + "Content-Length: 0\r\n\r\n">>), |
1122 | + ?assertRequest(Socket, |
1123 | + <<"GET / HTTP/1.1\r\n" |
1124 | + "Connection: close\r\n\r\n">>, |
1125 | + <<"HTTP/1.1 200 OK\r\n" |
1126 | + "Content-Length: 0\r\n" |
1127 | + "Connection: close\r\n\r\n">>), |
1128 | + ?assertEqual({error, closed}, openstack_socket:recv(Socket, 0)), |
1129 | + Socket2 = server_test_socket(), |
1130 | + ?assertRequest(Socket2, |
1131 | + <<"GET / HTTP/1.0\r\n" |
1132 | + "Connection: Keep-Alive\r\n\r\n">>, |
1133 | + <<"HTTP/1.1 200 OK\r\n" |
1134 | + "Content-Length: 0\r\n" |
1135 | + "Connection: Keep-Alive\r\n\r\n">>), |
1136 | + ?assertRequest(Socket2, |
1137 | + <<"GET / HTTP/1.0\r\n" |
1138 | + "Connection: close\r\n\r\n">>, |
1139 | + <<"HTTP/1.1 200 OK\r\n" |
1140 | + "Content-Length: 0\r\n" |
1141 | + "Connection: close\r\n\r\n">>), |
1142 | + ?assertEqual({error, closed}, openstack_socket:recv(Socket2, 0)), |
1143 | + Socket3 = server_test_socket(), |
1144 | + ?assertRequest(Socket3, |
1145 | + <<"GET / HTTP/1.0\r\n" |
1146 | + "Connection: other\r\n\r\n">>, |
1147 | + <<"HTTP/1.1 200 OK\r\n" |
1148 | + "Content-Length: 0\r\n" |
1149 | + "Connection: close\r\n\r\n">>), |
1150 | + ?assertEqual({error, closed}, openstack_socket:recv(Socket3, 0)), |
1151 | + ?assertEqual(ok, openstack:stop()). |
1152 | + |
1153 | +server_connection_handle(_Request, Response, State) -> |
1154 | + {Response#response{headers = []}, State}. |
1155 | + |
1156 | +server_error_test() -> |
1157 | + ?assertEqual(ok, openstack:start()), |
1158 | + ?assertEqual(ok, |
1159 | + server([32123], <<"test">>, fun server_connection_handle/3, undefined)), |
1160 | + Socket = server_test_socket(), |
1161 | + ?assertEqual(ok, openstack_socket:send(Socket, <<"GET * BAD\r\n">>)), |
1162 | + {ok, << |
1163 | + "HTTP/1.1 400 Bad Request\r\n" |
1164 | + "Date: ", _Date:29/binary, "\r\n" |
1165 | + "Server: OpenStack (test)\r\n" |
1166 | + "Content-Length: 0\r\n" |
1167 | + "Connection: close\r\n\r\n">>} = openstack_socket:recv(Socket, 0), |
1168 | + openstack_socket:close(server_test_socket()), |
1169 | + ?assertEqual(ok, openstack:stop()). |
1170 | + |
1171 | +server_uri_test() -> |
1172 | + ?assertEqual(ok, openstack:start()), |
1173 | + ?assertEqual(ok, |
1174 | + server([32123], <<"test">>, fun server_connection_handle/3, undefined)), |
1175 | + Socket = server_test_socket(), |
1176 | + ?assertRequest(Socket, |
1177 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1178 | + <<"HTTP/1.1 200 OK\r\n" |
1179 | + "Content-Length: 0\r\n\r\n">>), |
1180 | + ?assertRequest(Socket, |
1181 | + <<"GET test HTTP/1.1\r\n\r\n">>, |
1182 | + <<"HTTP/1.1 200 OK\r\n" |
1183 | + "Content-Length: 0\r\n\r\n">>), |
1184 | + ?assertRequest(Socket, |
1185 | + <<"GET * HTTP/1.1\r\n\r\n">>, |
1186 | + <<"HTTP/1.1 200 OK\r\n" |
1187 | + "Content-Length: 0\r\n\r\n">>), |
1188 | + ?assertEqual(ok, openstack:stop()). |
1189 | + |
1190 | +server_header_test() -> |
1191 | + ?assertEqual(ok, openstack:start()), |
1192 | + ?assertEqual(ok, |
1193 | + server([32123], <<"test">>, fun server_header_handle/3, undefined)), |
1194 | + ?assertRequest(server_test_socket(), |
1195 | + <<"GET / HTTP/1.0\r\n" |
1196 | + "Host: localhost\r\n" |
1197 | + "X-Test: test\r\n" |
1198 | + "Expect: other\r\n" |
1199 | + "Content-Length: 0\r\n\r\n">>, |
1200 | + <<"HTTP/1.1 200 OK\r\n" |
1201 | + "Date: x\r\n" |
1202 | + "Server: 1\r\n" |
1203 | + "X-Test: value\r\n" |
1204 | + "Content-Length: 0\r\n" |
1205 | + "Connection: close\r\n\r\n">>), |
1206 | + ?assertEqual(ok, openstack:stop()). |
1207 | + |
1208 | +server_header_handle(Request, Response, State) -> |
1209 | + <<"test">> = get_request_header(<<"X-Test">>, Request), |
1210 | + <<"test">> = get_request_header(<<"x-test">>, Request), |
1211 | + undefined = get_request_header(<<"x-notfound">>, Request), |
1212 | + {set_response_headers([ |
1213 | + {'Date', <<"x">>}, |
1214 | + {'Server', 1}, |
1215 | + {"X-Test", "value"}], |
1216 | + Response), State}. |
1217 | + |
1218 | +server_request_body_test() -> |
1219 | + ?assertEqual(ok, openstack:start()), |
1220 | + ?assertEqual(ok, |
1221 | + server([32123], <<"test">>, fun server_request_body_handle/3, 0)), |
1222 | + Socket = server_test_socket(), |
1223 | + ?assertRequest(Socket, |
1224 | + <<"GET / HTTP/1.1\r\n" |
1225 | + "Content-Length: 4\r\n\r\n" |
1226 | + "body">>, |
1227 | + <<"HTTP/1.1 200 OK\r\n" |
1228 | + "X-Body: body\r\n" |
1229 | + "Content-Length: 0\r\n\r\n">>), |
1230 | + ?assertRequest(Socket, |
1231 | + <<"GET / HTTP/1.1\r\n" |
1232 | + "Expect: 100-continue\r\n" |
1233 | + "Content-Length: 4\r\n\r\n">>, |
1234 | + <<"HTTP/1.1 100 Continue\r\n\r\n">>), |
1235 | + ?assertRequest(Socket, |
1236 | + <<"body">>, |
1237 | + <<"HTTP/1.1 200 OK\r\n" |
1238 | + "X-Body: body\r\n" |
1239 | + "Content-Length: 0\r\n\r\n">>), |
1240 | + ?assertEqual(ok, openstack:stop()). |
1241 | + |
1242 | +server_request_body_handle(#request{length = Length, body = Body}, Response, |
1243 | + State) -> |
1244 | + {ok, Buffer, _Body} = Body(Length), |
1245 | + {Response#response{headers = [{<<"X-Body">>, Buffer}]}, State}. |
1246 | + |
1247 | +server_response_body_test() -> |
1248 | + ?assertEqual(ok, openstack:start()), |
1249 | + ?assertEqual(ok, |
1250 | + server([32123], <<"test">>, fun server_response_body_handle/3, 0)), |
1251 | + Socket = server_test_socket(), |
1252 | + ?assertRequest(Socket, |
1253 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1254 | + <<"HTTP/1.1 200 OK\r\n" |
1255 | + "Content-Length: 4\r\n\r\n" |
1256 | + "body">>), |
1257 | + ?assertRequest(Socket, |
1258 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1259 | + <<"HTTP/1.1 200 OK\r\n" |
1260 | + "Content-Length: 4\r\n\r\n" |
1261 | + "body">>), |
1262 | + ?assertRequest(Socket, |
1263 | + <<"GET / HTTP/1.0\r\n\r\n">>, |
1264 | + <<"HTTP/1.1 200 OK\r\n" |
1265 | + "Connection: close\r\n\r\n" |
1266 | + "body">>), |
1267 | + ?assertEqual(ok, openstack:stop()). |
1268 | + |
1269 | +server_response_body_handle(_Request, Response, 0) -> |
1270 | + {Response#response{headers = [], body = <<"body">>}, 1}; |
1271 | +server_response_body_handle(_Request, Response, 1) -> |
1272 | + {Response#response{ |
1273 | + headers = [], |
1274 | + length = 4, |
1275 | + body = fun(Send) -> Send(<<"body">>), Send(<<>>) end}, 2}; |
1276 | +server_response_body_handle(_Request, Response, State) -> |
1277 | + {Response#response{ |
1278 | + headers = [], |
1279 | + body = fun(Send) -> Send(<<"body">>), Send(<<>>) end}, State}. |
1280 | + |
1281 | +server_response_chunked_test() -> |
1282 | + ?assertEqual(ok, openstack:start()), |
1283 | + ?assertEqual(ok, |
1284 | + server([32123], <<"test">>, fun server_response_chunked_handle/3, 0)), |
1285 | + Socket = server_test_socket(), |
1286 | + ?assertRequest(Socket, |
1287 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1288 | + <<"HTTP/1.1 200 OK\r\n" |
1289 | + "Transfer-Encoding: chunked\r\n\r\n" |
1290 | + "0\r\n\r\n">>), |
1291 | + ?assertRequest(Socket, |
1292 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1293 | + <<"HTTP/1.1 200 OK\r\n" |
1294 | + "Transfer-Encoding: chunked\r\n\r\n" |
1295 | + "4\r\nbody\r\n0\r\n\r\n">>), |
1296 | + ?assertRequest(Socket, |
1297 | + <<"GET / HTTP/1.1\r\n\r\n">>, |
1298 | + <<"HTTP/1.1 200 OK\r\n" |
1299 | + "Transfer-Encoding: chunked\r\n\r\n" |
1300 | + "3\r\none\r\n3\r\ntwo\r\n0\r\n\r\n">>), |
1301 | + ?assertEqual(ok, openstack:stop()). |
1302 | + |
1303 | +server_response_chunked_handle(_Request, Response, 0) -> |
1304 | + {Response#response{ |
1305 | + headers = [], |
1306 | + body = fun(_Send) -> ok end}, 1}; |
1307 | +server_response_chunked_handle(_Request, Response, 1) -> |
1308 | + {Response#response{ |
1309 | + headers = [], |
1310 | + body = fun(Send) -> Send(<<"body">>), Send(<<>>) end}, 2}; |
1311 | +server_response_chunked_handle(_Request, Response, State) -> |
1312 | + {Response#response{ |
1313 | + headers = [], |
1314 | + body = fun(Send) -> Send(<<"one">>), Send(<<"two">>) end}, State}. |
1315 | + |
1316 | +parse_uri_test() -> |
1317 | + ?assertEqual({'*', []}, parse_uri('*')), |
1318 | + ?assertEqual({<<"*">>, []}, parse_uri(<<"*">>)), |
1319 | + ?assertEqual({<<"/">>, []}, parse_uri(<<"/">>)), |
1320 | + ?assertEqual({<<"/a/b/c">>, []}, parse_uri(<<"/a/b/c">>)), |
1321 | + ?assertEqual({<<"/a/b/c">>, [{<<"d">>, <<"e">>}, {<<"f">>, <<>>}]}, |
1322 | + parse_uri(<<"/a/b/c?d=e&f">>)), |
1323 | + ?assertEqual({<<"/">>, [{<<"a">>, <<>>}]}, parse_uri(<<"/?&&a&&">>)), |
1324 | + ?assertEqual({<<"/">>, [{<<"a">>, <<>>}]}, parse_uri(<<"/?a=">>)), |
1325 | + ?assertEqual({<<"/">>, [{<<"a">>, <<"1">>}]}, parse_uri(<<"/?a=1">>)), |
1326 | + ?assertEqual({<<"">>, []}, parse_uri(<<"?">>)), |
1327 | + ?assertEqual({<<"&">>, []}, parse_uri(<<"&">>)). |
1328 | + |
1329 | +status_name_test() -> |
1330 | + status_name_scan(0). |
1331 | + |
1332 | +status_name_scan(StatusCode = 600) -> |
1333 | + ?assertEqual(<<"600 Unknown">>, status_name(StatusCode)); |
1334 | +status_name_scan(StatusCode = 200) -> |
1335 | + ?assertEqual(<<"200 OK">>, status_name(StatusCode)), |
1336 | + status_name_scan(StatusCode + 1); |
1337 | +status_name_scan(StatusCode = 404) -> |
1338 | + ?assertEqual(<<"404 Not Found">>, status_name(StatusCode)), |
1339 | + status_name_scan(StatusCode + 1); |
1340 | +status_name_scan(StatusCode) -> |
1341 | + status_name(StatusCode), |
1342 | + status_name_scan(StatusCode + 1). |
1343 | + |
1344 | +-endif. |
1345 | |
1346 | === added file 'apps/openstack/src/openstack_server.erl' |
1347 | --- apps/openstack/src/openstack_server.erl 1970-01-01 00:00:00 +0000 |
1348 | +++ apps/openstack/src/openstack_server.erl 2011-02-28 07:20:04 +0000 |
1349 | @@ -0,0 +1,221 @@ |
1350 | +% Copyright (C) 2011 OpenStack LLC. |
1351 | +% |
1352 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
1353 | +% you may not use this file except in compliance with the License. |
1354 | +% You may obtain a copy of the License at |
1355 | +% |
1356 | +% http://www.apache.org/licenses/LICENSE-2.0 |
1357 | +% |
1358 | +% Unless required by applicable law or agreed to in writing, software |
1359 | +% distributed under the License is distributed on an "AS IS" BASIS, |
1360 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1361 | +% See the License for the specific language governing permissions and |
1362 | +% limitations under the License. |
1363 | + |
1364 | +% @doc Generic server module to help in building more specific servers. This |
1365 | +% module has a supervisor process and adds one child process to that |
1366 | +% supervisor for each listener socket created. |
1367 | + |
1368 | +-module(openstack_server). |
1369 | + |
1370 | +-export([start/3, listen/2, accept/2, set_options/2]). |
1371 | + |
1372 | +-behaviour(supervisor). |
1373 | +-export([child_spec/0, init/1]). |
1374 | + |
1375 | +-include("openstack_logging.hrl"). |
1376 | + |
1377 | +% @type state() = #state{accept = function(), callback_state = term()} |
1378 | +-record(state, {accept, callback_state}). |
1379 | + |
1380 | +% @doc Start up listener sockets on the given ports. This starts a new |
1381 | +% process for each port in the list and attaches them to the supervisor |
1382 | +% process. |
1383 | +% @spec (Ports::ports(), Accept::function(), CallbackState::term()) |
1384 | +% -> ok | {error, Reason} |
1385 | +% @type ports() = [ |
1386 | +% Port::integer() | |
1387 | +% {Type::port_type(), Port::integer()} | |
1388 | +% normalized_port()] |
1389 | +% @type normalized_port() = |
1390 | +% {Type::port_type(), Port::integer(), Options::[option()]} |
1391 | +% @type port_type() = tcp | ssl |
1392 | +% @type option() = [{atom(), term()}] |
1393 | +% @type normalized_ports() = [normalized_port()] |
1394 | +start(Ports, Accept, CallbackState) -> |
1395 | + start_port(normalize_ports(Ports, []), #state{ |
1396 | + accept = Accept, |
1397 | + callback_state = CallbackState}). |
1398 | + |
1399 | +% @doc Start a new listener process for the first port in the list, then |
1400 | +% loop to the next. |
1401 | +% @spec (Ports::normalized_ports(), State::state()) -> ok |
1402 | +start_port([Port | Ports], State) -> |
1403 | + ChildSpec = { |
1404 | + Port, |
1405 | + {proc_lib, start_link, [?MODULE, listen, [Port, State]]}, |
1406 | + transient, |
1407 | + brutal_kill, |
1408 | + worker, |
1409 | + [?MODULE]}, |
1410 | + case supervisor:start_child(?MODULE, ChildSpec) of |
1411 | + {ok, _Pid} -> |
1412 | + start_port(Ports, State); |
1413 | + {error, Reason} -> |
1414 | + throw(Reason) |
1415 | + end; |
1416 | +start_port([], _State) -> |
1417 | + ok. |
1418 | + |
1419 | +% @doc Bind a listening socket and enter the accept loop. |
1420 | +% @spec (Port::normalized_port(), State::state()) -> none() |
1421 | +listen(Port, State) -> |
1422 | + Listener = try_listen(Port, 10), |
1423 | + proc_lib:init_ack({ok, self()}), |
1424 | + ?INFO("Now listening on port: ~p~n", [Port]), |
1425 | + spawn(?MODULE, accept, [Listener, State]), |
1426 | + % Wait forever to keep the listener socket open. |
1427 | + receive |
1428 | + after infinity -> |
1429 | + ok |
1430 | + end. |
1431 | + |
1432 | +% @doc Try listening on a socket, retrying every 100ms for the number of |
1433 | +% retry times given. |
1434 | +% @spec (Port::normalized_port(), Retries::integer()) -> Listener::term() |
1435 | +try_listen(_Port, 0) -> |
1436 | + throw({listen_failed, eaddrinuse}); |
1437 | +try_listen({Type, Port, Options}, Retries) -> |
1438 | + NewOptions = set_options(Options, [ |
1439 | + binary, |
1440 | + {active, false}, |
1441 | + {backlog, 1024}, |
1442 | + {nodelay, true}, |
1443 | + {reuseaddr, true}]), |
1444 | + case openstack_socket:listen(Type, Port, NewOptions) of |
1445 | + {ok, Listener} -> |
1446 | + Listener; |
1447 | + {error, eaddrinuse} -> |
1448 | + timer:sleep(100), |
1449 | + try_listen({Type, Port, Options}, Retries - 1); |
1450 | + {error, Reason} -> |
1451 | + throw({listen_failed, Reason}) |
1452 | + end. |
1453 | + |
1454 | +% @doc Accept a new connection and spawn a process for the next one. |
1455 | +% @spec (Listener::term(), State::state()) -> none() |
1456 | +accept(Listener, State = #state{ |
1457 | + accept = Accept, |
1458 | + callback_state = CallbackState}) -> |
1459 | + case openstack_socket:accept(Listener) of |
1460 | + {ok, Socket} -> |
1461 | + spawn(?MODULE, accept, [Listener, State]), |
1462 | + openstack_socket:setopts(Socket, [{buffer, 8192}, {nodelay, true}]), |
1463 | + Accept(Socket, CallbackState), |
1464 | + openstack_socket:close(Socket); |
1465 | + {error, closed} -> |
1466 | + ok; |
1467 | + {error, Reason} -> |
1468 | + spawn(?MODULE, accept, [Listener, State]), |
1469 | + ?WARNING("Error accepting socket: ~p~n", [Reason]) |
1470 | + end. |
1471 | + |
1472 | +% @doc Merge listen options. |
1473 | +% @spec (Options::[option()], Result::[option()]) -> Result::[option()] |
1474 | +set_options([], Result) -> |
1475 | + Result; |
1476 | +set_options([{Key, Value} | Options], Result) -> |
1477 | + set_options(Options, [{Key, Value} | proplists:delete(Key, Result)]); |
1478 | +set_options([Key | Options], Result) -> |
1479 | + set_options(Options, [Key | proplists:delete(Key, Result)]). |
1480 | + |
1481 | +% @doc Normalize a list of mixed types representing port descriptions. |
1482 | +% @spec (Ports::ports(), Result::normalized_ports()) |
1483 | +% -> Result::normalized_ports() |
1484 | +normalize_ports([], Result) -> |
1485 | + lists:reverse(Result); |
1486 | +normalize_ports([{Type, Port, Options} | Ports], Result) -> |
1487 | + normalize_ports(Ports, [{Type, Port, Options} | Result]); |
1488 | +normalize_ports([{Type, Port} | Ports], Result) -> |
1489 | + normalize_ports([{Type, Port, []} | Ports], Result); |
1490 | +normalize_ports([Port | Ports], Result) when is_integer(Port) -> |
1491 | + normalize_ports(Ports, [{tcp, Port, []} | Result]); |
1492 | +normalize_ports([Port | _Ports], _Result) -> |
1493 | + throw({bad_port_type, Port}); |
1494 | +normalize_ports(Ports, _Result) -> |
1495 | + throw({bad_port_list, Ports}). |
1496 | + |
1497 | +% |
1498 | +% Callbacks for the supervisor behaviour. |
1499 | +% |
1500 | + |
1501 | +% @doc Get the child_spec for the supervisor process. See supervisor(3erl) |
1502 | +% for a description of the child_spec() type. |
1503 | +% @spec () -> child_spec() |
1504 | +child_spec() -> |
1505 | + {?MODULE, |
1506 | + {supervisor, start_link, [{local, ?MODULE}, ?MODULE, []]}, |
1507 | + permanent, |
1508 | + infinity, |
1509 | + supervisor, |
1510 | + [?MODULE]}. |
1511 | + |
1512 | +% @doc Init callback for the supervisor behaviour. This doesn't specify |
1513 | +% any child processes, they are added later when the servers are started |
1514 | +% in start(). See supervisor(3erl) for more details. |
1515 | +init(_Args) -> |
1516 | + {ok, {{one_for_one, 10, 10}, []}}. |
1517 | + |
1518 | +% |
1519 | +% Test functions. |
1520 | +% |
1521 | + |
1522 | +-ifdef(TEST). |
1523 | +-include_lib("eunit/include/eunit.hrl"). |
1524 | + |
1525 | +start_test() -> |
1526 | + ?assertEqual(ok, openstack:start()), |
1527 | + ?assertEqual(ok, |
1528 | + start([{tcp, 32123, [binary, {nodelay, true}]}], fun test_accept/2, |
1529 | + undefined)), |
1530 | + Options = [binary, {active, false}, {nodelay, true}], |
1531 | + {ok, Socket} = openstack_socket:connect(tcp, "localhost", 32123, Options), |
1532 | + ?assertEqual({ok, <<"test">>}, openstack_socket:recv(Socket, 0)), |
1533 | + {ok, Socket2} = |
1534 | + openstack_socket:connect(tcp, "localhost", 32123, Options, 5), |
1535 | + ?assertEqual({ok, <<"test">>}, openstack_socket:recv(Socket2, 0, 5)), |
1536 | + ?assertEqual(ok, openstack:stop()). |
1537 | + |
1538 | +ssl_start_test() -> |
1539 | + ?assertEqual(ok, openstack:start()), |
1540 | + ?assertEqual(ok, |
1541 | + start([{ssl, 32123, [ |
1542 | + {certfile, "../example.com-cert.pem"}, |
1543 | + {keyfile, "../example.com-key.pem"}]}], |
1544 | + fun test_accept/2, undefined)), |
1545 | + Options = [binary, {active, false}, {nodelay, true}], |
1546 | + {ok, Socket} = openstack_socket:connect(ssl, "localhost", 32123, Options), |
1547 | + ?assertEqual({ok, <<"test">>}, openstack_socket:recv(Socket, 0)), |
1548 | + {ok, Socket2} = |
1549 | + openstack_socket:connect(ssl, "localhost", 32123, Options, 5), |
1550 | + ?assertEqual({ok, <<"test">>}, openstack_socket:recv(Socket2, 0, 5)), |
1551 | + ?assertEqual(ok, openstack:stop()). |
1552 | + |
1553 | +test_accept(Socket, _State) -> |
1554 | + ?assertEqual({ok, [{active, false}]}, |
1555 | + openstack_socket:getopts(Socket, [active])), |
1556 | + {ok, {Address, 32123}} = openstack_socket:sockname(Socket), |
1557 | + {ok, {Address, _Port}} = openstack_socket:peername(Socket), |
1558 | + ?assertEqual(ok, openstack_socket:send(Socket, <<"test">>)). |
1559 | + |
1560 | +normalize_ports_test_() -> [ |
1561 | + ?_assertEqual([], normalize_ports([], [])), |
1562 | + ?_assertEqual([{tcp, 1, []}, {tcp, 2, []}, {tcp, 3, []}], |
1563 | + normalize_ports([1, {tcp, 2}, {tcp, 3, []}], [])), |
1564 | + ?_assertEqual([{ssl, 1, []}, {ssl, 2, []}], |
1565 | + normalize_ports([{ssl, 1}, {ssl, 2, []}], [])), |
1566 | + ?_assertException(throw, {bad_port_type, <<0>>}, |
1567 | + normalize_ports([<<0>>], [])), |
1568 | + ?_assertException(throw, {bad_port_list, 1}, normalize_ports(1, []))]. |
1569 | + |
1570 | +-endif. |
1571 | |
1572 | === added file 'apps/openstack/src/openstack_socket.erl' |
1573 | --- apps/openstack/src/openstack_socket.erl 1970-01-01 00:00:00 +0000 |
1574 | +++ apps/openstack/src/openstack_socket.erl 2011-02-28 07:20:04 +0000 |
1575 | @@ -0,0 +1,127 @@ |
1576 | +% Copyright (C) 2011 OpenStack LLC. |
1577 | +% |
1578 | +% Licensed under the Apache License, Version 2.0 (the "License"); |
1579 | +% you may not use this file except in compliance with the License. |
1580 | +% You may obtain a copy of the License at |
1581 | +% |
1582 | +% http://www.apache.org/licenses/LICENSE-2.0 |
1583 | +% |
1584 | +% Unless required by applicable law or agreed to in writing, software |
1585 | +% distributed under the License is distributed on an "AS IS" BASIS, |
1586 | +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
1587 | +% See the License for the specific language governing permissions and |
1588 | +% limitations under the License. |
1589 | + |
1590 | +% @doc Wrapper for socket functions to abstract away TCP and SSL |
1591 | +% differences. See the gen_tcp, inet, and ssl module documentation for |
1592 | +% details. |
1593 | + |
1594 | +-module(openstack_socket). |
1595 | + |
1596 | +-export([connect/4, connect/5, listen/3, accept/1, peername/1, sockname/1, |
1597 | + getopts/2, setopts/2, recv/2, recv/3, send/2, close/1]). |
1598 | + |
1599 | +connect(ssl, Address, Port, Options) -> |
1600 | + case ssl:connect(Address, Port, Options) of |
1601 | + {ok, Socket} -> |
1602 | + {ok, {ssl, Socket}}; |
1603 | + Error -> |
1604 | + Error |
1605 | + end; |
1606 | +connect(tcp, Address, Port, Options) -> |
1607 | + case gen_tcp:connect(Address, Port, Options) of |
1608 | + {ok, Socket} -> |
1609 | + {ok, {tcp, Socket}}; |
1610 | + Error -> |
1611 | + Error |
1612 | + end. |
1613 | + |
1614 | +connect(ssl, Address, Port, Options, Timeout) -> |
1615 | + case ssl:connect(Address, Port, Options, Timeout) of |
1616 | + {ok, Socket} -> |
1617 | + {ok, {ssl, Socket}}; |
1618 | + Error -> |
1619 | + Error |
1620 | + end; |
1621 | +connect(tcp, Address, Port, Options, Timeout) -> |
1622 | + case gen_tcp:connect(Address, Port, Options, Timeout) of |
1623 | + {ok, Socket} -> |
1624 | + {ok, {tcp, Socket}}; |
1625 | + Error -> |
1626 | + Error |
1627 | + end. |
1628 | + |
1629 | +listen(ssl, Port, Options) -> |
1630 | + case ssl:listen(Port, Options) of |
1631 | + {ok, Listener} -> |
1632 | + {ok, {ssl, Listener}}; |
1633 | + Error -> |
1634 | + Error |
1635 | + end; |
1636 | +listen(tcp, Port, Options) -> |
1637 | + case gen_tcp:listen(Port, Options) of |
1638 | + {ok, Listener} -> |
1639 | + {ok, {tcp, Listener}}; |
1640 | + Error -> |
1641 | + Error |
1642 | + end. |
1643 | + |
1644 | +accept({ssl, Listener}) -> |
1645 | + case ssl:transport_accept(Listener) of |
1646 | + {ok, Socket} -> |
1647 | + case ssl:ssl_accept(Socket) of |
1648 | + ok -> |
1649 | + {ok, {ssl, Socket}}; |
1650 | + Error -> |
1651 | + Error |
1652 | + end; |
1653 | + Error -> |
1654 | + Error |
1655 | + end; |
1656 | +accept({tcp, Listener}) -> |
1657 | + case gen_tcp:accept(Listener) of |
1658 | + {ok, Socket} -> |
1659 | + {ok, {tcp, Socket}}; |
1660 | + Error -> |
1661 | + Error |
1662 | + end. |
1663 | + |
1664 | +peername({ssl, Socket}) -> |
1665 | + ssl:peername(Socket); |
1666 | +peername({tcp, Socket}) -> |
1667 | + inet:peername(Socket). |
1668 | + |
1669 | +sockname({ssl, Socket}) -> |
1670 | + ssl:sockname(Socket); |
1671 | +sockname({tcp, Socket}) -> |
1672 | + inet:sockname(Socket). |
1673 | + |
1674 | +getopts({ssl, Socket}, Options) -> |
1675 | + ssl:getopts(Socket, Options); |
1676 | +getopts({tcp, Socket}, Options) -> |
1677 | + inet:getopts(Socket, Options). |
1678 | + |
1679 | +setopts({ssl, Socket}, Options) -> |
1680 | + ssl:setopts(Socket, Options); |
1681 | +setopts({tcp, Socket}, Options) -> |
1682 | + inet:setopts(Socket, Options). |
1683 | + |
1684 | +recv({ssl, Socket}, Length) -> |
1685 | + ssl:recv(Socket, Length); |
1686 | +recv({tcp, Socket}, Length) -> |
1687 | + gen_tcp:recv(Socket, Length). |
1688 | + |
1689 | +recv({ssl, Socket}, Length, Timeout) -> |
1690 | + ssl:recv(Socket, Length, Timeout); |
1691 | +recv({tcp, Socket}, Length, Timeout) -> |
1692 | + gen_tcp:recv(Socket, Length, Timeout). |
1693 | + |
1694 | +send({ssl, Socket}, Data) -> |
1695 | + ssl:send(Socket, Data); |
1696 | +send({tcp, Socket}, Data) -> |
1697 | + gen_tcp:send(Socket, Data). |
1698 | + |
1699 | +close({ssl, Socket}) -> |
1700 | + ssl:close(Socket); |
1701 | +close({tcp, Socket}) -> |
1702 | + gen_tcp:close(Socket). |
1703 | |
1704 | === modified file 'rebar.config' |
1705 | --- rebar.config 2011-02-23 01:59:49 +0000 |
1706 | +++ rebar.config 2011-02-28 07:20:04 +0000 |
1707 | @@ -14,7 +14,11 @@ |
1708 | |
1709 | {sub_dirs, [ |
1710 | "apps/burrow", |
1711 | - "rel" |
1712 | -]}. |
1713 | + "apps/openstack", |
1714 | + "rel"]}. |
1715 | |
1716 | {cover_enabled, true}. |
1717 | + |
1718 | +{edoc_opts, [{private, true}]}. |
1719 | + |
1720 | +{erl_opts, [{i, "../"}]}. |
1721 | |
1722 | === modified file 'rel/reltool.config' |
1723 | --- rel/reltool.config 2011-02-23 01:59:49 +0000 |
1724 | +++ rel/reltool.config 2011-02-28 07:20:04 +0000 |
1725 | @@ -17,20 +17,19 @@ |
1726 | {rel, "burrow", "2011.2", [ |
1727 | burrow, |
1728 | kernel, |
1729 | + openstack, |
1730 | stdlib, |
1731 | - sasl |
1732 | - ]}, |
1733 | + sasl]}, |
1734 | {rel, "start_clean", "", [ |
1735 | kernel, |
1736 | - stdlib |
1737 | - ]}, |
1738 | + stdlib]}, |
1739 | {boot_rel, "burrow"}, |
1740 | {profile, embedded}, |
1741 | {excl_sys_filters, [ |
1742 | "^bin/.*", |
1743 | - "^erts.*/bin/(dialyzer|typer)" |
1744 | - ]}, |
1745 | + "^erts.*/bin/(dialyzer|typer)"]}, |
1746 | {app, burrow, [{incl_cond, include}]}, |
1747 | + {app, openstack, [{incl_cond, include}]}, |
1748 | {app, sasl, [{incl_cond, include}]} |
1749 | ]}. |
1750 | |
1751 | @@ -42,5 +41,4 @@ |
1752 | {copy, "files/nodetool", "{{erts_vsn}}/bin/nodetool"}, |
1753 | {copy, "files/burrow", "bin/burrow"}, |
1754 | {copy, "files/app.config", "etc/app.config"}, |
1755 | - {copy, "files/vm.args", "etc/vm.args"} |
1756 | -]}. |
1757 | + {copy, "files/vm.args", "etc/vm.args"}]}. |
1758 | |
1759 | === modified file 'start' |
1760 | --- start 2011-02-23 01:59:49 +0000 |
1761 | +++ start 2011-02-28 07:20:04 +0000 |
1762 | @@ -13,4 +13,4 @@ |
1763 | # See the License for the specific language governing permissions and |
1764 | # limitations under the License. |
1765 | |
1766 | -erl -pa apps/burrow/ebin -s burrow start |
1767 | +erl -pa apps/*/ebin -s burrow start $* |
The attempt to merge lp:~eday/burrow/openstack_app into lp:burrow failed. Below is the output from the failed tests.
==> burrow (compile) socket. erl server. erl http.erl
Compiled src/burrow.erl
Compiled src/burrow_http.erl
==> openstack (compile)
Compiled src/openstack_
Compiled src/openstack.erl
Compiled src/openstack_
Compiled src/openstack_
==> rel (compile)
==> tmpDUU9Bk (compile)
==> burrow (eunit)
Compiled src/burrow_http.erl
Compiled src/burrow.erl
=INFO REPORT==== 27-Feb- 2011::03: 16:23 ===
{{burrow, start,[ normal, []]},
{{listen_ failed, eaddrinuse} ,
{child, undefined,
{tcp,8080, []},
{proc_ lib,start_ link,
[openstack _server, listen,
[ {tcp,8080, []},
{state, #Fun<openstack_ http.0. 62395619> ,
{state, #Fun<burrow_ http.0. 98356872> ,
undefined, <<"Burrow/ 2011.2" >>,undefined,
undefined} }]]},
transient, brutal_ kill,worker,
[openstack_ server] }}}} )...*failed* {assertEqual_ failed,
[{module, burrow} ,
{line, 84},
{expression ,"start ( )"},
{expected, ok},
{error,
{bad_ return,
{{burrow, start,[ normal, []]},
{{listen_ failed, eaddrinuse} ,
{child, undefined, ...}}}} }}]} '-application_ test/0- fun-0-' /1 application_ test/0
application: burrow
exited: {bad_return,
type: temporary
burrow: application_test (module 'burrow'
::error:
{value,
in function burrow:
in call from burrow:
======= ======= ======= ======= ======= ======= ======= ====== apps/burrow/ .eunit/ index.html
Failed: 1. Skipped: 0. Passed: 0.
Cover analysis: /tmp/tmpDUU9Bk/
ERROR: One or more eunit tests failed.