grafana / har-to-k6 Goto Github PK
View Code? Open in Web Editor NEWJSON config representation of K6 script
License: Apache License 2.0
JSON config representation of K6 script
License: Apache License 2.0
This tool is missing a Dockerfile
, and a CI process that pushes new releases to the Docker Hub, which would be very useful for non-JS developers. Now, I either have to run it with npx
(super slow and annoying) or I have to install it with npm install
Hello,
I try to use har-to-k6 to create a javascript. One of the code looks like this:
address = new URI(
"https://azqamtell06/LoadReturn.htm"
);
address
.addQuery("t", `${vars["VMName_25_Token"]}`)
.addQuery("g", `${vars["VMName_25_GUID"]}`)
.addQuery("pg", `${vars["VMName_25_PublicGUID"]}`)
.addQuery("stoken", `${vars["VMName_25_SessionToken"]}`)
When I run the code, it gives following error:
ERRO[0063] ReferenceError: URI is not defined
at file:///C:/Users/776ec4fd-12f6-4f41-ae01-89ea8b1efd82.js:895:19(2431)
at github.com/loadimpact/k6/js/common.Bind.func1 (native)s
at main (file:///C:/Users/776ec4fd-12f6-4f41-ae01-89ea8b1efd82.js:28:17(17)) executor=per-vu-iterations scenario=default source=stacktrace
First off, thanks a bunch for developing this amazing tool to convert HAR to k6!
I just tried using it on a HAR archive exported from Chrome and I got tons of errors related the invalid header field names. It seems as though the conversion is not trimming the header fields that are prefixed with a colon, e.g.,
:method
:path
:authority
:scheme
WARN[0001] Request Failed error="Get https://furbaby.briantully.com/: net/http: invalid header field name \":method\""
WARN[0001] Request Failed error="Get https://furbaby.briantully.com/: net/http: invalid header field name \":path\""
WARN[0001] Request Failed error="Get https://furbaby.briantully.com/: net/http: invalid header field name \":authority\""
WARN[0006] Request Failed error="Get https://furbaby.briantully.com/: net/http: invalid header field name \":scheme\""
I was able to get the k6 script to work by manually editing the code and removing the ":" from the beginning of the header fields, but I was wondering if this is a known issue and/or if it is unique to Chrome HAR exports.
Thanks again!
Brian
I was trying to convert this very simple HAR file: test.loadimpact.com_Archive [19-12-13 12-52-20].har.txt
And this is what I got:
% npx har-to-k6 test.loadimpact.com_Archive\ \[19-12-13\ 12-52-20\].har
Converting 'test.loadimpact.com_Archive [19-12-13 12-52-20].har'
Error converting archive:
Missing page title (0)
The following error is seen in the browser console when trying to use the standalone.js
in website.
standalone.js:11 Uncaught (in promise) ReferenceError: regeneratorRuntime is not defined
at Object.d (standalone.js:11:356755)
at e.exports [as liHARToK6Script] (standalone.js:11:357262)
at run ((index):48:36)
at (index):52:3
This is the code used to create the website
<!DOCTYPE html>
<html lang="en">
<head>
<title>k6 test</title>
</head>
<body>
Check the console.
</body>
<script src="standalone.js"></script>
<script>
async function run () {
const config = {
log: {
entries: [
{
request: {
method: "POST",
url: "http://test.loadimpact.com/login",
headers: [
{
"name": "Content-Type",
"value": "application/json"
}
],
postData: {
"mimeType": "application/json",
"text": "{\"user\":\"admin\",\"password\":\"123\"}"
},
},
}
],
}
};
const { main } = await harToK6.liHARToK6Script(config);
console.log(main);
}
run()
</script>
</html>
I tried both the pre-built standalone.js in the repo and building one myself with the webpack config in the repo.
In the current implementation we're throwing errors without returning any structured data to determine where the validation error has happened. That information is being returned via the error message, which is not always ideal.
Example: A user provided a HAR config with an invalid request url.
{
"log": {
"entries": [
{
"request": {
"method": "POST",
"url": true
}
}
]
}
}
Such a HAR config would throw InvalidArchiveError
with a message
of Invalid request url (0): must be string
and with a set property name
equal to InvalidRequestUrl
.
This implementation has a couple of issues:
The error message provides too much detail when not used in a CLI. Currently error messages almost always provide the index where the validation error happened. This probably leads to more confusion than actually helping the user if we're displaying error messages outside of a CLI.
A possible solution would be to remove the index part of the error message when validating HAR configs and only augmenting them with the index when used in a CLI.
This means the error message of our example would become:
Invalid request url must be string
Invalid request url must be string. Occurred in an entry with an index of 0.
Errors do not provide any structured data to determine where the validation error happened. Knowing where the validation error has happened would add more flexbility to the user to determine what to do with a given error.
Currently the only additional information that we provide is a name
property (like InvalidRequestUrl
or InvalidRequestQuery
).
I'm suggesting extending the error object with two additional properties:
type
- specifies the type of validation error. Since we're validating different types of data (like request
, headers
, cookies
, queryString
, etc.) it would be beneficial to know the type of validation error.index
- specifies the index of validation error. Usually this property would be a number
. In some cases it would be a tuple [number, number]
. For example, when throwing validation errors for headers
we would like to know the indexes of both the entries
and headers
arrays respectively. With the addition of type
property or checking for the name
property the user will know what type to expect from index
.path
- pinpoints where the validation error has happened. Example: { ..., path: 'entries[4].request.headers[0].name'}
.Let me know your thoughts on the given suggestions. Thanks!
$ har-to-k6 wikipedia.har -o wikipedia.js
Converting 'wikipedia.har'
Error converting archive:
Missing post data MIME type (0)
The wikipedia.har
is from fiddle recording and attached.
wikipedia.zip
Hello, I'm using har-to-k6 to test a flow in my company's app. In this flow there are multiple requests to a graphql endpoint where the endpoint url is always the same and what differs is the postData. Prior to using har-to-k6 I had the ability to add request tags which I used to add the gql operation, which would then propogate through to the json output of the k6 run.
Since the k6 script is now generated instead of hand written, I can't add these tags anymore.
I did try and add the tags onto the request body, but it seems like these params are only supported by k6/http. Is there a workaround to be able to set tags, or would that be a feature request?
application/x-www-form-urlencoded multiparam value with no variable throws
Error converting archive:
items.filter is not a function
Steps to reproduce:
config.har used: https://gist.github.com/legander/2a05898ac84675cec0122ebdc1825f5b
./bin/har-to-k6.js example/config.har
We've been using k6 convert with the --only
and the --enable-status-code-checks
options. We now have a HAR where k6 convert does not convert a large portion of the HAR file. har-to-k6 does, but the lack of --only
and --enable-status-code-checks
options means we can't use it either. I believe --only
is #39 so also please add --enable-status-code-checks
.
Removal of jsonpath as direct dependency
Configurations utilizing VariableType JSONPath and CheckType JSONPath currently rely on jsonpath which is being provided by the compatibility layer
import { jsonpath } from "./compat.js"
.
There is a proposal to add this type of functionality to the K6 lib directly, making it accessible like
import { jsonpath } from "k6/encoding"
(hypothetical example).
When jsonpath lands in K6 it can then be dropped as a direct dependency.
Currently, the script generator produces code like this for application/x-www-form-urlencoded POST data:
address: "Street%20Address%201"
But, because we URL-encode values behind-the-scenes at runtime, we end up sending this unless the user modifies the generated code:
Street%2520Address%25201
We should attempt a URL-decode when we know the POST data may be encoded. I'll take a look at this myself but thought I'd log it here as well for comments.
$ har-to-k6 loadimpact.com.har -o loadimpact.js
Converting 'loadimpact.com.har'
Error converting archive:
Duplicate cookie name
(0:15): sessionid
Steps to reproduce:
har-to-k6.cmd .\localhost.har -o loadtest.js
I generated this localhost.har file using the Dev Tools of Google Chrome Version 101.0.4951.67
The HAR file contains an URL with a query param which contains a space (encoded as +
):
"url": "http://api.example.com?foo=bar+baz.pdf",
"queryString": [
{
"name": "foo",
"value": "bar+baz.pdf"
}
]
The output loadtest.js file contains a duplicated param:
...
export default function main() {
let response
response = http.get('http://api.example.com/?foo=bar+baz.pdf&foo=bar%2Bbaz.pdf')
// should be:
// response = http.get('http://api.example.com/?foo=bar+baz.pdf')
...
}
If I replace the +
character by let's say -
from the har file, it works as expected.
The har file exists, but the docker image is always returning a ENOENT error (e.g. using docker run grafana/har-to-k6:latest new-recording_19332.har > my-k6-script.js
).
docker run grafana/har-to-k6:latest new-recording_19332.har > my-k6-script.js
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
Converting 'new-recording_19332.har'
Error reading archive file:
ENOENT: no such file or directory, open 'new-recording_19332.har', at path: new-recording_19332.har
OS Info:
ProductName: macOS
ProductVersion: 14.1.1
BuildVersion: 23B81
Docker version 24.0.6, build ed223bc
With some refactoring and a few new features we can make the k6 HAR converter produce much more readable and user-friendly scripts without loosing anything in correctness or functionality. Some of the things I think can be changed:
user-agent
header to the global options
object and remove it from each request (unless for some reason it's different than the global, though that should basically never happen with HAR files)host
header by default (i.e. for example by adding a new --remove-headers
option in the HAR converter that by default has host
in it)connection
header when its value is keep-alive
, since that's what the default HTTP 1.1 value is is that's what k6 does by defaultaccept-encoding
headers (arguable if we should remove it, but I think since it's super dependent on the HTTP client, we actually should clear it by default, because it's totally possible for a web-browser to put encodings there that we don't support in k6, thereby causing a bug)--correlate
or something like that), don't save it in res
http.batch()
call to actually just use http.request()
or even http.get()
/http.post()
/etc. callsaccept
http header by default, since I think it doesn't usually do all that much and is almost universally ignored by servers, while adding messiness to the codeuserAgent
, if we had the general option we can also use it for other headers that are basically the same for every request in a HAR file, like the Accept-Language
(and Accept-Encoding
, if we decide it's not worth removing it).Referer
header) and factor them outGenerally I'd like if generated scripts go from looking like this:
import { group, sleep } from 'k6';
import http from 'k6/http';
// Version: 1.3
// Creator: Load Impact URL test analyzer
export let options = {
stages: [
{
"duration": "5m0s",
"target": 25
}
],
maxRedirects: 0,
discardResponseBodies: true,
};
export default function() {
group("page_1 - http://test.loadimpact.com", function() {
let req, res;
req = [{
"method": "get",
"url": "http://test.loadimpact.com/",
"params": {
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Connection": "keep-alive",
"Accept-Encoding": "gzip, deflate",
"Host": "test.loadimpact.com",
"Accept-Language": "en-US",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36"
}
}
}];
res = http.batch(req);
sleep(0.65);
req = [{
"method": "get",
"url": "http://test.loadimpact.com/style.css",
"params": {
"headers": {
"Accept": "text/css,*/*;q=0.1",
"Connection": "keep-alive",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://test.loadimpact.com/",
"Host": "test.loadimpact.com",
"Accept-Language": "en-US",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36"
}
}
},{
"method": "get",
"url": "http://test.loadimpact.com/images/logo.png",
"params": {
"headers": {
"Accept": "image/webp,image/apng,image/*,*/*;q=0.8",
"Connection": "keep-alive",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://test.loadimpact.com/style.css",
"Host": "test.loadimpact.com",
"Accept-Language": "en-US",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36"
}
}
}];
res = http.batch(req);
// Random sleep between 5s and 10s
sleep(Math.floor(Math.random()*5+5));
});
}
to something more like this:
import { group, sleep } from 'k6';
import http from 'k6/http';
// Version: 1.3
// Creator: Load Impact URL test analyzer
export let options = {
stages: [
{
"duration": "5m0s",
"target": 25
}
],
maxRedirects: 0,
discardResponseBodies: true,
headers: {
"Accept-Language": "en-US",
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/62.0.3183.0 Safari/537.36",
},
};
export default function () {
group("page_1 - http://test.loadimpact.com", function () {
http.get("http://test.loadimpact.com/", {
"headers": {
"Upgrade-Insecure-Requests": "1",
}
});
sleep(0.65);
http.batch([{
"method": "get",
"url": "http://test.loadimpact.com/style.css",
"params": {
"headers": {
"Referer": "http://test.loadimpact.com/",
}
}
}, {
"method": "get",
"url": "http://test.loadimpact.com/images/logo.png",
"params": {
"headers": {
"Referer": "http://test.loadimpact.com/style.css",
}
}
}]);
// Random sleep between 5s and 10s
sleep(Math.floor(Math.random() * 5 + 5));
});
}
src/utils/har/variables/model/index.ts in tryDeserializeRequest at line 37:16
mimeType.includes('multipart/form-data') && Array.isArray(params)
mimeType
can be undefined
, hence causing a TypeError
Steps to reproduce:
config.har used: https://gist.github.com/legander/778ff2e40c267e97d7cd598dbcc65921
./bin/har-to-k6.js example/variable.har
Converting 'example/variable.har'
Error converting archive:
Header value referenced undefined variable (0:0): accessToken
"request": {
"method": "",
"url": "ws://localhost:2992/sockjs-node/248/gp0g1n1n/websocket",
"httpVersion": "",
"headers": [],
"queryString": [],
"cookies": [],
"headersSize": -1,
"bodySize": 0
},
$ har-to-k6 www.google.es.har -o loadimpact.js
Converting 'www.google.es.har'
Error converting archive:
Missing query item name (40:0)
This fragment of HAR:
"request": {
"method": "GET",
"url": "https://example.com/api/layers/layer?assetId=bdaed40b-bed9-4b6f-abc4-56093ed1a43c&assetLayer=a9f5d33e-ab3b-4435-b3f6-427bc5aeb9c1&assetLayerConfiguration={%22Fabric%22:{%22assetId%22:%22bcff95a5-1b8c-4bad-a238-d8d6ca9c76ac%22},%22Collar%22:{%22assetId%22:%224c6b7595-083c-4905-a8f9-ae62d6a89c0e%22},%22Cuff%22:{%22assetId%22:%22acba39b1-05e7-43ae-bf80-1305a3a9277e%22},%22Pocket%22:{%22assetId%22:%2223eb7c46-b9da-4b68-80fa-d12971d46a93%22}}&orgId=0f556303-22e6-4015-b3b7-17438a673967",
"queryString": [
{
"name": "assetId",
"value": "bdaed40b-bed9-4b6f-abc4-56093ed1a43c"
},
{
"name": "assetLayer",
"value": "a9f5d33e-ab3b-4435-b3f6-427bc5aeb9c1"
},
{
"name": "assetLayerConfiguration",
"value": "{%22Fabric%22:{%22assetId%22:%22bcff95a5-1b8c-4bad-a238-d8d6ca9c76ac%22},%22Collar%22:{%22assetId%22:%224c6b7595-083c-4905-a8f9-ae62d6a89c0e%22},%22Cuff%22:{%22assetId%22:%22acba39b1-05e7-43ae-bf80-1305a3a9277e%22},%22Pocket%22:{%22assetId%22:%2223eb7c46-b9da-4b68-80fa-d12971d46a93%22}}"
},
{
"name": "orgId",
"value": "0f556303-22e6-4015-b3b7-17438a673967"
},
],
gets converted into:
response = http.get(
"https://example.com/api/layers/layer?assetId=bdaed40b-bed9-4b6f-abc4-56093ed1a43c&assetLayer=a9f5d33e-ab3b-4435-b3f6-427bc5aeb9c1&assetLayerConfiguration=%7B%22Fabric%22%3A%7B%22assetId%22%3A%22bcff95a5-1b8c-4bad-a238-d8d6ca9c76ac%22%7D%2C%22Collar%22%3A%7B%22assetId%22%3A%224c6b7595-083c-4905-a8f9-ae62d6a89c0e%22%7D%2C%22Cuff%22%3A%7B%22assetId%22%3A%22acba39b1-05e7-43ae-bf80-1305a3a9277e%22%7D%2C%22Pocket%22%3A%7B%22assetId%22%3A%2223eb7c46-b9da-4b68-80fa-d12971d46a93%22%7D%7D&assetLayerConfiguration=%7B%2522Fabric%2522%3A%7B%2522assetId%2522%3A%2522bcff95a5-1b8c-4bad-a238-d8d6ca9c76ac%2522%7D%2C%2522Collar%2522%3A%7B%2522assetId%2522%3A%25224c6b7595-083c-4905-a8f9-ae62d6a89c0e%2522%7D%2C%2522Cuff%2522%3A%7B%2522assetId%2522%3A%2522acba39b1-05e7-43ae-bf80-1305a3a9277e%2522%7D%2C%2522Pocket%2522%3A%7B%2522assetId%2522%3A%252223eb7c46-b9da-4b68-80fa-d12971d46a93%2522%7D%7D&orgId=0f556303-22e6-4015-b3b7-17438a673967",
Notice how some query string parameters get added twice and the second time they're added they get double URI encoded.
Steps to reproduce
Add following to any HAR then run convert
{
"postData": {
"mimeType": "application/x-www-form-urlencoded",
"params": [
{
"name": "login",
"value": ""
}
]
}
}
We cannot deprecate the built-in k6 convert
support until this tool outputs concurrent requests with http.batch()
How to reproduce:
Run
fetch('http://example.com', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
}),
body: 'foo0=bar0&foo1=bar1'
})
.then(res => res.json())
.then(console.log)
in Chrome v78.0.3904.97 DevTools Console and Save Content as HAR the Network Tab.
Trying to convert it using the har-to-k6 converter results in the error
InvalidPostDataType: Invalid structured post data MIME type (0): application/x-www-form-urlencoded; charset=UTF-8
Problem:
HAR 1.2 specification doesn't explicitly exclude charset attributes in the mimeType in the postData of requests. But since Chrome puts the content-type
header in the mimeType too, it should be supported by the converter too.
This is a replacement of grafana/k6#650, since as I'll explain in there, I think the issue has merit.
If there's a comment attached to a page entry [1] include it as a comment in the generated JS script.
Implement the tool according to spec.
When the HAR file is converted to K6 JS and run, the response is HTTP 415.
HAR File
{
"startedDateTime": "2023-04-28T18:56:21.054Z",
"time": 237.21100005786866,
"timings": {
"blocked": 138.46699999260903,
"dns": -1,
"ssl": -1,
"connect": -1,
"send": 0.07799999999999999,
"wait": 95.88000003886968,
"receive": 2.7860000263899565
},
"request": {
"method": "POST",
"url": "http://127.0.0.1:9080/smc-web/getCaseCategorySearchPrefOption.do",
"httpVersion": "http/1.1",
"headers": [
{
"name": "Accept",
"value": "text/html, */*; q=0.01"
},
{
"name": "Accept-Encoding",
"value": "gzip, deflate, br"
},
{
"name": "Accept-Language",
"value": "en-US,en;q=0.9"
},
{
"name": "Cache-Control",
"value": "no-cache"
},
{
"name": "Content-Length",
"value": "58"
},
{
"name": "Content-Type",
"value": "application/json; charset=UTF-8"
},
{
"name": "Cookie",
"value": "JSESSIONID=44B0A5B8A4A18DD157B12BCF3EAF26C4; crowd.token_key=T6rMIuipjGwpg6Oqsv42lAAAAAAAAIACc21jeXBkZXY"
},
{
"name": "Host",
"value": "127.0.0.1:9080"
},
{
"name": "Origin",
"value": "http://127.0.0.1:9080"
},
{
"name": "Pragma",
"value": "no-cache"
},
{
"name": "Proxy-Connection",
"value": "keep-alive"
},
{
"name": "Referer",
"value": "http://127.0.0.1:9080/smc-web/"
},
{
"name": "Sec-Fetch-Dest",
"value": "empty"
},
{
"name": "Sec-Fetch-Mode",
"value": "cors"
},
{
"name": "Sec-Fetch-Site",
"value": "same-origin"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58"
},
{
"name": "X-Cypress-Is-XHR-Or-Fetch",
"value": "xhr"
},
{
"name": "X-Requested-With",
"value": "XMLHttpRequest"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"112\", \"Microsoft Edge\";v=\"112\", \"Not:A-Brand\";v=\"99\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?0"
},
{
"name": "sec-ch-ua-platform",
"value": "\"Windows\""
}
],
"queryString": [],
"cookies": [
{
"name": "jSESSIONID",
"value": "44B0A5B8A4A18DD157B12BCF3EAF26C4",
"httpOnly": false,
"secure": false
},
{
"name": "crowd.token_key",
"value": "T6rMIuipjGwpg6Oqsv42lAAAAAAAAIACc21jeXBkZXY",
"httpOnly": false,
"secure": false
}
],
"headersSize": 0,
"bodySize": 58,
"postData": {
"mimeType": "application/json; charset=UTF-8",
"text": "{\"globalDataSource\":\"\",\"cupsId\":\"caseCategory.searchPref\"}",
"params": []
}
},
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "Connection",
"value": "keep-alive"
},
{
"name": "Content-Encoding",
"value": "gzip"
},
{
"name": "Keep-Alive",
"value": "timeout=5"
},
{
"name": "Transfer-Encoding",
"value": "chunked"
},
{
"name": "Vary",
"value": "Accept-Encoding"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "cache-control",
"value": "no-cache, no-store"
},
{
"name": "content-disposition",
"value": "inline;filename=f.txt"
},
{
"name": "content-type",
"value": "application/json;charset=UTF-8"
},
{
"name": "date",
"value": "Fri, 28 Apr 2023 18:56:20 GMT"
},
{
"name": "expires",
"value": "Thu, 01 Jan 1970 00:00:00 GMT"
},
{
"name": "pragma",
"value": "no-cache"
},
{
"name": "x-content-type-options",
"value": "nosniff"
},
{
"name": "x-xss-protection",
"value": "1; mode=block"
}
],
"cookies": [],
"content": {
"size": 23,
"mimeType": "application/json",
"compression": -36
},
"redirectURL": "",
"headersSize": 459,
"bodySize": 59,
"_transferSize": 518
},
"cache": {},
"serverIPAddress": "127.0.0.1",
"_priority": "VeryHigh",
"_resourceType": "XHR",
"_webSocketMessages": [],
"_eventSourceMessages": [],
"connection": "329"
},
JS
response = http.post(
'http://127.0.0.1:9080/smc-web/getCaseCategorySearchPrefOption.do',
'{"globalDataSource":"","cupsId":"caseCategory.searchPref"}',
{
headers: {
Accept: 'text/html, */*; q=0.01',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'en-US,en;q=0.9',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json; charset=UTF-8',
Cookie:
'JSESSIONID=44B0A5B8A4A18DD157B12BCF3EAF26C4; crowd.token_key=T6rMIuipjGwpg6Oqsv42lAAAAAAAAIACc21jeXBkZXY',
Host: '127.0.0.1:9080',
Origin: 'http://127.0.0.1:9080',
Pragma: 'no-cache',
'Proxy-Connection': 'keep-alive',
Referer: 'http://127.0.0.1:9080/smc-web/',
'Sec-Fetch-Dest': 'empty',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Site': 'same-origin',
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 Edg/112.0.1722.58',
'X-Cypress-Is-XHR-Or-Fetch': 'xhr',
'X-Requested-With': 'XMLHttpRequest',
'sec-ch-ua': '"Chromium";v="112", "Microsoft Edge";v="112", "Not:A-Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
},
}
)
Response
{
"remote_ip": "127.0.0.1",
"remote_port": 9080,
"url": "http://127.0.0.1:9080/smc-web/getCaseCategorySearchPrefOption.do",
"status": 415,
"status_text": "415 ",
"proto": "HTTP/1.1",
"headers": {
"Content-Type": "text/html;charset=utf-8",
"Content-Language": "en",
"Pragma": "no-cache",
"X-Content-Type-Options": "nosniff",
"Access-Control-Allow-Origin": "*",
"Date": "Mon, 01 May 2023 16:32:26 GMT",
"Cache-Control": "no-cache, no-store",
"X-Xss-Protection": "1; mode=block",
"Content-Length": "1087",
"Expires": "Thu, 01 Jan 1970 00:00:00 GMT",
"X-Frame-Options": "DENY"
},
"cookies": {},
"body": "<!doctype html><html lang=\"en\"><head><title>HTTP Status 415 – Unsupported Media Type</title><style type=\"text/css\">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 415 – Unsupported Media Type</h1><hr class=\"line\" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server is refusing to service the request because the payload is in a format not supported by this method on the target resource.</p><hr class=\"line\" /><h3>Apache Tomcat/9.0.12</h3></body></html>",
"timings": {
"duration": 15.0709,
"blocked": 0,
"looking_up": 0,
"connecting": 0,
"tls_handshaking": 0,
"sending": 0,
"waiting": 15.0709,
"receiving": 0
},
"tls_version": "",
"tls_cipher_suite": "",
"ocsp": {
"produced_at": 0,
"this_update": 0,
"next_update": 0,
"revoked_at": 0,
"revocation_reason": "",
"status": ""
},
"error": "",
"error_code": 1415,
"request": {
"method": "POST",
"url": "http://127.0.0.1:9080/smc-web/getCaseCategorySearchPrefOption.do",
"headers": {
"User-Agent": [
"k6/0.44.0 (https://k6.io/)"
],
"Cookie": [
"JSESSIONID=E1D4533BA6077DA4A8848CCE7206E688; crowd.token_key=ZRGfyAJLLiQGadn50rOc0gAAAAAAAIACc21jeXBkZXY"
]
},
"body": "{globalDataSource:\"\",cupsId:\"caseCategory.searchPref\"}",
"cookies": {
"crowd.token_key": [
{
"name": "crowd.token_key",
"value": "ZRGfyAJLLiQGadn50rOc0gAAAAAAAIACc21jeXBkZXY",
"replace": false
}
],
"JSESSIONID": [
{
"name": "JSESSIONID",
"value": "E1D4533BA6077DA4A8848CCE7206E688",
"replace": false
}
]
}
}
}
Configurations specifying postData mimeTypeapplication/x-www-form-urlencoded
and a payload with multivalue parameter (eg search=kitten&search=puppy); and 2) there's a parameter with a variable (eg session=${session}) currently rely on form-urlencoded.
There is a proposal to support the Web API interface for composing URLs and query strings.
When support for this lands in K6 we can drop form-urlencoded as a dependency.
How to reproduce:
Run
fetch('http://example.com', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded'
}),
body: 'foo0=bar0&foo1=bar1'
})
.then(res => res.json())
.then(console.log)
in Chrome v78.0.3904.97 DevTools Console and Save Content as HAR the Network Tab.
Trying to convert it using the har-to-k6 converter results in the error
Post data conflict (0): specify 1 of params or text
Problem:
Now, from how I understand the HAR 1.2 specification, a HAR postData entry should only contain either a non-empty params
or text
. And according to https://github.com/loadimpact/har-to-k6/blob/master/src/validate/postData.js#L54, the converter implemented that specification correctly.
However, the Firefox and Chrome DevTools both export a POST request with an application/x-www-form-urlencoded encoded request body with parameters into a HAR entry with both params
and text
:
...
"postData": {
"mimeType": "application/x-www-form-urlencoded",
"text": "foo0=bar0&foo1=bar1",
"params": [{
"name": "foo0",
"value": "bar0"
},{
"name": "foo1",
"value": "bar1"
}]
}
...
Since POST request bodies with parameters are fairly common, and Firefox and Chrome DevTools are probably fairly common tools to create HAR files, what's the appropriate way to use this converter on HAR files with POST bodies with parameters?
(Worst case, I'll have to prefix this converter with a script that sets the text
element to an empty string for postData
.)
Hello
I'm working on a script to automate the generation of a JS script from HAR, the data injection and the JS script execution.
I want my JS script to contain a main function and a setup function such as:
export function setup() {
let vars = {};
// read a csv file that contains my dataset
// update vars with data
return vars;
}
export default function main(vars) {
const randomIndex = ...; // computed from vars and the strategy
const username = vars[randomIndex].username;
group(...)
}
My current approach is to edit the JS file with another script that adds manually the wanted lines.
Is there a way to update the HAR in order to get this structure by running har-to-k6 ?
(If it's not implemented yet, just give me the recipe, I'll try a fork and submit a PR if the result is interesting)
This issue was prompted by https://community.k6.io/t/k6-unsupported-protocol-scheme-wss/781. At a first pass, we probably should ignore websocket connections for now, merely adding a comment in the generated k6 script, so we don't cause errors. In the long-term, it might be possible to also convert them into k6 websocket connections, though that will probably have a ton of issues.
k6-tests: har-to-k6 loadimpact.com.har -o loadimpact2.js
Converting 'loadimpact.com.har'
Error converting archive:
[(...groupedEntries.entries(...))].flatMap is not a function
www.google.es.har.zip
loadimpact.com.har.zip
cc @legander
Hey all
i am currently working with "har-to-k6" and it is a great tool that has helped me a lot. However, I would like to suggest a feature that I think could be a useful addition for many users.
It would be very helpful to be able to include a list of user-defined parameters in the converter, which are automatically replaced by a regex during the creation of the k6 script.
saml_request_1 = "samlreq"="(.*)"
bearer_1 = Bearer\s([a-zA-Z0-9_\-\.]+)
The idea is that as soon as a response comes from the website and has a match with the user defined parameter, this should be remembered. All other values that are the same in a response should be replaced by the variable saml_request_1
. If there is another match, the same should happen, but with a different key --> saml_request_2
, saml_request_3
, ...
Best Regards Achim
I have downloaded the latest node.js version v18.16.0. Then installed har-to-k6 using command npm install -g har-to-k6. When I tried to convert har file to k6 js script. I am getting error as Invalid query item name <0:0>: must be string . This har file is generated using Fiddler.
SearchWorkOrdersByWOSite.zip
Using har-to-k6 and k6 results in one of the POST's crashing the server with an "unknown transfer-encoding" error. The HAR file does not contain any Content-Transfer-Encoding headers in the postData.text field yet har-to-k6 includes this header on all fields in the multipart/form-data body.
When POST data contains both "params" and "text", there is a check for their equivalence in validate/postData.js
This fails when escaped characters exist in the text data, as below.
Problem can be worked around by removing the check (does it have any purpose?)
"params": [
{
"name": "page",
"value": "SandBox"
},
{
"name": "wikimarkup",
"value": "line 1"
}
],
"text": "page=SandBox&wikimarkup=line%201"
Output from hacked version of code, having added this to error block:
console.log(seralizeURLSearchParams(node.params))
console.log(node.text)
har-to-k6 buggy.har -o buggy.js
Converting 'buggy.har'
page=SandBox&wikimarkup=line 1
page=SandBox&wikimarkup=line%201
Error converting archive:
Post data conflict (0): params and text must be equal if both are provided
Extend config format to add ability to specify K6 options duration/stages/target etc.
This would allow the output script to directly be run with K6.
Upcoming option changes:
grafana/k6#1007
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.