Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tornado: testing multipart request using AsyncHTTPTestCase

I am writing an API for user upload file using multipart request. I see that Tornado version 4.5 has supported multipart request. But after that, I want to test this API.

My question is:

  • How can I test an multipart request on Tornado. I have google many references, but I cannot find useful resources.
  • If cannot doing this in Tornado, how can I propose a multipart request so I can use with tornado's AsyncHTTPTestCase.

Thanks

like image 752
Trần Kim Dự Avatar asked Apr 12 '18 08:04

Trần Kim Dự


1 Answers

Tornado does not have a built-in support for making multipart requests. You'll have to complie a multipart request manually.

Let's first look at how a multipart/form-data request looks like.

Sample form:

<form method="POST" enctype="multipart/form-data">
    <input type="text" name="field1">
    <input type="file" name="field2">

    <input type="submit">
</form>

If you enter Hello in field1 and choose a file called myfile.png for field2, the HTTP request will look like this:

POST /url HTTP/1.1
Content-Type: multipart/form-data; boundary="boundary"

--boundary
Content-Disposition: form-data; name="field1"

Hello
--boundary
Content-Disposition: form-data; name="field2"; filename="myfile.png"
Conent-Type: image/png

<binary content of myfile.png>
--boundary--

All you have to do is compile a similar request.


Before I show you an example, let me make something clear to you, if you don't already know - in the raw HTTP request example above, at the end of every line there are these characters - \r\n. They aren't visible here but they are present in an actual HTTP request. Even the blank lines have \r\n characters present.

This is important to know. If you're gonna compile an HTTP request manually, you'll have to add \r\n characters at the end of every line.

Let's get to the example.

class MyTestCase(AsyncHTTPTestCase):
    def test_something(self):
        # create a boundary
        boundary = 'SomeRandomBoundary'

        # set the Content-Type header
        headers = {
            'Content-Type': 'multipart/form-data; boundary=%s' % boundary
        }

        # create the body

        # opening boundary
        body = '--%s\r\n' % boundary 

        # data for field1
        body += 'Content-Disposition: form-data; name="field1"\r\n'
        body += '\r\n' # blank line
        body += 'Hello\r\n'

        # separator boundary
        body += '--%s\r\n' % boundary 

        # data for field2
        body += 'Content-Disposition: form-data; name="field2"; filename="myfile.png"\r\n'
        body += '\r\n' # blank line
        # now read myfile.png and add that data to the body
        with open('myfile.png', 'rb') as f:
            body += '%s\r\n' % f.read()

        # the closing boundary
        body += "--%s--\r\n" % boundary


        # make a request
        self.fetch(url, method='POST', headers=headers, body=body)

The above code is very basic. If you have multiple files and arguments, you should consider writing a separate function for that and make use of for loops. Click here for an example code from Tornado's github repo.

like image 174
xyres Avatar answered Nov 14 '22 18:11

xyres