Commit 0f1d8afe authored by Jérome Perrin's avatar Jérome Perrin

recipe/simplehttpserver: fix application/x-www-form-urlencoded POST on py3

In deploy test, this recipe is used with url encoded POST [1], but the
test only tested multipart encoded POSTs. In python 3, they are different,
with multipart POSTs, FieldStorage values are bytes, but with url encoded
POSTs, the values are string.

This small server from the recipe only supported multipart POSTs, this
change add support for url encoded POSTs.

[1]: https://lab.nexedi.com/nexedi/slapos/blob/dd7038feab70866e54028de89d0e0e1017fc276c/software/erp5testnode/testsuite/deploy-test/deploy-script-controller#L86
parent 4dcf80d4
Pipeline #23420 failed with stage
...@@ -36,6 +36,15 @@ class ServerHandler(SimpleHTTPRequestHandler): ...@@ -36,6 +36,15 @@ class ServerHandler(SimpleHTTPRequestHandler):
SimpleHTTPRequestHandler.do_GET(self) SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self): def do_POST(self):
"""Write to a file on the server.
request keys:
path: the path of the file
content: content of the file
clear: (0|1 default 1) overwrite the file if 1
request can be encoded as application/x-www-form-urlencoded or multipart/form-data
"""
logging.info('%s - POST: %s \n%s' % (self.client_address[0], self.path, self.headers)) logging.info('%s - POST: %s \n%s' % (self.client_address[0], self.path, self.headers))
if self.restrictedRootAccess(): if self.restrictedRootAccess():
return return
...@@ -46,14 +55,20 @@ class ServerHandler(SimpleHTTPRequestHandler): ...@@ -46,14 +55,20 @@ class ServerHandler(SimpleHTTPRequestHandler):
environ={'REQUEST_METHOD': 'POST', environ={'REQUEST_METHOD': 'POST',
'CONTENT_TYPE': self.headers['Content-Type']} 'CONTENT_TYPE': self.headers['Content-Type']}
) )
name = form['path'].value.decode('utf-8')
content = form['content'].value file_content = form['content'].value
method = 'ab' file_path = form['path'].value
if 'clear' in form and form['clear'].value == '1': if form['content'].file:
method = 'wb' # post data as multipart/form-data , values are bytes
self.writeFile(name, content, method) file_path = file_path.decode('utf-8')
else:
# application/x-www-form-urlencoded , values are str
file_content = file_content.encode('utf-8')
file_open_mode = 'wb' if ('clear' in form and form['clear'].value in ('1', b'1')) else 'ab'
self.writeFile(file_path, file_content, file_open_mode)
self.respond(200, type=self.headers['Content-Type']) self.respond(200, type=self.headers['Content-Type'])
self.wfile.write(b"Content written to %s" % str2bytes(name)) self.wfile.write(b"Content written to %s" % str2bytes(file_path))
def writeFile(self, filename, content, method='ab'): def writeFile(self, filename, content, method='ab'):
file_path = os.path.abspath(os.path.join(self.document_path, filename)) file_path = os.path.abspath(os.path.join(self.document_path, filename))
......
...@@ -71,22 +71,43 @@ class SimpleHTTPServerTest(unittest.TestCase): ...@@ -71,22 +71,43 @@ class SimpleHTTPServerTest(unittest.TestCase):
'server did not start.\nout: %s error: %s' % self.process.communicate()) 'server did not start.\nout: %s error: %s' % self.process.communicate())
self.assertIn('Directory listing for /', resp.text) self.assertIn('Directory listing for /', resp.text)
# post with multipart/form-data encoding
resp = requests.post( resp = requests.post(
server_base_url, server_base_url,
files={ files={
'path': 'hello.txt', 'path': 'hello-form-data.txt',
'content': b'hello', 'content': 'hello-form-data',
}, },
) )
self.assertEqual(resp.status_code, requests.codes.ok) self.assertEqual(resp.status_code, requests.codes.ok)
self.assertEqual(resp.text, 'Content written to hello-form-data.txt')
with open( with open(
os.path.join(self.base_path, self.recipe.options['path'], os.path.join(self.base_path, self.recipe.options['path'],
'hello.txt')) as f: 'hello-form-data.txt')) as f:
self.assertEqual(f.read(), 'hello') self.assertEqual(f.read(), 'hello-form-data')
self.assertIn('hello.txt', requests.get(server_base_url).text) self.assertIn('hello-form-data.txt', requests.get(server_base_url).text)
self.assertEqual( self.assertEqual(
requests.get(server_base_url + '/hello.txt').text, 'hello') requests.get(server_base_url + '/hello-form-data.txt').text, 'hello-form-data')
# post as application/x-www-form-urlencoded
resp = requests.post(
server_base_url,
data={
'path': 'hello-form-urlencoded.txt',
'content': 'hello-form-urlencoded',
},
)
self.assertEqual(resp.status_code, requests.codes.ok)
with open(
os.path.join(self.base_path, self.recipe.options['path'],
'hello-form-urlencoded.txt')) as f:
self.assertEqual(f.read(), 'hello-form-urlencoded')
self.assertIn('hello-form-urlencoded.txt', requests.get(server_base_url).text)
self.assertEqual(resp.text, 'Content written to hello-form-urlencoded.txt')
self.assertEqual(
requests.get(server_base_url + '/hello-form-urlencoded.txt').text, 'hello-form-urlencoded')
# incorrect paths are refused # incorrect paths are refused
for path in '/hello.txt', '../hello.txt': for path in '/hello.txt', '../hello.txt':
......
  • @tomo this fixes a problem with deploy test on python3

  • @tomo to have it in the deploy tests, we need to use a new version of KVM including 564cb10a , like you did in f8ff14f1

    is it OK to simply update to 1.0.274 or do we need something like having this software in binary cache first ?

  • is it OK to simply update to 1.0.274 or do we need something like having this software in binary cache first ?

    Yes it's OK, binary cache is not used for this one

  • Thanks, I did it ( c132790b ) let's see.

Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment