k6
k6 copied to clipboard
Type of Response.request.headers[header] is not string
Brief summary
Short snippet:
import { uuidv4 } from 'https://jslib.k6.io/k6-utils/1.3.0/index.js'
const X_REQUEST_ID = 'X-Request-Id'
const fetch = (path, payload) => {
const params = {
headers: {
[X_REQUEST_ID]: uuidv4(),
},
}
return session.post(path, payload, params)
}
const responseHasRequestID = () => {
return (response) => {
return response.headers[X_REQUEST_ID] === response.request.headers[X_REQUEST_ID][0] // problem here
}
}
export default () => {
const response = fetch('/health', '')
check(response, {
'response has X-Request-ID header': responseHasRequestID(),
})
Problem:
typeof response.request.headers[X_REQUEST_ID] is object array, not string as expected.
From docs:
Response.request.headers object Request headers.
I think Response.request.headers should be an object with string-string pairs, not string-objectarray.
Or change the docs with explain of this object type.
k6 version
0.37.0
OS
Doesn't matter, run on docker
Docker version and image (if applicable)
0.37.0
Steps to reproduce the problem
See summary
Expected behaviour
typeof response.request.headers[X_REQUEST_ID] is string
Actual behaviour
typeof response.request.headers[X_REQUEST_ID] is array
Thanks for opening this issue! This is a simpler code snippet to demonstrate it:
import http from 'k6/http';
export default function () {
let resp = http.get('https://httpbin.test.k6.io/anything', { headers: { foo: 'bar' } });
console.log(JSON.stringify(resp.request.headers, null, 4));
}
it will result in this log message:
{
"Foo": [
"bar"
],
"User-Agent": [
"k6/0.38.3 (https://k6.io/)"
]
}
I think this is because the http.Header type from the Go standard library is map[string][]string, so that is what we use to represent Response.headers in our JS API :disappointed:
https://github.com/grafana/k6/blob/27f2ead359dac24b4131dac4b57c95c058f06e48/lib/netext/httpext/response.go#L91
https://github.com/grafana/k6/blob/27f2ead359dac24b4131dac4b57c95c058f06e48/lib/netext/httpext/request.go#L55
While that format falls more in line with the actual reality that HTTP requests can sometimes contain multiple headers with the same name (see this and this for RFC links and discussions), in k6 we don't allow users to do that, since request headers are specified with a simple {key: value} map...
And we haven't documented exactly what the structure of Response.request.headers is, just that it's an object that contains the "Request headers" :thinking: So yeah, we should either:
- fix the JS type, make
Response.request.headersinto a{string: string}map to matchRequest.headers - or, document the current type structure properly in the docs
I'm leaning towards option number 1... @grafana/k6-devs, what do you think?
I am for 2. - documenting it.
This has been the behaviour forever. So this will be a breaking change.
That plus the fact that k6/http is more or less in maintenance mode until we get to implement the new HTTP API :tm:.
While this likely is a lot less used than response.headers I would expect other users have come across response.request.headers, figured it out the same way @rugleb did and then just used it in their scripts.
Given that we can't know how many people use it (or if at all for that matter) and this hasn't been brought up before - I would argue it's better to just document it. It seems to be of small enough usefulness in general that breaking it will not improve the experience for most users, but will break it for the ones that already use it.