Merge lp:~pedronis/u1db/cors-middleware into lp:u1db

Proposed by Samuele Pedroni
Status: Merged
Approved by: Samuele Pedroni
Approved revision: 426
Merged at revision: 426
Proposed branch: lp:~pedronis/u1db/cors-middleware
Merge into: lp:u1db
Diff against target: 153 lines (+114/-2)
3 files modified
u1db/remote/cors_middleware.py (+42/-0)
u1db/tests/test_cors_middleware.py (+66/-0)
u1db/tests/test_remote_sync_target.py (+6/-2)
To merge this branch: bzr merge lp:~pedronis/u1db/cors-middleware
Reviewer Review Type Date Requested Status
Stuart Langridge (community) Approve
Lucio Torre (community) Approve
Review via email: mp+128991@code.launchpad.net

Commit message

add CORS middleware

Description of the change

- CORS middleware
- tweak to allow passing all tests with u1db.js under different origin conditions

testing beyond unit tests/trying:

get lp:~pedronis/u1db/u1db-js-different-origin

run in it:

TEST_BROWSER=firefox PYTHONPATH=../cors-middleware/:. unit2 tests.test_bridged

atm there should be only 1 unrelated failure there

To post a comment you must log in.
Revision history for this message
Samuele Pedroni (pedronis) wrote :

confirmed working against: chromium, safari, and firefox

Revision history for this message
Lucio Torre (lucio.torre) wrote :

code looks sane.

review: Approve
Revision history for this message
Stuart Langridge (sil) wrote :

Serving correct CORS headers, and u1db.js will sync with a u1db-serve which provides these headers.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'u1db/remote/cors_middleware.py'
2--- u1db/remote/cors_middleware.py 1970-01-01 00:00:00 +0000
3+++ u1db/remote/cors_middleware.py 2012-10-10 16:31:57 +0000
4@@ -0,0 +1,42 @@
5+# Copyright 2012 Canonical Ltd.
6+#
7+# This file is part of u1db.
8+#
9+# u1db is free software: you can redistribute it and/or modify
10+# it under the terms of the GNU Lesser General Public License version 3
11+# as published by the Free Software Foundation.
12+#
13+# u1db is distributed in the hope that it will be useful,
14+# but WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+# GNU Lesser General Public License for more details.
17+#
18+# You should have received a copy of the GNU Lesser General Public License
19+# along with u1db. If not, see <http://www.gnu.org/licenses/>.
20+"""U1DB Cross-Origin Resource Sharing WSGI middleware."""
21+
22+
23+class CORSMiddleware(object):
24+ """U1DB Cross-Origin Resource Sharing WSGI middleware."""
25+
26+ def __init__(self, app, accept_cors_connections):
27+ self.origins = ' '.join(accept_cors_connections)
28+ self.app = app
29+
30+ def _cors_headers(self):
31+ return [('access-control-allow-origin', self.origins),
32+ ('access-control-allow-headers',
33+ 'authorization, content-type'),
34+ ('access-control-allow-methods',
35+ 'GET, POST, PUT, DELETE, OPTIONS')]
36+
37+ def __call__(self, environ, start_response):
38+ def wrap_start_response(status, headers, exc_info=None):
39+ headers += self._cors_headers()
40+ return start_response(status, headers, exc_info)
41+
42+ if environ['REQUEST_METHOD'].lower() == 'options':
43+ wrap_start_response("200 OK", [('content-type', 'text/plain')])
44+ return ['']
45+
46+ return self.app(environ, wrap_start_response)
47
48=== added file 'u1db/tests/test_cors_middleware.py'
49--- u1db/tests/test_cors_middleware.py 1970-01-01 00:00:00 +0000
50+++ u1db/tests/test_cors_middleware.py 2012-10-10 16:31:57 +0000
51@@ -0,0 +1,66 @@
52+# Copyright 2012 Canonical Ltd.
53+#
54+# This file is part of u1db.
55+#
56+# u1db is free software: you can redistribute it and/or modify
57+# it under the terms of the GNU Lesser General Public License version 3
58+# as published by the Free Software Foundation.
59+#
60+# u1db is distributed in the hope that it will be useful,
61+# but WITHOUT ANY WARRANTY; without even the implied warranty of
62+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63+# GNU Lesser General Public License for more details.
64+#
65+# You should have received a copy of the GNU Lesser General Public License
66+# along with u1db. If not, see <http://www.gnu.org/licenses/>.
67+
68+"""Test CORS wsgi middleware"""
69+import paste.fixture
70+
71+from u1db import tests
72+
73+from u1db.remote.cors_middleware import CORSMiddleware
74+
75+
76+class TestCORSMiddleware(tests.TestCase):
77+
78+ def setUp(self):
79+ super(TestCORSMiddleware, self).setUp()
80+
81+ def app(self, accept_cors_connections):
82+
83+ def base_app(environ, start_response):
84+ start_response("200 OK", [("content-type", "application/json")])
85+ return ['{}']
86+
87+ return paste.fixture.TestApp(CORSMiddleware(base_app,
88+ accept_cors_connections))
89+
90+ def _check_cors_headers(self, resp, expect):
91+ self.assertEqual(expect, resp.header('access-control-allow-origin'))
92+ self.assertEqual("GET, POST, PUT, DELETE, OPTIONS",
93+ resp.header('access-control-allow-methods'))
94+ self.assertEqual("authorization, content-type",
95+ resp.header('access-control-allow-headers'))
96+
97+ def test_options(self):
98+ app = self.app(['*'])
99+ resp = app._gen_request('OPTIONS', '/')
100+ self.assertEqual(200, resp.status)
101+ self._check_cors_headers(resp, '*')
102+ self.assertEqual('', resp.body)
103+
104+ app = self.app(['http://bar.example', 'http://foo.example'])
105+ resp = app._gen_request('OPTIONS', '/db1')
106+ self.assertEqual(200, resp.status)
107+ self._check_cors_headers(resp, 'http://bar.example http://foo.example')
108+ self.assertEqual('', resp.body)
109+
110+ def test_pass_through(self):
111+ app = self.app(['*'])
112+ resp = app.get('/db0')
113+ self.assertEqual(200, resp.status)
114+ self._check_cors_headers(resp, '*')
115+ self.assertEqual('application/json', resp.header('content-type'))
116+ self.assertEqual('{}', resp.body)
117+
118
119=== modified file 'u1db/tests/test_remote_sync_target.py'
120--- u1db/tests/test_remote_sync_target.py 2012-09-25 20:04:08 +0000
121+++ u1db/tests/test_remote_sync_target.py 2012-10-10 16:31:57 +0000
122@@ -177,9 +177,13 @@
123 self.assertGetDoc(
124 db, 'doc-here', 'replica:1', '{"value": "here"}', False)
125
126+ failure_scenario_exceptions = (Exception, errors.HTTPError)
127+
128 def test_sync_exchange_send_failure_and_retry_scenario(self):
129 self.startServer()
130
131+ server_side_exc, client_side_exc = self.failure_scenario_exceptions
132+
133 def blackhole_getstderr(inst):
134 return cStringIO.StringIO()
135
136@@ -193,7 +197,7 @@
137 replica_uid=None, replica_gen=None,
138 replica_trans_id=None):
139 if doc.doc_id in trigger_ids:
140- raise Exception
141+ raise server_side_exc
142 return _put_doc_if_newer(doc, save_conflict=save_conflict,
143 replica_uid=replica_uid, replica_gen=replica_gen,
144 replica_trans_id=replica_trans_id)
145@@ -209,7 +213,7 @@
146 doc2 = self.make_document('doc-here2', 'replica:1',
147 '{"value": "here2"}')
148 self.assertRaises(
149- errors.HTTPError,
150+ client_side_exc,
151 remote_target.sync_exchange,
152 [(doc1, 10, 'T-sid'), (doc2, 11, 'T-sud')],
153 'replica', last_known_generation=0, last_known_trans_id=None,

Subscribers

People subscribed via source and target branches