=== WebSockets

WebSockets are added using the javadoc:Router[ws] method:

.WebSocket
[source,java,role="primary"]
----
{
  ws("/ws", (ctx, configurer) -> {             // <1>
    configurer.onConnect(ws -> {
      ws.send("Connected");                    // <2>
    });
    
    configurer.onMessage((ws, message) -> {
      ws.send("Got " + message.value());       // <3>
    });
    
    configurer.onClose((ws, statusCode) -> {
      // Clean up resources                      <4>
    });
    
    configurer.onError((ws, cause) -> {
      // Handle exceptions                       <5>
    });
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  ws("/ws") { ctx, configurer ->               // <1>
    configurer.onConnect { ws ->
      ws.send("Connected")                     // <2>
    }
    
    configurer.onMessage { ws, message ->
      ws.send("Got " + message.value())        // <3>
    }
    
    configurer.onClose { ws, statusCode ->
      // Clean up resources                      <4>
    }

    configurer.onError { ws, cause ->
      // Handle exceptions                       <5>
    }
  }
}
----

<1> Register a WebSocket handler.
<2> On connection (open), send a message back to the client. This is also a good place to initialize resources.
<3> On receiving a new message, send a response back to the client.
<4> The WebSocket is about to close. You must free/release any acquired resources here.
<5> The WebSocket encountered an exception. Useful for logging the error or providing an alternative response if the socket is still open.

You are free to access the HTTP context from the WebSocket configurer or callbacks, but **it is forbidden to modify the HTTP context or produce an HTTP response from it**.

.Accessing Context
[source,java,role="primary"]
----
{
  ws("/ws/{key}", (ctx, configurer) -> {
    String key = ctx.path("key").value();           // <1>
    String foo = ctx.session().get("foo").value();  // <2>
    // ...
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
{
  ws("/ws/{key}") { ctx, configurer ->
    val key = ctx.path("key").value()               // <1>
    val foo = ctx.session().get("foo").value()      // <2>
    // ...
  }
}
----

<1> Access a path variable (`key`).
<2> Access a session variable (`foo`).

==== Structured Data

Structured data (like JSON) is supported using the Value API and the javadoc:WebSocket[render, java.lang.Object] method.

To use structured messages, you need a registered javadoc:MessageDecoder[] and javadoc:MessageEncoder[]. In the following example, both are provided by the `JacksonModule`.

.JSON Example
[source,java,role="primary"]
----
import io.jooby.jackson.JacksonModule;

{
  install(new JacksonModule());                        // <1>

  ws("/ws", (ctx, configurer) -> {
    configurer.onMessage((ws, message) -> {
      MyObject myobject = message.to(MyObject.class);  // <2>
      ws.render(myobject);                             // <3>
    });
  });
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.jackson.JacksonModule

{
  install(JacksonModule())                             // <1>

  ws("/ws") { ctx, configurer ->
    configurer.onMessage { ws, message -> 
      val myobject = message.to<MyObject>()            // <2>
      ws.render(myobject)                              // <3>
    }
  }
}
----

<1> Install the Jackson module (required for JSON decoding/encoding).
<2> Parse and decode the incoming message to a `MyObject`.
<3> Encode `myobject` as JSON and send it to the client.

Alternatively, you can explicitly tell the WebSocket which decoder/encoder to use by specifying the `consumes` and `produces` attributes:

.Explicit Content Types
[source,java,role="primary"]
----
import io.jooby.jackson.JacksonModule;

{
  install(new JacksonModule());                        // <1>

  ws("/ws", (ctx, configurer) -> {
    configurer.onMessage((ws, message) -> {
      MyObject myobject = message.to(MyObject.class);  // <2>
      ws.render(myobject);                             // <3>
    });
  })
  .consumes(MediaType.json)
  .produces(MediaType.json);
}
----

.Kotlin
[source,kotlin,role="secondary"]
----
import io.jooby.jackson.JacksonModule

{
  install(JacksonModule())                             // <1>

  ws("/ws") { ctx, configurer ->
    configurer.onMessage { ws, message -> 
      val myobject = message.to<MyObject>()            // <2>
      ws.render(myobject)                              // <3>
    }
  }.consumes(MediaType.json)
   .produces(MediaType.json)
}
----

==== Options

===== Connection Timeouts
Jooby automatically times out idle connections that have no activity after 5 minutes. You can control this behavior by setting the `websocket.idleTimeout` property in your configuration file:

.application.conf
[source, properties]
----
websocket.idleTimeout = 1h
----

See the Typesafe Config documentation for the supported https://github.com/lightbend/config/blob/master/HOCON.md#duration-format[duration format].

===== Max Size

The maximum message size is set to `128K` by default. You can override it using the `websocket.maxSize` property:

.application.conf
[source, properties]
----
websocket.maxSize = 128K
----

See the Typesafe Config documentation for the supported https://github.com/lightbend/config/blob/master/HOCON.md#size-in-bytes-format[size in bytes format].
