Merge lp:~mwhudson/loggerhead/no-transport-sharing into lp:loggerhead
- no-transport-sharing
- Merge into trunk-rich
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 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Loggerhead Team | Pending | ||
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Hudson-Doyle (mwhudson) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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_
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.
> 70 + raise httpexceptions.
Like I said before, I think you've got the logic here reversed.
> 78 + self.check_
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().
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Matt Nordhoff (mnordhoff) wrote : | # |
> > 78 + self.check_
>
> 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?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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.)
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Michael Hudson-Doyle (mwhudson) wrote : | # |
Matt Nordhoff wrote:
>>> 78 + self.check_
>> 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
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
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_
>
> 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.
>> 70 + raise httpexceptions.
>
> Like I said before, I think you've got the logic here reversed.
Fixed.
>> 78 + self.check_
>
> 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.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Matt Nordhoff (mnordhoff) wrote : | # |
Michael Hudson wrote:
> Matt Nordhoff wrote:
>>>> 78 + self.check_
>>> 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
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 |
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