From 675df1ae8574b660e8d1ee86c9e14eb229b012b6 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Thu, 15 Oct 2020 17:20:18 -0700 Subject: [PATCH 1/2] fix: regression in header case-insensitivity --- src/helpers/headers.js | 38 ++++++++++++++++ src/index.js | 15 ++++++- src/targets/clojure/clj_http.js | 13 +++--- src/targets/csharp/httpclient.js | 5 ++- src/targets/csharp/restsharp.js | 7 ++- src/targets/javascript/jquery.js | 8 +++- src/targets/javascript/xhr.js | 7 ++- src/targets/php/http2.js | 7 ++- src/targets/powershell/common.js | 3 +- src/targets/r/httr.js | 6 +-- .../output/c/libcurl/multipart-form-data.c | 2 +- .../csharp/restsharp/multipart-form-data.cs | 2 +- .../output/go/native/multipart-form-data.go | 2 +- .../java/asynchttp/multipart-form-data.java | 2 +- .../java/nethttp/multipart-form-data.java | 2 +- .../java/okhttp/multipart-form-data.java | 2 +- .../java/unirest/multipart-form-data.java | 2 +- .../javascript/axios/multipart-form-data.js | 2 +- .../javascript/fetch/multipart-form-data.js | 2 +- .../kotlin/okhttp/multipart-form-data.kt | 2 +- .../output/node/axios/multipart-form-data.js | 2 +- .../output/node/fetch/multipart-form-data.js | 2 +- .../output/node/native/multipart-form-data.js | 2 +- .../node/request/multipart-form-data.js | 2 +- .../node/unirest/multipart-form-data.js | 2 +- .../objc/nsurlsession/multipart-form-data.m | 2 +- .../ocaml/cohttp/multipart-form-data.ml | 2 +- .../output/php/curl/multipart-form-data.php | 2 +- .../output/php/http1/multipart-form-data.php | 2 +- .../restmethod/multipart-form-data.ps1 | 2 +- .../webrequest/multipart-form-data.ps1 | 2 +- .../python/python3/multipart-form-data.py | 2 +- .../python/requests/multipart-form-data.py | 2 +- .../output/ruby/native/multipart-form-data.rb | 2 +- .../output/shell/curl/multipart-form-data.sh | 2 +- .../shell/httpie/multipart-form-data.sh | 2 +- .../output/shell/wget/multipart-form-data.sh | 2 +- .../nsurlsession/multipart-form-data.swift | 2 +- .../requests/multipart-form-data.json | 2 +- test/headers.js | 43 +++++++++++++++++++ test/requests.js | 2 +- 41 files changed, 162 insertions(+), 50 deletions(-) create mode 100644 src/helpers/headers.js create mode 100644 test/headers.js diff --git a/src/helpers/headers.js b/src/helpers/headers.js new file mode 100644 index 000000000..5e01605c9 --- /dev/null +++ b/src/helpers/headers.js @@ -0,0 +1,38 @@ +module.exports = { + /** + * Given a headers object retrieve the contents of a header out of it via a case-insensitive key. + * + * @param {object} headers + * @param {string} name + * @return {string} + */ + getHeader: (headers, name) => { + return headers[Object.keys(headers).find(k => k.toLowerCase() === name.toLowerCase())] + }, + + /** + * Given a headers object retrieve a specific header out of it via a case-insensitive key. + * + * @param {object} headers + * @param {string} name + * @return {string} + */ + getHeaderName: (headers, name) => { + return Object.keys(headers).find(k => { + if (k.toLowerCase() === name.toLowerCase()) { + return k + } + }) + }, + + /** + * Determine if a given case-insensitive header exists within a header object. + * + * @param {object} headers + * @param {string} name + * @return {(integer|boolean)} + */ + hasHeader: (headers, name) => { + return Boolean(Object.keys(headers).find(k => k.toLowerCase() === name.toLowerCase())) + } +} diff --git a/src/index.js b/src/index.js index 29fcb389b..9a9d41915 100644 --- a/src/index.js +++ b/src/index.js @@ -165,7 +165,20 @@ HTTPSnippet.prototype.prepare = function (request) { } request.postData.boundary = boundary - request.headersObj['content-type'] = 'multipart/form-data; boundary=' + boundary + + // Since headers are case-sensitive we need to see if there's an existing `Content-Type` header that we can + // override. + let foundContentType = false + Object.keys(request.headersObj).forEach(header => { + if (header.toLowerCase() === 'content-type') { + foundContentType = true + request.headersObj[header] = 'multipart/form-data; boundary=' + boundary + } + }) + + if (!foundContentType) { + request.headersObj['content-type'] = 'multipart/form-data; boundary=' + boundary + } } break diff --git a/src/targets/clojure/clj_http.js b/src/targets/clojure/clj_http.js index ec13d5364..694a2761b 100644 --- a/src/targets/clojure/clj_http.js +++ b/src/targets/clojure/clj_http.js @@ -11,6 +11,7 @@ 'use strict' var CodeBuilder = require('../../helpers/code-builder') +var helpers = require('../../helpers/headers') var Keyword = function (name) { this.name = name @@ -101,15 +102,15 @@ module.exports = function (source, options) { case 'application/json': params['content-type'] = new Keyword('json') params['form-params'] = source.postData.jsonObj - delete params.headers['content-type'] + delete params.headers[helpers.getHeaderName(params.headers, 'content-type')] break case 'application/x-www-form-urlencoded': params['form-params'] = source.postData.paramsObj - delete params.headers['content-type'] + delete params.headers[helpers.getHeaderName(params.headers, 'content-type')] break case 'text/plain': params.body = source.postData.text - delete params.headers['content-type'] + delete params.headers[helpers.getHeaderName(params.headers, 'content-type')] break case 'multipart/form-data': params.multipart = source.postData.params.map(function (x) { @@ -121,14 +122,14 @@ module.exports = function (source, options) { content: x.value} } }) - delete params.headers['content-type'] + delete params.headers[helpers.getHeaderName(params.headers, 'content-type')] break } - switch (params.headers.accept) { + switch (helpers.getHeader(params.headers, 'accept')) { case 'application/json': params.accept = new Keyword('json') - delete params.headers.accept + delete params.headers[helpers.getHeaderName(params.headers, 'accept')] break } diff --git a/src/targets/csharp/httpclient.js b/src/targets/csharp/httpclient.js index bff153c06..da1f52a30 100644 --- a/src/targets/csharp/httpclient.js +++ b/src/targets/csharp/httpclient.js @@ -1,9 +1,10 @@ 'use strict' var CodeBuilder = require('../../helpers/code-builder') +var helpers = require('../../helpers/headers') function getDecompressionMethods (source) { - var acceptEncoding = source.allHeaders['accept-encoding'] + var acceptEncoding = helpers.getHeader(source.allHeaders, 'accept-encoding') if (!acceptEncoding) { return [] // no decompression } @@ -67,7 +68,7 @@ module.exports = function (source, options) { code.push(1, 'RequestUri = new Uri("%s"),', source.fullUrl) var headers = Object.keys(source.allHeaders).filter(function (header) { - switch (header) { + switch (header.toLowerCase()) { case 'content-type': case 'content-length': case 'accept-encoding': diff --git a/src/targets/csharp/restsharp.js b/src/targets/csharp/restsharp.js index 0b8fc2735..a42b0f542 100644 --- a/src/targets/csharp/restsharp.js +++ b/src/targets/csharp/restsharp.js @@ -1,6 +1,7 @@ 'use strict' var CodeBuilder = require('../../helpers/code-builder') +var helpers = require('../../helpers/headers') module.exports = function (source, options) { var code = new CodeBuilder() @@ -31,7 +32,11 @@ module.exports = function (source, options) { } if (source.postData.text) { - code.push('request.AddParameter("%s", %s, ParameterType.RequestBody);', source.allHeaders['content-type'], JSON.stringify(source.postData.text)) + code.push( + 'request.AddParameter("%s", %s, ParameterType.RequestBody);', + helpers.getHeader(source.allHeaders, 'content-type'), + JSON.stringify(source.postData.text) + ) } code.push('IRestResponse response = client.Execute(request);') diff --git a/src/targets/javascript/jquery.js b/src/targets/javascript/jquery.js index e84e390b6..2de37bf08 100644 --- a/src/targets/javascript/jquery.js +++ b/src/targets/javascript/jquery.js @@ -11,6 +11,7 @@ 'use strict' var CodeBuilder = require('../../helpers/code-builder') +var helpers = require('../../helpers/headers') module.exports = function (source, options) { var opts = Object.assign({ @@ -50,9 +51,12 @@ module.exports = function (source, options) { settings.data = '[form]' // remove the contentType header - if (~settings.headers['content-type'].indexOf('boundary')) { - delete settings.headers['content-type'] + if (helpers.hasHeader(settings.headers, 'content-type')) { + if (helpers.getHeader(settings.headers, 'content-type').indexOf('boundary')) { + delete settings.headers[helpers.getHeaderName(settings.headers, 'content-type')] + } } + code.blank() break diff --git a/src/targets/javascript/xhr.js b/src/targets/javascript/xhr.js index e2793c62b..3f7134132 100644 --- a/src/targets/javascript/xhr.js +++ b/src/targets/javascript/xhr.js @@ -11,6 +11,7 @@ 'use strict' var CodeBuilder = require('../../helpers/code-builder') +var helpers = require('../../helpers/headers') module.exports = function (source, options) { var opts = Object.assign({ @@ -34,8 +35,10 @@ module.exports = function (source, options) { }) // remove the contentType header - if (source.allHeaders['content-type'].indexOf('boundary')) { - delete source.allHeaders['content-type'] + if (helpers.hasHeader(source.allHeaders, 'content-type')) { + if (helpers.getHeader(source.allHeaders, 'content-type').indexOf('boundary')) { + delete source.allHeaders[helpers.getHeaderName(source.allHeaders, 'content-type')] + } } code.blank() diff --git a/src/targets/php/http2.js b/src/targets/php/http2.js index 9f48e45a8..a5defa4dc 100644 --- a/src/targets/php/http2.js +++ b/src/targets/php/http2.js @@ -11,6 +11,7 @@ 'use strict' var helpers = require('./helpers') +var headerHelpers = require('../../helpers/headers') var CodeBuilder = require('../../helpers/code-builder') module.exports = function (source, options) { @@ -65,8 +66,10 @@ module.exports = function (source, options) { ) // remove the contentType header - if (~source.headersObj['content-type'].indexOf('boundary')) { - delete source.headersObj['content-type'] + if (headerHelpers.hasHeader(source.headersObj, 'content-type')) { + if (headerHelpers.getHeader(source.headersObj, 'content-type').indexOf('boundary')) { + delete source.headersObj[headerHelpers.getHeaderName(source.headersObj, 'content-type')] + } } code.blank() diff --git a/src/targets/powershell/common.js b/src/targets/powershell/common.js index 38339d091..5cfbae3d8 100644 --- a/src/targets/powershell/common.js +++ b/src/targets/powershell/common.js @@ -1,6 +1,7 @@ 'use strict' var CodeBuilder = require('../../helpers/code-builder') +var helpers = require('../../helpers/headers') module.exports = function (command) { return function (source, options) { @@ -44,7 +45,7 @@ module.exports = function (command) { } if (source.postData.text) { - commandOptions.push("-ContentType '" + source.allHeaders['content-type'] + "'") + commandOptions.push("-ContentType '" + helpers.getHeader(source.allHeaders, 'content-type') + "'") commandOptions.push("-Body '" + source.postData.text + "'") } diff --git a/src/targets/r/httr.js b/src/targets/r/httr.js index b06a5be1a..c7cd5b790 100644 --- a/src/targets/r/httr.js +++ b/src/targets/r/httr.js @@ -93,13 +93,13 @@ module.exports = function (source, options) { var accept for (head in headers) { - if (head === 'accept') { + if (head.toLowerCase() === 'accept') { accept = ', accept("' + headers[head] + '")' headerCount = headerCount - 1 - } else if (head === 'cookie') { + } else if (head.toLowerCase() === 'cookie') { cookies = ', set_cookies(`' + headers[head].replace(/;/g, '", `').replace(/` /g, '`').replace(/=/g, '` = "') + '")' headerCount = headerCount - 1 - } else if (head !== 'content-type') { + } else if (head.toLowerCase() !== 'content-type') { header = header + head.replace('-', '_') + " = '" + headers[head] if (headerCount > 1) { header = header + "', " } } diff --git a/test/fixtures/output/c/libcurl/multipart-form-data.c b/test/fixtures/output/c/libcurl/multipart-form-data.c index 232a092fc..e47d6e573 100644 --- a/test/fixtures/output/c/libcurl/multipart-form-data.c +++ b/test/fixtures/output/c/libcurl/multipart-form-data.c @@ -4,7 +4,7 @@ curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(hnd, CURLOPT_URL, "http://mockbin.com/har"); struct curl_slist *headers = NULL; -headers = curl_slist_append(headers, "content-type: multipart/form-data; boundary=---011000010111000001101001"); +headers = curl_slist_append(headers, "Content-Type: multipart/form-data; boundary=---011000010111000001101001"); curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n"); diff --git a/test/fixtures/output/csharp/restsharp/multipart-form-data.cs b/test/fixtures/output/csharp/restsharp/multipart-form-data.cs index 1ad23e37a..aae6aa514 100644 --- a/test/fixtures/output/csharp/restsharp/multipart-form-data.cs +++ b/test/fixtures/output/csharp/restsharp/multipart-form-data.cs @@ -1,5 +1,5 @@ var client = new RestClient("http://mockbin.com/har"); var request = new RestRequest(Method.POST); -request.AddHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001"); +request.AddHeader("Content-Type", "multipart/form-data; boundary=---011000010111000001101001"); request.AddParameter("multipart/form-data; boundary=---011000010111000001101001", "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n", ParameterType.RequestBody); IRestResponse response = client.Execute(request); diff --git a/test/fixtures/output/go/native/multipart-form-data.go b/test/fixtures/output/go/native/multipart-form-data.go index d587d473f..cf31548a9 100644 --- a/test/fixtures/output/go/native/multipart-form-data.go +++ b/test/fixtures/output/go/native/multipart-form-data.go @@ -15,7 +15,7 @@ func main() { req, _ := http.NewRequest("POST", url, payload) - req.Header.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001") + req.Header.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") res, _ := http.DefaultClient.Do(req) diff --git a/test/fixtures/output/java/asynchttp/multipart-form-data.java b/test/fixtures/output/java/asynchttp/multipart-form-data.java index 06c752bf2..90c80c631 100644 --- a/test/fixtures/output/java/asynchttp/multipart-form-data.java +++ b/test/fixtures/output/java/asynchttp/multipart-form-data.java @@ -1,6 +1,6 @@ AsyncHttpClient client = new DefaultAsyncHttpClient(); client.prepare("POST", "http://mockbin.com/har") - .setHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .setHeader("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") .setBody("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n") .execute() .toCompletableFuture() diff --git a/test/fixtures/output/java/nethttp/multipart-form-data.java b/test/fixtures/output/java/nethttp/multipart-form-data.java index 044bb716a..5a8c8f9f9 100644 --- a/test/fixtures/output/java/nethttp/multipart-form-data.java +++ b/test/fixtures/output/java/nethttp/multipart-form-data.java @@ -1,6 +1,6 @@ HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://mockbin.com/har")) - .header("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") .method("POST", HttpRequest.BodyPublishers.ofString("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n")) .build(); HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); diff --git a/test/fixtures/output/java/okhttp/multipart-form-data.java b/test/fixtures/output/java/okhttp/multipart-form-data.java index ab1e1d2c4..517e4fb48 100644 --- a/test/fixtures/output/java/okhttp/multipart-form-data.java +++ b/test/fixtures/output/java/okhttp/multipart-form-data.java @@ -5,7 +5,7 @@ Request request = new Request.Builder() .url("http://mockbin.com/har") .post(body) - .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .addHeader("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") .build(); Response response = client.newCall(request).execute(); diff --git a/test/fixtures/output/java/unirest/multipart-form-data.java b/test/fixtures/output/java/unirest/multipart-form-data.java index 38ce54749..ec517f373 100644 --- a/test/fixtures/output/java/unirest/multipart-form-data.java +++ b/test/fixtures/output/java/unirest/multipart-form-data.java @@ -1,4 +1,4 @@ HttpResponse response = Unirest.post("http://mockbin.com/har") - .header("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .header("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") .body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n") .asString(); diff --git a/test/fixtures/output/javascript/axios/multipart-form-data.js b/test/fixtures/output/javascript/axios/multipart-form-data.js index 6c2658cc8..b000f6353 100644 --- a/test/fixtures/output/javascript/axios/multipart-form-data.js +++ b/test/fixtures/output/javascript/axios/multipart-form-data.js @@ -6,7 +6,7 @@ form.append("foo", "bar"); const options = { method: 'POST', url: 'http://mockbin.com/har', - headers: {'content-type': 'multipart/form-data; boundary=---011000010111000001101001'}, + headers: {'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001'}, data: '[form]' }; diff --git a/test/fixtures/output/javascript/fetch/multipart-form-data.js b/test/fixtures/output/javascript/fetch/multipart-form-data.js index f0c050724..f52ef8c38 100644 --- a/test/fixtures/output/javascript/fetch/multipart-form-data.js +++ b/test/fixtures/output/javascript/fetch/multipart-form-data.js @@ -4,7 +4,7 @@ form.append("foo", "bar"); fetch("http://mockbin.com/har", { "method": "POST", "headers": { - "content-type": "multipart/form-data; boundary=---011000010111000001101001" + "Content-Type": "multipart/form-data; boundary=---011000010111000001101001" } }) .then(response => { diff --git a/test/fixtures/output/kotlin/okhttp/multipart-form-data.kt b/test/fixtures/output/kotlin/okhttp/multipart-form-data.kt index cde86d9ed..4148c7865 100644 --- a/test/fixtures/output/kotlin/okhttp/multipart-form-data.kt +++ b/test/fixtures/output/kotlin/okhttp/multipart-form-data.kt @@ -5,7 +5,7 @@ val body = RequestBody.create(mediaType, "-----011000010111000001101001\r\nConte val request = Request.Builder() .url("http://mockbin.com/har") .post(body) - .addHeader("content-type", "multipart/form-data; boundary=---011000010111000001101001") + .addHeader("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") .build() val response = client.newCall(request).execute() diff --git a/test/fixtures/output/node/axios/multipart-form-data.js b/test/fixtures/output/node/axios/multipart-form-data.js index c529bacc4..d0ee89517 100644 --- a/test/fixtures/output/node/axios/multipart-form-data.js +++ b/test/fixtures/output/node/axios/multipart-form-data.js @@ -3,7 +3,7 @@ var axios = require("axios").default; var options = { method: 'POST', url: 'http://mockbin.com/har', - headers: {'content-type': 'multipart/form-data; boundary=---011000010111000001101001'}, + headers: {'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001'}, data: '-----011000010111000001101001\r\nContent-Disposition: form-data; name="foo"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n' }; diff --git a/test/fixtures/output/node/fetch/multipart-form-data.js b/test/fixtures/output/node/fetch/multipart-form-data.js index 523deaad6..bad53c7c8 100644 --- a/test/fixtures/output/node/fetch/multipart-form-data.js +++ b/test/fixtures/output/node/fetch/multipart-form-data.js @@ -7,7 +7,7 @@ let url = 'http://mockbin.com/har'; let options = { method: 'POST', - headers: {'content-type': 'multipart/form-data; boundary=---011000010111000001101001'} + headers: {'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001'} }; options.body = formData; diff --git a/test/fixtures/output/node/native/multipart-form-data.js b/test/fixtures/output/node/native/multipart-form-data.js index 7d3aabd62..dfde8da81 100644 --- a/test/fixtures/output/node/native/multipart-form-data.js +++ b/test/fixtures/output/node/native/multipart-form-data.js @@ -6,7 +6,7 @@ const options = { "port": null, "path": "/har", "headers": { - "content-type": "multipart/form-data; boundary=---011000010111000001101001" + "Content-Type": "multipart/form-data; boundary=---011000010111000001101001" } }; diff --git a/test/fixtures/output/node/request/multipart-form-data.js b/test/fixtures/output/node/request/multipart-form-data.js index ad4b47049..e808b7162 100644 --- a/test/fixtures/output/node/request/multipart-form-data.js +++ b/test/fixtures/output/node/request/multipart-form-data.js @@ -3,7 +3,7 @@ const request = require('request'); const options = { method: 'POST', url: 'http://mockbin.com/har', - headers: {'content-type': 'multipart/form-data; boundary=---011000010111000001101001'}, + headers: {'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001'}, formData: {foo: 'bar'} }; diff --git a/test/fixtures/output/node/unirest/multipart-form-data.js b/test/fixtures/output/node/unirest/multipart-form-data.js index b13563c5a..ada44268c 100644 --- a/test/fixtures/output/node/unirest/multipart-form-data.js +++ b/test/fixtures/output/node/unirest/multipart-form-data.js @@ -3,7 +3,7 @@ const unirest = require("unirest"); const req = unirest("POST", "http://mockbin.com/har"); req.headers({ - "content-type": "multipart/form-data; boundary=---011000010111000001101001" + "Content-Type": "multipart/form-data; boundary=---011000010111000001101001" }); req.multipart([ diff --git a/test/fixtures/output/objc/nsurlsession/multipart-form-data.m b/test/fixtures/output/objc/nsurlsession/multipart-form-data.m index 3c4648d02..0790bf768 100644 --- a/test/fixtures/output/objc/nsurlsession/multipart-form-data.m +++ b/test/fixtures/output/objc/nsurlsession/multipart-form-data.m @@ -1,6 +1,6 @@ #import -NSDictionary *headers = @{ @"content-type": @"multipart/form-data; boundary=---011000010111000001101001" }; +NSDictionary *headers = @{ @"Content-Type": @"multipart/form-data; boundary=---011000010111000001101001" }; NSArray *parameters = @[ @{ @"name": @"foo", @"value": @"bar" } ]; NSString *boundary = @"---011000010111000001101001"; diff --git a/test/fixtures/output/ocaml/cohttp/multipart-form-data.ml b/test/fixtures/output/ocaml/cohttp/multipart-form-data.ml index 488c3e41b..efc66b71a 100644 --- a/test/fixtures/output/ocaml/cohttp/multipart-form-data.ml +++ b/test/fixtures/output/ocaml/cohttp/multipart-form-data.ml @@ -3,7 +3,7 @@ open Cohttp open Lwt let uri = Uri.of_string "http://mockbin.com/har" in -let headers = Header.add (Header.init ()) "content-type" "multipart/form-data; boundary=---011000010111000001101001" in +let headers = Header.add (Header.init ()) "Content-Type" "multipart/form-data; boundary=---011000010111000001101001" in let body = Cohttp_lwt_body.of_string "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n" in Client.call ~headers ~body `POST uri diff --git a/test/fixtures/output/php/curl/multipart-form-data.php b/test/fixtures/output/php/curl/multipart-form-data.php index ccce63ac5..038aa49c9 100644 --- a/test/fixtures/output/php/curl/multipart-form-data.php +++ b/test/fixtures/output/php/curl/multipart-form-data.php @@ -12,7 +12,7 @@ CURLOPT_CUSTOMREQUEST => "POST", CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n", CURLOPT_HTTPHEADER => [ - "content-type: multipart/form-data; boundary=---011000010111000001101001" + "Content-Type: multipart/form-data; boundary=---011000010111000001101001" ], ]); diff --git a/test/fixtures/output/php/http1/multipart-form-data.php b/test/fixtures/output/php/http1/multipart-form-data.php index 654e68e45..d7227ca78 100644 --- a/test/fixtures/output/php/http1/multipart-form-data.php +++ b/test/fixtures/output/php/http1/multipart-form-data.php @@ -5,7 +5,7 @@ $request->setMethod(HTTP_METH_POST); $request->setHeaders([ - 'content-type' => 'multipart/form-data; boundary=---011000010111000001101001' + 'Content-Type' => 'multipart/form-data; boundary=---011000010111000001101001' ]); $request->setBody('-----011000010111000001101001 diff --git a/test/fixtures/output/powershell/restmethod/multipart-form-data.ps1 b/test/fixtures/output/powershell/restmethod/multipart-form-data.ps1 index f389a2757..bb78bfb10 100644 --- a/test/fixtures/output/powershell/restmethod/multipart-form-data.ps1 +++ b/test/fixtures/output/powershell/restmethod/multipart-form-data.ps1 @@ -1,5 +1,5 @@ $headers=@{} -$headers.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001") +$headers.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") $response = Invoke-RestMethod -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001 Content-Disposition: form-data; name="foo" diff --git a/test/fixtures/output/powershell/webrequest/multipart-form-data.ps1 b/test/fixtures/output/powershell/webrequest/multipart-form-data.ps1 index 9fe0ed80b..91f16b8db 100644 --- a/test/fixtures/output/powershell/webrequest/multipart-form-data.ps1 +++ b/test/fixtures/output/powershell/webrequest/multipart-form-data.ps1 @@ -1,5 +1,5 @@ $headers=@{} -$headers.Add("content-type", "multipart/form-data; boundary=---011000010111000001101001") +$headers.Add("Content-Type", "multipart/form-data; boundary=---011000010111000001101001") $response = Invoke-WebRequest -Uri 'http://mockbin.com/har' -Method POST -Headers $headers -ContentType 'multipart/form-data; boundary=---011000010111000001101001' -Body '-----011000010111000001101001 Content-Disposition: form-data; name="foo" diff --git a/test/fixtures/output/python/python3/multipart-form-data.py b/test/fixtures/output/python/python3/multipart-form-data.py index 42bb17546..d5f5c0254 100644 --- a/test/fixtures/output/python/python3/multipart-form-data.py +++ b/test/fixtures/output/python/python3/multipart-form-data.py @@ -4,7 +4,7 @@ payload = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n" -headers = { 'content-type': "multipart/form-data; boundary=---011000010111000001101001" } +headers = { 'Content-Type': "multipart/form-data; boundary=---011000010111000001101001" } conn.request("POST", "/har", payload, headers) diff --git a/test/fixtures/output/python/requests/multipart-form-data.py b/test/fixtures/output/python/requests/multipart-form-data.py index 77bbb3438..53f598512 100644 --- a/test/fixtures/output/python/requests/multipart-form-data.py +++ b/test/fixtures/output/python/requests/multipart-form-data.py @@ -3,7 +3,7 @@ url = "http://mockbin.com/har" payload = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n" -headers = {'content-type': 'multipart/form-data; boundary=---011000010111000001101001'} +headers = {'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001'} response = requests.request("POST", url, data=payload, headers=headers) diff --git a/test/fixtures/output/ruby/native/multipart-form-data.rb b/test/fixtures/output/ruby/native/multipart-form-data.rb index 60d18faf3..e7800b288 100644 --- a/test/fixtures/output/ruby/native/multipart-form-data.rb +++ b/test/fixtures/output/ruby/native/multipart-form-data.rb @@ -6,7 +6,7 @@ http = Net::HTTP.new(url.host, url.port) request = Net::HTTP::Post.new(url) -request["content-type"] = 'multipart/form-data; boundary=---011000010111000001101001' +request["Content-Type"] = 'multipart/form-data; boundary=---011000010111000001101001' request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n" response = http.request(request) diff --git a/test/fixtures/output/shell/curl/multipart-form-data.sh b/test/fixtures/output/shell/curl/multipart-form-data.sh index d7b10e7ee..def1f4351 100644 --- a/test/fixtures/output/shell/curl/multipart-form-data.sh +++ b/test/fixtures/output/shell/curl/multipart-form-data.sh @@ -1,4 +1,4 @@ curl --request POST \ --url http://mockbin.com/har \ - --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ + --header 'Content-Type: multipart/form-data; boundary=---011000010111000001101001' \ --form foo=bar diff --git a/test/fixtures/output/shell/httpie/multipart-form-data.sh b/test/fixtures/output/shell/httpie/multipart-form-data.sh index a6d170094..28e8af7d4 100644 --- a/test/fixtures/output/shell/httpie/multipart-form-data.sh +++ b/test/fixtures/output/shell/httpie/multipart-form-data.sh @@ -5,4 +5,4 @@ bar -----011000010111000001101001-- ' | \ http POST http://mockbin.com/har \ - content-type:'multipart/form-data; boundary=---011000010111000001101001' + Content-Type:'multipart/form-data; boundary=---011000010111000001101001' diff --git a/test/fixtures/output/shell/wget/multipart-form-data.sh b/test/fixtures/output/shell/wget/multipart-form-data.sh index 8ccf44a74..6b1cd3b45 100644 --- a/test/fixtures/output/shell/wget/multipart-form-data.sh +++ b/test/fixtures/output/shell/wget/multipart-form-data.sh @@ -1,6 +1,6 @@ wget --quiet \ --method POST \ - --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ + --header 'Content-Type: multipart/form-data; boundary=---011000010111000001101001' \ --body-data '-----011000010111000001101001\r\nContent-Disposition: form-data; name="foo"\r\n\r\nbar\r\n-----011000010111000001101001--\r\n' \ --output-document \ - http://mockbin.com/har diff --git a/test/fixtures/output/swift/nsurlsession/multipart-form-data.swift b/test/fixtures/output/swift/nsurlsession/multipart-form-data.swift index bafceef17..3e2975b5d 100644 --- a/test/fixtures/output/swift/nsurlsession/multipart-form-data.swift +++ b/test/fixtures/output/swift/nsurlsession/multipart-form-data.swift @@ -1,6 +1,6 @@ import Foundation -let headers = ["content-type": "multipart/form-data; boundary=---011000010111000001101001"] +let headers = ["Content-Type": "multipart/form-data; boundary=---011000010111000001101001"] let parameters = [ [ "name": "foo", diff --git a/test/fixtures/requests/multipart-form-data.json b/test/fixtures/requests/multipart-form-data.json index 4db5eeea6..63d4bdc2e 100644 --- a/test/fixtures/requests/multipart-form-data.json +++ b/test/fixtures/requests/multipart-form-data.json @@ -3,7 +3,7 @@ "url": "http://mockbin.com/har", "headers": [ { - "name": "content-type", + "name": "Content-Type", "value": "multipart/form-data" } ], diff --git a/test/headers.js b/test/headers.js new file mode 100644 index 000000000..41476727c --- /dev/null +++ b/test/headers.js @@ -0,0 +1,43 @@ +/* global describe, it */ + +'use strict' + +var helpers = require('../src/helpers/headers') +var should = require('should') + +const headers = { + 'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001', + 'accept': 'application/json' +} + +describe('Headers', function () { + describe('#getHeader', () => { + it('should get a header', () => { + helpers.getHeader(headers, 'content-type').should.eql('multipart/form-data; boundary=---011000010111000001101001') + helpers.getHeader(headers, 'content-TYPE').should.eql('multipart/form-data; boundary=---011000010111000001101001') + helpers.getHeader(headers, 'Accept').should.eql('application/json') + + should.not.exist(helpers.getHeader(headers, 'authorization')) + }) + }) + + describe('#getHeaderName', () => { + it('should get a header name', () => { + helpers.getHeaderName(headers, 'content-type').should.eql('Content-Type') + helpers.getHeaderName(headers, 'content-TYPE').should.eql('Content-Type') + helpers.getHeaderName(headers, 'Accept').should.eql('accept') + + should.not.exist(helpers.getHeaderName(headers, 'authorization')) + }) + }) + + describe('#hasHeader', () => { + it('should return if a header is present', () => { + helpers.hasHeader(headers, 'content-type').should.be.true() + helpers.hasHeader(headers, 'content-TYPE').should.be.true() + helpers.hasHeader(headers, 'Accept').should.be.true() + + helpers.hasHeader(headers, 'authorization').should.be.false() + }) + }) +}) diff --git a/test/requests.js b/test/requests.js index dbc38e7e9..4d23594b5 100644 --- a/test/requests.js +++ b/test/requests.js @@ -53,7 +53,7 @@ fixtures.cli.forEach(function (cli) { // make an exception for multipart/form-data if (fixture.headers) { fixture.headers.forEach(function (header, index) { - if (header.name === 'content-type' && header.value === 'multipart/form-data') { + if (header.name.toLowerCase() === 'content-type' && header.value === 'multipart/form-data') { delete fixture.headers[index] } }) From ed445d843595fcaef173103693dfe81a5c023889 Mon Sep 17 00:00:00 2001 From: Jon Ursenbach Date: Sat, 31 Oct 2020 12:39:49 -0700 Subject: [PATCH 2/2] fix: cleaning up some unnecessary header lookup complexity --- src/index.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/index.js b/src/index.js index 9a9d41915..353c6e210 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ var es = require('event-stream') var MultiPartForm = require('form-data') var qs = require('querystring') var reducer = require('./helpers/reducer') +var helpers = require('./helpers/headers') var targets = require('./targets') var url = require('url') var validate = require('har-validator/lib/async') @@ -168,17 +169,11 @@ HTTPSnippet.prototype.prepare = function (request) { // Since headers are case-sensitive we need to see if there's an existing `Content-Type` header that we can // override. - let foundContentType = false - Object.keys(request.headersObj).forEach(header => { - if (header.toLowerCase() === 'content-type') { - foundContentType = true - request.headersObj[header] = 'multipart/form-data; boundary=' + boundary - } - }) + const contentTypeHeader = helpers.hasHeader(request.headersObj, 'content-type') + ? helpers.getHeaderName(request.headersObj, 'content-type') + : 'content-type' - if (!foundContentType) { - request.headersObj['content-type'] = 'multipart/form-data; boundary=' + boundary - } + request.headersObj[contentTypeHeader] = 'multipart/form-data; boundary=' + boundary } break