Skip to content

Commit

Permalink
Add support for Vert.x Web sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
Ladicek committed Oct 24, 2023
1 parent c7285f7 commit ebd03bd
Show file tree
Hide file tree
Showing 42 changed files with 1,423 additions and 9 deletions.
20 changes: 20 additions & 0 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,16 @@
<artifactId>quarkus-infinispan-client-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-sessions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-sessions-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jaeger</artifactId>
Expand Down Expand Up @@ -5972,6 +5982,11 @@
<artifactId>quarkus-redis-client-runtime-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client-sessions</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-cache</artifactId>
Expand All @@ -5988,6 +6003,11 @@
<artifactId>quarkus-redis-client-deployment-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client-sessions-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-cache-deployment</artifactId>
Expand Down
26 changes: 26 additions & 0 deletions devtools/bom-descriptor-json/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-sessions</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-info</artifactId>
Expand Down Expand Up @@ -1838,6 +1851,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client-sessions</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client</artifactId>
Expand Down
26 changes: 26 additions & 0 deletions docs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-sessions-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-info-deployment</artifactId>
Expand Down Expand Up @@ -1854,6 +1867,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client-sessions-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-deployment</artifactId>
Expand Down
128 changes: 127 additions & 1 deletion docs/src/main/asciidoc/http-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,132 @@ link:https://undertow.io/undertow-docs/undertow-docs-2.0.0/index.html#predicates

If you are using a `web.xml` file as your configuration file, you can place it in the `src/main/resources/META-INF` directory.

Check warning on line 545 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 545, "column": 11}}}, "severity": "INFO"}

Check warning on line 545 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 545, "column": 35}}}, "severity": "INFO"}

=== Built-in route order values
[[vertx-web-sessions]]
== Sessions

Quarkus includes support for sessions, based on https://vertx.io/docs/vertx-web/java/#_handling_sessions[Vert.x Web sessions].

By default, sessions are disabled.
To enable them, set the `quarkus.http.sessions.mode` configuration property to:

`in-memory`:: for sessions stored in memory of the Quarkus application
`redis`:: for sessions stored in an external Redis server; requires using the Quarkus Redis Client extension

Check warning on line 556 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 556, "column": 68}}}, "severity": "INFO"}
`infinispan`:: for sessions stored in an external Infinispan data grid; requires using the Quarkus Infinispan Client extension

Check warning on line 557 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 557, "column": 81}}}, "severity": "INFO"}

This configuration property is fixed at build time and cannot be changed at runtime.

WARNING: Undertow includes its own support for sessions.
If Undertow is present, Vert.x Web sessions cannot be enabled.

Check warning on line 562 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 562, "column": 48}}}, "severity": "INFO"}

Sessions require using a cookie, which holds the session identifier.
By default, the cookie name is `JSESSIONID`.

Check warning on line 565 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Spelling] Use correct American English spelling. Did you really mean 'Cookieless'? Raw Output: {"message": "[Quarkus.Spelling] Use correct American English spelling. Did you really mean 'Cookieless'?", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 565, "column": 13}}}, "severity": "WARNING"}
Cookieless sessions are not supported.
Storing session data directly into the session cookie is not supported either.

=== Accessing sessions

Check warning on line 569 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.HeadingPunctuation] Do not use end punctuation in headings. Raw Output: {"message": "[Quarkus.HeadingPunctuation] Do not use end punctuation in headings.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 569, "column": 1}}}, "severity": "INFO"}

When sessions are enabled, the Vert.x Web `Session` object may be obtained from the current `RoutingContext` using `RoutingContext.session()`.

Check warning on line 571 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 571, "column": 27}}}, "severity": "WARNING"}
Alternatively, the `io.vertx.ext.web.Session` object may be injected.

Check warning on line 572 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 572, "column": 21}}}, "severity": "WARNING"}

When using non-clustered in-memory sessions, arbitrary objects may be stored into a session.

Check warning on line 574 in docs/src/main/asciidoc/http-reference.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/http-reference.adoc", "range": {"start": {"line": 574, "column": 5}}}, "severity": "INFO"}

With cluster-wide in-memory sessions, Redis, or Infinispan, the following data types may be stored into a session:

* primitive wrapper types
** `java.lang.Boolean`
** `java.lang.Byte`
** `java.lang.Short`
** `java.lang.Integer`
** `java.lang.Long`
** `java.lang.Float`
** `java.lang.Double`
** `java.lang.Character`
* `java.lang.String`
* `byte[]`
* `io.vertx.core.buffer.Buffer`
* implementations of `io.vertx.core.shareddata.ClusterSerializable`

Sessions include a version number, so if multiple requests access the same session concurrently, only one of them will be able to persist the data back.
In general, sessions should not be accessed concurrently.

Session data are stored back to the session store when the response headers are written.
This operation is asynchronous to the response body writing operation, so it is possible that persisting the session to the session store takes longer than committing the response.
This may especially be the case for tiny responses.

=== General configuration

include::{generated-dir}/config/quarkus-vertx-http-config-group-sessions-build-time-config.adoc[leveloffset=+1, opts=optional]

include::{generated-dir}/config/quarkus-vertx-http-config-group-sessions-config.adoc[leveloffset=+1, opts=optional]

=== In-memory sessions

When `quarkus.http.sessions.mode` is set to `in-memory`, session data are stored in a Vert.x shared map.
By default, that shared map is _local_, which means that the session data are stored only in the JVM heap of the Quarkus application.

In this mode, if an application is deployed in multiple replicas fronted with a load balancer, it is necessary to enable sticky sessions (also known as session affinity) on the load balancer.
Still, losing a replica means losing all sessions stored on that replica.
In a multi-replica deployment, it is recommended to use an external session store (Redis or Infinispan).

Alternatively, if Vert.x clustering is configured, in-memory sessions may also be configured to be _cluster-wide_.
In this mode, a Vert.x _cluster-wide_ shared map is used to store session data, which means that sticky sessions are not necessary and losing one replica doesn't lead to session data loss.

include::{generated-dir}/config/quarkus-http-sessions-in-memory-sessions-in-memory-config.adoc[leveloffset=+1, opts=optional]

=== Redis sessions

When `quarkus.http.sessions.mode` is set to `redis`, session data are stored in an external Redis server.
The xref:./redis.adoc[Quarkus Redis Client] extension must be present and a connection to the Redis server used to store session data must be configured there.

By default, the default (unnamed) Redis connection is used.
To select a different (named) Redis connection, set the `quarkus.http.sessions.redis.client-name` configuration property.
For example:

[source,properties]
----
quarkus.http.sessions.mode=redis
quarkus.http.sessions.redis.client-name=web-sessions <1>
quarkus.redis.web-sessions.hosts=redis://localhost:6379/7 <2>
----
<1> Use the `web-sessions` Redis client for storing session data.
<2> Use database `7` on the Redis server at `localhost:6379`.

The Redis-based session store requires an entire Redis database for itself.
When using a standalone Redis server, you can use a https://redis.io/commands/select/[logical database] that is not used for other purposes.
If you want to store session data into a Redis cluster, you need to dedicate an entire cluster, because Redis cluster only supports database zero.

include::{generated-dir}/config/quarkus-redis-sessions.adoc[leveloffset=+1, opts=optional]

=== Infinispan sessions

When `quarkus.http.sessions.mode` is set to `infinispan`, session data are stored in an external Infinispan data grid.
The xref:./infinispan-client.adoc[Quarkus Infinispan Client] extension must be present and a connection to the Infinispan data grid used to store session data must be configured there.

By default, the default (unnamed) Infinispan connection is used.
To select a different (named) Infinispan connection, set the `quarkus.http.sessions.infinispan.client-name` configuration property.
For example:

[source,properties]
----
quarkus.http.sessions.mode=infinispan
quarkus.http.sessions.infinispan.client-name=web-sessions <1>
quarkus.infinispan-client.web-sessions.hosts=localhost:11222 <2>
----
<1> Use the `web-sessions` Infinispan client for storing session data.
<2> Use the Infinispan data grid at `localhost:11222`.

By default, the Infinispan cache used for storing session data is called `quarkus.sessions`.
To use a different cache, set the `quarkus.http.sessions.infinispan.cache-name` configuration property.

The Infinispan session store verifies if the configured cache exists.
If it does not, it is created automatically from the `DIST_SYNC` default template.
To be able to do that, the Infinispan client must be configured to connect as a user with permissions equivalent to at least the `deployer` Infinispan role.

include::{generated-dir}/config/quarkus-infinispan-sessions.adoc[leveloffset=+1, opts=optional]

== Built-in route order values

Route order values are the values that are specified via Vert.x route `io.vertx.ext.web.Route.order(int)` function.

Expand All @@ -563,6 +688,7 @@ Route order constants defined in `io.quarkus.vertx.http.runtime.RouteConstants`
| `Integer.MIN_VALUE` | `ROUTE_ORDER_BODY_HANDLER_MANAGEMENT` | Body handler for the management router.
| `Integer.MIN_VALUE` | `ROUTE_ORDER_HEADERS` | Handlers that add headers specified in the configuration.
| `Integer.MIN_VALUE` | `ROUTE_ORDER_CORS_MANAGEMENT` | CORS-Origin handler of the management router.
| `Integer.MIN_VALUE` | `ROUTE_ORDER_SESSION_HANDLER` | Session handler, if enabled in the configuration.
| `Integer.MIN_VALUE + 1` | `ROUTE_ORDER_BODY_HANDLER` | Body handler.
| `-2` | `ROUTE_ORDER_UPLOAD_LIMIT` | Route that enforces the upload body size limit.
| `0` | `ROUTE_ORDER_COMPRESSION` | Compression handler.
Expand Down
3 changes: 3 additions & 0 deletions extensions/infinispan-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@
<module>deployment-spi</module>
<module>runtime</module>
<module>runtime-spi</module>

<module>sessions/deployment</module>
<module>sessions/runtime</module>
</modules>
</project>
5 changes: 5 additions & 0 deletions extensions/infinispan-client/runtime/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@
<artifactId>quarkus-kubernetes-service-binding</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-sessions</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
Expand Down
52 changes: 52 additions & 0 deletions extensions/infinispan-client/sessions/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-parent</artifactId>
<version>999-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>quarkus-infinispan-client-sessions-deployment</artifactId>

<name>Quarkus - Infinispan Client - Vert.x Web Sessions - Deployment</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-sessions</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-infinispan-client-deployment-spi</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${project.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.infinispan.sessions.deployment;

import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

/**
* Configuration of Vert.x Web sessions stored in remote Infinispan cache.
*/
@ConfigRoot(name = "http.sessions.infinispan", phase = ConfigPhase.BUILD_TIME)
public class InfinispanSessionsBuildTimeConfig {
/**
* Name of the Infinispan client configured in the Quarkus Infinispan Client extension configuration.
* If not set, uses the default (unnamed) Infinispan client.
* <p>
* Note that the Infinispan client must be configured to connect as a user with the necessary permissions
* on the Infinispan server. The required minimum is equivalent to the Infinispan {@code deployer} role.
*/
@ConfigItem
public Optional<String> clientName;
}
Loading

0 comments on commit ebd03bd

Please sign in to comment.