From d3e4725efdb6944c70e82ef4a824be876c93fc90 Mon Sep 17 00:00:00 2001 From: Rikard Qvarforth Date: Thu, 6 Nov 2014 08:52:55 +0100 Subject: [PATCH] Adding CR to multipart requests based on multipart content-type header, Closes #74 #118 #29 --- src/transaction-runner.coffee | 23 ++++- test/unit/transaction-runner-test.coffee | 116 +++++++++++++++++++++++ 2 files changed, 135 insertions(+), 4 deletions(-) diff --git a/src/transaction-runner.coffee b/src/transaction-runner.coffee index 79a91afe6..9a1b5c3b2 100644 --- a/src/transaction-runner.coffee +++ b/src/transaction-runner.coffee @@ -17,7 +17,7 @@ logger = require './logger' String::startsWith = (str) -> - return this.slice(0, str.length) is str + return this.slice(0, str.length) is str class TransactionRunner constructor: (@configuration) -> @@ -113,11 +113,11 @@ class TransactionRunner # Add length of body if no Content-Length present # Doing here instead of in configureTransaction, because request body can be edited in before hook - caseInsensitiveMap = {} + caseInsensitiveRequestHeadersMap = {} for key, value of transaction.request.headers - caseInsensitiveMap[key.toLowerCase()] = key + caseInsensitiveRequestHeadersMap[key.toLowerCase()] = key - if not caseInsensitiveMap['content-length'] and transaction.request['body'] != '' + if not caseInsensitiveRequestHeadersMap['content-length'] and transaction.request['body'] != '' transaction.request.headers['Content-Length'] = Buffer.byteLength(transaction.request['body'], 'utf8') requestOptions = @@ -209,6 +209,9 @@ class TransactionRunner return callback() transport = if transaction.protocol is 'https:' then https else http + if transaction.request['body'] and @isMultipart requestOptions + @replaceLineFeedInBody transaction, requestOptions + try req = transport.request requestOptions, handleRequest req.write transaction.request['body'] if transaction.request['body'] != '' @@ -217,4 +220,16 @@ class TransactionRunner configuration.emitter.emit 'test error', error, test if error return callback() + isMultipart: (requestOptions) => + caseInsensitiveRequestHeaders = {} + for key, value of requestOptions.headers + caseInsensitiveRequestHeaders[key.toLowerCase()] = value + caseInsensitiveRequestHeaders['content-type']?.indexOf("multipart") > -1 + + replaceLineFeedInBody: (transaction, requestOptions) => + transaction.request['body'] = transaction.request['body'].replace(/\n/g, '\r\n') + transaction.request['headers']['Content-Length'] = transaction.request['body'].length + requestOptions.headers = transaction.request['headers'] + + module.exports = TransactionRunner diff --git a/test/unit/transaction-runner-test.coffee b/test/unit/transaction-runner-test.coffee index 1bd9bde69..40c147a38 100644 --- a/test/unit/transaction-runner-test.coffee +++ b/test/unit/transaction-runner-test.coffee @@ -405,3 +405,119 @@ describe 'TransactionRunner', ()-> runner.executeTransaction transaction, () -> assert.ok server.isDone() done() + + describe 'executeTransaction(transaction, callback) multipart', () -> + multiPartTransaction = null + notMultiPartTransaction = null + runner = null + beforeEach () -> + runner = new Runner(configuration) + multiPartTransaction = + name: 'Group Machine > Machine > Post Message> Bogus example name' + id: 'POST /machines/message' + host: 'localhost' + port: '3000' + request: + body: '\n--BOUNDARY \ncontent-disposition: form-data; name="mess12"\n\n{"message":"mess1"}\n--BOUNDARY\n\nContent-Disposition: form-data; name="mess2"\n\n{"message":"mess1"}\n--BOUNDARY--' + headers: + 'Content-Type': 'multipart/form-data; boundary=BOUNDARY' + 'User-Agent': 'Dredd/0.2.1 (Darwin 13.0.0; x64)' + 'Content-Length': 180 + uri: '/machines/message' + method: 'POST' + expected: + headers: + 'content-type': 'text/htm' + body: '' + status: '204' + origin: + resourceGroupName: 'Group Machine' + resourceName: 'Machine' + actionName: 'Post Message' + exampleName: 'Bogus example name' + fullPath: '/machines/message' + protocol: 'http:' + + notMultiPartTransaction = + name: 'Group Machine > Machine > Post Message> Bogus example name' + id: 'POST /machines/message' + host: 'localhost' + port: '3000' + request: + body: '\n--BOUNDARY \ncontent-disposition: form-data; name="mess12"\n\n{"message":"mess1"}\n--BOUNDARY\n\nContent-Disposition: form-data; name="mess2"\n\n{"message":"mess1"}\n--BOUNDARY--' + headers: + 'Content-Type': 'text/plain' + 'User-Agent': 'Dredd/0.2.1 (Darwin 13.0.0; x64)' + 'Content-Length': 180 + uri: '/machines/message' + method: 'POST' + expected: + headers: + 'content-type': 'text/htm' + body: '' + status: '204' + origin: + resourceGroupName: 'Group Machine' + resourceName: 'Machine' + actionName: 'Post Message' + exampleName: 'Bogus example name' + fullPath: '/machines/message' + protocol: 'http:' + + describe 'when multipart header in request', () -> + + parsedBody = '\r\n--BOUNDARY \r\ncontent-disposition: form-data; name="mess12"\r\n\r\n{"message":"mess1"}\r\n--BOUNDARY\r\n\r\nContent-Disposition: form-data; name="mess2"\r\n\r\n{"message":"mess1"}\r\n--BOUNDARY--' + beforeEach () -> + server = nock('http://localhost:3000'). + post('/machines/message'). + reply 204 + configuration.server = 'http://localhost:3000' + + afterEach () -> + nock.cleanAll() + + it 'should replace line feed in body', (done) -> + runner.executeTransaction multiPartTransaction, () -> + assert.ok server.isDone() + assert.equal multiPartTransaction['request']['body'], parsedBody, 'Body' + assert.include multiPartTransaction['request']['body'], "\r\n" + done() + + describe 'when multipart header in request is with lowercase key', () -> + + parsedBody = '\r\n--BOUNDARY \r\ncontent-disposition: form-data; name="mess12"\r\n\r\n{"message":"mess1"}\r\n--BOUNDARY\r\n\r\nContent-Disposition: form-data; name="mess2"\r\n\r\n{"message":"mess1"}\r\n--BOUNDARY--' + beforeEach () -> + server = nock('http://localhost:3000'). + post('/machines/message'). + reply 204 + configuration.server = 'http://localhost:3000' + + delete multiPartTransaction['request']['headers']['Content-Type'] + multiPartTransaction['request']['headers']['content-type'] = 'multipart/form-data; boundary=BOUNDARY' + + afterEach () -> + nock.cleanAll() + + it 'should replace line feed in body', (done) -> + runner.executeTransaction multiPartTransaction, () -> + assert.ok server.isDone() + assert.equal multiPartTransaction['request']['body'], parsedBody, 'Body' + assert.include multiPartTransaction['request']['body'], "\r\n" + done() + + describe 'when multipart header is not in request', () -> + beforeEach () -> + server = nock('http://localhost:3000'). + post('/machines/message'). + reply 204 + configuration.server = 'http://localhost:3000' + + afterEach () -> + nock.cleanAll() + + it 'should not include any line-feed in body', (done) -> + runner.executeTransaction notMultiPartTransaction, () -> + assert.ok server.isDone() + assert.notInclude multiPartTransaction['request']['body'], "\r\n" + done() +