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
HTTP Archive 1.2 support #1501
HTTP Archive 1.2 support #1501
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -726,6 +726,9 @@ The first argument can be either a `url` or an `options` object. The only requir | |
|
||
- `time` - If `true`, the request-response cycle (including all redirects) is timed at millisecond resolution, and the result provided on the response's `elapsedTime` property. | ||
|
||
--- | ||
|
||
- `har` - A [HAR 1.2 Request Object](http://www.softwareishard.com/blog/har-12-spec/#request), will be processed from HAR format into options overwriting matching values *(see example below for details)* | ||
|
||
The callback argument gets 3 arguments: | ||
|
||
|
@@ -738,6 +741,49 @@ The callback argument gets 3 arguments: | |
|
||
--- | ||
|
||
## Support for HAR 1.2 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say put the new doc section right after the TLS/SSL Protocol section instead, and add it to the table of contents too. |
||
|
||
The `options.har` property will override the values: `url`, `method`, `qs`, `headers`, `form`, `formData`, `body`, `json`, as well as construct multipart data and read files from disk when `request.postData.params[].fileName` is present without a matching `value`. | ||
|
||
a validation step will check if the HAR Request format matches the latest spec (v1.2) and will skip parsing if not matching. | ||
|
||
```js | ||
var request = require('request') | ||
request({ | ||
// will be ignored | ||
method: 'GET' | ||
uri: 'http://www.google.com', | ||
|
||
// HTTP Archive Request Object | ||
har: { | ||
url: 'http://www.mockbin.com/har' | ||
method: 'POST', | ||
headers: [ | ||
{ | ||
name: 'content-type', | ||
value: 'application/x-www-form-urlencoded' | ||
} | ||
], | ||
postData: { | ||
mimeType: 'application/x-www-form-urlencoded', | ||
params: [ | ||
{ | ||
name: 'foo', | ||
value: 'bar' | ||
}, | ||
{ | ||
name: 'hello', | ||
value: 'world' | ||
} | ||
] | ||
} | ||
} | ||
}) | ||
|
||
// a POST request will be sent to http://www.mockbin.com | ||
// with body an application/x-www-form-urlencoded body: | ||
// foo=bar&hello=world | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To match the style of the other doc sections, add a link back to the top and a |
||
|
||
## Convenience methods | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,205 @@ | ||
'use strict' | ||
|
||
var fs = require('fs') | ||
var qs = require('querystring') | ||
var validate = require('har-validator') | ||
var util = require('util') | ||
|
||
function Har (request) { | ||
this.request = request | ||
} | ||
|
||
Har.prototype.reducer = function (obj, pair) { | ||
// new property ? | ||
if (obj[pair.name] === undefined) { | ||
obj[pair.name] = pair.value | ||
return obj | ||
} | ||
|
||
// existing? convert to array | ||
var arr = [ | ||
obj[pair.name], | ||
pair.value | ||
] | ||
|
||
obj[pair.name] = arr | ||
|
||
return obj | ||
} | ||
|
||
Har.prototype.prep = function (data) { | ||
// construct utility properties | ||
data.queryObj = {} | ||
data.headersObj = {} | ||
data.postData.jsonObj = false | ||
data.postData.paramsObj = false | ||
|
||
// construct query objects | ||
if (data.queryString && data.queryString.length) { | ||
data.queryObj = data.queryString.reduce(this.reducer, {}) | ||
} | ||
|
||
// construct headers objects | ||
if (data.headers && data.headers.length) { | ||
// loweCase header keys | ||
data.headersObj = data.headers.reduceRight(function (headers, header) { | ||
headers[header.name] = header.value | ||
return headers | ||
}, {}) | ||
} | ||
|
||
// construct Cookie heade | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo, should be |
||
if (data.cookies && data.cookies.length) { | ||
var cookies = data.cookies.map(function (cookie) { | ||
return cookie.name + '=' + cookie.value | ||
}) | ||
|
||
if (cookies.length) { | ||
data.headersObj.cookie = cookies.join('; ') | ||
} | ||
} | ||
|
||
// prep body | ||
switch (data.postData.mimeType) { | ||
case 'multipart/mixed': | ||
case 'multipart/related': | ||
case 'multipart/form-data': | ||
case 'multipart/alternative': | ||
// reset values | ||
data.postData.mimeType = 'multipart/form-data' | ||
break | ||
|
||
case 'application/x-www-form-urlencoded': | ||
if (!data.postData.params) { | ||
data.postData.text = '' | ||
} else { | ||
data.postData.paramsObj = data.postData.params.reduce(this.reducer, {}) | ||
|
||
// always overwrite | ||
data.postData.text = qs.stringify(data.postData.paramsObj) | ||
} | ||
break | ||
|
||
case 'text/json': | ||
case 'text/x-json': | ||
case 'application/json': | ||
case 'application/x-json': | ||
data.postData.mimeType = 'application/json' | ||
|
||
if (data.postData.text) { | ||
try { | ||
data.postData.jsonObj = JSON.parse(data.postData.text) | ||
} catch (e) { | ||
this.request.debug(e) | ||
|
||
// force back to text/plain | ||
data.postData.mimeType = 'text/plain' | ||
} | ||
} | ||
break | ||
} | ||
|
||
return data | ||
} | ||
|
||
Har.prototype.options = function (options) { | ||
// skip if no har property defined | ||
if (!options.har) { | ||
return options | ||
} | ||
|
||
var har = util._extend({}, options.har) | ||
|
||
// only process the first entry | ||
if (har.log && har.log.entries) { | ||
har = har.log.entries[0] | ||
} | ||
|
||
// add optional properties to make validation successful | ||
har.url = har.url || options.url || options.uri || options.baseUrl || '/' | ||
har.httpVersion = har.httpVersion || 'HTTP/1.1' | ||
har.queryString = har.queryString || [] | ||
har.headers = har.headers || [] | ||
har.cookies = har.cookies || [] | ||
har.postData = har.postData || {} | ||
har.postData.mimeType = har.postData.mimeType || 'application/octet-stream' | ||
|
||
har.bodySize = 0 | ||
har.headersSize = 0 | ||
har.postData.size = 0 | ||
|
||
if (!validate.request(har)) { | ||
return options | ||
} | ||
|
||
// clean up and get some utility properties | ||
var req = this.prep(har) | ||
|
||
// construct new options | ||
if (req.url) { | ||
options.url = req.url | ||
} | ||
|
||
if (req.method) { | ||
options.method = req.method | ||
} | ||
|
||
if (Object.keys(req.queryObj).length) { | ||
options.qs = req.queryObj | ||
} | ||
|
||
if (Object.keys(req.headersObj).length) { | ||
options.headers = req.headersObj | ||
} | ||
|
||
switch (req.postData.mimeType) { | ||
case 'application/x-www-form-urlencoded': | ||
options.form = req.postData.paramsObj | ||
break | ||
|
||
case 'application/json': | ||
if (req.postData.jsonObj) { | ||
options.body = req.postData.jsonObj | ||
options.json = true | ||
} | ||
break | ||
|
||
case 'multipart/form-data': | ||
options.formData = {} | ||
|
||
req.postData.params.forEach(function (param) { | ||
var attachement = {} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In English this should be |
||
|
||
if (!param.fileName && !param.fileName && !param.contentType) { | ||
options.formData[param.name] = param.value | ||
return | ||
} | ||
|
||
// attempt to read from disk! | ||
if (param.fileName && !param.value) { | ||
attachement.value = fs.createReadStream(param.fileName) | ||
} else if (param.value) { | ||
attachement.value = param.value | ||
} | ||
|
||
if (param.fileName) { | ||
attachement.options = { | ||
filename: param.fileName, | ||
contentType: param.contentType ? param.contentType : null | ||
} | ||
} | ||
|
||
options.formData[param.name] = attachement | ||
}) | ||
break | ||
|
||
default: | ||
if (req.postData.text) { | ||
options.body = req.postData.text | ||
} | ||
} | ||
|
||
return options | ||
} | ||
|
||
exports.Har = Har |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's cross-link to the HAR section instead (syntax may not be quite right):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, was going to do this first, then I followed the examples from
encoding
...