Merge lp:~mwhudson/loggerhead/no-transport-sharing into lp:loggerhead

Proposed by Michael Hudson-Doyle
Status: Merged
Merged at revision: not available
Proposed branch: lp:~mwhudson/loggerhead/no-transport-sharing
Merge into: lp:loggerhead
Diff against target: None lines
To merge this branch: bzr merge lp:~mwhudson/loggerhead/no-transport-sharing
Reviewer Review Type Date Requested Status
Loggerhead Team Pending
Review via email: mp+7870@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Hi,

This branch was inspired by the bug where we ended up sharing a transport's connection between threads (bug 390972) but I ended up fixing a bunch of other things too:

 * interpreting the 'http_serve' config value more liberally
 * generally, distinguish between .bzr and non-.bzr requests after traversing up to the .bzr data rather than right at the start
 * making serving .bzr directories when --allow-writes is not passed work again
 * check http_serve when serving non-branch .bzr data

Cheers,
mwh

Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

"it seems ok to me, but you should really ask beuno". And I didn't actually test it. (I...probably will later.) But anyway... :P

Just a few unimportant comments and questions:

> 49 + def app_for_bazaar_data(self, relpath):

should have the TODO comment from the old function:

> 120 - # TODO: Use something here that uses the transport API
> 121 - # rather than relying on the local filesystem API.

> 69 + elif _bools.get(value.lower(), True):
> 70 + raise httpexceptions.HTTPNotFound()

Like I said before, I think you've got the logic here reversed.

> 78 + self.check_serveable(LocationConfig(self.transport.base))

Will LocationConfig explode horribly if something goes wrong? Are there any exceptions that should be caught?

> 211 + load_plugins()

load_plugins() is already called in main().

Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

> > 78 + self.check_serveable(LocationConfig(self.transport.base))
>
> Will LocationConfig explode horribly if something goes wrong? Are there any
> exceptions that should be caught?

Hmm, as it is, LocationConfig will wind up opening another transport, won't it? Is that a problem?

Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

Oh, NEWS should be updated for all the bugs this fixes. (I'm linking them against this branch right now. There probably aren't any bugs about the http_serve config value thing, but maybe it should be mentioned too.)

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Matt Nordhoff wrote:
>>> 78 + self.check_serveable(LocationConfig(self.transport.base))
>> Will LocationConfig explode horribly if something goes wrong? Are there any
>> exceptions that should be caught?
>
> Hmm, as it is, LocationConfig will wind up opening another transport, won't it?

I don't think so.

> Is that a problem?

No :)

Cheers,
mwh

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

Matt Nordhoff wrote:
> "it seems ok to me, but you should really ask beuno". And I didn't actually test it. (I...probably will later.) But anyway... :P

:)

> Just a few unimportant comments and questions:
>
>> 49 + def app_for_bazaar_data(self, relpath):
>
> should have the TODO comment from the old function:
>
>> 120 - # TODO: Use something here that uses the transport API
>> 121 - # rather than relying on the local filesystem API.

Added.

>> 69 + elif _bools.get(value.lower(), True):
>> 70 + raise httpexceptions.HTTPNotFound()
>
> Like I said before, I think you've got the logic here reversed.

Fixed.

>> 78 + self.check_serveable(LocationConfig(self.transport.base))
>
> Will LocationConfig explode horribly if something goes wrong? Are there any exceptions that should be caught?

As discussed on IRC, I don't think I care very much...

>> 211 + load_plugins()
>
> load_plugins() is already called in main().

Oh yeah! This was bad conflict resolution, I think.

Thanks for the comments.

Revision history for this message
Matt Nordhoff (mnordhoff) wrote :

Michael Hudson wrote:
> Matt Nordhoff wrote:
>>>> 78 + self.check_serveable(LocationConfig(self.transport.base))
>>> Will LocationConfig explode horribly if something goes wrong? Are there any
>>> exceptions that should be caught?
>> Hmm, as it is, LocationConfig will wind up opening another transport, won't it?
>
> I don't think so.
>
>> Is that a problem?
>
> No :)
>
> Cheers,
> mwh

OK. :-D
--

381. By Michael Hudson-Doyle

mismerge

382. By Michael Hudson-Doyle

may as well delay getting a transport a bit

383. By Michael Hudson-Doyle

restore TODO

384. By Michael Hudson-Doyle

some NEWS

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'loggerhead/apps/transport.py'
2--- loggerhead/apps/transport.py 2009-06-24 04:29:10 +0000
3+++ loggerhead/apps/transport.py 2009-06-25 00:47:19 +0000
4@@ -16,8 +16,11 @@
5 #
6 """Serve branches at urls that mimic a transport's file system layout."""
7
8+import threading
9+
10 from bzrlib import branch, errors, lru_cache, urlutils
11-from bzrlib.bzrdir import BzrDir
12+from bzrlib.config import LocationConfig
13+from bzrlib.transport import get_transport
14 from bzrlib.transport.http import wsgi
15
16 from paste.request import path_info_pop
17@@ -26,9 +29,14 @@
18
19 from loggerhead.apps.branch import BranchWSGIApp
20 from loggerhead.apps import favicon_app, static_app
21-from loggerhead.config import LoggerheadConfig
22 from loggerhead.controllers.directory_ui import DirectoryUI
23
24+_bools = {
25+ 'yes': True, 'no': False,
26+ 'on': True, 'off': False,
27+ '1': True, '0': False,
28+ 'true': True, 'false': False,
29+ }
30
31 class BranchesFromTransportServer(object):
32
33@@ -62,9 +70,8 @@
34 name = self.name
35 else:
36 name = '/'
37- return DirectoryUI(environ['loggerhead.static.url'],
38- self.transport,
39- name)
40+ return DirectoryUI(
41+ environ['loggerhead.static.url'], self.transport, name)
42 else:
43 new_transport = self.transport.clone(segment)
44 if self.name:
45@@ -73,53 +80,70 @@
46 new_name = '/' + segment
47 return BranchesFromTransportServer(new_transport, self.root, new_name)
48
49+ def app_for_bazaar_data(self, relpath):
50+ if relpath == '/.bzr/smart':
51+ wsgi_app = wsgi.SmartWSGIApp(self.transport)
52+ return wsgi.RelpathSetter(wsgi_app, '', 'PATH_INFO')
53+ else:
54+ base = self.transport.base
55+ readonly_prefix = 'readonly+'
56+ if base.startswith(readonly_prefix):
57+ base = base[len(readonly_prefix):]
58+ try:
59+ path = urlutils.local_path_from_url(base)
60+ except errors.InvalidURL, e:
61+ raise httpexceptions.HTTPNotFound()
62+ else:
63+ return urlparser.make_static(None, path)
64+
65+ def check_serveable(self, config):
66+ value = config.get_user_option('http_serve')
67+ if value is None:
68+ return
69+ elif _bools.get(value.lower(), True):
70+ raise httpexceptions.HTTPNotFound()
71+
72 def __call__(self, environ, start_response):
73+ path = environ['PATH_INFO']
74 try:
75 b = branch.Branch.open_from_transport(self.transport)
76 except errors.NotBranchError:
77+ if path.startswith('/.bzr'):
78+ self.check_serveable(LocationConfig(self.transport.base))
79+ return self.app_for_bazaar_data(path)(environ, start_response)
80 if not self.transport.listable() or not self.transport.has('.'):
81 raise httpexceptions.HTTPNotFound()
82 return self.app_for_non_branch(environ)(environ, start_response)
83 else:
84- if b.get_config().get_user_option('http_serve') == 'False':
85- raise httpexceptions.HTTPNotFound()
86+ self.check_serveable(b.get_config())
87+ if path.startswith('/.bzr'):
88+ return self.app_for_bazaar_data(path)(environ, start_response)
89 else:
90 return self.app_for_branch(b)(environ, start_response)
91
92
93+_transport_store = threading.local()
94+
95+def get_transport_for_thread(base):
96+ """ """
97+ thread_transports = getattr(_transport_store, 'transports', None)
98+ if thread_transports is None:
99+ thread_transports = _transport_store.transports = {}
100+ if base in thread_transports:
101+ return thread_transports[base]
102+ transport = get_transport(base)
103+ return transport
104+
105+
106 class BranchesFromTransportRoot(object):
107
108- def __init__(self, transport, config):
109+ def __init__(self, base, config):
110 self.graph_cache = lru_cache.LRUCache(10)
111- self.transport = transport
112- wsgi_app = wsgi.SmartWSGIApp(self.transport)
113- self.smart_server_app = wsgi.RelpathSetter(wsgi_app, '', 'PATH_INFO')
114+ self.base = base
115 self._config = config
116
117- def get_local_path(self):
118- """Raise exception if it's not a local path, otherwise return it"""
119-
120- # TODO: Use something here that uses the transport API
121- # rather than relying on the local filesystem API.
122- try:
123- path = urlutils.local_path_from_url(self.transport.base)
124- except errors.InvalidURL:
125- raise httpexceptions.HTTPNotFound()
126- else:
127- return path
128-
129- def check_is_a_branch(self, path_info):
130- """Check if it's a branch, and that it's allowed to be shown"""
131- try:
132- bzrdir = BzrDir.open_containing_from_transport(
133- self.transport.clone(path_info))[0]
134- branch = bzrdir.open_branch()
135- if branch.get_config().get_user_option('http_serve') == 'False':
136- raise httpexceptions.HTTPNotFound()
137- except errors.NotBranchError:
138- return
139-
140 def __call__(self, environ, start_response):
141+ transport = get_transport_for_thread(self.base)
142 environ['loggerhead.static.url'] = environ['SCRIPT_NAME']
143 if environ['PATH_INFO'].startswith('/static/'):
144 segment = path_info_pop(environ)
145@@ -127,30 +151,23 @@
146 return static_app(environ, start_response)
147 elif environ['PATH_INFO'] == '/favicon.ico':
148 return favicon_app(environ, start_response)
149- elif environ['PATH_INFO'].endswith("/.bzr/smart"):
150- self.check_is_a_branch(environ['PATH_INFO'])
151- return self.smart_server_app(environ, start_response)
152- elif '/.bzr/' in environ['PATH_INFO']:
153- self.check_is_a_branch(environ['PATH_INFO'])
154- path = self.get_local_path()
155- app = urlparser.make_static(None, path)
156- return app(environ, start_response)
157 else:
158 return BranchesFromTransportServer(
159- self.transport, self)(environ, start_response)
160+ transport, self)(environ, start_response)
161
162
163 class UserBranchesFromTransportRoot(object):
164
165- def __init__(self, transport, config):
166+ def __init__(self, base, config):
167 self.graph_cache = lru_cache.LRUCache(10)
168- self.transport = transport
169+ self.base = base
170 self._config = config
171 self.trunk_dir = config.get_option('trunk_dir')
172
173 def __call__(self, environ, start_response):
174 environ['loggerhead.static.url'] = environ['SCRIPT_NAME']
175 path_info = environ['PATH_INFO']
176+ transport = get_transport_for_thread(self.base)
177 if path_info.startswith('/static/'):
178 segment = path_info_pop(environ)
179 assert segment == 'static'
180@@ -161,10 +178,11 @@
181 # segments starting with ~ are user branches
182 if path_info.startswith('/~'):
183 segment = path_info_pop(environ)
184- new_transport = self.transport.clone(segment[1:])
185+ new_transport = transport.clone(segment[1:])
186 return BranchesFromTransportServer(
187- new_transport, self, segment)(environ, start_response)
188+ transport.clone(segment[1:]), self, segment)(
189+ environ, start_response)
190 else:
191- new_transport = self.transport.clone(self.trunk_dir)
192 return BranchesFromTransportServer(
193- new_transport, self)(environ, start_response)
194+ transport.clone(self.trunk_dir), self)(
195+ environ, start_response)
196
197=== modified file 'loggerhead/main.py'
198--- loggerhead/main.py 2009-06-24 05:11:14 +0000
199+++ loggerhead/main.py 2009-06-25 00:50:40 +0000
200@@ -48,10 +48,16 @@
201 config.print_help()
202 sys.exit(1)
203 elif config.arg_count == 1:
204- path = config.get_arg(0)
205+ base = config.get_arg(0)
206 else:
207- path = '.'
208- return config, path
209+ base = '.'
210+
211+ load_plugins()
212+
213+ if not config.get_option('allow_writes'):
214+ base = 'readonly+' + base
215+
216+ return config, base
217
218
219 def setup_logging(config):
220@@ -72,12 +78,7 @@
221 return logger
222
223
224-def make_app_for_config_and_path(config, path):
225- if config.get_option('allow_writes'):
226- transport = get_transport(path)
227- else:
228- transport = get_transport('readonly+' + path)
229-
230+def make_app_for_config_and_path(config, base):
231 if config.get_option('trunk_dir') and not config.get_option('user_dirs'):
232 print "--trunk-dir is only valid with --user-dirs"
233 sys.exit(1)
234@@ -92,9 +93,9 @@
235 if not config.get_option('trunk_dir'):
236 print "You didn't specify a directory for the trunk directories."
237 sys.exit(1)
238- app = UserBranchesFromTransportRoot(transport, config)
239+ app = UserBranchesFromTransportRoot(base, config)
240 else:
241- app = BranchesFromTransportRoot(transport, config)
242+ app = BranchesFromTransportRoot(base, config)
243
244 setup_logging(config)
245

Subscribers

People subscribed via source and target branches