Merge lp:~eday/burrow/openstack_app into lp:~burrow-core/burrow/cactus

Proposed by Eric Day
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
Reviewer Review Type Date Requested Status
Burrow Core Team Pending
Review via email: mp+51173@code.launchpad.net

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.

To post a comment you must log in.
Revision history for this message
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_socket.erl
Compiled src/openstack.erl
Compiled src/openstack_server.erl
Compiled src/openstack_http.erl
==> rel (compile)
==> tmpDUU9Bk (compile)
==> burrow (eunit)
Compiled src/burrow_http.erl
Compiled src/burrow.erl

=INFO REPORT==== 27-Feb-2011::03:16:23 ===
    application: burrow
    exited: {bad_return,
                {{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]}}}}
    type: temporary
burrow: application_test (module 'burrow')...*failed*
::error:{assertEqual_failed,
          [{module,burrow},
           {line,84},
           {expression,"start ( )"},
           {expected,ok},
           {value,
               {error,
                   {bad_return,
                       {{burrow,start,[normal,[]]},
                        {{listen_failed,eaddrinuse},
                         {child,undefined,...}}}}}}]}
  in function burrow:'-application_test/0-fun-0-'/1
  in call from burrow:application_test/0

=======================================================
  Failed: 1. Skipped: 0. Passed: 0.
Cover analysis: /tmp/tmpDUU9Bk/apps/burrow/.eunit/index.html
ERROR: One or more eunit tests failed.

Revision history for this message
OpenStack Infra (hudson-openstack) wrote :
Download full text (5.5 KiB)

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_server.erl
Compiled src/openstack_socket.erl
Compiled src/openstack.erl
src/openstack_http.erl:225: Warning: variable 'Name' is unused
Compiled src/openstack_http.erl
==> rel (compile)
==> tmpChhJXr (compile)
==> burrow (eunit)
Compiled src/burrow_http.erl
Compiled src/burrow.erl

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
Now listening on port: {tcp,32123,[]}

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
    application: burrow
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
    application: ssl
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
Now listening on port: {tcp,32123,[]}

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
    application: burrow
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:27 ===
    application: ssl
    exited: stopped
    type: temporary
  Test passed.
Cover analysis: /tmp/tmpChhJXr/apps/burrow/.eunit/index.html
==> openstack (eunit)
Compiled src/openstack_socket.erl
Compiled src/openstack.erl
Compiled src/openstack_server.erl
src/openstack_http.erl:225: Warning: variable 'Name' is unused
src/openstack_http.erl:725: Warning: variable 'Send' is unused
Compiled src/openstack_http.erl

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: ssl
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: ssl
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
Now listening on port: {tcp,32123,[]}

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: ssl
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
Now listening on port: {tcp,32123,[]}

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: ssl
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
Now listening on port: {tcp,32123,[]}

=ERROR REPORT==== 28-Feb-2011::02:51:28 ===
Error during HTTP request: <<"GET * BAD\r\n">>
=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: openstack
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
    application: ssl
    exited: stopped
    type: temporary

=INFO REPORT==== 28-Feb-2011::02:51:28 ===
Now listening on port: {tcp,32123,[]}

=INFO REPORT==== ...

Read more...

lp:~eday/burrow/openstack_app updated
8. By Eric Day

Fixed a couple warnings.

9. By Eric Day

Fixed a few odd behaviors with SSL application.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 $*

Subscribers

People subscribed via source and target branches

to all changes: