FranXho
FranXho

Reputation: 765

Is this possible to server websocket handler and normal servlet over same context?

Is this possible to serve websocket handler and normal servlet over same context?

Sample

http://localhost/appcontext/endpoint  --> serving by normal servlet
ws://localhost/appcontext/endpoint --> serving by websocket handler

I have tried multiple ways but can't able to do it. It seem url mapping is filtered by the context path and not by the protocol like 'http' or 'ws'. May be what I found is wrong. I was trying to see this using spring and java.

Upvotes: 0

Views: 1450

Answers (1)

Al-un
Al-un

Reputation: 3412

Short answer:

Nope, you cannot use the same path served either by servlet or websocket. Each http request to http://localhost/appcontext/endpoint is expected to have a specific behaviour

Long answer:

This answer goes out of Java scope as it mainly relies on RFC 6455, aka the websocket bible. The reason why lies in the handshake process.

Websocket URIs

In section 3 of RFC 6455, the websocket URI is defined as followed:

ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]

wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]

Leaving the URI details aside, one might actually thinks that it is possible to solely access the HTTP counterpart with only HTTP protocol.

The handshake: client side

Reminder: websocket is a bidirectional protocol. Consequently, the client is the entity which initialises the connection. However, once the connection is established, both client and server can send a message or closes the communication. But the server and client concept is kept to know who is who

The client initialises the handshake: "Hey server! I want to establish a webosocket communication". However, the handshake starts over http. On page 17 of RFC 6455 (section 4.1):

  1. The handshake MUST be a valid HTTP request as specified by [RFC2616].

  2. The method of the request MUST be GET, and the HTTP version MUST be at least 1.1.

    For example, if the WebSocket URI is "ws://example.com/chat", the first line sent should be "GET /chat HTTP/1.1".

  3. The "Request-URI" part of the request MUST match the /resource name/ defined in Section 3 (a relative URI) or be an absolute http/https URI that, when parsed, has a /resource name/, /host/, and /port/ that match the corresponding ws/wss URI.

Behind every websocket communication initialisation hides an HTTP request of the same path. To get back to your example:

  1. client asks ws://localhost/appcontext/endpoint
  2. it actually asks to http://localhost/appcontext/endpoint with an upgrade request and the appropriate headers defined by RFC 6455
  3. server answers and only after the handshake the communication switches from http(s) to ws(s)

The handshake: server side

But what if you want to cheat and say "I pretend I don't know that this is a websocket URI and I want to access the HTTP resources behind this URL". On the server-side, your application had registered a path http://localhost/appcontext/endpoint for URI matching as "I'm waiting websocket connections here", similarly to web services path matching process. In section 4.2.1 of RFC 6455:

The client’s opening handshake consists of the following parts. If the server, while reading the handshake, finds that the client did not send a handshake that matches the description below (note that as per [RFC2616], the order of the header fields is not important), including but not limited to any violations of the ABNF grammar specified for the components of the handshake, the server MUST stop processing the client’s handshake and return an HTTP response with an appropriate error code (such as 400 Bad Request).

  1. An HTTP/1.1 or higher GET request, including a "Request-URI" [RFC2616] that should be interpreted as a /resource name/ defined in Section 3 (or an absolute HTTP/HTTPS URI containing the /resource name/).

  2. A |Host| header field containing the server’s authority.

  3. An |Upgrade| header field containing the value "websocket", treated as an ASCII case-insensitive value.

As http://localhost/appcontext/endpoint is registered for websocket handshake, a basic HTTP request does not include the Upgrade header and is then rejected by the server.

Edit

While writing this answer, I tried it with:

  1. Endpoint: appcontext/endpoint
  2. A page: appcontext/endpoint/index.xhtml
  3. welcome file: index.xhtml: so appcontext/endpoint redirects to index.xhtml

As expected, my websocket connection fails. If I change the page path from endpoint/index.xhtml to endpoint2/index.xhtml, the websocket works back.

However, I just found that http://demos.kaazing.com/echo/ uses the same URL for its websocket connection and I couldn't find why >_<

Upvotes: 3

Related Questions