Skip to content

Commit

Permalink
⚡ Disable undertow option to add Date response header and replace it …
Browse files Browse the repository at this point in the history
…with DateHeaderInjector

Undertow uses ThreadLocal<SimpleDateFormat> and this is not optimal for virtual threads
  • Loading branch information
ujibang committed May 10, 2024
1 parent 8a450aa commit 7743969
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 23 deletions.
8 changes: 6 additions & 2 deletions commons/src/main/java/org/restheart/configuration/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@ private static String __valueFromEnv(final String confParameter, final String ke
UNDERTOW_OPTIONS.add(UndertowOptions.ALLOW_EQUALS_IN_COOKIE_VALUE);
UNDERTOW_OPTIONS.add(UndertowOptions.ALLOW_UNKNOWN_PROTOCOLS);
UNDERTOW_OPTIONS.add(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL);
UNDERTOW_OPTIONS.add(UndertowOptions.ALWAYS_SET_DATE);
UNDERTOW_OPTIONS.add(UndertowOptions.ALWAYS_SET_KEEP_ALIVE);
UNDERTOW_OPTIONS.add(UndertowOptions.BUFFER_PIPELINED_DATA);
UNDERTOW_OPTIONS.add(UndertowOptions.DECODE_URL);
Expand Down Expand Up @@ -464,11 +463,16 @@ public static void setConnectionOptions(Builder builder, Configuration configura
}
}
});

// In Undertow, the `Date` header is added via {@code ThreadLocal<SimpleDateFormat>}.
// * However, this approach is not optimal for virtual threads
// we disable it and add the header with DateHeaderInjector
builder.setServerOption(UndertowOptions.ALWAYS_SET_DATE, false);
}

// matches ; in a way that we can ignore matches that are inside quotes
// inspired by https://stackoverflow.com/a/23667311/4481670
private static Pattern SPLIT_REGEX = Pattern.compile(
private static final Pattern SPLIT_REGEX = Pattern.compile(
"\\\\\"|\"(?:\\\\\"|[^\"])*\"" +
"|\\\\'|'(?:\\\\'|[^'])*'" +
"|(;)");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*-
* ========================LICENSE_START=================================
* restheart-core
* %%
* Copyright (C) 2014 - 2024 SoftInstigate
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* =========================LICENSE_END==================================
*/
package org.restheart.handlers.injectors;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

import org.restheart.exchange.ServiceRequest;
import org.restheart.exchange.ServiceResponse;
import org.restheart.plugins.RegisterPlugin;
import org.restheart.plugins.WildcardInterceptor;

import com.google.common.net.HttpHeaders;

import io.undertow.util.HttpString;

/**
* Author: Andrea Di Cesare <[email protected]>
*
* According to the HTTP specification, the `Date` header should be included in all responses,
* except when the server lacks an accurate clock.
*
* In Undertow, the `Date` header is added via {@code ThreadLocal<SimpleDateFormat>}.
* However, this approach is not optimal for virtual threads.
*/
@RegisterPlugin(name="dateHeaderInjector", description="", enabledByDefault=true)
public class DateHeaderInjector implements WildcardInterceptor {
private static final HttpString DATE = HttpString.tryFromString(HttpHeaders.DATE);
private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z";
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(RFC1123_PATTERN, Locale.US);
private static final ZoneId GMT = ZoneId.of("GMT");

@Override
public void handle(ServiceRequest<?> request, ServiceResponse<?> response) throws Exception {
response.getHeaders().add(DATE, FORMATTER.format(ZonedDateTime.now(GMT)));
}

@Override
public boolean resolve(ServiceRequest<?> request, ServiceResponse<?> response) {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -568,10 +568,4 @@ connection-options:
# If this is true then a Connection: keep-alive header will be added to responses,
# even when it is not strictly required by the specification.
# Defaults to true
ALWAYS_SET_KEEP_ALIVE: true

# If this is true then a Date header will be added to all responses.
# The HTTP spec says this header should be added to all responses,
# unless the server does not have an accurate clock.
# Defaults to true
ALWAYS_SET_DATE: true
ALWAYS_SET_KEEP_ALIVE: true
8 changes: 1 addition & 7 deletions core/src/main/resources/restheart-default-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -549,10 +549,4 @@ connection-options:
# If this is true then a Connection: keep-alive header will be added to responses,
# even when it is not strictly required by the specification.
# Defaults to true
ALWAYS_SET_KEEP_ALIVE: true

# If this is true then a Date header will be added to all responses.
# The HTTP spec says this header should be added to all responses,
# unless the server does not have an accurate clock.
# Defaults to true
ALWAYS_SET_DATE: true
ALWAYS_SET_KEEP_ALIVE: true
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
*/
package org.restheart.mongodb;

import com.google.common.collect.Sets;
import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnio.Option;

import com.google.common.collect.Sets;

import io.undertow.Undertow.Builder;
import io.undertow.UndertowOptions;

/**
*
* @author Andrea Di Cesare {@literal <[email protected]>}
Expand All @@ -47,7 +50,6 @@ public class ConfigurationHelper {
UNDERTOW_OPTIONS.add(UndertowOptions.ALLOW_EQUALS_IN_COOKIE_VALUE);
UNDERTOW_OPTIONS.add(UndertowOptions.ALLOW_UNKNOWN_PROTOCOLS);
UNDERTOW_OPTIONS.add(UndertowOptions.ALLOW_UNESCAPED_CHARACTERS_IN_URL);
UNDERTOW_OPTIONS.add(UndertowOptions.ALWAYS_SET_DATE);
UNDERTOW_OPTIONS.add(UndertowOptions.ALWAYS_SET_KEEP_ALIVE);
UNDERTOW_OPTIONS.add(UndertowOptions.BUFFER_PIPELINED_DATA);
UNDERTOW_OPTIONS.add(UndertowOptions.DECODE_URL);
Expand Down Expand Up @@ -89,9 +91,7 @@ public class ConfigurationHelper {
* @param configuration
*/
@SuppressWarnings("unchecked")
public static void setConnectionOptions(
Builder builder,
MongoServiceConfiguration configuration) {
public static void setConnectionOptions(Builder builder, MongoServiceConfiguration configuration) {

Map<String, Object> options = configuration.getConnectionOptions();

Expand All @@ -117,5 +117,10 @@ public static void setConnectionOptions(
}
}
});

// In Undertow, the `Date` header is added via {@code ThreadLocal<SimpleDateFormat>}.
// * However, this approach is not optimal for virtual threads
// we disable it and add the header with DateHeaderInjector
builder.setServerOption(UndertowOptions.ALWAYS_SET_DATE, false);
}
}

0 comments on commit 7743969

Please sign in to comment.