websocket.open() obscures websocket upgrade request

Gatling’s implementation of the open method obscures the “websocket upgrade request” (HTTP GET request to ws:// including special headers).
As a consequence, it isn’t logged.
Also, I cannot discern a means to add headers to this request (like a session cookie).

If this is (already fixed/won’t fix/not a bug/feature request) please let me know and I’ll file an issue on the github repo.

EDIT: I’ve configured ALL level logging on io.gatling.http; my understanding is that this should result in logging the HTTP GET request issued as part of the websocket upgrade, yet it does not.

EDIT2: I just realized that perhaps the logging fails because WebsocketUpgradeHandler is imported from com.ning.http.client.websocket.

However, this does not fix the issue of being unable to set headers on the websocket upgrade request.

I don’t really get it? Are you trying to add some custom headers during the handshake?
Is so, I’ll ask my favorite expert (Jean-François Arcand), but my first guess is that this is not allowed by the spec.

Could you elaborate, please?

Yes - I’m trying to add HTTP session cookies to the upgrade request to associate the websocket connection with pre-existing user state.

From http://tools.ietf.org/html/rfc6455
The relevant part is about adding additional headers, which is contained in the very last line.

### [1.2](http://tools.ietf.org/html/rfc6455#section-1.2).  Protocol Overview

   _This section is non-normative._

   The protocol has two parts: a handshake and the data transfer.

   The handshake from the client looks as follows:

        GET /chat HTTP/1.1
        Host: server.example.com
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
        Origin: http://example.com
        Sec-WebSocket-Protocol: chat, superchat
        Sec-WebSocket-Version: 13

   The handshake from the server looks as follows:

        HTTP/1.1 101 Switching Protocols
        Upgrade: websocket
        Connection: Upgrade
        Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
        Sec-WebSocket-Protocol: chat

   The leading line from the client follows the Request-Line format.
   The leading line from the server follows the Status-Line format.  The
   Request-Line and Status-Line productions are defined in [[RFC2616](http://tools.ietf.org/html/rfc2616)].

   An unordered set of header fields comes after the leading line in
   both cases.  The meaning of these header fields is specified in
   [Section 4](http://tools.ietf.org/html/rfc6455#section-4) of this document.  Additional header fields may also be
   present, such as cookies [[RFC6265](http://tools.ietf.org/html/rfc6265)].

OK, so we’re not talking about whatever header, but about Cookie.
Gatling uses AsyncHttpClient, and it covers what can be done with the Javascript API, i.e. most WebSocket client usage. And, correct me if I’m wrong, the Javascript API won’t let you set headers.

What the Javascript API lets you do if craft cookies and add them to the browser cookie jar.

So my first question is: where does your cookie come from? Does it come from the server, from a regular HTTP request that happened prior to connecting the WebSocket? Or is it crafted in Javascript, client side?

If the former, Gatling is supposed to automatically deal with this, and propagate existing cookies to the WebSocket. If it doesn’t work as expected, could you enable debug logging (root level, or Netty’s package) and provide more information, please?
If the latter, Gatling provides a way to manually add cookies to the cookie jar.

It’s the former, and not working as expected - I’ll get some more information regarding the issue tomorrow.

Yes, please. I tried to reproduce but Gatling propagated the Cookie as expected:

11:05:00.016 [DEBUG] c.n.h.c.p.n.r.NettyConnectListener - Request using non cached Channel ‘[id: 0x7c2fca49, /127.0.0.1:56481 => localhost/127.0.0.1:9000]’:
DefaultHttpRequest(chunked: false)
GET /room/chat?username=Steph6185223545226085922-0 HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
DNT: 1
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0
Referer: http://localhost:9000/room?username=Steph6185223545226085922-0
Cookie: Hello=World
Upgrade: WebSocket
Connection: Upgrade
Origin: http://localhost:9000
Sec-WebSocket-Key: KsHfiIpn7/iJzyFgYwI+xA==
Sec-WebSocket-Version: 13
Host: localhost:9000

I could have something to do with your HTTP vs WebSocket flow, or CookieJar rules (domain and path matching).
Please enable debug logging.

Cheers,

Stéphane

It must be in my usage of the DSL - I’ve tracked down where the session is consumed for the websocket open action - in my logs the http request and websocket upgrade request have different cookies (both valid, both different).

I checked all of the http requests before the websocket connection - it looks like the first request stores a session cookie, the subsequent requests neither store nor consume the previous key, and the websocket upgrade consumes the key from the first request.

nor consume the previous key

Do you mean that they don’t send it back to the server? Or that they don’t expire it?

the websocket upgrade consumes the key from the first request

So Gatling works as expected, right?

In my test scenario I have several HTTP requests that are sent (get, post, etc) before the upgrade to websockets. It seems that the websocket upgrade will use the cookie from the FIRST http request made, and that (for whatever reason) session cookies are not maintained across the http requests. I’m not sure that gatling works as expected in this case.

It could be a bug in the domain and path matching.

Could you please provide the expected and observed sequences of requests with the following information: url, Set-Cookie, Cookie?

I’ve gotten it working by eliminating the intermediate http requests.

Originally:

.exec(http(“Get session token”).get([redacted]))
.pause(1)
.exec(session => session.set(“id”, session.userId))

.exec(http(“Login HTTP Post”)
.post([redacted])
.formParam([redacted], [redacted])
.formParam([redacted], [redacted])
.formParam([redacted], [redacted])
.check(status.is(200))
)

.exec(ws(“Open Websocket”).open([redacted])

resulted in the two HTTP requests acquiring different session tokens, and the first http request’s token being used for the websocket upgrade request.

I’ve subsequently removed the first request, and the issue is resolved, although I’d love to know why the token wasn’t persisting across those HTTP requests.

By token, you mean cookie, right?

Then, as I said, I’d really need the logs with the sequence of (url, Set-Cookie, Cookie).