Merge lp:~spiv/bzr/escape-smart-server-requested-paths-458762 into lp:bzr

Proposed by Andrew Bennetts
Status: Merged
Merged at revision: not available
Proposed branch: lp:~spiv/bzr/escape-smart-server-requested-paths-458762
Merge into: lp:bzr
Diff against target: 134 lines
6 files modified
NEWS (+3/-0)
bzrlib/smart/request.py (+1/-1)
bzrlib/smart/vfs.py (+9/-0)
bzrlib/tests/test_smart.py (+26/-0)
bzrlib/tests/test_smart_transport.py (+3/-1)
doc/developers/network-protocol.txt (+3/-0)
To merge this branch: bzr merge lp:~spiv/bzr/escape-smart-server-requested-paths-458762
Reviewer Review Type Date Requested Status
Martin Pool Approve
Review via email: mp+14126@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Andrew Bennetts (spiv) wrote :

This fixes inconsistencies in server-side HPSS path decoding that was breaking unicode paths. The client has always been consistent in how it was transmitting requests (VFS requests use URL escaped paths, others use UTF-8), but the server was assuming URL escaped everywhere. This branch fixes the server to match reality, and adds some tests (and fixes an existing one!). Thanks very much to Michael Hudson for investigating this issue and writing the first version of this patch.

Revision history for this message
Martin Pool (mbp) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'NEWS'
--- NEWS 2009-10-28 00:12:03 +0000
+++ NEWS 2009-10-29 00:35:22 +0000
@@ -42,6 +42,9 @@
42* TreeTransform.adjust_path updates the limbo paths of descendants of adjusted42* TreeTransform.adjust_path updates the limbo paths of descendants of adjusted
43 files. (Aaron Bentley)43 files. (Aaron Bentley)
4444
45* Unicode paths are now handled correctly and consistently by the smart
46 server. (Andrew Bennetts, Michael Hudson, #458762)
47
45Improvements48Improvements
46************49************
4750
4851
=== modified file 'bzrlib/smart/request.py'
--- bzrlib/smart/request.py 2009-10-22 06:53:08 +0000
+++ bzrlib/smart/request.py 2009-10-29 00:35:22 +0000
@@ -188,7 +188,7 @@
188 relpath = urlutils.joinpath('/', path)188 relpath = urlutils.joinpath('/', path)
189 if not relpath.startswith('/'):189 if not relpath.startswith('/'):
190 raise ValueError(relpath)190 raise ValueError(relpath)
191 return '.' + relpath191 return urlutils.escape('.' + relpath)
192 else:192 else:
193 raise errors.PathNotChild(client_path, self._root_client_path)193 raise errors.PathNotChild(client_path, self._root_client_path)
194194
195195
=== modified file 'bzrlib/smart/vfs.py'
--- bzrlib/smart/vfs.py 2009-03-23 14:59:43 +0000
+++ bzrlib/smart/vfs.py 2009-10-29 00:35:22 +0000
@@ -27,6 +27,7 @@
27import os27import os
2828
29from bzrlib import errors29from bzrlib import errors
30from bzrlib import urlutils
30from bzrlib.smart import request31from bzrlib.smart import request
3132
3233
@@ -59,6 +60,14 @@
59 if not vfs_enabled():60 if not vfs_enabled():
60 raise errors.DisabledMethod(self.__class__.__name__)61 raise errors.DisabledMethod(self.__class__.__name__)
6162
63 def translate_client_path(self, relpath):
64 # VFS requests are made with escaped paths so the escaping done in
65 # SmartServerRequest.translate_client_path leads to double escaping.
66 # Remove it here -- the fact that the result is still escaped means
67 # that the str() will not fail on valid input.
68 x = request.SmartServerRequest.translate_client_path(self, relpath)
69 return str(urlutils.unescape(x))
70
6271
63class HasRequest(VfsRequest):72class HasRequest(VfsRequest):
6473
6574
=== modified file 'bzrlib/tests/test_smart.py'
--- bzrlib/tests/test_smart.py 2009-09-24 05:31:23 +0000
+++ bzrlib/tests/test_smart.py 2009-10-29 00:35:22 +0000
@@ -43,6 +43,7 @@
43import bzrlib.smart.bzrdir, bzrlib.smart.bzrdir as smart_dir43import bzrlib.smart.bzrdir, bzrlib.smart.bzrdir as smart_dir
44import bzrlib.smart.packrepository44import bzrlib.smart.packrepository
45import bzrlib.smart.repository45import bzrlib.smart.repository
46import bzrlib.smart.vfs
46from bzrlib.smart.request import (47from bzrlib.smart.request import (
47 FailedSmartServerResponse,48 FailedSmartServerResponse,
48 SmartServerRequest,49 SmartServerRequest,
@@ -170,6 +171,18 @@
170 self.assertRaises(171 self.assertRaises(
171 errors.PathNotChild, request.translate_client_path, 'bar/')172 errors.PathNotChild, request.translate_client_path, 'bar/')
172 self.assertEqual('./baz', request.translate_client_path('foo/baz'))173 self.assertEqual('./baz', request.translate_client_path('foo/baz'))
174 e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'.encode('utf-8')
175 self.assertEqual('./' + urlutils.escape(e_acute),
176 request.translate_client_path('foo/' + e_acute))
177
178 def test_translate_client_path_vfs(self):
179 """VfsRequests receive escaped paths rather than raw UTF-8."""
180 transport = self.get_transport()
181 request = smart.vfs.VfsRequest(transport, 'foo/')
182 e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'.encode('utf-8')
183 escaped = urlutils.escape('foo/' + e_acute)
184 self.assertEqual('./' + urlutils.escape(e_acute),
185 request.translate_client_path(escaped))
173186
174 def test_transport_from_client_path(self):187 def test_transport_from_client_path(self):
175 transport = self.get_transport()188 transport = self.get_transport()
@@ -1690,6 +1703,19 @@
1690 self.assertEqual(SmartServerResponse(('ok',)), response)1703 self.assertEqual(SmartServerResponse(('ok',)), response)
16911704
16921705
1706class TestSmartServerVfsGet(tests.TestCaseWithMemoryTransport):
1707
1708 def test_unicode_path(self):
1709 """VFS requests expect unicode paths to be escaped."""
1710 filename = u'foo\N{INTERROBANG}'
1711 filename_escaped = urlutils.escape(filename)
1712 backing = self.get_transport()
1713 request = smart.vfs.GetRequest(backing)
1714 backing.put_bytes_non_atomic(filename_escaped, 'contents')
1715 self.assertEqual(SmartServerResponse(('ok', ), 'contents'),
1716 request.execute(filename_escaped))
1717
1718
1693class TestHandlers(tests.TestCase):1719class TestHandlers(tests.TestCase):
1694 """Tests for the request.request_handlers object."""1720 """Tests for the request.request_handlers object."""
16951721
16961722
=== modified file 'bzrlib/tests/test_smart_transport.py'
--- bzrlib/tests/test_smart_transport.py 2009-09-24 05:31:23 +0000
+++ bzrlib/tests/test_smart_transport.py 2009-10-29 00:35:22 +0000
@@ -657,8 +657,10 @@
657 # wire-to-wire, using the whole stack, with a UTF-8 filename.657 # wire-to-wire, using the whole stack, with a UTF-8 filename.
658 transport = memory.MemoryTransport('memory:///')658 transport = memory.MemoryTransport('memory:///')
659 utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')659 utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
660 # VFS requests use filenames, not raw UTF-8.
661 hpss_path = urlutils.escape(utf8_filename)
660 transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')662 transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
661 to_server = StringIO('get\001' + utf8_filename + '\n')663 to_server = StringIO('get\001' + hpss_path + '\n')
662 from_server = StringIO()664 from_server = StringIO()
663 server = medium.SmartServerPipeStreamMedium(665 server = medium.SmartServerPipeStreamMedium(
664 to_server, from_server, transport)666 to_server, from_server, transport)
665667
=== modified file 'doc/developers/network-protocol.txt'
--- doc/developers/network-protocol.txt 2009-09-17 03:21:14 +0000
+++ doc/developers/network-protocol.txt 2009-10-29 00:35:22 +0000
@@ -436,6 +436,9 @@
436using `bzrlib.transport.pathfilter` and `os.path.expanduser`, taking care436using `bzrlib.transport.pathfilter` and `os.path.expanduser`, taking care
437to respect the virtual root.437to respect the virtual root.
438438
439Paths in request arguments are UTF-8 encoded, except for the legacy VFS
440requests which expect escaped (`bzrlib.urlutils.escape`) paths.
441
439442
440Requests443Requests
441========444========