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
1=== modified file 'NEWS'
2--- NEWS 2009-10-28 00:12:03 +0000
3+++ NEWS 2009-10-29 00:35:22 +0000
4@@ -42,6 +42,9 @@
5 * TreeTransform.adjust_path updates the limbo paths of descendants of adjusted
6 files. (Aaron Bentley)
7
8+* Unicode paths are now handled correctly and consistently by the smart
9+ server. (Andrew Bennetts, Michael Hudson, #458762)
10+
11 Improvements
12 ************
13
14
15=== modified file 'bzrlib/smart/request.py'
16--- bzrlib/smart/request.py 2009-10-22 06:53:08 +0000
17+++ bzrlib/smart/request.py 2009-10-29 00:35:22 +0000
18@@ -188,7 +188,7 @@
19 relpath = urlutils.joinpath('/', path)
20 if not relpath.startswith('/'):
21 raise ValueError(relpath)
22- return '.' + relpath
23+ return urlutils.escape('.' + relpath)
24 else:
25 raise errors.PathNotChild(client_path, self._root_client_path)
26
27
28=== modified file 'bzrlib/smart/vfs.py'
29--- bzrlib/smart/vfs.py 2009-03-23 14:59:43 +0000
30+++ bzrlib/smart/vfs.py 2009-10-29 00:35:22 +0000
31@@ -27,6 +27,7 @@
32 import os
33
34 from bzrlib import errors
35+from bzrlib import urlutils
36 from bzrlib.smart import request
37
38
39@@ -59,6 +60,14 @@
40 if not vfs_enabled():
41 raise errors.DisabledMethod(self.__class__.__name__)
42
43+ def translate_client_path(self, relpath):
44+ # VFS requests are made with escaped paths so the escaping done in
45+ # SmartServerRequest.translate_client_path leads to double escaping.
46+ # Remove it here -- the fact that the result is still escaped means
47+ # that the str() will not fail on valid input.
48+ x = request.SmartServerRequest.translate_client_path(self, relpath)
49+ return str(urlutils.unescape(x))
50+
51
52 class HasRequest(VfsRequest):
53
54
55=== modified file 'bzrlib/tests/test_smart.py'
56--- bzrlib/tests/test_smart.py 2009-09-24 05:31:23 +0000
57+++ bzrlib/tests/test_smart.py 2009-10-29 00:35:22 +0000
58@@ -43,6 +43,7 @@
59 import bzrlib.smart.bzrdir, bzrlib.smart.bzrdir as smart_dir
60 import bzrlib.smart.packrepository
61 import bzrlib.smart.repository
62+import bzrlib.smart.vfs
63 from bzrlib.smart.request import (
64 FailedSmartServerResponse,
65 SmartServerRequest,
66@@ -170,6 +171,18 @@
67 self.assertRaises(
68 errors.PathNotChild, request.translate_client_path, 'bar/')
69 self.assertEqual('./baz', request.translate_client_path('foo/baz'))
70+ e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'.encode('utf-8')
71+ self.assertEqual('./' + urlutils.escape(e_acute),
72+ request.translate_client_path('foo/' + e_acute))
73+
74+ def test_translate_client_path_vfs(self):
75+ """VfsRequests receive escaped paths rather than raw UTF-8."""
76+ transport = self.get_transport()
77+ request = smart.vfs.VfsRequest(transport, 'foo/')
78+ e_acute = u'\N{LATIN SMALL LETTER E WITH ACUTE}'.encode('utf-8')
79+ escaped = urlutils.escape('foo/' + e_acute)
80+ self.assertEqual('./' + urlutils.escape(e_acute),
81+ request.translate_client_path(escaped))
82
83 def test_transport_from_client_path(self):
84 transport = self.get_transport()
85@@ -1690,6 +1703,19 @@
86 self.assertEqual(SmartServerResponse(('ok',)), response)
87
88
89+class TestSmartServerVfsGet(tests.TestCaseWithMemoryTransport):
90+
91+ def test_unicode_path(self):
92+ """VFS requests expect unicode paths to be escaped."""
93+ filename = u'foo\N{INTERROBANG}'
94+ filename_escaped = urlutils.escape(filename)
95+ backing = self.get_transport()
96+ request = smart.vfs.GetRequest(backing)
97+ backing.put_bytes_non_atomic(filename_escaped, 'contents')
98+ self.assertEqual(SmartServerResponse(('ok', ), 'contents'),
99+ request.execute(filename_escaped))
100+
101+
102 class TestHandlers(tests.TestCase):
103 """Tests for the request.request_handlers object."""
104
105
106=== modified file 'bzrlib/tests/test_smart_transport.py'
107--- bzrlib/tests/test_smart_transport.py 2009-09-24 05:31:23 +0000
108+++ bzrlib/tests/test_smart_transport.py 2009-10-29 00:35:22 +0000
109@@ -657,8 +657,10 @@
110 # wire-to-wire, using the whole stack, with a UTF-8 filename.
111 transport = memory.MemoryTransport('memory:///')
112 utf8_filename = u'testfile\N{INTERROBANG}'.encode('utf-8')
113+ # VFS requests use filenames, not raw UTF-8.
114+ hpss_path = urlutils.escape(utf8_filename)
115 transport.put_bytes(utf8_filename, 'contents\nof\nfile\n')
116- to_server = StringIO('get\001' + utf8_filename + '\n')
117+ to_server = StringIO('get\001' + hpss_path + '\n')
118 from_server = StringIO()
119 server = medium.SmartServerPipeStreamMedium(
120 to_server, from_server, transport)
121
122=== modified file 'doc/developers/network-protocol.txt'
123--- doc/developers/network-protocol.txt 2009-09-17 03:21:14 +0000
124+++ doc/developers/network-protocol.txt 2009-10-29 00:35:22 +0000
125@@ -436,6 +436,9 @@
126 using `bzrlib.transport.pathfilter` and `os.path.expanduser`, taking care
127 to respect the virtual root.
128
129+Paths in request arguments are UTF-8 encoded, except for the legacy VFS
130+requests which expect escaped (`bzrlib.urlutils.escape`) paths.
131+
132
133 Requests
134 ========