Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: adding a new harIsAlreadyEncoded option #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Type: `object`

Available options:

* `escapeQueryStrings` (`boolean`): In the event of you supplying a `source` that alreay contains escaped query strings, this allows you to disable automatic of query strings and prevents them from being double-escaped.
* `harIsAlreadyEncoded` (`boolean`): In the event of you supplying a `source` HAR that already contains escaped data (query and cookie parameters)strings, this allows you to disable automatic encoding of those parameters to prevent them from being double-escaped.

### convert(target [, options])

Expand Down Expand Up @@ -155,10 +155,9 @@ For detailed information on each target, please review the [wiki](https://github

The main difference between this library and the upstream [httpsnippet](https://github.com/Kong/httpsnippet) library are:

* This targets Node 10+
* Does not ship with a CLI component
* Adds a `useObjectBody` option to the `node` and `javascript` targets. This option is a boolean flag that causes the request body to be rendered as an object literal wrapped in `JSON.stringify`. If disabled, it falls back to the original behavior of a stringified object body. This flag defaults to disabled.
* Contains a `escapeQueryStrings` option on the core library to disable escaping of query strings in URLs. Helpful if the HAR being supplied already has them escaped.
* Contains a `harIsAlreadyEncoded` option on the core library to disable [escaping](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) of cookies and query strings in URLs. Helpful if the HAR being supplied already has them escaped.

## License

Expand Down
12 changes: 8 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const HTTPSnippet = function (data, opts = {}) {
const input = Object.assign({}, data)

const options = Object.assign({
escapeQueryStrings: true
harIsAlreadyEncoded: false
}, opts)

// prep the main container
Expand Down Expand Up @@ -96,7 +96,11 @@ HTTPSnippet.prototype.prepare = function (request, options) {

// construct Cookie header
const cookies = request.cookies.map(function (cookie) {
return encodeURIComponent(cookie.name) + '=' + encodeURIComponent(cookie.value)
if (options.harIsAlreadyEncoded) {
return cookie.name + '=' + cookie.value
} else {
return encodeURIComponent(cookie.name) + '=' + encodeURIComponent(cookie.value)
}
})

if (cookies.length) {
Expand Down Expand Up @@ -228,13 +232,13 @@ HTTPSnippet.prototype.prepare = function (request, options) {

// update the uri object
request.uriObj.query = request.queryObj
if (options.escapeQueryStrings) {
if (options.harIsAlreadyEncoded) {
request.uriObj.search = qs.stringify(request.queryObj, {
encode: false,
indices: false
})
} else {
request.uriObj.search = qs.stringify(request.queryObj, {
encode: false,
indices: false
})
}
Expand Down
19 changes: 2 additions & 17 deletions src/targets/node/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ module.exports = function (source, options) {
method: source.method
}

if (Object.keys(source.headersObj).length) {
reqOpts.headers = source.headersObj
if (Object.keys(source.allHeaders).length) {
reqOpts.headers = source.allHeaders
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the implications of consuming allHeaders instead of headersObj?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

allHeaders contains the cookie header if it's present, headersObj is a k/v store of headers from the HAR excluding cookie because that's dynamically generated from the cookies array in the HAR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And by changing this to allHeaders this target no longer needs to combine its own cookie data into a cookie header because that's already been done in src/index.js.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it, thanks for the explanation!

}

switch (source.postData.mimeType) {
Expand Down Expand Up @@ -74,21 +74,6 @@ module.exports = function (source, options) {
}
}

// construct cookies argument
if (source.cookies.length) {
let cookies = ''
source.cookies.forEach(function (cookie) {
cookies = cookies + encodeURIComponent(cookie.name) + '=' + encodeURIComponent(cookie.value) + '; '
})

if (reqOpts.headers) {
reqOpts.headers.cookie = cookies
} else {
reqOpts.headers = {}
reqOpts.headers.cookie = cookies
}
}

code.blank()
code.push('const url = \'' + url + '\';')
code.push('const options = %s;', stringifyObject(reqOpts, {
Expand Down
9 changes: 6 additions & 3 deletions src/targets/node/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,18 @@ module.exports = function (source, options) {
}

// construct cookies argument
if (source.cookies.length) {
if (source.allHeaders.cookie) {
reqOpts.jar = 'JAR'

code.push('const jar = request.jar();')

const url = source.url

source.cookies.forEach(function (cookie) {
code.push("jar.setCookie(request.cookie('%s=%s'), '%s');", encodeURIComponent(cookie.name), encodeURIComponent(cookie.value), url)
// Cookies are already encoded within `source.allHeaders` so we can pull them out of that instead of doing our
// own encoding work.
source.allHeaders.cookie.split('; ').forEach(function (cookie) {
const [name, value] = cookie.split('=')
code.push("jar.setCookie(request.cookie('%s=%s'), '%s');", name, value, url)
})
code.blank()
}
Expand Down
9 changes: 6 additions & 3 deletions src/targets/node/unirest.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ module.exports = function (source, options) {
.push('const req = unirest("%s", "%s");', source.method, source.url)
.blank()

if (source.cookies.length) {
if (source.allHeaders.cookie) {
code.push('const CookieJar = unirest.jar();')

source.cookies.forEach(function (cookie) {
code.push('CookieJar.add("%s=%s","%s");', encodeURIComponent(cookie.name), encodeURIComponent(cookie.value), source.url)
// Cookies are already encoded within `source.allHeaders` so we can pull them out of that instead of doing our
// own encoding work.
source.allHeaders.cookie.split('; ').forEach(function (cookie) {
const [name, value] = cookie.split('=')
code.push('CookieJar.add("%s=%s","%s");', name, value, source.url)
})

code.push('req.jar(CookieJar);')
Expand Down
8 changes: 2 additions & 6 deletions src/targets/php/curl.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,8 @@ module.exports = function (source, options) {
})

// construct cookies
const cookies = source.cookies.map(function (cookie) {
return encodeURIComponent(cookie.name) + '=' + encodeURIComponent(cookie.value)
})

if (cookies.length) {
curlopts.push(util.format('CURLOPT_COOKIE => "%s",', cookies.join('; ')))
if (source.allHeaders.cookie) {
curlopts.push(util.format('CURLOPT_COOKIE => "%s",', source.allHeaders.cookie))
}

// construct cookies
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/output/node/fetch/cookies.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const fetch = require('node-fetch');

const url = 'http://mockbin.com/har';
const options = {method: 'POST', headers: {cookie: 'foo=bar; bar=baz; '}};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool side effect of this work is now cookies for node-fetch won't have an unnecessary space at the end.

const options = {method: 'POST', headers: {cookie: 'foo=bar; bar=baz'}};

fetch(url, options)
.then(res => res.json())
Expand Down
4 changes: 2 additions & 2 deletions test/fixtures/output/node/fetch/full.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const url = 'http://mockbin.com/har?foo=bar&foo=baz&baz=abc&key=value';
const options = {
method: 'POST',
headers: {
cookie: 'foo=bar; bar=baz',
accept: 'application/json',
'content-type': 'application/x-www-form-urlencoded',
cookie: 'foo=bar; bar=baz; '
'content-type': 'application/x-www-form-urlencoded'
},
body: encodedParams
};
Expand Down
2 changes: 1 addition & 1 deletion test/targets.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const itShouldGenerateOutput = function (request, path, target, client) {
const options = {}
if (request === 'query-encoded') {
// Query strings in this HAR are already escaped.
options.escapeQueryStrings = false
options.harIsAlreadyEncoded = true
}

const instance = new HTTPSnippet(fixtures.requests[request], options)
Expand Down
68 changes: 53 additions & 15 deletions test/targets/shell/curl.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,25 +64,63 @@ module.exports = function (HTTPSnippet, fixtures) {
result.should.eql('curl --request GET --url http://mockbin.com/request --http1.0')
})

it('should escape brackets in query strings when `escapeQueryStrings` is `false` and `escapeBrackets` is `true`', function () {
const har = {
method: 'GET',
url: 'http://mockbin.com/har',
httpVersion: 'HTTP/1.1',
queryString: [
{
name: 'where',
value: '[["$attributed_flow","=","FLOW_ID"]]'
describe('`harIsAlreadyEncoded` option', () => {
it('should not double-encode already encoded data', function () {
const har = {
log: {
entries: [
{
request: {
cookies: [
{ name: 'user', value: encodeURIComponent('abc^') }
],
headers: [],
headersSize: 0,
queryString: [
{ name: 'stringPound', value: encodeURIComponent('somethign&nothing=true') },
{ name: 'stringHash', value: encodeURIComponent('hash#data') },
{ name: 'stringArray', value: encodeURIComponent('where[4]=10') },
{ name: 'stringWeird', value: encodeURIComponent('properties["$email"] == "testing"') },
{ name: 'array', value: encodeURIComponent('something&nothing=true') },
{ name: 'array', value: encodeURIComponent('nothing&something=false') },
{ name: 'array', value: encodeURIComponent('another item') }
],
bodySize: 0,
method: 'POST',
url: 'https://httpbin.org/anything',
httpVersion: 'HTTP/1.1'
}
}
]
}
]
}
}

const result = new HTTPSnippet(har, { escapeQueryStrings: false }).convert('shell', 'curl', {
escapeBrackets: true
const result = new HTTPSnippet(har, { harIsAlreadyEncoded: true }).convert('shell', 'curl')

result.should.be.a.String()
result.replace(/\\\n/g, '').should.eql("curl --request POST --url 'https://httpbin.org/anything?stringPound=somethign%26nothing%3Dtrue&stringHash=hash%23data&stringArray=where%5B4%5D%3D10&stringWeird=properties%5B%22%24email%22%5D%20%3D%3D%20%22testing%22&array=something%26nothing%3Dtrue&array=nothing%26something%3Dfalse&array=another%20item' --cookie user=abc%5E")
})

result.should.be.a.String()
result.replace(/\\\n/g, '').should.eql("curl --request GET --url 'http://mockbin.com/har?where=\\[\\[\"$attributed_flow\",\"=\",\"FLOW_ID\"\\]\\]'")
it('should escape brackets in query strings when `harIsAlreadyEncoded` is `true` and `escapeBrackets` is `true`', function () {
const har = {
method: 'GET',
url: 'http://mockbin.com/har',
httpVersion: 'HTTP/1.1',
queryString: [
{
name: 'where',
value: '[["$attributed_flow","=","FLOW_ID"]]'
}
]
}

const result = new HTTPSnippet(har, { harIsAlreadyEncoded: true }).convert('shell', 'curl', {
escapeBrackets: true
})

result.should.be.a.String()
result.replace(/\\\n/g, '').should.eql("curl --request GET --url 'http://mockbin.com/har?where=\\[\\[\"$attributed_flow\",\"=\",\"FLOW_ID\"\\]\\]'")
})
})

it('should use custom indentation', function () {
Expand Down