diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 5d16905627..4162e1ec8b 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -5,16 +5,20 @@ name: Test PR
on:
pull_request:
- branches: [ master ]
+ branches: [ 2.x ]
+ push:
+ branches: [ 2.x ]
+ workflow_dispatch:
jobs:
build:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - name: Set up JDK 1.8
- uses: actions/setup-java@v1
+ - uses: actions/checkout@v6
+ - name: Set up JDK 8
+ uses: actions/setup-java@v5
with:
- java-version: 1.8
+ java-version: 8
+ distribution: 'corretto'
- name: Build and test with Maven
- run: mvn test -Ptest-output
+ run: mvn test -ntp -B -Ptest-output -DexcludedGroups=online
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..8fa953c3d2
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,63 @@
+name: Release 2.x
+
+on:
+ push:
+ branches:
+ - 2.14.5
+
+ workflow_dispatch:
+ inputs:
+ snapshot:
+ description: 'Deploy SNAPSHOT'
+ type: boolean
+ default: false
+
+permissions:
+ contents: read
+
+jobs:
+
+ deploy:
+ if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - uses: actions/setup-java@v5
+ with:
+ distribution: 'corretto'
+ java-version: '8'
+
+ - name: Validate SNAPSHOT version
+ if: inputs.snapshot == true
+ run: |
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ if [[ "$VERSION" != *-SNAPSHOT ]]; then
+ echo "::error::Version $VERSION is not a SNAPSHOT version"
+ exit 1
+ fi
+
+ - name: Remove old Maven Settings
+ run: rm -f /home/runner/.m2/settings.xml
+
+ - name: Maven Settings
+ uses: s4u/maven-settings-action@v4.0.0
+ with:
+ servers: |
+ [{
+ "id": "central",
+ "username": "${{ secrets.OSSRH_USERNAME }}",
+ "password": "${{ secrets.OSSRH_PASSWORD }}"
+ }]
+
+ - name: Import GPG
+ uses: crazy-max/ghaction-import-gpg@v7.0.0
+ with:
+ gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+ passphrase: ${{ secrets.GPG_PASSPHRASE }}
+
+ - name: Deploy
+ env:
+ GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ run: mvn -B -ntp deploy -DskipTests -Dgpg.keyname=${GPG_KEY_NAME} -Dgpg.passphrase=${GPG_PASSPHRASE}
diff --git a/LICENSES/LICENSE.public-suffix-list.txt b/LICENSES/LICENSE.public-suffix-list.txt
new file mode 100644
index 0000000000..625e64ae87
--- /dev/null
+++ b/LICENSES/LICENSE.public-suffix-list.txt
@@ -0,0 +1,13 @@
+The file client/src/main/resources/org/asynchttpclient/cookie/public_suffix_list.dat is a copy of the
+ICANN section of the Public Suffix List, maintained by the Mozilla Foundation and distributed from
+https://publicsuffix.org/list/public_suffix_list.dat.
+
+It is used to implement RFC 6265 section 5.3 step 5, which requires a cookie naming a public suffix
+in its Domain attribute to be ignored.
+
+Only the ICANN section is included; the private section is omitted. The upstream licence header and
+the section markers are preserved in the file. No rules were altered.
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at https://mozilla.org/MPL/2.0/.
diff --git a/bom/pom.xml b/bom/pom.xml
index 867f23157e..ff4898eb88 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -5,7 +5,7 @@
org.asynchttpclient
async-http-client-project
- 2.12.3
+ 2.16.1
async-http-client-bom
diff --git a/client/pom.xml b/client/pom.xml
index 59b67c17d1..b384b7fc5d 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -2,7 +2,7 @@
org.asynchttpclient
async-http-client-project
- 2.12.3
+ 2.16.1
4.0.0
async-http-client
diff --git a/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java
index a761322dc3..3a22946e27 100644
--- a/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java
+++ b/client/src/main/java/org/asynchttpclient/AsyncHttpClientConfig.java
@@ -22,6 +22,7 @@
import io.netty.util.Timer;
import org.asynchttpclient.channel.ChannelPool;
import org.asynchttpclient.channel.KeepAliveStrategy;
+import org.asynchttpclient.config.AsyncHttpClientConfigDefaults;
import org.asynchttpclient.cookie.CookieStore;
import org.asynchttpclient.filter.IOExceptionFilter;
import org.asynchttpclient.filter.RequestFilter;
@@ -347,6 +348,31 @@ public interface AsyncHttpClientConfig {
int getIoThreadsCount();
+ /**
+ * Indicates whether the Authorization header should be stripped during redirects to a different domain.
+ *
+ * @return true if the Authorization header should be stripped, false otherwise.
+ */
+ default boolean isStripAuthorizationOnRedirect() {
+ return false;
+ }
+
+ /**
+ * The maximum number of bytes a compressed response body may inflate to, counted over the whole
+ * response. Guards against a "decompression bomb": a small response that expands without bound once
+ * decoded. {@code 0} disables the limit.
+ *
+ * The literal is hardcoded rather than read through {@link AsyncHttpClientConfigDefaults}: this default
+ * runs for any third-party implementation of this interface, and it is reached on the event loop when
+ * the pipeline is built, so a property lookup plus {@code Integer.parseInt} would turn a typo in a
+ * configuration file into a failure on every connection rather than at startup.
+ *
+ * @return the decompressed response ceiling in bytes
+ */
+ default int getMaxDecompressedResponseSize() {
+ return 268435456;
+ }
+
enum ResponseBodyPartFactory {
EAGER {
diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java
index 7cc3e6e341..45094ed7e6 100644
--- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java
+++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java
@@ -214,7 +214,7 @@ public ListenableFuture executeRequest(Request request, AsyncHandler h
if (!cookies.isEmpty()) {
RequestBuilder requestBuilder = request.toBuilder();
for (Cookie cookie : cookies) {
- requestBuilder.addOrReplaceCookie(cookie);
+ requestBuilder.addCookieIfUnset(cookie);
}
request = requestBuilder.build();
}
diff --git a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java
index 0f4e62c560..011402a275 100644
--- a/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java
+++ b/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java
@@ -61,8 +61,10 @@ public class DefaultAsyncHttpClientConfig implements AsyncHttpClientConfig {
private final boolean useLaxCookieEncoder;
private final boolean disableZeroCopy;
private final boolean keepEncodingHeader;
+ private final int maxDecompressedResponseSize;
private final ProxyServerSelector proxyServerSelector;
private final boolean validateResponseHeaders;
+ private final boolean stripAuthorizationOnRedirect;
// websockets
private final boolean aggregateWebSocketFrameFragments;
@@ -149,10 +151,12 @@ private DefaultAsyncHttpClientConfig(// http
boolean useLaxCookieEncoder,
boolean disableZeroCopy,
boolean keepEncodingHeader,
+ int maxDecompressedResponseSize,
ProxyServerSelector proxyServerSelector,
boolean validateResponseHeaders,
boolean aggregateWebSocketFrameFragments,
boolean enablewebSocketCompression,
+ boolean stripAuthorizationOnRedirect,
// timeouts
int connectTimeout,
@@ -237,8 +241,10 @@ private DefaultAsyncHttpClientConfig(// http
this.useLaxCookieEncoder = useLaxCookieEncoder;
this.disableZeroCopy = disableZeroCopy;
this.keepEncodingHeader = keepEncodingHeader;
+ this.maxDecompressedResponseSize = maxDecompressedResponseSize;
this.proxyServerSelector = proxyServerSelector;
this.validateResponseHeaders = validateResponseHeaders;
+ this.stripAuthorizationOnRedirect = stripAuthorizationOnRedirect;
// websocket
this.aggregateWebSocketFrameFragments = aggregateWebSocketFrameFragments;
@@ -377,6 +383,11 @@ public boolean isKeepEncodingHeader() {
return keepEncodingHeader;
}
+ @Override
+ public int getMaxDecompressedResponseSize() {
+ return maxDecompressedResponseSize;
+ }
+
@Override
public ProxyServerSelector getProxyServerSelector() {
return proxyServerSelector;
@@ -483,6 +494,11 @@ public boolean isValidateResponseHeaders() {
return validateResponseHeaders;
}
+ @Override
+ public boolean isStripAuthorizationOnRedirect() {
+ return stripAuthorizationOnRedirect;
+ }
+
// ssl
@Override
public boolean isUseOpenSsl() {
@@ -709,10 +725,12 @@ public static class Builder {
private boolean useLaxCookieEncoder = defaultUseLaxCookieEncoder();
private boolean disableZeroCopy = defaultDisableZeroCopy();
private boolean keepEncodingHeader = defaultKeepEncodingHeader();
+ private int maxDecompressedResponseSize = defaultMaxDecompressedResponseSize();
private ProxyServerSelector proxyServerSelector;
private boolean useProxySelector = defaultUseProxySelector();
private boolean useProxyProperties = defaultUseProxyProperties();
private boolean validateResponseHeaders = defaultValidateResponseHeaders();
+ private boolean stripAuthorizationOnRedirect = false; // default value
// websocket
private boolean aggregateWebSocketFrameFragments = defaultAggregateWebSocketFrameFragments();
@@ -800,7 +818,9 @@ public Builder(AsyncHttpClientConfig config) {
useLaxCookieEncoder = config.isUseLaxCookieEncoder();
disableZeroCopy = config.isDisableZeroCopy();
keepEncodingHeader = config.isKeepEncodingHeader();
+ maxDecompressedResponseSize = config.getMaxDecompressedResponseSize();
proxyServerSelector = config.getProxyServerSelector();
+ stripAuthorizationOnRedirect = config.isStripAuthorizationOnRedirect();
// websocket
aggregateWebSocketFrameFragments = config.isAggregateWebSocketFrameFragments();
@@ -930,11 +950,27 @@ public Builder setKeepEncodingHeader(boolean keepEncodingHeader) {
return this;
}
+ /**
+ * Bound how far a compressed response body may inflate, in bytes. {@code 0} disables the limit.
+ *
+ * @param maxDecompressedResponseSize the decompressed response ceiling in bytes
+ * @return this
+ */
+ public Builder setMaxDecompressedResponseSize(int maxDecompressedResponseSize) {
+ this.maxDecompressedResponseSize = maxDecompressedResponseSize;
+ return this;
+ }
+
public Builder setProxyServerSelector(ProxyServerSelector proxyServerSelector) {
this.proxyServerSelector = proxyServerSelector;
return this;
}
+ public Builder setStripAuthorizationOnRedirect(boolean value) {
+ this.stripAuthorizationOnRedirect = value;
+ return this;
+ }
+
public Builder setValidateResponseHeaders(boolean validateResponseHeaders) {
this.validateResponseHeaders = validateResponseHeaders;
return this;
@@ -1310,10 +1346,12 @@ public DefaultAsyncHttpClientConfig build() {
useLaxCookieEncoder,
disableZeroCopy,
keepEncodingHeader,
+ maxDecompressedResponseSize,
resolveProxyServerSelector(),
validateResponseHeaders,
aggregateWebSocketFrameFragments,
enablewebSocketCompression,
+ stripAuthorizationOnRedirect,
connectTimeout,
requestTimeout,
readTimeout,
diff --git a/client/src/main/java/org/asynchttpclient/Realm.java b/client/src/main/java/org/asynchttpclient/Realm.java
index c6324fd0b4..83b6b6102e 100644
--- a/client/src/main/java/org/asynchttpclient/Realm.java
+++ b/client/src/main/java/org/asynchttpclient/Realm.java
@@ -23,8 +23,8 @@
import java.nio.charset.Charset;
import java.security.MessageDigest;
+import java.security.SecureRandom;
import java.util.Map;
-import java.util.concurrent.ThreadLocalRandom;
import static java.nio.charset.StandardCharsets.*;
import static org.asynchttpclient.util.Assertions.assertNotNull;
@@ -253,6 +253,9 @@ public enum AuthScheme {
*/
public static class Builder {
+ // cnonce must be unpredictable (RFC 7616 section 3.3), like the NTLM nonce
+ private static final ThreadLocal CNONCE_RANDOM = ThreadLocal.withInitial(SecureRandom::new);
+
private final String principal;
private final String password;
private AuthScheme scheme;
@@ -413,11 +416,35 @@ private String parseRawQop(String rawQop) {
return null;
}
+ /**
+ * The scheme a parsed challenge authenticates with.
+ *
+ * A challenge that announces itself as Digest stays Digest even when its parameters do not parse.
+ * Deciding this from the presence of a nonce instead meant any Digest challenge we could not read
+ * became a Basic one, and answering it put the password on the wire in the clear, which is the single
+ * thing Digest exists to prevent. Omitting the nonce, or sending an empty one, was enough to trigger
+ * it. A Digest challenge with no nonce now produces no Authorization header at all, so the request
+ * fails rather than leaking.
+ */
+ private static AuthScheme challengedScheme(String headerLine, String nonce) {
+ if (isNonEmpty(nonce)) {
+ return AuthScheme.DIGEST;
+ }
+ if (headerLine == null) {
+ return AuthScheme.BASIC;
+ }
+ int start = 0;
+ while (start < headerLine.length() && headerLine.charAt(start) == ' ') {
+ start++;
+ }
+ return headerLine.regionMatches(true, start, "Digest", 0, 6) ? AuthScheme.DIGEST : AuthScheme.BASIC;
+ }
+
public Builder parseWWWAuthenticateHeader(String headerLine) {
setRealmName(match(headerLine, "realm"))
.setNonce(match(headerLine, "nonce"))
.setOpaque(match(headerLine, "opaque"))
- .setScheme(isNonEmpty(nonce) ? AuthScheme.DIGEST : AuthScheme.BASIC);
+ .setScheme(challengedScheme(headerLine, nonce));
String algorithm = match(headerLine, "algorithm");
if (isNonEmpty(algorithm)) {
setAlgorithm(algorithm);
@@ -436,7 +463,7 @@ public Builder parseProxyAuthenticateHeader(String headerLine) {
setRealmName(match(headerLine, "realm"))
.setNonce(match(headerLine, "nonce"))
.setOpaque(match(headerLine, "opaque"))
- .setScheme(isNonEmpty(nonce) ? AuthScheme.DIGEST : AuthScheme.BASIC);
+ .setScheme(challengedScheme(headerLine, nonce));
String algorithm = match(headerLine, "algorithm");
if (isNonEmpty(algorithm)) {
setAlgorithm(algorithm);
@@ -449,7 +476,7 @@ public Builder parseProxyAuthenticateHeader(String headerLine) {
private void newCnonce(MessageDigest md) {
byte[] b = new byte[8];
- ThreadLocalRandom.current().nextBytes(b);
+ CNONCE_RANDOM.get().nextBytes(b);
b = md.digest(b);
cnonce = toHexString(b);
}
diff --git a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java
index 35c8145776..a7fae7f430 100644
--- a/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java
+++ b/client/src/main/java/org/asynchttpclient/RequestBuilderBase.java
@@ -308,15 +308,31 @@ public T addCookie(Cookie cookie) {
/**
* Add/replace a cookie based on its name
+ *
* @param cookie the new cookie
* @return this
*/
public T addOrReplaceCookie(Cookie cookie) {
+ return maybeAddOrReplaceCookie(cookie, true);
+ }
+
+ /**
+ * Add a cookie based on its name, if it does not exist yet. Cookies that
+ * are already set will be ignored.
+ *
+ * @param cookie the new cookie
+ * @return this
+ */
+ public T addCookieIfUnset(Cookie cookie) {
+ return maybeAddOrReplaceCookie(cookie, false);
+ }
+
+ private T maybeAddOrReplaceCookie(Cookie cookie, boolean allowReplace) {
String cookieKey = cookie.name();
boolean replace = false;
int index = 0;
lazyInitCookies();
- for (Cookie c : this.cookies) {
+ for (Cookie c : cookies) {
if (c.name().equals(cookieKey)) {
replace = true;
break;
@@ -324,10 +340,11 @@ public T addOrReplaceCookie(Cookie cookie) {
index++;
}
- if (replace)
- this.cookies.set(index, cookie);
- else
- this.cookies.add(cookie);
+ if (!replace) {
+ cookies.add(cookie);
+ } else if (allowReplace) {
+ cookies.set(index, cookie);
+ }
return asDerivedType();
}
diff --git a/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java b/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java
index 14dcec3bfd..daaa1e83b7 100644
--- a/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java
+++ b/client/src/main/java/org/asynchttpclient/config/AsyncHttpClientConfigDefaults.java
@@ -67,6 +67,7 @@ public final class AsyncHttpClientConfigDefaults {
public static final String WEBSOCKET_MAX_BUFFER_SIZE_CONFIG = "webSocketMaxBufferSize";
public static final String WEBSOCKET_MAX_FRAME_SIZE_CONFIG = "webSocketMaxFrameSize";
public static final String KEEP_ENCODING_HEADER_CONFIG = "keepEncodingHeader";
+ public static final String MAX_DECOMPRESSED_RESPONSE_SIZE_CONFIG = "maxDecompressedResponseSize";
public static final String SHUTDOWN_QUIET_PERIOD_CONFIG = "shutdownQuietPeriod";
public static final String SHUTDOWN_TIMEOUT_CONFIG = "shutdownTimeout";
public static final String USE_NATIVE_TRANSPORT_CONFIG = "useNativeTransport";
@@ -282,6 +283,10 @@ public static boolean defaultKeepEncodingHeader() {
return AsyncHttpClientConfigHelper.getAsyncHttpClientConfig().getBoolean(ASYNC_CLIENT_CONFIG_ROOT + KEEP_ENCODING_HEADER_CONFIG);
}
+ public static int defaultMaxDecompressedResponseSize() {
+ return AsyncHttpClientConfigHelper.getAsyncHttpClientConfig().getInt(ASYNC_CLIENT_CONFIG_ROOT + MAX_DECOMPRESSED_RESPONSE_SIZE_CONFIG);
+ }
+
public static int defaultShutdownQuietPeriod() {
return AsyncHttpClientConfigHelper.getAsyncHttpClientConfig().getInt(ASYNC_CLIENT_CONFIG_ROOT + SHUTDOWN_QUIET_PERIOD_CONFIG);
}
diff --git a/client/src/main/java/org/asynchttpclient/cookie/PublicSuffixList.java b/client/src/main/java/org/asynchttpclient/cookie/PublicSuffixList.java
new file mode 100644
index 0000000000..66d0ef4d6b
--- /dev/null
+++ b/client/src/main/java/org/asynchttpclient/cookie/PublicSuffixList.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient.cookie;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.HashSet;
+import java.util.Set;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * The ICANN section of the Mozilla Public Suffix List, used to decide whether a cookie {@code Domain}
+ * attribute names a registry rather than a site.
+ *
+ *
RFC 6265 Section 5.3 step 5 requires rejecting a {@code Domain} that is a public suffix, and that
+ * rule cannot be approximated: {@code co.uk} has a dot like any ordinary domain, so counting labels does
+ * not distinguish a registry from a site. Without the list a host under {@code co.uk} can set a cookie for
+ * {@code co.uk} itself and every other host under that suffix receives it.
+ *
+ *
Only the ICANN section is bundled. The private section describes organisations that let others
+ * register names beneath them, which is a weaker property than a registry and not what step 5 is about.
+ *
+ *
Matching lowercases with {@link Locale#ROOT}. The default locale would be wrong here in a way that
+ * matters: under Turkish, {@code "INFO".toLowerCase()} is not {@code info}, so the check would answer
+ * false for every I-initial suffix and fail open exactly where it is meant to hold.
+ *
+ *
The list is data and goes stale as registries change. A suffix added upstream after this release is
+ * not recognised until the bundled copy is refreshed, so this narrows the exposure rather than closing it
+ * for all time. If the resource cannot be read the check reports nothing as a public suffix, leaving
+ * behaviour as it was rather than rejecting cookies that used to work.
+ */
+public final class PublicSuffixList {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PublicSuffixList.class);
+ private static final String RESOURCE = "/org/asynchttpclient/cookie/public_suffix_list.dat";
+
+ private static final Set EXACT;
+ private static final Set WILDCARD;
+ private static final Set EXCEPTIONS;
+
+ static {
+ Set exact = new HashSet<>(8192);
+ Set wildcard = new HashSet<>(32);
+ Set exceptions = new HashSet<>(16);
+ try (InputStream in = PublicSuffixList.class.getResourceAsStream(RESOURCE)) {
+ if (in == null) {
+ LOGGER.warn("Public suffix list {} is missing; a cookie Domain naming a public suffix "
+ + "cannot be rejected", RESOURCE);
+ } else {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in, UTF_8));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String rule = line.trim();
+ if (rule.isEmpty() || rule.startsWith("//")) {
+ continue;
+ }
+ if (rule.charAt(0) == '!') {
+ exceptions.add(rule.substring(1).toLowerCase(Locale.ROOT));
+ } else if (rule.startsWith("*.")) {
+ wildcard.add(rule.substring(2).toLowerCase(Locale.ROOT));
+ } else {
+ exact.add(rule.toLowerCase(Locale.ROOT));
+ }
+ }
+ }
+ } catch (IOException e) {
+ LOGGER.warn("Could not read the public suffix list; a cookie Domain naming a public suffix "
+ + "cannot be rejected", e);
+ }
+ EXACT = Collections.unmodifiableSet(exact);
+ WILDCARD = Collections.unmodifiableSet(wildcard);
+ EXCEPTIONS = Collections.unmodifiableSet(exceptions);
+ }
+
+ private PublicSuffixList() {
+ }
+
+ /**
+ * Whether {@code domain} is a public suffix, and so may not be the {@code Domain} of a cookie.
+ *
+ * @param domain a hostname, without a leading dot
+ */
+ public static boolean isPublicSuffix(String domain) {
+ if (domain == null || domain.isEmpty()) {
+ return false;
+ }
+ String candidate = domain.toLowerCase(Locale.ROOT);
+ if (candidate.charAt(candidate.length() - 1) == '.') {
+ candidate = candidate.substring(0, candidate.length() - 1);
+ }
+ // An exception rule names something that IS registrable despite matching a wildcard above it.
+ if (EXCEPTIONS.contains(candidate)) {
+ return false;
+ }
+ if (EXACT.contains(candidate)) {
+ return true;
+ }
+ // A wildcard rule such as *.ck makes every direct child of ck a suffix, so the candidate is one
+ // when its parent carries the rule.
+ int dot = candidate.indexOf('.');
+ return dot > 0 && WILDCARD.contains(candidate.substring(dot + 1));
+ }
+}
diff --git a/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java b/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java
index 8cdc29f45e..25c07f3d13 100644
--- a/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java
+++ b/client/src/main/java/org/asynchttpclient/cookie/ThreadSafeCookieStore.java
@@ -154,6 +154,11 @@ private boolean hasCookieExpired(Cookie cookie, long whenCreated) {
return false;
}
+ // rfc6265#section-5.1.3
+ private boolean domainsMatch(String cookieDomain, String requestDomain) {
+ return requestDomain.equals(cookieDomain) || requestDomain.endsWith('.' + cookieDomain);
+ }
+
// rfc6265#section-5.1.4
private boolean pathsMatch(String cookiePath, String requestPath) {
return Objects.equals(cookiePath, requestPath) ||
@@ -164,6 +169,26 @@ private void add(String requestDomain, String requestPath, Cookie cookie) {
AbstractMap.SimpleEntry pair = cookieDomain(cookie.domain(), requestDomain);
String keyDomain = pair.getKey();
boolean hostOnly = pair.getValue();
+
+ // rfc6265#section-5.3 step 6: ignore a cookie whose Domain attribute is not
+ // domain-matched by the request host, otherwise a host can plant cookies for
+ // unrelated domains (cookie tossing).
+ if (!hostOnly && !domainsMatch(keyDomain, requestDomain)) {
+ return;
+ }
+
+ // rfc6265#section-5.3 step 5: a Domain naming a public suffix must not be honoured. Step 6 above only
+ // asks whether the request host sits under the Domain, which evil.co.uk setting Domain=co.uk
+ // satisfies, so on its own it still lets one site plant a cookie every other site under that registry
+ // receives. Label counting cannot stand in: co.uk has a dot like any other domain.
+ //
+ // The step also keeps a cookie whose Domain equals the request host, as a host-only cookie. Dropping
+ // it would break ordinary single-label hosts: dev, app, box and cloud are ICANN suffixes as well as
+ // the short names Docker Compose and Kubernetes hand out.
+ if (!hostOnly && PublicSuffixList.isPublicSuffix(keyDomain) && !keyDomain.equals(requestDomain)) {
+ return;
+ }
+
String keyPath = cookiePath(cookie.path(), requestPath);
CookieKey key = new CookieKey(cookie.name().toLowerCase(), keyPath);
diff --git a/client/src/main/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessor.java b/client/src/main/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessor.java
index 1eb99f11ba..ea1f2066c6 100644
--- a/client/src/main/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessor.java
+++ b/client/src/main/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessor.java
@@ -16,14 +16,25 @@
import org.slf4j.LoggerFactory;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.OutputStream;
+import java.nio.channels.Channels;
+import java.nio.file.FileSystems;
import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.FileAttribute;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.EnumSet;
import java.util.Map;
import java.util.Scanner;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.nio.file.StandardOpenOption.CREATE_NEW;
+import static java.nio.file.StandardOpenOption.WRITE;
import static org.asynchttpclient.util.MiscUtils.closeSilently;
/**
@@ -34,8 +45,19 @@ public class PropertiesBasedResumableProcessor implements ResumableAsyncHandler.
private final static Logger log = LoggerFactory.getLogger(PropertiesBasedResumableProcessor.class);
private final static File TMP = new File(System.getProperty("java.io.tmpdir"), "ahc");
private final static String storeName = "ResumableAsyncHandler.properties";
+ private final static boolean POSIX = FileSystems.getDefault().supportedFileAttributeViews().contains("posix");
+ private final static FileAttribute>[] DIR_ATTRIBUTES = ownerOnlyAttributes("rwx------");
+ private final static FileAttribute>[] FILE_ATTRIBUTES = ownerOnlyAttributes("rw-------");
+ private final static Set CREATE_OPTIONS = EnumSet.of(WRITE, CREATE_NEW);
+
private final ConcurrentHashMap properties = new ConcurrentHashMap<>();
+ private static FileAttribute>[] ownerOnlyAttributes(String permissions) {
+ return POSIX
+ ? new FileAttribute>[]{PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString(permissions))}
+ : new FileAttribute>[0];
+ }
+
private static String append(Map.Entry e) {
return e.getKey() + '=' + e.getValue() + '\n';
}
@@ -67,18 +89,18 @@ public void save(Map map) {
OutputStream os = null;
try {
- if (!TMP.exists() && !TMP.mkdirs()) {
- throw new IllegalStateException("Unable to create directory: " + TMP.getAbsolutePath());
- }
- File f = new File(TMP, storeName);
- if (!f.exists() && !f.createNewFile()) {
- throw new IllegalStateException("Unable to create temp file: " + f.getAbsolutePath());
- }
- if (!f.canWrite()) {
- throw new IllegalStateException();
+ Path dir = TMP.toPath();
+ if (!Files.isDirectory(dir, LinkOption.NOFOLLOW_LINKS)) {
+ Files.createDirectory(dir, DIR_ATTRIBUTES);
}
- os = Files.newOutputStream(f.toPath());
+ // The store sits at a fixed path in the shared temp directory and holds the URLs being downloaded,
+ // so it is recreated here with owner-only permissions instead of being written through whatever is
+ // already at that path. CREATE_NEW after the delete fails rather than opens if another local user
+ // re-plants a file or a symlink in between.
+ Path f = dir.resolve(storeName);
+ Files.deleteIfExists(f);
+ os = Channels.newOutputStream(Files.newByteChannel(f, CREATE_OPTIONS, FILE_ATTRIBUTES));
for (Map.Entry e : properties.entrySet()) {
os.write(append(e).getBytes(UTF_8));
}
@@ -97,7 +119,8 @@ public void save(Map map) {
public Map load() {
Scanner scan = null;
try {
- scan = new Scanner(new File(TMP, storeName), UTF_8.name());
+ // NOFOLLOW_LINKS: refuse to read the state back through a symlink planted at the predictable path
+ scan = new Scanner(Files.newInputStream(new File(TMP, storeName).toPath(), LinkOption.NOFOLLOW_LINKS), UTF_8.name());
scan.useDelimiter("[=\n]");
String key;
@@ -108,7 +131,7 @@ public Map load() {
properties.put(key, Long.valueOf(value));
}
log.debug("Loading previous download state {}", properties.toString());
- } catch (FileNotFoundException ex) {
+ } catch (NoSuchFileException ex) {
log.debug("Missing {}", storeName);
} catch (Throwable ex) {
// Survive any exceptions
diff --git a/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java b/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java
index dddebfff41..4174bffd9e 100755
--- a/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java
+++ b/client/src/main/java/org/asynchttpclient/netty/NettyResponseFuture.java
@@ -14,6 +14,7 @@
package org.asynchttpclient.netty;
import io.netty.channel.Channel;
+import io.netty.handler.codec.http.HttpMethod;
import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.ListenableFuture;
import org.asynchttpclient.Realm;
@@ -80,7 +81,9 @@ public final class NettyResponseFuture implements ListenableFuture {
private final long start = unpreciseMillisTime();
private final ChannelPoolPartitioning connectionPoolPartitioning;
private final ConnectionSemaphore connectionSemaphore;
- private final ProxyServer proxyServer;
+ // Not final: a filter replay can retarget this future at a different origin, reached through a
+ // different proxy or none at all. getPartitionKey() reads it, so it has to move with the target.
+ private ProxyServer proxyServer;
private final int maxRetry;
private final CompletableFuture future = new CompletableFuture<>();
public Throwable pendingException;
@@ -116,6 +119,7 @@ public final class NettyResponseFuture implements ListenableFuture {
private boolean headersAlreadyWrittenOnContinue;
private boolean dontWriteBodyBecauseExpectContinue;
private boolean allowConnect;
+ private boolean tunnelEstablished;
private Realm realm;
private Realm proxyRealm;
@@ -306,6 +310,15 @@ public Uri getUri() {
return targetRequest.getUri();
}
+ /**
+ * Points this future at the proxy serving its current target. Only a replay onto a different origin
+ * needs this: leaving the previous origin's proxy in place leaves the connection pool partition key
+ * naming a route the future no longer takes.
+ */
+ public void setProxyServer(ProxyServer proxyServer) {
+ this.proxyServer = proxyServer;
+ }
+
public ProxyServer getProxyServer() {
return proxyServer;
}
@@ -338,9 +351,33 @@ public final NettyRequest getNettyRequest() {
}
public final void setNettyRequest(NettyRequest nettyRequest) {
+ if (nettyRequest != null && nettyRequest.getHttpRequest().method() == HttpMethod.CONNECT) {
+ // A new tunnel attempt is starting, so whatever an earlier CONNECT established no longer holds.
+ // Only ConnectSuccessInterceptor may set this back to true. Keeping the invariant here rather than
+ // at the call site means no future path can attach a CONNECT while leaving the flag stale.
+ tunnelEstablished = false;
+ }
this.nettyRequest = nettyRequest;
}
+ /**
+ * Whether a CONNECT on the channel this exchange is using actually succeeded, i.e. whether the socket is
+ * a tunnel to the origin rather than a plaintext hop to the proxy. Set only by
+ * {@link org.asynchttpclient.netty.handler.intercept.ConnectSuccessInterceptor}. The fact that the last
+ * request sent was a CONNECT proves nothing on its own: it is equally true when the proxy REJECTED the
+ * CONNECT, and treating that as an established tunnel puts the origin's credentials on a socket the
+ * proxy is still reading in the clear.
+ *
+ * @return true if a CONNECT has been answered successfully for the current channel
+ */
+ public boolean isTunnelEstablished() {
+ return tunnelEstablished;
+ }
+
+ public void setTunnelEstablished(boolean tunnelEstablished) {
+ this.tunnelEstablished = tunnelEstablished;
+ }
+
public final AsyncHandler getAsyncHandler() {
return asyncHandler;
}
diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
index b93dfb380e..35747456b8 100755
--- a/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
+++ b/client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java
@@ -23,7 +23,7 @@
import io.netty.channel.kqueue.KQueueEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.codec.http.HttpClientCodec;
-import io.netty.handler.codec.http.HttpContentDecompressor;
+import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder;
import io.netty.handler.codec.http.websocketx.WebSocket08FrameEncoder;
import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator;
@@ -46,8 +46,10 @@
import org.asynchttpclient.netty.NettyResponseFuture;
import org.asynchttpclient.netty.OnLastHttpContentCallback;
import org.asynchttpclient.netty.handler.AsyncHttpClientHandler;
+import org.asynchttpclient.netty.handler.BoundedHttpContentDecompressor;
import org.asynchttpclient.netty.handler.HttpHandler;
import org.asynchttpclient.netty.handler.WebSocketHandler;
+import org.asynchttpclient.netty.request.NettyRequest;
import org.asynchttpclient.netty.request.NettyRequestSender;
import org.asynchttpclient.netty.ssl.DefaultSslEngineFactory;
import org.asynchttpclient.proxy.ProxyServer;
@@ -253,16 +255,44 @@ protected void initChannel(Channel ch) {
});
}
- private HttpContentDecompressor newHttpContentDecompressor() {
- if (config.isKeepEncodingHeader())
- return new HttpContentDecompressor() {
- @Override
- protected String getTargetContentEncoding(String contentEncoding) {
- return contentEncoding;
- }
- };
- else
- return new HttpContentDecompressor();
+ /**
+ * A decompressor whose output is counted over the whole response, failing the request once it exceeds
+ * {@link AsyncHttpClientConfig#getMaxDecompressedResponseSize()}, so a small but highly compressible body
+ * cannot inflate without bound. Netty's own {@code maxAllocation} parameter is deliberately left at 0 -
+ * see {@link BoundedHttpContentDecompressor} for why it neither bounds a response nor is safe to set.
+ */
+ private BoundedHttpContentDecompressor newHttpContentDecompressor() {
+ return new BoundedHttpContentDecompressor(config.isKeepEncodingHeader(), config.getMaxDecompressedResponseSize());
+ }
+
+ /**
+ * A channel is pooled under a partition key that names the target origin, and
+ * {@code NettyRequestSender.sendRequestThroughProxy} takes a channel polled under such a key to be a
+ * tunnel that is already up: it sends the ORIGIN request on it rather than a CONNECT. A channel whose
+ * last request was a CONNECT is exactly the opposite, because a CONNECT the proxy DID accept is consumed
+ * by {@link org.asynchttpclient.netty.handler.intercept.ConnectSuccessInterceptor}, which takes over the
+ * channel and never lets the exchange reach the pool. So reaching here with a CONNECT in hand means the
+ * proxy refused the tunnel and the socket is still a plaintext hop to it - which must be closed, not
+ * pooled, or the next exchange for that origin sends the origin request, Authorization header included,
+ * down a hop the proxy is still reading in the clear.
+ */
+ private static boolean isRefusedTunnel(NettyResponseFuture> future) {
+ NettyRequest nettyRequest = future.getNettyRequest();
+ return nettyRequest != null && nettyRequest.getHttpRequest().method() == HttpMethod.CONNECT;
+ }
+
+ public final void tryToOfferChannelToPool(Channel channel, NettyResponseFuture> future, boolean keepAlive, Object partitionKey) {
+ tryToOfferChannelToPool(channel, future.getAsyncHandler(), keepAlive, partitionKey, isRefusedTunnel(future));
+ }
+
+ private void tryToOfferChannelToPool(Channel channel, AsyncHandler> asyncHandler, boolean keepAlive, Object partitionKey,
+ boolean refusedTunnel) {
+ if (refusedTunnel) {
+ LOGGER.debug("Not offering channel {} to the pool: the CONNECT on it was never established", channel);
+ closeChannel(channel);
+ return;
+ }
+ tryToOfferChannelToPool(channel, asyncHandler, keepAlive, partitionKey);
}
public final void tryToOfferChannelToPool(Channel channel, AsyncHandler> asyncHandler, boolean keepAlive, Object partitionKey) {
@@ -290,6 +320,14 @@ public Channel poll(Uri uri, String virtualHost, ProxyServer proxy, ChannelPoolP
Object partitionKey = connectionPoolPartitioning.getPartitionKey(uri, virtualHost, proxy);
return channelPool.poll(partitionKey);
}
+ /**
+ * Polls with a partition key the caller already derived, so the caller can scope it by the
+ * authenticated identity. See {@link PrincipalScopedPartitionKey}.
+ */
+ public Channel poll(Object partitionKey) {
+ return channelPool.poll(partitionKey);
+ }
+
public void removeAll(Channel connection) {
channelPool.removeAll(connection);
@@ -467,15 +505,21 @@ public void upgradePipelineForWebSockets(ChannelPipeline pipeline) {
private OnLastHttpContentCallback newDrainCallback(final NettyResponseFuture> future, final Channel channel, final boolean keepAlive, final Object partitionKey) {
+ // Sampled here rather than in call(), for the same reason keepAlive and partitionKey are: callers hand
+ // the drain over and immediately move the future on to the NEXT request, so by the time the last chunk
+ // arrives the future no longer describes the response being drained off this channel.
+ final boolean refusedTunnel = isRefusedTunnel(future);
+
return new OnLastHttpContentCallback(future) {
public void call() {
- tryToOfferChannelToPool(channel, future.getAsyncHandler(), keepAlive, partitionKey);
+ tryToOfferChannelToPool(channel, future.getAsyncHandler(), keepAlive, partitionKey, refusedTunnel);
}
};
}
public void drainChannelAndOffer(Channel channel, NettyResponseFuture> future) {
- drainChannelAndOffer(channel, future, future.isKeepAlive(), future.getPartitionKey());
+ drainChannelAndOffer(channel, future, future.isKeepAlive(),
+ PrincipalScopedPartitionKey.scope(future.getPartitionKey(), future.getRealm()));
}
public void drainChannelAndOffer(Channel channel, NettyResponseFuture> future, boolean keepAlive, Object partitionKey) {
diff --git a/client/src/main/java/org/asynchttpclient/netty/channel/PrincipalScopedPartitionKey.java b/client/src/main/java/org/asynchttpclient/netty/channel/PrincipalScopedPartitionKey.java
new file mode 100644
index 0000000000..0f4e3277fb
--- /dev/null
+++ b/client/src/main/java/org/asynchttpclient/netty/channel/PrincipalScopedPartitionKey.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient.netty.channel;
+
+import org.asynchttpclient.Realm;
+
+import java.util.Objects;
+
+/**
+ * Channel-pool partition key for schemes that authenticate the connection rather than the request.
+ *
+ * NTLM and Negotiate complete a handshake once and the server then treats every later request arriving
+ * on that socket as coming from the identity that authenticated it. The regular partition key describes
+ * only where the connection goes, so a socket one principal authenticated could be handed to a request
+ * belonging to another, and the server would serve it as the first principal. Nothing on the wire shows the
+ * identity changed, because the second request carries no authentication headers of its own.
+ *
+ *
Folding the principal into the key keeps those connections separated. Basic and Digest need no such
+ * thing: they authenticate each request and their credentials travel with it.
+ *
+ *
If a site that offers a connection to the pool and a site that polls for one ever disagree about
+ * whether to scope, the poll simply misses and a new connection is opened. That costs reuse, never
+ * correctness, which is the right way round for this to fail.
+ */
+public final class PrincipalScopedPartitionKey {
+
+ private final Object baseKey;
+ private final Realm.AuthScheme scheme;
+ private final String principal;
+
+ private PrincipalScopedPartitionKey(Object baseKey, Realm.AuthScheme scheme, String principal) {
+ this.baseKey = baseKey;
+ this.scheme = scheme;
+ this.principal = principal;
+ }
+
+ /**
+ * Wraps {@code baseKey} with the authenticated identity when {@code realm} uses a scheme that
+ * authenticates the connection, and returns {@code baseKey} unchanged otherwise. Every site that
+ * derives a pool key must apply this the same way.
+ */
+ public static Object scope(Object baseKey, Realm realm) {
+ if (realm == null || realm.getPrincipal() == null || !authenticatesTheConnection(realm.getScheme())) {
+ return baseKey;
+ }
+ return new PrincipalScopedPartitionKey(baseKey, realm.getScheme(), realm.getPrincipal());
+ }
+
+ private static boolean authenticatesTheConnection(Realm.AuthScheme scheme) {
+ return scheme == Realm.AuthScheme.NTLM
+ || scheme == Realm.AuthScheme.KERBEROS
+ || scheme == Realm.AuthScheme.SPNEGO;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PrincipalScopedPartitionKey that = (PrincipalScopedPartitionKey) o;
+ return Objects.equals(baseKey, that.baseKey)
+ && scheme == that.scheme
+ && Objects.equals(principal, that.principal);
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * (31 * Objects.hashCode(baseKey) + Objects.hashCode(scheme)) + Objects.hashCode(principal);
+ }
+
+ @Override
+ public String toString() {
+ // The principal is a username, not a secret, and the existing keys print their host and proxy.
+ return "PrincipalScopedPartitionKey(baseKey=" + baseKey + ", scheme=" + scheme + ", principal=" + principal + ')';
+ }
+}
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java b/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java
index ec158673f0..9fa7ee091f 100755
--- a/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/AsyncHttpClientHandler.java
@@ -29,6 +29,7 @@
import org.asynchttpclient.netty.OnLastHttpContentCallback;
import org.asynchttpclient.netty.channel.ChannelManager;
import org.asynchttpclient.netty.channel.Channels;
+import org.asynchttpclient.netty.channel.PrincipalScopedPartitionKey;
import org.asynchttpclient.netty.future.StackTraceInspector;
import org.asynchttpclient.netty.handler.intercept.Interceptors;
import org.asynchttpclient.netty.request.NettyRequestSender;
@@ -234,7 +235,10 @@ void finishUpdate(NettyResponseFuture> future, Channel channel, boolean close)
if (close) {
channelManager.closeChannel(channel);
} else {
- channelManager.tryToOfferChannelToPool(channel, future.getAsyncHandler(), true, future.getPartitionKey());
+ // A connection authenticated by NTLM or Negotiate is filed under the identity that
+ // authenticated it, so no other principal can draw it.
+ channelManager.tryToOfferChannelToPool(channel, future, true,
+ PrincipalScopedPartitionKey.scope(future.getPartitionKey(), future.getRealm()));
}
try {
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/BoundedHttpContentDecompressor.java b/client/src/main/java/org/asynchttpclient/netty/handler/BoundedHttpContentDecompressor.java
new file mode 100644
index 0000000000..02bb314bdf
--- /dev/null
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/BoundedHttpContentDecompressor.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.compression.DecompressionException;
+import io.netty.handler.codec.http.HttpContentDecompressor;
+import io.netty.util.ReferenceCountUtil;
+
+/**
+ * An {@link HttpContentDecompressor} that bounds how far a response body may inflate, guarding against a
+ * decompression bomb: a small, highly compressible response that expands without limit once decoded and
+ * exhausts the client's heap.
+ *
+ *
The bound is cumulative over one response body, and that is the whole point. Netty's
+ * own {@code maxAllocation} constructor parameter is not a substitute: it reaches {@code ZlibDecoder} as
+ * the maxCapacity of the buffer produced by one {@code decode()} call, with no counter spanning
+ * calls. Since the HTTP codec hands the decompressor at most
+ * {@link org.asynchttpclient.AsyncHttpClientConfig#getHttpClientCodecMaxChunkSize()} bytes at a time
+ * (8 KiB by default), a body at any ordinary compression ratio never reaches it however large the total
+ * becomes. Setting a non-zero {@code maxAllocation} is also actively harmful here: Netty 4.1's
+ * {@code HttpContentDecompressor.newContentDecoder} forwards it to {@code new BrotliDecoder(int)}, whose
+ * single-argument form is an input buffer size, and {@code BrotliDecoder.handlerAdded} eagerly
+ * allocates a native {@code DecoderJNI.Wrapper} of exactly that size - so a 256 MiB "ceiling" would cost
+ * 256 MiB of direct memory for every {@code Content-Encoding: br} response, empty body included. This
+ * class leaves {@code maxAllocation} at Netty's default of 0 and counts the bytes itself.
+ *
+ *
The counter is installed inside the decoder Netty builds per response, at the end of that
+ * embedded pipeline, rather than downstream of this handler. {@code HttpContentDecoder} publishes inflated
+ * content by firing it at its own outer {@link ChannelHandlerContext} from a forwarder appended to that
+ * same embedded pipeline, so an override of {@code decode} would observe nothing. Hooking here also gets
+ * the scope right for free: a decoder exists only for a body actually being inflated, and only for one
+ * response, so an unencoded body is never counted (bounding those would fail large plain downloads the
+ * caller asked for) and nothing has to be reset between responses on a pooled connection.
+ *
+ *
Not shareable: it holds the state of one connection's decoder.
+ */
+public class BoundedHttpContentDecompressor extends HttpContentDecompressor {
+
+ private final boolean keepEncodingHeader;
+ /**
+ * Ceiling in bytes for a single response body; a non-positive value disables the check.
+ */
+ private final long maxDecompressedBytes;
+
+ public BoundedHttpContentDecompressor(boolean keepEncodingHeader, long maxDecompressedBytes) {
+ // Deliberately the no-arg super: see the class javadoc for why maxAllocation must stay 0.
+ super();
+ this.keepEncodingHeader = keepEncodingHeader;
+ this.maxDecompressedBytes = maxDecompressedBytes;
+ }
+
+ @Override
+ protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
+ EmbeddedChannel decoder = super.newContentDecoder(contentEncoding);
+ if (decoder != null && maxDecompressedBytes > 0) {
+ // Appended before HttpContentDecoder appends its own forwarder, so this sits between the
+ // decompressor and the forwarder and sees every inflated buffer before it leaves the decoder.
+ decoder.pipeline().addLast(new DecompressedSizeLimit());
+ }
+ return decoder;
+ }
+
+ @Override
+ protected String getTargetContentEncoding(String contentEncoding) throws Exception {
+ // keepEncodingHeader leaves the response advertising the encoding it arrived with, for callers that
+ // inspect it; the default rewrites it to "identity" because the body downstream is no longer encoded.
+ return keepEncodingHeader ? contentEncoding : super.getTargetContentEncoding(contentEncoding);
+ }
+
+ /**
+ * Counts the inflated output of the response this decoder was created for, and fails the exchange once
+ * it passes the ceiling.
+ */
+ private final class DecompressedSizeLimit extends ChannelInboundHandlerAdapter {
+
+ private long decompressedBytes;
+ private boolean exceeded;
+
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (exceeded) {
+ // Already failed. Tearing the decoder down calls finishAndReleaseAll(), which re-runs it over
+ // whatever is left cumulated and would throw a second time out of cleanup; drop the leftovers
+ // quietly instead so the caller sees the exception raised below.
+ ReferenceCountUtil.release(msg);
+ return;
+ }
+
+ if (msg instanceof ByteBuf) {
+ decompressedBytes += ((ByteBuf) msg).readableBytes();
+ if (decompressedBytes > maxDecompressedBytes) {
+ exceeded = true;
+ ReferenceCountUtil.release(msg);
+ // EmbeddedChannel rethrows this out of the writeInbound driving the decoder, inside
+ // HttpContentDecoder.decode; being a DecoderException it reaches AsyncHttpClientHandler's
+ // exceptionCaught unwrapped, which fails the request and closes the connection.
+ throw new DecompressionException("Decompressed response body exceeds the maximum of "
+ + maxDecompressedBytes + " bytes (" + decompressedBytes + " bytes decompressed so far)");
+ }
+ }
+
+ ctx.fireChannelRead(msg);
+ }
+ }
+}
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java b/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java
index dddaeb34cb..447b903e5a 100755
--- a/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/HttpHandler.java
@@ -28,6 +28,7 @@
import org.asynchttpclient.netty.channel.ChannelManager;
import org.asynchttpclient.netty.channel.Channels;
import org.asynchttpclient.netty.request.NettyRequestSender;
+import org.asynchttpclient.util.HttpConstants.ResponseStatusCodes;
import java.io.IOException;
import java.net.InetSocketAddress;
@@ -40,8 +41,17 @@ public HttpHandler(AsyncHttpClientConfig config, ChannelManager channelManager,
}
private boolean abortAfterHandlingStatus(AsyncHandler> handler,
+ HttpMethod httpMethod,
NettyResponseStatus status) throws Exception {
- return handler.onStatusReceived(status) == State.ABORT;
+ // For a non-200 response to a CONNECT the tunnel was NOT established: the socket is still a plaintext
+ // hop terminated by the proxy. It has to be either closed or reused by sending the CONNECT again, and
+ // closing is the simpler of the two. Letting the exchange run on would instead reach handleChunk, where
+ // close is only `!future.isKeepAlive()` - i.e. whatever the proxy chose to say - and the still-plaintext
+ // socket would be offered to the pool under a partition key that names the secured origin. The next
+ // exchange polls it and, believing a channel under that key to be a tunnel, sends the ORIGIN request
+ // down it, Authorization header included.
+ return handler.onStatusReceived(status) == State.ABORT
+ || httpMethod == HttpMethod.CONNECT && status.getStatusCode() != ResponseStatusCodes.OK_200;
}
private boolean abortAfterHandlingHeaders(AsyncHandler> handler,
@@ -75,7 +85,7 @@ private void handleHttpResponse(final HttpResponse response, final Channel chann
HttpHeaders responseHeaders = response.headers();
if (!interceptors.exitAfterIntercept(channel, future, handler, response, status, responseHeaders)) {
- boolean abort = abortAfterHandlingStatus(handler, status) || //
+ boolean abort = abortAfterHandlingStatus(handler, httpRequest.method(), status) || //
abortAfterHandlingHeaders(handler, responseHeaders) || //
abortAfterHandlingReactiveStreams(channel, future, handler);
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java b/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java
index 533322f4bd..c36f314b44 100755
--- a/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/WebSocketHandler.java
@@ -67,6 +67,7 @@ private void upgrade(Channel channel, NettyResponseFuture> future, WebSocketUp
String key = getAcceptKey(future.getNettyRequest().getHttpRequest().headers().get(SEC_WEBSOCKET_KEY));
if (accept == null || !accept.equals(key)) {
requestSender.abort(channel, future, new IOException("Invalid challenge. Actual: " + accept + ". Expected: " + key));
+ return;
}
// set back the future so the protocol gets notified of frames
@@ -99,6 +100,16 @@ private void abort(Channel channel, NettyResponseFuture> future, WebSocketUpgr
public void handleRead(Channel channel, NettyResponseFuture> future, Object e) throws Exception {
if (e instanceof HttpResponse) {
+ // Unlike HttpHandler, this check cannot guard the whole method: a successful upgrade completes the
+ // future (see upgrade() below) and the channel then keeps serving frames, so isDone() is true for
+ // all normal WebSocket traffic. It belongs on the upgrade response alone, where a 101 arriving just
+ // as a request timeout aborted the future would otherwise still run upgrade() and deliver onOpen -
+ // and the rest of the WebSocket lifecycle - after onThrowable had already fired.
+ if (future.isDone()) {
+ channelManager.closeChannel(channel);
+ return;
+ }
+
HttpResponse response = (HttpResponse) e;
if (logger.isDebugEnabled()) {
HttpRequest httpRequest = future.getNettyRequest().getHttpRequest();
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ConnectSuccessInterceptor.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ConnectSuccessInterceptor.java
index eb2e98e36f..681e425d9f 100644
--- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ConnectSuccessInterceptor.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ConnectSuccessInterceptor.java
@@ -45,6 +45,10 @@ public boolean exitAfterHandlingConnect(Channel channel,
if (future.isKeepAlive())
future.attachChannel(channel, true);
+ // The single place this may be set. From here on the socket is a tunnel to the origin, so the origin's
+ // credentials may travel on it; until this point it is a plaintext hop to the proxy.
+ future.setTunnelEstablished(true);
+
Uri requestUri = request.getUri();
LOGGER.debug("Connecting to proxy {} for scheme {}", proxyServer, requestUri.getScheme());
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java
index 134213f60a..8b52427bf7 100644
--- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Interceptors.java
@@ -67,11 +67,21 @@ public boolean exitAfterIntercept(Channel channel,
ProxyServer proxyServer = future.getProxyServer();
int statusCode = response.status().code();
Request request = future.getCurrentRequest();
- Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm();
+ // The realm carried by the exchange (seeded from the request or the config when the exchange
+ // started, and reset to null by Redirect30xInterceptor on a cross-origin or scheme-downgrade
+ // redirect). Re-deriving from config.getRealm() here re-attaches the client-wide credentials to a
+ // redirect target whose auth was just stripped, leaking them to a different origin that answers 401.
+ Realm realm = future.getRealm();
+
+ // A CONNECT is addressed to the PROXY and travels in the clear, so its response is the proxy's, not
+ // the origin's - which changes both what may be stored from it and who may act on it.
+ boolean connectRequest = httpRequest.method() == HttpMethod.CONNECT;
// This MUST BE called before Redirect30xInterceptor because latter assumes cookie store is already updated
CookieStore cookieStore = config.getCookieStore();
- if (cookieStore != null) {
+ if (cookieStore != null && !connectRequest) {
+ // Skipped on a CONNECT: currentRequest is the ORIGIN request, so a Set-Cookie in the proxy's answer
+ // would be filed against the URI of an origin the request never reached.
for (String cookieStr : responseHeaders.getAll(SET_COOKIE)) {
Cookie c = cookieDecoder.decode(cookieStr);
if (c != null) {
@@ -85,6 +95,21 @@ public boolean exitAfterIntercept(Channel channel,
return true;
}
+ // Only two answers to a CONNECT may be acted on: a 200 that establishes the tunnel, and a 407 asking
+ // the proxy realm for credentials. Everything else - 401, 3xx, 100 - must NOT reach the origin-request
+ // interceptors, which rebuild the exchange as the ORIGIN request with setReuseChannel(true). That put
+ // the origin's Authorization on a socket still terminated by the proxy in plaintext, and let a hostile
+ // or compromised proxy solicit it with nothing more than a 401 or a 302.
+ if (connectRequest) {
+ if (statusCode == PROXY_AUTHENTICATION_REQUIRED_407) {
+ return proxyUnauthorized407Interceptor.exitAfterHandling407(channel, future, response, request, proxyServer, httpRequest);
+ }
+ if (statusCode == OK_200) {
+ return connectSuccessInterceptor.exitAfterHandlingConnect(channel, future, request, proxyServer);
+ }
+ return false;
+ }
+
if (statusCode == UNAUTHORIZED_401) {
return unauthorized401Interceptor.exitAfterHandling401(channel, future, response, request, realm, httpRequest);
@@ -97,9 +122,6 @@ public boolean exitAfterIntercept(Channel channel,
} else if (Redirect30xInterceptor.REDIRECT_STATUSES.contains(statusCode)) {
return redirect30xInterceptor.exitAfterHandlingRedirect(channel, future, response, request, statusCode, realm);
- } else if (httpRequest.method() == HttpMethod.CONNECT && statusCode == OK_200) {
- return connectSuccessInterceptor.exitAfterHandlingConnect(channel, future, request, proxyServer);
-
}
return false;
}
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407Interceptor.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407Interceptor.java
index 57436e9ae5..d087b670bb 100644
--- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407Interceptor.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407Interceptor.java
@@ -58,10 +58,10 @@ public boolean exitAfterHandling407(Channel channel,
ProxyServer proxyServer,
HttpRequest httpRequest) {
- if (future.isAndSetInProxyAuth(true)) {
- LOGGER.info("Can't handle 407 as auth was already performed");
- return false;
- }
+ // The three questions below all decide whether the PROXY wrote this response, and none of them may be
+ // asked after isAndSetInProxyAuth: that latch is the exchange's one shot at proxy authentication, and
+ // burning it on a 407 we then decline makes a later, legitimate proxy challenge on the same exchange
+ // fail with "auth was already performed" when no proxy auth was ever performed at all.
Realm proxyRealm = future.getProxyRealm();
@@ -70,6 +70,32 @@ public boolean exitAfterHandling407(Channel channel,
return false;
}
+ // A SOCKS proxy tunnels at the transport layer and never speaks HTTP, so a 407 arriving over one was
+ // written by the ORIGIN. Answering it mints proxy credentials for the origin: the NTLM and
+ // Kerberos/SPNEGO branches below write Proxy-Authorization straight onto the request headers, which
+ // newNettyRequest then copies verbatim, bypassing the proxy-type gate that guards the preemptive path.
+ if (proxyServer == null || !proxyServer.getProxyType().isHttp()) {
+ LOGGER.debug("Can't handle 407: not an HTTP proxy, so the 407 came from the origin");
+ return false;
+ }
+
+ // Being an HTTP proxy only says the proxy CAN write a 407; it does not say it wrote this one. A CONNECT
+ // is addressed to the proxy, so its 407 is the proxy's. Anything else on a tunnelled socket reaches the
+ // ORIGIN, and answering the origin's 407 hands it the proxy's credentials. The tunnel may have been
+ // established by this exchange (tunnelEstablished) or inherited with a channel taken from the pool, in
+ // which case the flag is false but the target still implies one: behind an HTTP proxy a secured or
+ // WebSocket target is only ever reached through a CONNECT.
+ if (httpRequest.method() != HttpMethod.CONNECT
+ && (future.isTunnelEstablished() || request.getUri().isSecured() || request.getUri().isWebSocket())) {
+ LOGGER.debug("Can't handle 407: it arrived through a tunnel, so it came from the origin");
+ return false;
+ }
+
+ if (future.isAndSetInProxyAuth(true)) {
+ LOGGER.info("Can't handle 407 as auth was already performed");
+ return false;
+ }
+
List proxyAuthHeaders = response.headers().getAll(PROXY_AUTHENTICATE);
if (proxyAuthHeaders.isEmpty()) {
diff --git a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java
index a2ddbd9467..ce292128ad 100644
--- a/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java
+++ b/client/src/main/java/org/asynchttpclient/netty/handler/intercept/Redirect30xInterceptor.java
@@ -27,6 +27,7 @@
import org.asynchttpclient.handler.MaxRedirectException;
import org.asynchttpclient.netty.NettyResponseFuture;
import org.asynchttpclient.netty.channel.ChannelManager;
+import org.asynchttpclient.netty.channel.PrincipalScopedPartitionKey;
import org.asynchttpclient.netty.request.NettyRequestSender;
import org.asynchttpclient.uri.Uri;
import org.slf4j.Logger;
@@ -62,11 +63,13 @@ public class Redirect30xInterceptor {
private final AsyncHttpClientConfig config;
private final NettyRequestSender requestSender;
private final MaxRedirectException maxRedirectException;
+ private final boolean stripAuthorizationOnRedirect;
Redirect30xInterceptor(ChannelManager channelManager, AsyncHttpClientConfig config, NettyRequestSender requestSender) {
this.channelManager = channelManager;
this.config = config;
this.requestSender = requestSender;
+ this.stripAuthorizationOnRedirect = config.isStripAuthorizationOnRedirect();
maxRedirectException = unknownStackTrace(new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects()), Redirect30xInterceptor.class,
"exitAfterHandlingRedirect");
}
@@ -92,15 +95,37 @@ public boolean exitAfterHandlingRedirect(Channel channel,
&& !originalMethod.equals(OPTIONS) && !originalMethod.equals(HEAD) && (statusCode == MOVED_PERMANENTLY_301 || statusCode == SEE_OTHER_303 || (statusCode == FOUND_302 && !config.isStrict302Handling()));
boolean keepBody = statusCode == TEMPORARY_REDIRECT_307 || statusCode == PERMANENT_REDIRECT_308 || (statusCode == FOUND_302 && config.isStrict302Handling());
+ HttpHeaders responseHeaders = response.headers();
+ String location = responseHeaders.get(LOCATION);
+ Uri newUri = Uri.create(future.getUri(), location);
+ LOGGER.debug("Redirecting to {}", newUri);
+
+ boolean sameBase = request.getUri().isSameBase(newUri);
+ boolean schemeDowngrade = request.getUri().isSecured() && !newUri.isSecured();
+ boolean stripAuth = !sameBase || schemeDowngrade || stripAuthorizationOnRedirect;
+
+ if (stripAuth && (request.getRealm() != null
+ || request.getHeaders().contains(AUTHORIZATION)
+ || request.getHeaders().contains(COOKIE))) {
+ LOGGER.debug("Stripping credentials on redirect to {}", newUri);
+ }
+
final RequestBuilder requestBuilder = new RequestBuilder(switchToGet ? GET : originalMethod)
.setChannelPoolPartitioning(request.getChannelPoolPartitioning())
.setFollowRedirect(true)
.setLocalAddress(request.getLocalAddress())
.setNameResolver(request.getNameResolver())
.setProxyServer(request.getProxyServer())
- .setRealm(request.getRealm())
+ .setRealm(stripAuth ? null : request.getRealm())
.setRequestTimeout(request.getRequestTimeout());
+ if (stripAuth) {
+ // Clear both realms on the future so NettyRequestFactory cannot regenerate
+ // Authorization or Proxy-Authorization headers on the redirected request.
+ future.setRealm(null);
+ future.setProxyRealm(null);
+ }
+
if (keepBody) {
requestBuilder.setCharset(request.getCharset());
if (isNonEmpty(request.getFormParams()))
@@ -118,29 +143,26 @@ else if (isNonEmpty(request.getBodyParts())) {
}
}
- requestBuilder.setHeaders(propagatedHeaders(request, realm, keepBody));
+ requestBuilder.setHeaders(propagatedHeaders(request, realm, keepBody, stripAuth));
// in case of a redirect from HTTP to HTTPS, future
// attributes might change
final boolean initialConnectionKeepAlive = future.isKeepAlive();
- final Object initialPartitionKey = future.getPartitionKey();
-
- HttpHeaders responseHeaders = response.headers();
- String location = responseHeaders.get(LOCATION);
- Uri newUri = Uri.create(future.getUri(), location);
- LOGGER.debug("Redirecting to {}", newUri);
+ // Scoped like every other offer: an NTLM or Negotiate connection must stay with the identity
+ // that authenticated it, and a same-host redirect during NTLM is the ordinary case.
+ final Object initialPartitionKey = PrincipalScopedPartitionKey.scope(
+ future.getPartitionKey(), future.getRealm());
CookieStore cookieStore = config.getCookieStore();
if (cookieStore != null) {
// Update request's cookies assuming that cookie store is already updated by Interceptors
List cookies = cookieStore.get(newUri);
if (!cookies.isEmpty())
- for (Cookie cookie : cookies)
- requestBuilder.addOrReplaceCookie(cookie);
+ for (Cookie cookie : cookieStore.get(newUri)) {
+ requestBuilder.addCookieIfUnset(cookie);
+ }
}
- boolean sameBase = request.getUri().isSameBase(newUri);
-
if (sameBase) {
// we can only assume the virtual host is still valid if the baseUrl is the same
requestBuilder.setVirtualHost(request.getVirtualHost());
@@ -173,7 +195,7 @@ else if (isNonEmpty(request.getBodyParts())) {
return false;
}
- private HttpHeaders propagatedHeaders(Request request, Realm realm, boolean keepBody) {
+ private HttpHeaders propagatedHeaders(Request request, Realm realm, boolean keepBody, boolean stripAuthorization) {
HttpHeaders headers = request.getHeaders()
.remove(HOST)
@@ -183,7 +205,13 @@ private HttpHeaders propagatedHeaders(Request request, Realm realm, boolean keep
headers.remove(CONTENT_TYPE);
}
- if (realm != null && realm.getScheme() == AuthScheme.NTLM) {
+ if (stripAuthorization) {
+ // Cookie is dropped only on the security boundary; the URI-scoped CookieStore re-adds
+ // any cookies that legitimately match the new target after this method returns.
+ headers.remove(AUTHORIZATION)
+ .remove(PROXY_AUTHORIZATION)
+ .remove(COOKIE);
+ } else if (realm != null && realm.getScheme() == AuthScheme.NTLM) {
headers.remove(AUTHORIZATION)
.remove(PROXY_AUTHORIZATION);
}
diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java
index 4cfee06cd6..8556cd5d14 100755
--- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java
+++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java
@@ -199,10 +199,20 @@ public NettyRequest newNettyRequest(Request request, boolean performConnectReque
headers.set(HOST, virtualHost != null ? virtualHost : hostHeader(uri));
}
- // don't override authorization but append
- addAuthorizationHeader(headers, perRequestAuthorizationHeader(request, realm));
- // only set proxy auth on request over plain HTTP, or when performing CONNECT
- if (!uri.isSecured() || connect) {
+ // don't override authorization but append. Skip it on a CONNECT: that request is sent to the
+ // proxy in the clear to open the tunnel, so the origin Authorization would be exposed to the
+ // proxy. It is added to the tunneled request, which is built separately once the tunnel is up.
+ if (!connect) {
+ addAuthorizationHeader(headers, perRequestAuthorizationHeader(request, realm));
+ }
+ // Only set proxy auth on a request sent to an HTTP proxy: either over plain HTTP (the origin request
+ // carries an absolute URI straight to the proxy) or on a CONNECT (sent to the proxy to open the
+ // tunnel). A SOCKS proxy tunnels at the transport layer, so the request reaches the ORIGIN - a
+ // Proxy-Authorization header would leak the proxy credentials to the origin. Guard on the proxy type.
+ // A ws:// request is tunnelled through CONNECT the same way wss:// is (see NettyRequestSender's
+ // needConnect check), so the upgrade request that follows also reaches the origin, not the proxy;
+ // exclude it from the plain-HTTP branch the same way wss:// already is.
+ if ((connect || (!uri.isSecured() && !uri.isWebSocket())) && proxyServer != null && proxyServer.getProxyType().isHttp()) {
setProxyAuthorizationHeader(headers, perRequestProxyAuthorizationHeader(request, proxyRealm));
}
@@ -224,9 +234,12 @@ private String requestUri(Uri uri, ProxyServer proxyServer, boolean connect) {
// proxy tunnelling, connect need host and explicit port
return uri.getAuthority();
- } else if (proxyServer != null && !uri.isSecured() && proxyServer.getProxyType().isHttp()) {
- // proxy over HTTP, need full url
- return uri.toUrl();
+ } else if (proxyServer != null && !uri.isSecured() && !uri.isWebSocket() && proxyServer.getProxyType().isHttp()) {
+ // proxy over HTTP, need full url, minus the userinfo: this request line is sent to the proxy in the
+ // clear and RFC 9110 section 4.2.4 forbids userinfo in a generated request target. A ws:// request is
+ // tunnelled through CONNECT, so its upgrade request reaches the origin and takes the origin-form
+ // request target below
+ return uri.toUrlWithoutUserInfo();
} else {
// direct connection to target host or tunnel already connected: only path and query
diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
index aed08b7a70..0adcf72759 100755
--- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
+++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java
@@ -35,6 +35,7 @@
import org.asynchttpclient.netty.OnLastHttpContentCallback;
import org.asynchttpclient.netty.SimpleFutureListener;
import org.asynchttpclient.netty.channel.*;
+import org.asynchttpclient.netty.channel.PrincipalScopedPartitionKey;
import org.asynchttpclient.netty.handler.StreamedResponsePublisher;
import org.asynchttpclient.netty.timeout.TimeoutsHolder;
import org.asynchttpclient.proxy.ProxyServer;
@@ -117,6 +118,11 @@ public ListenableFuture sendRequest(final Request request,
private boolean isConnectAlreadyDone(Request request, NettyResponseFuture> future) {
return future != null
+ // The last request having been a CONNECT proves nothing: it is equally true when the proxy
+ // REJECTED it with 401, 407 or a redirect. ConnectSuccessInterceptor never ran in that case,
+ // so the socket is still a plaintext hop to the proxy. Skipping the CONNECT here would send
+ // the ORIGIN request - Authorization header and all - straight down it.
+ && future.isTunnelEstablished()
&& future.getNettyRequest() != null
&& future.getNettyRequest().getHttpRequest().method() == HttpMethod.CONNECT
&& !request.getMethod().equals(CONNECT);
@@ -283,8 +289,20 @@ private ListenableFuture sendRequestWithNewChannel(Request request,
}
Realm realm = future.getRealm();
Realm proxyRealm = future.getProxyRealm();
- requestFactory.addAuthorizationHeader(headers, perConnectionAuthorizationHeader(request, proxy, realm));
- requestFactory.setProxyAuthorizationHeader(headers, perConnectionProxyAuthorizationHeader(request, proxyRealm));
+ // On the tunnel path this is the CONNECT request, sent to the proxy in the clear before the TLS
+ // tunnel exists. Preemptive NTLM/Kerberos/SPNEGO realms attach their header here rather than in
+ // the factory, so skip it on CONNECT to keep the origin credentials off the plaintext hop. They
+ // travel on the tunneled request once the tunnel is up.
+ if (future.getNettyRequest().getHttpRequest().method() != HttpMethod.CONNECT) {
+ requestFactory.addAuthorizationHeader(headers, perConnectionAuthorizationHeader(request, proxy, realm));
+ }
+ // Preemptive per-connection proxy auth (NTLM/Kerberos/SPNEGO) belongs only on a request sent to an
+ // HTTP proxy. Over a SOCKS proxy the transport tunnels this request to the ORIGIN, so attaching
+ // Proxy-Authorization here would leak the proxy credentials to the origin. Guard on the proxy type,
+ // mirroring the per-request gate in NettyRequestFactory.
+ if (proxy != null && proxy.getProxyType().isHttp()) {
+ requestFactory.setProxyAuthorizationHeader(headers, perConnectionProxyAuthorizationHeader(request, proxyRealm));
+ }
future.setInAuth(realm != null && realm.isUsePreemptiveAuth() && realm.getScheme() != AuthScheme.NTLM);
future.setInProxyAuth(
@@ -587,7 +605,14 @@ private Channel pollPooledChannel(Request request, ProxyServer proxy, AsyncHandl
Uri uri = request.getUri();
String virtualHost = request.getVirtualHost();
- final Channel channel = channelManager.poll(uri, virtualHost, proxy, request.getChannelPoolPartitioning());
+ // Scope the pool lookup by the authenticated identity: NTLM and Negotiate authenticate the socket,
+ // not the request, so a connection one principal completed the handshake on must not be handed to
+ // another. A disagreement with the offer side costs a pool miss, never a wrong reuse.
+ // Resolved as newNettyRequestAndResponseFuture resolves it, so the poll and the offer agree.
+ Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm();
+ Object partitionKey = PrincipalScopedPartitionKey.scope(
+ request.getChannelPoolPartitioning().getPartitionKey(uri, virtualHost, proxy), realm);
+ final Channel channel = channelManager.poll(partitionKey);
if (channel != null) {
LOGGER.debug("Using pooled Channel '{}' for '{}' to '{}'", channel, request.getMethod(), uri);
@@ -612,7 +637,28 @@ public void replayRequest(final NettyResponseFuture> future, FilterContext fc,
return;
}
- channelManager.drainChannelAndOffer(channel, future);
+ // A replay may target a different origin than the one this future was built for; failover onto a
+ // second host is the documented use of a ResponseFilter. Everything keyed off the future's TARGET has
+ // to move with it or it keeps describing the previous origin: the connection pool partition key, and
+ // the credentials, which must not follow the request to a host they were not meant for.
+ Uri previousUri = future.getTargetRequest().getUri();
+ Uri newUri = newRequest.getUri();
+ boolean sameBase = previousUri.isSameBase(newUri);
+ boolean schemeDowngrade = previousUri.isSecured() && !newUri.isSecured();
+ // Sampled while the future still describes the old origin: that is what the drained channel is
+ // connected to, so that is the key it has to be filed under.
+ Object initialPartitionKey = PrincipalScopedPartitionKey.scope(
+ future.getPartitionKey(), future.getRealm());
+
+ if (!sameBase || schemeDowngrade) {
+ future.setRealm(newRequest.getRealm());
+ future.setProxyRealm(null);
+ }
+ // The proxy is the other half of the pool key and the replay may resolve to a different one, or none.
+ future.setProxyServer(getProxyServer(config, newRequest));
+ future.setTargetRequest(newRequest);
+
+ channelManager.drainChannelAndOffer(channel, future, future.isKeepAlive(), initialPartitionKey);
sendNextRequest(newRequest, future);
}
diff --git a/client/src/main/java/org/asynchttpclient/uri/Uri.java b/client/src/main/java/org/asynchttpclient/uri/Uri.java
index 19986dcfc3..580c495a73 100644
--- a/client/src/main/java/org/asynchttpclient/uri/Uri.java
+++ b/client/src/main/java/org/asynchttpclient/uri/Uri.java
@@ -149,6 +149,30 @@ public String toUrl() {
return url;
}
+ /**
+ * Same as {@link #toUrl()} but without the deprecated userinfo subcomponent. RFC 9110 section 4.2.4: a
+ * sender MUST NOT generate the userinfo subcomponent (and its "@" delimiter) when an origin-form or
+ * absolute-form URI reference is generated for a request target, so this is what belongs on the wire when
+ * an absolute-form target is required. {@link #toUrl()} keeps the userinfo for the caller-visible URL.
+ *
+ * @return [scheme]://[hostname](:[port])/path(?[query])
+ */
+ public String toUrlWithoutUserInfo() {
+ if (userInfo == null) {
+ return toUrl();
+ }
+
+ StringBuilder sb = StringBuilderPool.DEFAULT.stringBuilder();
+ sb.append(scheme).append("://").append(host);
+ if (port != -1)
+ sb.append(':').append(port);
+ if (path != null)
+ sb.append(path);
+ if (query != null)
+ sb.append('?').append(query);
+ return sb.toString();
+ }
+
/**
* @return [scheme]://[hostname](:[port])/path. Port is omitted if it matches the scheme's default one.
*/
diff --git a/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java b/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java
index 00d69af7d2..fe3c694e20 100644
--- a/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java
+++ b/client/src/main/java/org/asynchttpclient/util/AuthenticatorUtils.java
@@ -55,7 +55,13 @@ private static String computeBasicAuthentication(String principal, String passwo
public static String computeRealmURI(Uri uri, boolean useAbsoluteURI, boolean omitQuery) {
if (useAbsoluteURI) {
- return omitQuery && MiscUtils.isNonEmpty(uri.getQuery()) ? uri.withNewQuery(null).toUrl() : uri.toUrl();
+ // Absolute form, so this string goes on the wire as the Digest uri="..." parameter, and the digest
+ // itself is computed over it. RFC 9110 section 4.2.4 forbids generating the userinfo subcomponent in
+ // a request target, and Digest is used precisely on hops where the password must not travel in the
+ // clear - toUrl() would render "user:secret@" straight into the header. Use the userinfo-free form.
+ return omitQuery && MiscUtils.isNonEmpty(uri.getQuery())
+ ? uri.withNewQuery(null).toUrlWithoutUserInfo()
+ : uri.toUrlWithoutUserInfo();
} else {
String path = uri.getNonEmptyPath();
return omitQuery || !MiscUtils.isNonEmpty(uri.getQuery()) ? path : path + "?" + uri.getQuery();
@@ -155,6 +161,17 @@ public static String perRequestProxyAuthorizationHeader(Request request, Realm p
return proxyAuthorization;
}
+ /**
+ * The service the origin realm's Negotiate token is minted for. It is always the origin, even when a
+ * proxy is configured: building it against the proxy host produced a service ticket for the proxy's SPN,
+ * a confused deputy where the origin's credential is delivered to, and only usable by, the proxy, while
+ * origin authentication fails. The proxy realm has its own path, in
+ * perConnectionProxyAuthorizationHeader, and is unaffected.
+ */
+ static String negotiateHost(Request request) {
+ return request.getVirtualHost() != null ? request.getVirtualHost() : request.getUri().getHost();
+ }
+
public static String perConnectionAuthorizationHeader(Request request, ProxyServer proxyServer, Realm realm) {
String authorizationHeader = null;
@@ -166,13 +183,7 @@ public static String perConnectionAuthorizationHeader(Request request, ProxyServ
break;
case KERBEROS:
case SPNEGO:
- String host;
- if (proxyServer != null)
- host = proxyServer.getHost();
- else if (request.getVirtualHost() != null)
- host = request.getVirtualHost();
- else
- host = request.getUri().getHost();
+ String host = negotiateHost(request);
try {
authorizationHeader = NEGOTIATE + " " + SpnegoEngine.instance(
diff --git a/client/src/main/java/org/asynchttpclient/util/HttpUtils.java b/client/src/main/java/org/asynchttpclient/util/HttpUtils.java
index 779dba9c78..5930d31d7e 100644
--- a/client/src/main/java/org/asynchttpclient/util/HttpUtils.java
+++ b/client/src/main/java/org/asynchttpclient/util/HttpUtils.java
@@ -23,8 +23,8 @@
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
+import java.security.SecureRandom;
import java.util.List;
-import java.util.concurrent.ThreadLocalRandom;
import static java.nio.charset.StandardCharsets.*;
@@ -114,9 +114,16 @@ private static String extractContentTypeAttribute(String contentType, String att
// The pool of ASCII chars to be used for generating a multipart boundary.
private static byte[] MULTIPART_CHARS = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(US_ASCII);
+ // The boundary is what separates parts whose content is not escaped, so it must be unpredictable, like
+ // the cnonce in Realm. It is also the single largest window a peer gets onto the generator: 30 to 40
+ // consecutive nextInt(64) outputs, echoed verbatim in the Content-Type header and in the body. With a
+ // non-cryptographic generator that over-determines the state, so a boundary observed on one request
+ // predicts every subsequent draw from the same thread - including the Digest cnonce.
+ private static final ThreadLocal BOUNDARY_RANDOM = ThreadLocal.withInitial(SecureRandom::new);
+
// a random size from 30 to 40
public static byte[] computeMultipartBoundary() {
- ThreadLocalRandom random = ThreadLocalRandom.current();
+ SecureRandom random = BOUNDARY_RANDOM.get();
byte[] bytes = new byte[random.nextInt(11) + 30];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = MULTIPART_CHARS[random.nextInt(MULTIPART_CHARS.length)];
diff --git a/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java b/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java
index 31bd8f5c2a..4861ac7b92 100644
--- a/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java
+++ b/client/src/main/java/org/asynchttpclient/ws/WebSocketUtils.java
@@ -13,8 +13,7 @@
*/
package org.asynchttpclient.ws;
-import io.netty.util.internal.ThreadLocalRandom;
-
+import java.security.SecureRandom;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.US_ASCII;
@@ -23,12 +22,14 @@
public final class WebSocketUtils {
private static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ // RFC 6455 section 10.3 requires the handshake nonce to come from a strong source of entropy: it is
+ // what proves the 101 was produced by a peer that saw this request. Netty's ThreadLocalRandom is a
+ // 48-bit LCG, so an off-path party who can guess the key can precompute a valid Sec-WebSocket-Accept.
+ private static final ThreadLocal KEY_RANDOM = ThreadLocal.withInitial(SecureRandom::new);
+
public static String getWebSocketKey() {
byte[] nonce = new byte[16];
- ThreadLocalRandom random = ThreadLocalRandom.current();
- for (int i = 0; i < nonce.length; i++) {
- nonce[i] = (byte) random.nextInt(256);
- }
+ KEY_RANDOM.get().nextBytes(nonce);
return Base64.getEncoder().encodeToString(nonce);
}
diff --git a/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties b/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties
index 62bc177726..014dc5d613 100644
--- a/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties
+++ b/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties
@@ -46,6 +46,7 @@ org.asynchttpclient.chunkedFileChunkSize=8192
org.asynchttpclient.webSocketMaxBufferSize=128000000
org.asynchttpclient.webSocketMaxFrameSize=10240
org.asynchttpclient.keepEncodingHeader=false
+org.asynchttpclient.maxDecompressedResponseSize=268435456
org.asynchttpclient.shutdownQuietPeriod=2000
org.asynchttpclient.shutdownTimeout=15000
org.asynchttpclient.useNativeTransport=false
diff --git a/client/src/main/resources/org/asynchttpclient/cookie/public_suffix_list.dat b/client/src/main/resources/org/asynchttpclient/cookie/public_suffix_list.dat
new file mode 100644
index 0000000000..0827ebab62
--- /dev/null
+++ b/client/src/main/resources/org/asynchttpclient/cookie/public_suffix_list.dat
@@ -0,0 +1,11255 @@
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at https://mozilla.org/MPL/2.0/.
+
+// Please pull this list from, and only from https://publicsuffix.org/list/public_suffix_list.dat,
+// rather than any other VCS sites. Pulling from any other URL is not guaranteed to be supported.
+
+// Instructions on pulling and using this list can be found at https://publicsuffix.org/list/.
+
+// ===BEGIN ICANN DOMAINS===
+
+// ac : http://nic.ac/rules.htm
+ac
+com.ac
+edu.ac
+gov.ac
+mil.ac
+net.ac
+org.ac
+
+// ad : https://www.iana.org/domains/root/db/ad.html
+// Confirmed by Amadeu Abril i Abril (CORE) 2024-11-17
+ad
+
+// ae : https://www.iana.org/domains/root/db/ae.html
+ae
+ac.ae
+co.ae
+gov.ae
+mil.ae
+net.ae
+org.ae
+sch.ae
+
+// aero : https://information.aero/registration/policies/dmp
+aero
+// 2LDs
+airline.aero
+airport.aero
+// 2LDs (currently not accepting registration, seemingly never have)
+// As of 2024-07, these are marked as reserved for potential 3LD
+// registrations (clause 11 "allocated subdomains" in the 2006 TLD
+// policy), but the relevant industry partners have not opened them up
+// for registration. Current status can be determined from the TLD's
+// policy document: 2LDs that are open for registration must list
+// their policy in the TLD's policy. Any 2LD without such a policy is
+// not open for registrations.
+accident-investigation.aero
+accident-prevention.aero
+aerobatic.aero
+aeroclub.aero
+aerodrome.aero
+agents.aero
+air-surveillance.aero
+air-traffic-control.aero
+aircraft.aero
+airtraffic.aero
+ambulance.aero
+association.aero
+author.aero
+ballooning.aero
+broker.aero
+caa.aero
+cargo.aero
+catering.aero
+certification.aero
+championship.aero
+charter.aero
+civilaviation.aero
+club.aero
+conference.aero
+consultant.aero
+consulting.aero
+control.aero
+council.aero
+crew.aero
+design.aero
+dgca.aero
+educator.aero
+emergency.aero
+engine.aero
+engineer.aero
+entertainment.aero
+equipment.aero
+exchange.aero
+express.aero
+federation.aero
+flight.aero
+freight.aero
+fuel.aero
+gliding.aero
+government.aero
+groundhandling.aero
+group.aero
+hanggliding.aero
+homebuilt.aero
+insurance.aero
+journal.aero
+journalist.aero
+leasing.aero
+logistics.aero
+magazine.aero
+maintenance.aero
+marketplace.aero
+media.aero
+microlight.aero
+modelling.aero
+navigation.aero
+parachuting.aero
+paragliding.aero
+passenger-association.aero
+pilot.aero
+press.aero
+production.aero
+recreation.aero
+repbody.aero
+res.aero
+research.aero
+rotorcraft.aero
+safety.aero
+scientist.aero
+services.aero
+show.aero
+skydiving.aero
+software.aero
+student.aero
+taxi.aero
+trader.aero
+trading.aero
+trainer.aero
+union.aero
+workinggroup.aero
+works.aero
+
+// af : https://www.nic.af/domain-price
+af
+com.af
+edu.af
+gov.af
+net.af
+org.af
+
+// ag : http://www.nic.ag/prices.htm
+ag
+co.ag
+com.ag
+net.ag
+nom.ag
+org.ag
+
+// ai : http://nic.com.ai/
+ai
+com.ai
+net.ai
+off.ai
+org.ai
+
+// al : http://www.ert.gov.al/ert_alb/faq_det.html?Id=31
+al
+com.al
+edu.al
+gov.al
+mil.al
+net.al
+org.al
+
+// am : https://www.amnic.net/policy/en/Policy_EN.pdf
+// Confirmed by ISOC AM 2024-11-18
+am
+co.am
+com.am
+commune.am
+net.am
+org.am
+
+// ao : https://www.iana.org/domains/root/db/ao.html
+// https://www.dns.ao/ao/
+ao
+co.ao
+ed.ao
+edu.ao
+gov.ao
+gv.ao
+it.ao
+og.ao
+org.ao
+pb.ao
+
+// aq : https://www.iana.org/domains/root/db/aq.html
+aq
+
+// ar : https://nic.ar/es/nic-argentina/normativa
+ar
+bet.ar
+com.ar
+coop.ar
+edu.ar
+gob.ar
+gov.ar
+int.ar
+mil.ar
+musica.ar
+mutual.ar
+net.ar
+org.ar
+seg.ar
+senasa.ar
+tur.ar
+
+// arpa : https://www.iana.org/domains/root/db/arpa.html
+// Confirmed by registry 2008-06-18
+arpa
+e164.arpa
+home.arpa
+in-addr.arpa
+ip6.arpa
+iris.arpa
+uri.arpa
+urn.arpa
+
+// as : https://www.iana.org/domains/root/db/as.html
+as
+gov.as
+
+// asia : https://www.iana.org/domains/root/db/asia.html
+asia
+
+// at : https://www.iana.org/domains/root/db/at.html
+// Confirmed by registry 2008-06-17
+at
+ac.at
+sth.ac.at
+co.at
+gv.at
+or.at
+
+// au : https://www.iana.org/domains/root/db/au.html
+// https://www.auda.org.au/
+// Confirmed by registry 2025-07-16
+au
+// 2LDs
+asn.au
+com.au
+edu.au
+gov.au
+id.au
+net.au
+org.au
+// Historic 2LDs (closed to new registration, but sites still exist)
+conf.au
+oz.au
+// CGDNs : https://www.auda.org.au/au-domain-names/the-different-au-domain-names/state-and-territory-domain-names/
+act.au
+nsw.au
+nt.au
+qld.au
+sa.au
+tas.au
+vic.au
+wa.au
+// 3LDs
+act.edu.au
+catholic.edu.au
+// eq.edu.au - Removed at the request of the Queensland Department of Education
+nsw.edu.au
+nt.edu.au
+qld.edu.au
+sa.edu.au
+tas.edu.au
+vic.edu.au
+wa.edu.au
+// act.gov.au - Bug 984824 - Removed at request of Greg Tankard
+// nsw.gov.au - Bug 547985 - Removed at request of
+// nt.gov.au - Bug 940478 - Removed at request of Greg Connors
+qld.gov.au
+sa.gov.au
+tas.gov.au
+vic.gov.au
+wa.gov.au
+// 4LDs
+// education.tas.edu.au - Removed at the request of the Department of Education Tasmania
+// schools.nsw.edu.au - Removed at the request of the New South Wales Department of Education.
+
+// aw : https://www.iana.org/domains/root/db/aw.html
+aw
+com.aw
+
+// ax : https://www.iana.org/domains/root/db/ax.html
+ax
+
+// az : https://www.iana.org/domains/root/db/az.html
+// Confirmed via https://whois.az/?page_id=10 2024-12-11
+az
+biz.az
+co.az
+com.az
+edu.az
+gov.az
+info.az
+int.az
+mil.az
+name.az
+net.az
+org.az
+pp.az
+// No longer available for registration, however domains exist as of 2024-12-11
+// see https://whois.az/?page_id=783
+pro.az
+
+// ba : https://www.iana.org/domains/root/db/ba.html
+ba
+com.ba
+edu.ba
+gov.ba
+mil.ba
+net.ba
+org.ba
+
+// bb : https://www.iana.org/domains/root/db/bb.html
+bb
+biz.bb
+co.bb
+com.bb
+edu.bb
+gov.bb
+info.bb
+net.bb
+org.bb
+store.bb
+tv.bb
+
+// bd : https://www.iana.org/domains/root/db/bd.html
+// Confirmed by registry
+bd
+ac.bd
+ai.bd
+co.bd
+com.bd
+edu.bd
+gov.bd
+id.bd
+info.bd
+it.bd
+mil.bd
+net.bd
+org.bd
+sch.bd
+tv.bd
+
+// be : https://www.iana.org/domains/root/db/be.html
+// Confirmed by registry 2008-06-08
+be
+ac.be
+
+// bf : https://www.iana.org/domains/root/db/bf.html
+bf
+gov.bf
+
+// bg : https://www.iana.org/domains/root/db/bg.html
+// https://www.register.bg/user/static/rules/en/index.html
+bg
+0.bg
+1.bg
+2.bg
+3.bg
+4.bg
+5.bg
+6.bg
+7.bg
+8.bg
+9.bg
+a.bg
+b.bg
+c.bg
+d.bg
+e.bg
+f.bg
+g.bg
+h.bg
+i.bg
+j.bg
+k.bg
+l.bg
+m.bg
+n.bg
+o.bg
+p.bg
+q.bg
+r.bg
+s.bg
+t.bg
+u.bg
+v.bg
+w.bg
+x.bg
+y.bg
+z.bg
+
+// bh : https://www.iana.org/domains/root/db/bh.html
+bh
+com.bh
+edu.bh
+gov.bh
+net.bh
+org.bh
+
+// bi : https://www.iana.org/domains/root/db/bi.html
+// http://whois.nic.bi/
+bi
+co.bi
+com.bi
+edu.bi
+or.bi
+org.bi
+
+// biz : https://www.iana.org/domains/root/db/biz.html
+biz
+
+// bj : https://nic.bj/bj-suffixes.txt
+// Submitted by registry
+bj
+africa.bj
+agro.bj
+architectes.bj
+assur.bj
+avocats.bj
+co.bj
+com.bj
+eco.bj
+econo.bj
+edu.bj
+info.bj
+loisirs.bj
+money.bj
+net.bj
+org.bj
+ote.bj
+restaurant.bj
+resto.bj
+tourism.bj
+univ.bj
+
+// bm : https://www.bermudanic.bm/domain-registration/index.php
+bm
+com.bm
+edu.bm
+gov.bm
+net.bm
+org.bm
+
+// bn : http://www.bnnic.bn/faqs
+bn
+com.bn
+edu.bn
+gov.bn
+net.bn
+org.bn
+
+// bo : https://nic.bo
+// Confirmed by registry 2024-11-19
+bo
+com.bo
+edu.bo
+gob.bo
+int.bo
+mil.bo
+net.bo
+org.bo
+tv.bo
+web.bo
+// Social Domains
+academia.bo
+agro.bo
+arte.bo
+blog.bo
+bolivia.bo
+ciencia.bo
+cooperativa.bo
+democracia.bo
+deporte.bo
+ecologia.bo
+economia.bo
+empresa.bo
+indigena.bo
+industria.bo
+info.bo
+medicina.bo
+movimiento.bo
+musica.bo
+natural.bo
+nombre.bo
+noticias.bo
+patria.bo
+plurinacional.bo
+politica.bo
+profesional.bo
+pueblo.bo
+revista.bo
+salud.bo
+tecnologia.bo
+tksat.bo
+transporte.bo
+wiki.bo
+
+// br : http://registro.br/dominio/categoria.html
+// Submitted by registry
+br
+9guacu.br
+abc.br
+adm.br
+adv.br
+agr.br
+aju.br
+am.br
+anani.br
+aparecida.br
+api.br
+app.br
+arq.br
+art.br
+ato.br
+b.br
+barueri.br
+belem.br
+bet.br
+bhz.br
+bib.br
+bio.br
+blog.br
+bmd.br
+boavista.br
+bsb.br
+campinagrande.br
+campinas.br
+caxias.br
+cim.br
+cng.br
+cnt.br
+com.br
+contagem.br
+coop.br
+coz.br
+cri.br
+cuiaba.br
+curitiba.br
+def.br
+des.br
+det.br
+dev.br
+ecn.br
+eco.br
+edu.br
+emp.br
+enf.br
+eng.br
+esp.br
+etc.br
+eti.br
+far.br
+feira.br
+flog.br
+floripa.br
+fm.br
+fnd.br
+fortal.br
+fot.br
+foz.br
+fst.br
+g12.br
+geo.br
+ggf.br
+goiania.br
+gov.br
+// gov.br 26 states + df https://en.wikipedia.org/wiki/States_of_Brazil
+ac.gov.br
+al.gov.br
+am.gov.br
+ap.gov.br
+ba.gov.br
+ce.gov.br
+df.gov.br
+es.gov.br
+go.gov.br
+ma.gov.br
+mg.gov.br
+ms.gov.br
+mt.gov.br
+pa.gov.br
+pb.gov.br
+pe.gov.br
+pi.gov.br
+pr.gov.br
+rj.gov.br
+rn.gov.br
+ro.gov.br
+rr.gov.br
+rs.gov.br
+sc.gov.br
+se.gov.br
+sp.gov.br
+to.gov.br
+gru.br
+ia.br
+imb.br
+ind.br
+inf.br
+jab.br
+jampa.br
+jdf.br
+joinville.br
+jor.br
+jus.br
+leg.br
+leilao.br
+lel.br
+log.br
+londrina.br
+macapa.br
+maceio.br
+manaus.br
+maringa.br
+mat.br
+med.br
+mil.br
+morena.br
+mp.br
+mus.br
+natal.br
+net.br
+niteroi.br
+*.nom.br
+not.br
+ntr.br
+odo.br
+ong.br
+org.br
+osasco.br
+palmas.br
+poa.br
+ppg.br
+pro.br
+psc.br
+psi.br
+pvh.br
+qsl.br
+radio.br
+rec.br
+recife.br
+rep.br
+ribeirao.br
+rio.br
+riobranco.br
+riopreto.br
+salvador.br
+sampa.br
+santamaria.br
+santoandre.br
+saobernardo.br
+saogonca.br
+seg.br
+sjc.br
+slg.br
+slz.br
+social.br
+sorocaba.br
+srv.br
+taxi.br
+tc.br
+tec.br
+teo.br
+the.br
+tmp.br
+trd.br
+tur.br
+tv.br
+udi.br
+vet.br
+vix.br
+vlog.br
+wiki.br
+xyz.br
+zlg.br
+
+// bs : http://www.nic.bs/rules.html
+bs
+com.bs
+edu.bs
+gov.bs
+net.bs
+org.bs
+
+// bt : https://www.iana.org/domains/root/db/bt.html
+bt
+com.bt
+edu.bt
+gov.bt
+net.bt
+org.bt
+
+// bv : No registrations at this time.
+// Submitted by registry
+bv
+
+// bw : https://www.iana.org/domains/root/db/bw.html
+// https://nic.net.bw/bw-name-structure
+bw
+ac.bw
+co.bw
+gov.bw
+net.bw
+org.bw
+
+// by : https://www.iana.org/domains/root/db/by.html
+// http://tld.by/rules_2006_en.html
+// list of other 2nd level tlds ?
+by
+gov.by
+mil.by
+// Official information does not indicate that com.by is a reserved
+// second-level domain, but it's being used as one (see www.google.com.by and
+// www.yahoo.com.by, for example), so we list it here for safety's sake.
+com.by
+// http://hoster.by/
+of.by
+
+// bz : https://www.iana.org/domains/root/db/bz.html
+// http://www.belizenic.bz/
+bz
+co.bz
+com.bz
+edu.bz
+gov.bz
+net.bz
+org.bz
+
+// ca : https://www.iana.org/domains/root/db/ca.html
+ca
+// ca geographical names
+ab.ca
+bc.ca
+mb.ca
+nb.ca
+nf.ca
+nl.ca
+ns.ca
+nt.ca
+nu.ca
+on.ca
+pe.ca
+qc.ca
+sk.ca
+yk.ca
+// gc.ca: https://en.wikipedia.org/wiki/.gc.ca
+// see also: http://registry.gc.ca/en/SubdomainFAQ
+gc.ca
+
+// cat : https://www.iana.org/domains/root/db/cat.html
+cat
+
+// cc : https://www.iana.org/domains/root/db/cc.html
+cc
+
+// cd : https://www.iana.org/domains/root/db/cd.html
+// https://www.nic.cd
+cd
+gov.cd
+
+// cf : https://www.iana.org/domains/root/db/cf.html
+cf
+
+// cg : https://www.iana.org/domains/root/db/cg.html
+cg
+
+// ch : https://www.iana.org/domains/root/db/ch.html
+ch
+
+// ci : https://www.iana.org/domains/root/db/ci.html
+ci
+ac.ci
+aéroport.ci
+asso.ci
+co.ci
+com.ci
+ed.ci
+edu.ci
+go.ci
+gouv.ci
+int.ci
+net.ci
+or.ci
+org.ci
+
+// ck : https://www.iana.org/domains/root/db/ck.html
+*.ck
+!www.ck
+
+// cl : https://www.nic.cl
+// Confirmed by .CL registry
+cl
+co.cl
+gob.cl
+gov.cl
+mil.cl
+
+// cm : https://www.iana.org/domains/root/db/cm.html plus bug 981927
+cm
+co.cm
+com.cm
+gov.cm
+net.cm
+
+// cn : https://www.iana.org/domains/root/db/cn.html
+// Submitted by registry
+cn
+ac.cn
+com.cn
+edu.cn
+gov.cn
+mil.cn
+net.cn
+org.cn
+公司.cn
+網絡.cn
+网络.cn
+// cn geographic names
+ah.cn
+bj.cn
+cq.cn
+fj.cn
+gd.cn
+gs.cn
+gx.cn
+gz.cn
+ha.cn
+hb.cn
+he.cn
+hi.cn
+hk.cn
+hl.cn
+hn.cn
+jl.cn
+js.cn
+jx.cn
+ln.cn
+mo.cn
+nm.cn
+nx.cn
+qh.cn
+sc.cn
+sd.cn
+sh.cn
+sn.cn
+sx.cn
+tj.cn
+tw.cn
+xj.cn
+xz.cn
+yn.cn
+zj.cn
+
+// co : https://www.iana.org/domains/root/db/co.html
+// https://www.cointernet.com.co/como-funciona-un-dominio-restringido
+// Confirmed by registry 2024-11-18
+co
+com.co
+edu.co
+gov.co
+mil.co
+net.co
+nom.co
+org.co
+
+// com : https://www.iana.org/domains/root/db/com.html
+com
+
+// coop : https://www.iana.org/domains/root/db/coop.html
+coop
+
+// cr : https://nic.cr/capitulo-1-registro-de-un-nombre-de-dominio/
+cr
+ac.cr
+co.cr
+ed.cr
+fi.cr
+go.cr
+or.cr
+sa.cr
+
+// cu : https://www.iana.org/domains/root/db/cu.html
+cu
+com.cu
+edu.cu
+gob.cu
+inf.cu
+nat.cu
+net.cu
+org.cu
+
+// cv : https://www.iana.org/domains/root/db/cv.html
+// https://ola.cv/domain-extensions-under-cv/
+// Confirmed by registry 2024-11-26
+cv
+com.cv
+edu.cv
+id.cv
+int.cv
+net.cv
+nome.cv
+org.cv
+publ.cv
+
+// cw : https://www.uoc.cw/cw-registry
+// Confirmed by registry 2024-11-19
+cw
+com.cw
+edu.cw
+net.cw
+org.cw
+
+// cx : https://www.iana.org/domains/root/db/cx.html
+// list of other 2nd level tlds ?
+cx
+gov.cx
+
+// cy : http://www.nic.cy/
+// Submitted by Panayiotou Fotia
+// https://nic.cy/wp-content/uploads/2024/01/Create-Request-for-domain-name-registration-1.pdf
+cy
+ac.cy
+biz.cy
+com.cy
+ekloges.cy
+gov.cy
+ltd.cy
+mil.cy
+net.cy
+org.cy
+press.cy
+pro.cy
+tm.cy
+
+// cz : https://www.iana.org/domains/root/db/cz.html
+// Confirmed by registry 2025-08-06
+cz
+gov.cz
+
+// de : https://www.iana.org/domains/root/db/de.html
+// Confirmed by registry (with technical
+// reservations) 2008-07-01
+de
+
+// dj : https://www.iana.org/domains/root/db/dj.html
+dj
+
+// dk : https://www.iana.org/domains/root/db/dk.html
+// Confirmed by registry 2008-06-17
+dk
+
+// dm : https://www.iana.org/domains/root/db/dm.html
+// https://nic.dm/policies/pdf/DMRulesandGuidelines2024v1.pdf
+// Confirmed by registry 2024-11-19
+dm
+co.dm
+com.dm
+edu.dm
+gov.dm
+net.dm
+org.dm
+
+// do : https://www.iana.org/domains/root/db/do.html
+do
+art.do
+com.do
+edu.do
+gob.do
+gov.do
+mil.do
+net.do
+org.do
+sld.do
+web.do
+
+// dz : http://www.nic.dz/images/pdf_nic/charte.pdf
+dz
+art.dz
+asso.dz
+com.dz
+edu.dz
+gov.dz
+net.dz
+org.dz
+pol.dz
+soc.dz
+tm.dz
+
+// ec : https://www.nic.ec/
+// Submitted by registry
+ec
+abg.ec
+adm.ec
+agron.ec
+arqt.ec
+art.ec
+bar.ec
+chef.ec
+com.ec
+cont.ec
+cpa.ec
+cue.ec
+dent.ec
+dgn.ec
+disco.ec
+doc.ec
+edu.ec
+eng.ec
+esm.ec
+fin.ec
+fot.ec
+gal.ec
+gob.ec
+gov.ec
+gye.ec
+ibr.ec
+info.ec
+k12.ec
+lat.ec
+loj.ec
+med.ec
+mil.ec
+mktg.ec
+mon.ec
+net.ec
+ntr.ec
+odont.ec
+org.ec
+pro.ec
+prof.ec
+psic.ec
+psiq.ec
+pub.ec
+rio.ec
+rrpp.ec
+sal.ec
+tech.ec
+tul.ec
+tur.ec
+uio.ec
+vet.ec
+xxx.ec
+
+// edu : https://www.iana.org/domains/root/db/edu.html
+edu
+
+// ee : https://www.internet.ee/domains/general-domains-and-procedure-for-registration-of-sub-domains-under-general-domains
+ee
+aip.ee
+com.ee
+edu.ee
+fie.ee
+gov.ee
+lib.ee
+med.ee
+org.ee
+pri.ee
+riik.ee
+
+// eg : https://www.iana.org/domains/root/db/eg.html
+// https://domain.eg/en/domain-rules/subdomain-names-types/
+eg
+ac.eg
+com.eg
+edu.eg
+eun.eg
+gov.eg
+info.eg
+me.eg
+mil.eg
+name.eg
+net.eg
+org.eg
+sci.eg
+sport.eg
+tv.eg
+
+// er : https://www.iana.org/domains/root/db/er.html
+*.er
+
+// es : https://www.dominios.es/en
+es
+com.es
+edu.es
+gob.es
+nom.es
+org.es
+
+// et : https://www.iana.org/domains/root/db/et.html
+et
+biz.et
+com.et
+edu.et
+gov.et
+info.et
+name.et
+net.et
+org.et
+
+// eu : https://www.iana.org/domains/root/db/eu.html
+eu
+
+// fi : https://www.iana.org/domains/root/db/fi.html
+fi
+// aland.fi : https://www.iana.org/domains/root/db/ax.html
+// This domain is being phased out in favor of .ax. As there are still many
+// domains under aland.fi, we still keep it on the list until aland.fi is
+// completely removed.
+aland.fi
+
+// fj : https://www.iana.org/domains/root/db/fj.html
+fj
+ac.fj
+biz.fj
+com.fj
+edu.fj
+gov.fj
+id.fj
+info.fj
+mil.fj
+name.fj
+net.fj
+org.fj
+pro.fj
+
+// fk : https://www.iana.org/domains/root/db/fk.html
+*.fk
+
+// fm : https://www.iana.org/domains/root/db/fm.html
+fm
+com.fm
+edu.fm
+net.fm
+org.fm
+
+// fo : https://www.iana.org/domains/root/db/fo.html
+fo
+
+// fr : https://www.afnic.fr/ https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+fr
+asso.fr
+com.fr
+gouv.fr
+nom.fr
+prd.fr
+tm.fr
+// Other SLDs now selfmanaged out of AFNIC range. Former "domaines sectoriels", still registration suffixes
+avoues.fr
+cci.fr
+greta.fr
+huissier-justice.fr
+
+// ga : https://www.iana.org/domains/root/db/ga.html
+ga
+
+// gb : This registry is effectively dormant
+// Submitted by registry
+gb
+
+// gd : https://www.iana.org/domains/root/db/gd.html
+gd
+edu.gd
+gov.gd
+
+// ge : https://nic.ge/en/administrator/the-ge-domain-regulations
+// Confirmed by registry 2024-11-20
+ge
+com.ge
+edu.ge
+gov.ge
+net.ge
+org.ge
+pvt.ge
+school.ge
+
+// gf : https://www.iana.org/domains/root/db/gf.html
+gf
+
+// gg : https://www.channelisles.net/register-1/register-direct
+// Confirmed by registry 2013-11-28
+gg
+co.gg
+net.gg
+org.gg
+
+// gh : https://www.iana.org/domains/root/db/gh.html
+// https://www.nic.gh/
+// Although domains directly at second level are not possible at the moment,
+// they have been possible for some time and may come back.
+gh
+biz.gh
+com.gh
+edu.gh
+gov.gh
+mil.gh
+net.gh
+org.gh
+
+// gi : http://www.nic.gi/rules.html
+gi
+com.gi
+edu.gi
+gov.gi
+ltd.gi
+mod.gi
+org.gi
+
+// gl : https://www.iana.org/domains/root/db/gl.html
+// http://nic.gl
+gl
+co.gl
+com.gl
+edu.gl
+net.gl
+org.gl
+
+// gm : http://www.nic.gm/htmlpages%5Cgm-policy.htm
+gm
+
+// gn : http://psg.com/dns/gn/gn.txt
+// Submitted by registry
+gn
+ac.gn
+com.gn
+edu.gn
+gov.gn
+net.gn
+org.gn
+
+// gov : https://www.iana.org/domains/root/db/gov.html
+gov
+
+// gp : http://www.nic.gp/index.php?lang=en
+gp
+asso.gp
+com.gp
+edu.gp
+mobi.gp
+net.gp
+org.gp
+
+// gq : https://www.iana.org/domains/root/db/gq.html
+gq
+
+// gr : https://www.iana.org/domains/root/db/gr.html
+// Submitted by registry
+gr
+com.gr
+edu.gr
+gov.gr
+net.gr
+org.gr
+
+// gs : https://www.iana.org/domains/root/db/gs.html
+gs
+
+// gt : https://www.gt/sitio/registration_policy.php?lang=en
+gt
+com.gt
+edu.gt
+gob.gt
+ind.gt
+mil.gt
+net.gt
+org.gt
+
+// gu : http://gadao.gov.gu/register.html
+// University of Guam : https://www.uog.edu
+// Submitted by uognoc@triton.uog.edu
+gu
+com.gu
+edu.gu
+gov.gu
+guam.gu
+info.gu
+net.gu
+org.gu
+web.gu
+
+// gw : https://www.iana.org/domains/root/db/gw.html
+// gw : https://nic.gw/regras/
+gw
+
+// gy : https://www.iana.org/domains/root/db/gy.html
+// http://registry.gy/
+gy
+co.gy
+com.gy
+edu.gy
+gov.gy
+net.gy
+org.gy
+
+// hk : https://www.hkirc.hk
+// Submitted by registry
+hk
+com.hk
+edu.hk
+gov.hk
+idv.hk
+net.hk
+org.hk
+个人.hk
+個人.hk
+公司.hk
+政府.hk
+敎育.hk
+教育.hk
+箇人.hk
+組織.hk
+組织.hk
+網絡.hk
+網络.hk
+组織.hk
+组织.hk
+网絡.hk
+网络.hk
+
+// hm : https://www.iana.org/domains/root/db/hm.html
+hm
+
+// hn : https://www.iana.org/domains/root/db/hn.html
+hn
+com.hn
+edu.hn
+gob.hn
+mil.hn
+net.hn
+org.hn
+
+// hr : http://www.dns.hr/documents/pdf/HRTLD-regulations.pdf
+hr
+com.hr
+from.hr
+iz.hr
+name.hr
+
+// ht : http://www.nic.ht/info/charte.cfm
+ht
+adult.ht
+art.ht
+asso.ht
+com.ht
+coop.ht
+edu.ht
+firm.ht
+gouv.ht
+info.ht
+med.ht
+net.ht
+org.ht
+perso.ht
+pol.ht
+pro.ht
+rel.ht
+shop.ht
+
+// hu : https://www.iana.org/domains/root/db/hu.html
+// Confirmed by registry 2008-06-12
+hu
+2000.hu
+agrar.hu
+bolt.hu
+casino.hu
+city.hu
+co.hu
+erotica.hu
+erotika.hu
+film.hu
+forum.hu
+games.hu
+hotel.hu
+info.hu
+ingatlan.hu
+jogasz.hu
+konyvelo.hu
+lakas.hu
+media.hu
+news.hu
+org.hu
+priv.hu
+reklam.hu
+sex.hu
+shop.hu
+sport.hu
+suli.hu
+szex.hu
+tm.hu
+tozsde.hu
+utazas.hu
+video.hu
+
+// id : https://www.iana.org/domains/root/db/id.html
+id
+ac.id
+biz.id
+co.id
+desa.id
+go.id
+kop.id
+mil.id
+my.id
+net.id
+or.id
+ponpes.id
+sch.id
+web.id
+// xn--9tfky.id (.id, Und-Bali)
+ᬩᬮᬶ.id
+
+// ie : https://www.iana.org/domains/root/db/ie.html
+ie
+gov.ie
+
+// il : http://www.isoc.org.il/domains/
+// see also: https://en.isoc.org.il/il-cctld/registration-rules
+// ISOC-IL (operated by .il Registry)
+il
+ac.il
+co.il
+gov.il
+idf.il
+k12.il
+muni.il
+net.il
+org.il
+// xn--4dbrk0ce ("Israel", Hebrew) : IL
+ישראל
+// xn--4dbgdty6c.xn--4dbrk0ce.
+אקדמיה.ישראל
+// xn--5dbhl8d.xn--4dbrk0ce.
+ישוב.ישראל
+// xn--8dbq2a.xn--4dbrk0ce.
+צהל.ישראל
+// xn--hebda8b.xn--4dbrk0ce.
+ממשל.ישראל
+
+// im : https://www.nic.im/
+// Submitted by registry
+im
+ac.im
+co.im
+ltd.co.im
+plc.co.im
+com.im
+net.im
+org.im
+tt.im
+tv.im
+
+// in : https://www.iana.org/domains/root/db/in.html
+// see also: https://registry.in/policies
+// Please note, that nic.in is not an official eTLD, but used by most
+// government institutions.
+// Confirmed by Gaurav Kansal 2025-11-06
+in
+5g.in
+6g.in
+ac.in
+ai.in
+am.in
+bank.in
+bihar.in
+biz.in
+business.in
+ca.in
+cn.in
+co.in
+com.in
+coop.in
+cs.in
+delhi.in
+dr.in
+edu.in
+er.in
+fin.in
+firm.in
+gen.in
+gov.in
+gujarat.in
+ind.in
+info.in
+int.in
+internet.in
+io.in
+me.in
+mil.in
+net.in
+nic.in
+org.in
+pg.in
+post.in
+pro.in
+res.in
+travel.in
+tv.in
+uk.in
+up.in
+us.in
+
+// info : https://www.iana.org/domains/root/db/info.html
+info
+
+// int : https://www.iana.org/domains/root/db/int.html
+// Confirmed by registry 2008-06-18
+int
+eu.int
+
+// io : http://www.nic.io/rules.htm
+io
+co.io
+com.io
+edu.io
+gov.io
+mil.io
+net.io
+nom.io
+org.io
+
+// iq : http://www.cmc.iq/english/iq/iqregister1.htm
+iq
+com.iq
+edu.iq
+gov.iq
+mil.iq
+net.iq
+org.iq
+
+// ir : http://www.nic.ir/Terms_and_Conditions_ir,_Appendix_1_Domain_Rules
+// Also see http://www.nic.ir/Internationalized_Domain_Names
+// Two .ir entries added at request of , 2010-04-16
+ir
+ac.ir
+co.ir
+gov.ir
+id.ir
+net.ir
+org.ir
+sch.ir
+// xn--mgba3a4f16a.ir (.ir, Persian YEH)
+ایران.ir
+// xn--mgba3a4fra.ir (.ir, Arabic YEH)
+ايران.ir
+
+// is : http://www.isnic.is/domain/rules.php
+// Confirmed by registry 2024-11-17
+is
+
+// it : https://www.iana.org/domains/root/db/it.html
+// https://www.nic.it/
+it
+edu.it
+gov.it
+// Regions (3.3.1)
+// https://www.nic.it/en/manage-your-it/forms-and-docs -> "Assignment and Management of domain names"
+abr.it
+abruzzo.it
+aosta-valley.it
+aostavalley.it
+bas.it
+basilicata.it
+cal.it
+calabria.it
+cam.it
+campania.it
+emilia-romagna.it
+emiliaromagna.it
+emr.it
+friuli-v-giulia.it
+friuli-ve-giulia.it
+friuli-vegiulia.it
+friuli-venezia-giulia.it
+friuli-veneziagiulia.it
+friuli-vgiulia.it
+friuliv-giulia.it
+friulive-giulia.it
+friulivegiulia.it
+friulivenezia-giulia.it
+friuliveneziagiulia.it
+friulivgiulia.it
+fvg.it
+laz.it
+lazio.it
+lig.it
+liguria.it
+lom.it
+lombardia.it
+lombardy.it
+lucania.it
+mar.it
+marche.it
+mol.it
+molise.it
+piedmont.it
+piemonte.it
+pmn.it
+pug.it
+puglia.it
+sar.it
+sardegna.it
+sardinia.it
+sic.it
+sicilia.it
+sicily.it
+taa.it
+tos.it
+toscana.it
+trentin-sud-tirol.it
+trentin-süd-tirol.it
+trentin-sudtirol.it
+trentin-südtirol.it
+trentin-sued-tirol.it
+trentin-suedtirol.it
+trentino.it
+trentino-a-adige.it
+trentino-aadige.it
+trentino-alto-adige.it
+trentino-altoadige.it
+trentino-s-tirol.it
+trentino-stirol.it
+trentino-sud-tirol.it
+trentino-süd-tirol.it
+trentino-sudtirol.it
+trentino-südtirol.it
+trentino-sued-tirol.it
+trentino-suedtirol.it
+trentinoa-adige.it
+trentinoaadige.it
+trentinoalto-adige.it
+trentinoaltoadige.it
+trentinos-tirol.it
+trentinostirol.it
+trentinosud-tirol.it
+trentinosüd-tirol.it
+trentinosudtirol.it
+trentinosüdtirol.it
+trentinosued-tirol.it
+trentinosuedtirol.it
+trentinsud-tirol.it
+trentinsüd-tirol.it
+trentinsudtirol.it
+trentinsüdtirol.it
+trentinsued-tirol.it
+trentinsuedtirol.it
+tuscany.it
+umb.it
+umbria.it
+val-d-aosta.it
+val-daosta.it
+vald-aosta.it
+valdaosta.it
+valle-aosta.it
+valle-d-aosta.it
+valle-daosta.it
+valleaosta.it
+valled-aosta.it
+valledaosta.it
+vallee-aoste.it
+vallée-aoste.it
+vallee-d-aoste.it
+vallée-d-aoste.it
+valleeaoste.it
+valléeaoste.it
+valleedaoste.it
+valléedaoste.it
+vao.it
+vda.it
+ven.it
+veneto.it
+// Provinces (3.3.2)
+ag.it
+agrigento.it
+al.it
+alessandria.it
+alto-adige.it
+altoadige.it
+an.it
+ancona.it
+andria-barletta-trani.it
+andria-trani-barletta.it
+andriabarlettatrani.it
+andriatranibarletta.it
+ao.it
+aosta.it
+aoste.it
+ap.it
+aq.it
+aquila.it
+ar.it
+arezzo.it
+ascoli-piceno.it
+ascolipiceno.it
+asti.it
+at.it
+av.it
+avellino.it
+ba.it
+balsan.it
+balsan-sudtirol.it
+balsan-südtirol.it
+balsan-suedtirol.it
+bari.it
+barletta-trani-andria.it
+barlettatraniandria.it
+belluno.it
+benevento.it
+bergamo.it
+bg.it
+bi.it
+biella.it
+bl.it
+bn.it
+bo.it
+bologna.it
+bolzano.it
+bolzano-altoadige.it
+bozen.it
+bozen-sudtirol.it
+bozen-südtirol.it
+bozen-suedtirol.it
+br.it
+brescia.it
+brindisi.it
+bs.it
+bt.it
+bulsan.it
+bulsan-sudtirol.it
+bulsan-südtirol.it
+bulsan-suedtirol.it
+bz.it
+ca.it
+cagliari.it
+caltanissetta.it
+campidano-medio.it
+campidanomedio.it
+campobasso.it
+carbonia-iglesias.it
+carboniaiglesias.it
+carrara-massa.it
+carraramassa.it
+caserta.it
+catania.it
+catanzaro.it
+cb.it
+ce.it
+cesena-forli.it
+cesena-forlì.it
+cesenaforli.it
+cesenaforlì.it
+ch.it
+chieti.it
+ci.it
+cl.it
+cn.it
+co.it
+como.it
+cosenza.it
+cr.it
+cremona.it
+crotone.it
+cs.it
+ct.it
+cuneo.it
+cz.it
+dell-ogliastra.it
+dellogliastra.it
+en.it
+enna.it
+fc.it
+fe.it
+fermo.it
+ferrara.it
+fg.it
+fi.it
+firenze.it
+florence.it
+fm.it
+foggia.it
+forli-cesena.it
+forlì-cesena.it
+forlicesena.it
+forlìcesena.it
+fr.it
+frosinone.it
+ge.it
+genoa.it
+genova.it
+go.it
+gorizia.it
+gr.it
+grosseto.it
+iglesias-carbonia.it
+iglesiascarbonia.it
+im.it
+imperia.it
+is.it
+isernia.it
+kr.it
+la-spezia.it
+laquila.it
+laspezia.it
+latina.it
+lc.it
+le.it
+lecce.it
+lecco.it
+li.it
+livorno.it
+lo.it
+lodi.it
+lt.it
+lu.it
+lucca.it
+macerata.it
+mantova.it
+massa-carrara.it
+massacarrara.it
+matera.it
+mb.it
+mc.it
+me.it
+medio-campidano.it
+mediocampidano.it
+messina.it
+mi.it
+milan.it
+milano.it
+mn.it
+mo.it
+modena.it
+monza.it
+monza-brianza.it
+monza-e-della-brianza.it
+monzabrianza.it
+monzaebrianza.it
+monzaedellabrianza.it
+ms.it
+mt.it
+na.it
+naples.it
+napoli.it
+no.it
+novara.it
+nu.it
+nuoro.it
+og.it
+ogliastra.it
+olbia-tempio.it
+olbiatempio.it
+or.it
+oristano.it
+ot.it
+pa.it
+padova.it
+padua.it
+palermo.it
+parma.it
+pavia.it
+pc.it
+pd.it
+pe.it
+perugia.it
+pesaro-urbino.it
+pesarourbino.it
+pescara.it
+pg.it
+pi.it
+piacenza.it
+pisa.it
+pistoia.it
+pn.it
+po.it
+pordenone.it
+potenza.it
+pr.it
+prato.it
+pt.it
+pu.it
+pv.it
+pz.it
+ra.it
+ragusa.it
+ravenna.it
+rc.it
+re.it
+reggio-calabria.it
+reggio-emilia.it
+reggiocalabria.it
+reggioemilia.it
+rg.it
+ri.it
+rieti.it
+rimini.it
+rm.it
+rn.it
+ro.it
+roma.it
+rome.it
+rovigo.it
+sa.it
+salerno.it
+sassari.it
+savona.it
+si.it
+siena.it
+siracusa.it
+so.it
+sondrio.it
+sp.it
+sr.it
+ss.it
+südtirol.it
+suedtirol.it
+sv.it
+ta.it
+taranto.it
+te.it
+tempio-olbia.it
+tempioolbia.it
+teramo.it
+terni.it
+tn.it
+to.it
+torino.it
+tp.it
+tr.it
+trani-andria-barletta.it
+trani-barletta-andria.it
+traniandriabarletta.it
+tranibarlettaandria.it
+trapani.it
+trento.it
+treviso.it
+trieste.it
+ts.it
+turin.it
+tv.it
+ud.it
+udine.it
+urbino-pesaro.it
+urbinopesaro.it
+va.it
+varese.it
+vb.it
+vc.it
+ve.it
+venezia.it
+venice.it
+verbania.it
+vercelli.it
+verona.it
+vi.it
+vibo-valentia.it
+vibovalentia.it
+vicenza.it
+viterbo.it
+vr.it
+vs.it
+vt.it
+vv.it
+
+// je : https://www.iana.org/domains/root/db/je.html
+// Confirmed by registry 2013-11-28
+je
+co.je
+net.je
+org.je
+
+// jm : http://www.com.jm/register.html
+*.jm
+
+// jo : https://www.dns.jo/JoFamily.aspx
+// Confirmed by registry 2024-11-17
+jo
+agri.jo
+ai.jo
+com.jo
+edu.jo
+eng.jo
+fm.jo
+gov.jo
+mil.jo
+net.jo
+org.jo
+per.jo
+phd.jo
+sch.jo
+tv.jo
+
+// jobs : https://www.iana.org/domains/root/db/jobs.html
+jobs
+
+// jp : https://www.iana.org/domains/root/db/jp.html
+// http://jprs.co.jp/en/jpdomain.html
+// Confirmed by registry 2024-11-22
+jp
+// jp organizational type names
+ac.jp
+ad.jp
+co.jp
+ed.jp
+go.jp
+gr.jp
+lg.jp
+ne.jp
+or.jp
+// jp prefecture type names
+aichi.jp
+akita.jp
+aomori.jp
+chiba.jp
+ehime.jp
+fukui.jp
+fukuoka.jp
+fukushima.jp
+gifu.jp
+gunma.jp
+hiroshima.jp
+hokkaido.jp
+hyogo.jp
+ibaraki.jp
+ishikawa.jp
+iwate.jp
+kagawa.jp
+kagoshima.jp
+kanagawa.jp
+kochi.jp
+kumamoto.jp
+kyoto.jp
+mie.jp
+miyagi.jp
+miyazaki.jp
+nagano.jp
+nagasaki.jp
+nara.jp
+niigata.jp
+oita.jp
+okayama.jp
+okinawa.jp
+osaka.jp
+saga.jp
+saitama.jp
+shiga.jp
+shimane.jp
+shizuoka.jp
+tochigi.jp
+tokushima.jp
+tokyo.jp
+tottori.jp
+toyama.jp
+wakayama.jp
+yamagata.jp
+yamaguchi.jp
+yamanashi.jp
+三重.jp
+京都.jp
+佐賀.jp
+兵庫.jp
+北海道.jp
+千葉.jp
+和歌山.jp
+埼玉.jp
+大分.jp
+大阪.jp
+奈良.jp
+宮城.jp
+宮崎.jp
+富山.jp
+山口.jp
+山形.jp
+山梨.jp
+岐阜.jp
+岡山.jp
+岩手.jp
+島根.jp
+広島.jp
+徳島.jp
+愛媛.jp
+愛知.jp
+新潟.jp
+東京.jp
+栃木.jp
+沖縄.jp
+滋賀.jp
+熊本.jp
+石川.jp
+神奈川.jp
+福井.jp
+福岡.jp
+福島.jp
+秋田.jp
+群馬.jp
+茨城.jp
+長崎.jp
+長野.jp
+青森.jp
+静岡.jp
+香川.jp
+高知.jp
+鳥取.jp
+鹿児島.jp
+// jp geographic type names
+// http://jprs.jp/doc/rule/saisoku-1.html
+// 2024-11-22: JPRS confirmed that jp geographic type names no longer accept new registrations.
+// Once all existing registrations expire (marking full discontinuation), these suffixes
+// will be removed from the PSL.
+*.kawasaki.jp
+!city.kawasaki.jp
+*.kitakyushu.jp
+!city.kitakyushu.jp
+*.kobe.jp
+!city.kobe.jp
+*.nagoya.jp
+!city.nagoya.jp
+*.sapporo.jp
+!city.sapporo.jp
+*.sendai.jp
+!city.sendai.jp
+*.yokohama.jp
+!city.yokohama.jp
+// 4th level registration
+aisai.aichi.jp
+ama.aichi.jp
+anjo.aichi.jp
+asuke.aichi.jp
+chiryu.aichi.jp
+chita.aichi.jp
+fuso.aichi.jp
+gamagori.aichi.jp
+handa.aichi.jp
+hazu.aichi.jp
+hekinan.aichi.jp
+higashiura.aichi.jp
+ichinomiya.aichi.jp
+inazawa.aichi.jp
+inuyama.aichi.jp
+isshiki.aichi.jp
+iwakura.aichi.jp
+kanie.aichi.jp
+kariya.aichi.jp
+kasugai.aichi.jp
+kira.aichi.jp
+kiyosu.aichi.jp
+komaki.aichi.jp
+konan.aichi.jp
+kota.aichi.jp
+mihama.aichi.jp
+miyoshi.aichi.jp
+nishio.aichi.jp
+nisshin.aichi.jp
+obu.aichi.jp
+oguchi.aichi.jp
+oharu.aichi.jp
+okazaki.aichi.jp
+owariasahi.aichi.jp
+seto.aichi.jp
+shikatsu.aichi.jp
+shinshiro.aichi.jp
+shitara.aichi.jp
+tahara.aichi.jp
+takahama.aichi.jp
+tobishima.aichi.jp
+toei.aichi.jp
+togo.aichi.jp
+tokai.aichi.jp
+tokoname.aichi.jp
+toyoake.aichi.jp
+toyohashi.aichi.jp
+toyokawa.aichi.jp
+toyone.aichi.jp
+toyota.aichi.jp
+tsushima.aichi.jp
+yatomi.aichi.jp
+akita.akita.jp
+daisen.akita.jp
+fujisato.akita.jp
+gojome.akita.jp
+hachirogata.akita.jp
+happou.akita.jp
+higashinaruse.akita.jp
+honjo.akita.jp
+honjyo.akita.jp
+ikawa.akita.jp
+kamikoani.akita.jp
+kamioka.akita.jp
+katagami.akita.jp
+kazuno.akita.jp
+kitaakita.akita.jp
+kosaka.akita.jp
+kyowa.akita.jp
+misato.akita.jp
+mitane.akita.jp
+moriyoshi.akita.jp
+nikaho.akita.jp
+noshiro.akita.jp
+odate.akita.jp
+oga.akita.jp
+ogata.akita.jp
+semboku.akita.jp
+yokote.akita.jp
+yurihonjo.akita.jp
+aomori.aomori.jp
+gonohe.aomori.jp
+hachinohe.aomori.jp
+hashikami.aomori.jp
+hiranai.aomori.jp
+hirosaki.aomori.jp
+itayanagi.aomori.jp
+kuroishi.aomori.jp
+misawa.aomori.jp
+mutsu.aomori.jp
+nakadomari.aomori.jp
+noheji.aomori.jp
+oirase.aomori.jp
+owani.aomori.jp
+rokunohe.aomori.jp
+sannohe.aomori.jp
+shichinohe.aomori.jp
+shingo.aomori.jp
+takko.aomori.jp
+towada.aomori.jp
+tsugaru.aomori.jp
+tsuruta.aomori.jp
+abiko.chiba.jp
+asahi.chiba.jp
+chonan.chiba.jp
+chosei.chiba.jp
+choshi.chiba.jp
+chuo.chiba.jp
+funabashi.chiba.jp
+futtsu.chiba.jp
+hanamigawa.chiba.jp
+ichihara.chiba.jp
+ichikawa.chiba.jp
+ichinomiya.chiba.jp
+inzai.chiba.jp
+isumi.chiba.jp
+kamagaya.chiba.jp
+kamogawa.chiba.jp
+kashiwa.chiba.jp
+katori.chiba.jp
+katsuura.chiba.jp
+kimitsu.chiba.jp
+kisarazu.chiba.jp
+kozaki.chiba.jp
+kujukuri.chiba.jp
+kyonan.chiba.jp
+matsudo.chiba.jp
+midori.chiba.jp
+mihama.chiba.jp
+minamiboso.chiba.jp
+mobara.chiba.jp
+mutsuzawa.chiba.jp
+nagara.chiba.jp
+nagareyama.chiba.jp
+narashino.chiba.jp
+narita.chiba.jp
+noda.chiba.jp
+oamishirasato.chiba.jp
+omigawa.chiba.jp
+onjuku.chiba.jp
+otaki.chiba.jp
+sakae.chiba.jp
+sakura.chiba.jp
+shimofusa.chiba.jp
+shirako.chiba.jp
+shiroi.chiba.jp
+shisui.chiba.jp
+sodegaura.chiba.jp
+sosa.chiba.jp
+tako.chiba.jp
+tateyama.chiba.jp
+togane.chiba.jp
+tohnosho.chiba.jp
+tomisato.chiba.jp
+urayasu.chiba.jp
+yachimata.chiba.jp
+yachiyo.chiba.jp
+yokaichiba.chiba.jp
+yokoshibahikari.chiba.jp
+yotsukaido.chiba.jp
+ainan.ehime.jp
+honai.ehime.jp
+ikata.ehime.jp
+imabari.ehime.jp
+iyo.ehime.jp
+kamijima.ehime.jp
+kihoku.ehime.jp
+kumakogen.ehime.jp
+masaki.ehime.jp
+matsuno.ehime.jp
+matsuyama.ehime.jp
+namikata.ehime.jp
+niihama.ehime.jp
+ozu.ehime.jp
+saijo.ehime.jp
+seiyo.ehime.jp
+shikokuchuo.ehime.jp
+tobe.ehime.jp
+toon.ehime.jp
+uchiko.ehime.jp
+uwajima.ehime.jp
+yawatahama.ehime.jp
+echizen.fukui.jp
+eiheiji.fukui.jp
+fukui.fukui.jp
+ikeda.fukui.jp
+katsuyama.fukui.jp
+mihama.fukui.jp
+minamiechizen.fukui.jp
+obama.fukui.jp
+ohi.fukui.jp
+ono.fukui.jp
+sabae.fukui.jp
+sakai.fukui.jp
+takahama.fukui.jp
+tsuruga.fukui.jp
+wakasa.fukui.jp
+ashiya.fukuoka.jp
+buzen.fukuoka.jp
+chikugo.fukuoka.jp
+chikuho.fukuoka.jp
+chikujo.fukuoka.jp
+chikushino.fukuoka.jp
+chikuzen.fukuoka.jp
+chuo.fukuoka.jp
+dazaifu.fukuoka.jp
+fukuchi.fukuoka.jp
+hakata.fukuoka.jp
+higashi.fukuoka.jp
+hirokawa.fukuoka.jp
+hisayama.fukuoka.jp
+iizuka.fukuoka.jp
+inatsuki.fukuoka.jp
+kaho.fukuoka.jp
+kasuga.fukuoka.jp
+kasuya.fukuoka.jp
+kawara.fukuoka.jp
+keisen.fukuoka.jp
+koga.fukuoka.jp
+kurate.fukuoka.jp
+kurogi.fukuoka.jp
+kurume.fukuoka.jp
+minami.fukuoka.jp
+miyako.fukuoka.jp
+miyama.fukuoka.jp
+miyawaka.fukuoka.jp
+mizumaki.fukuoka.jp
+munakata.fukuoka.jp
+nakagawa.fukuoka.jp
+nakama.fukuoka.jp
+nishi.fukuoka.jp
+nogata.fukuoka.jp
+ogori.fukuoka.jp
+okagaki.fukuoka.jp
+okawa.fukuoka.jp
+oki.fukuoka.jp
+omuta.fukuoka.jp
+onga.fukuoka.jp
+onojo.fukuoka.jp
+oto.fukuoka.jp
+saigawa.fukuoka.jp
+sasaguri.fukuoka.jp
+shingu.fukuoka.jp
+shinyoshitomi.fukuoka.jp
+shonai.fukuoka.jp
+soeda.fukuoka.jp
+sue.fukuoka.jp
+tachiarai.fukuoka.jp
+tagawa.fukuoka.jp
+takata.fukuoka.jp
+toho.fukuoka.jp
+toyotsu.fukuoka.jp
+tsuiki.fukuoka.jp
+ukiha.fukuoka.jp
+umi.fukuoka.jp
+usui.fukuoka.jp
+yamada.fukuoka.jp
+yame.fukuoka.jp
+yanagawa.fukuoka.jp
+yukuhashi.fukuoka.jp
+aizubange.fukushima.jp
+aizumisato.fukushima.jp
+aizuwakamatsu.fukushima.jp
+asakawa.fukushima.jp
+bandai.fukushima.jp
+date.fukushima.jp
+fukushima.fukushima.jp
+furudono.fukushima.jp
+futaba.fukushima.jp
+hanawa.fukushima.jp
+higashi.fukushima.jp
+hirata.fukushima.jp
+hirono.fukushima.jp
+iitate.fukushima.jp
+inawashiro.fukushima.jp
+ishikawa.fukushima.jp
+iwaki.fukushima.jp
+izumizaki.fukushima.jp
+kagamiishi.fukushima.jp
+kaneyama.fukushima.jp
+kawamata.fukushima.jp
+kitakata.fukushima.jp
+kitashiobara.fukushima.jp
+koori.fukushima.jp
+koriyama.fukushima.jp
+kunimi.fukushima.jp
+miharu.fukushima.jp
+mishima.fukushima.jp
+namie.fukushima.jp
+nango.fukushima.jp
+nishiaizu.fukushima.jp
+nishigo.fukushima.jp
+okuma.fukushima.jp
+omotego.fukushima.jp
+ono.fukushima.jp
+otama.fukushima.jp
+samegawa.fukushima.jp
+shimogo.fukushima.jp
+shirakawa.fukushima.jp
+showa.fukushima.jp
+soma.fukushima.jp
+sukagawa.fukushima.jp
+taishin.fukushima.jp
+tamakawa.fukushima.jp
+tanagura.fukushima.jp
+tenei.fukushima.jp
+yabuki.fukushima.jp
+yamato.fukushima.jp
+yamatsuri.fukushima.jp
+yanaizu.fukushima.jp
+yugawa.fukushima.jp
+anpachi.gifu.jp
+ena.gifu.jp
+gifu.gifu.jp
+ginan.gifu.jp
+godo.gifu.jp
+gujo.gifu.jp
+hashima.gifu.jp
+hichiso.gifu.jp
+hida.gifu.jp
+higashishirakawa.gifu.jp
+ibigawa.gifu.jp
+ikeda.gifu.jp
+kakamigahara.gifu.jp
+kani.gifu.jp
+kasahara.gifu.jp
+kasamatsu.gifu.jp
+kawaue.gifu.jp
+kitagata.gifu.jp
+mino.gifu.jp
+minokamo.gifu.jp
+mitake.gifu.jp
+mizunami.gifu.jp
+motosu.gifu.jp
+nakatsugawa.gifu.jp
+ogaki.gifu.jp
+sakahogi.gifu.jp
+seki.gifu.jp
+sekigahara.gifu.jp
+shirakawa.gifu.jp
+tajimi.gifu.jp
+takayama.gifu.jp
+tarui.gifu.jp
+toki.gifu.jp
+tomika.gifu.jp
+wanouchi.gifu.jp
+yamagata.gifu.jp
+yaotsu.gifu.jp
+yoro.gifu.jp
+annaka.gunma.jp
+chiyoda.gunma.jp
+fujioka.gunma.jp
+higashiagatsuma.gunma.jp
+isesaki.gunma.jp
+itakura.gunma.jp
+kanna.gunma.jp
+kanra.gunma.jp
+katashina.gunma.jp
+kawaba.gunma.jp
+kiryu.gunma.jp
+kusatsu.gunma.jp
+maebashi.gunma.jp
+meiwa.gunma.jp
+midori.gunma.jp
+minakami.gunma.jp
+naganohara.gunma.jp
+nakanojo.gunma.jp
+nanmoku.gunma.jp
+numata.gunma.jp
+oizumi.gunma.jp
+ora.gunma.jp
+ota.gunma.jp
+shibukawa.gunma.jp
+shimonita.gunma.jp
+shinto.gunma.jp
+showa.gunma.jp
+takasaki.gunma.jp
+takayama.gunma.jp
+tamamura.gunma.jp
+tatebayashi.gunma.jp
+tomioka.gunma.jp
+tsukiyono.gunma.jp
+tsumagoi.gunma.jp
+ueno.gunma.jp
+yoshioka.gunma.jp
+asaminami.hiroshima.jp
+daiwa.hiroshima.jp
+etajima.hiroshima.jp
+fuchu.hiroshima.jp
+fukuyama.hiroshima.jp
+hatsukaichi.hiroshima.jp
+higashihiroshima.hiroshima.jp
+hongo.hiroshima.jp
+jinsekikogen.hiroshima.jp
+kaita.hiroshima.jp
+kui.hiroshima.jp
+kumano.hiroshima.jp
+kure.hiroshima.jp
+mihara.hiroshima.jp
+miyoshi.hiroshima.jp
+naka.hiroshima.jp
+onomichi.hiroshima.jp
+osakikamijima.hiroshima.jp
+otake.hiroshima.jp
+saka.hiroshima.jp
+sera.hiroshima.jp
+seranishi.hiroshima.jp
+shinichi.hiroshima.jp
+shobara.hiroshima.jp
+takehara.hiroshima.jp
+abashiri.hokkaido.jp
+abira.hokkaido.jp
+aibetsu.hokkaido.jp
+akabira.hokkaido.jp
+akkeshi.hokkaido.jp
+asahikawa.hokkaido.jp
+ashibetsu.hokkaido.jp
+ashoro.hokkaido.jp
+assabu.hokkaido.jp
+atsuma.hokkaido.jp
+bibai.hokkaido.jp
+biei.hokkaido.jp
+bifuka.hokkaido.jp
+bihoro.hokkaido.jp
+biratori.hokkaido.jp
+chippubetsu.hokkaido.jp
+chitose.hokkaido.jp
+date.hokkaido.jp
+ebetsu.hokkaido.jp
+embetsu.hokkaido.jp
+eniwa.hokkaido.jp
+erimo.hokkaido.jp
+esan.hokkaido.jp
+esashi.hokkaido.jp
+fukagawa.hokkaido.jp
+fukushima.hokkaido.jp
+furano.hokkaido.jp
+furubira.hokkaido.jp
+haboro.hokkaido.jp
+hakodate.hokkaido.jp
+hamatonbetsu.hokkaido.jp
+hidaka.hokkaido.jp
+higashikagura.hokkaido.jp
+higashikawa.hokkaido.jp
+hiroo.hokkaido.jp
+hokuryu.hokkaido.jp
+hokuto.hokkaido.jp
+honbetsu.hokkaido.jp
+horokanai.hokkaido.jp
+horonobe.hokkaido.jp
+ikeda.hokkaido.jp
+imakane.hokkaido.jp
+ishikari.hokkaido.jp
+iwamizawa.hokkaido.jp
+iwanai.hokkaido.jp
+kamifurano.hokkaido.jp
+kamikawa.hokkaido.jp
+kamishihoro.hokkaido.jp
+kamisunagawa.hokkaido.jp
+kamoenai.hokkaido.jp
+kayabe.hokkaido.jp
+kembuchi.hokkaido.jp
+kikonai.hokkaido.jp
+kimobetsu.hokkaido.jp
+kitahiroshima.hokkaido.jp
+kitami.hokkaido.jp
+kiyosato.hokkaido.jp
+koshimizu.hokkaido.jp
+kunneppu.hokkaido.jp
+kuriyama.hokkaido.jp
+kuromatsunai.hokkaido.jp
+kushiro.hokkaido.jp
+kutchan.hokkaido.jp
+kyowa.hokkaido.jp
+mashike.hokkaido.jp
+matsumae.hokkaido.jp
+mikasa.hokkaido.jp
+minamifurano.hokkaido.jp
+mombetsu.hokkaido.jp
+moseushi.hokkaido.jp
+mukawa.hokkaido.jp
+muroran.hokkaido.jp
+naie.hokkaido.jp
+nakagawa.hokkaido.jp
+nakasatsunai.hokkaido.jp
+nakatombetsu.hokkaido.jp
+nanae.hokkaido.jp
+nanporo.hokkaido.jp
+nayoro.hokkaido.jp
+nemuro.hokkaido.jp
+niikappu.hokkaido.jp
+niki.hokkaido.jp
+nishiokoppe.hokkaido.jp
+noboribetsu.hokkaido.jp
+numata.hokkaido.jp
+obihiro.hokkaido.jp
+obira.hokkaido.jp
+oketo.hokkaido.jp
+okoppe.hokkaido.jp
+otaru.hokkaido.jp
+otobe.hokkaido.jp
+otofuke.hokkaido.jp
+otoineppu.hokkaido.jp
+oumu.hokkaido.jp
+ozora.hokkaido.jp
+pippu.hokkaido.jp
+rankoshi.hokkaido.jp
+rebun.hokkaido.jp
+rikubetsu.hokkaido.jp
+rishiri.hokkaido.jp
+rishirifuji.hokkaido.jp
+saroma.hokkaido.jp
+sarufutsu.hokkaido.jp
+shakotan.hokkaido.jp
+shari.hokkaido.jp
+shibecha.hokkaido.jp
+shibetsu.hokkaido.jp
+shikabe.hokkaido.jp
+shikaoi.hokkaido.jp
+shimamaki.hokkaido.jp
+shimizu.hokkaido.jp
+shimokawa.hokkaido.jp
+shinshinotsu.hokkaido.jp
+shintoku.hokkaido.jp
+shiranuka.hokkaido.jp
+shiraoi.hokkaido.jp
+shiriuchi.hokkaido.jp
+sobetsu.hokkaido.jp
+sunagawa.hokkaido.jp
+taiki.hokkaido.jp
+takasu.hokkaido.jp
+takikawa.hokkaido.jp
+takinoue.hokkaido.jp
+teshikaga.hokkaido.jp
+tobetsu.hokkaido.jp
+tohma.hokkaido.jp
+tomakomai.hokkaido.jp
+tomari.hokkaido.jp
+toya.hokkaido.jp
+toyako.hokkaido.jp
+toyotomi.hokkaido.jp
+toyoura.hokkaido.jp
+tsubetsu.hokkaido.jp
+tsukigata.hokkaido.jp
+urakawa.hokkaido.jp
+urausu.hokkaido.jp
+uryu.hokkaido.jp
+utashinai.hokkaido.jp
+wakkanai.hokkaido.jp
+wassamu.hokkaido.jp
+yakumo.hokkaido.jp
+yoichi.hokkaido.jp
+aioi.hyogo.jp
+akashi.hyogo.jp
+ako.hyogo.jp
+amagasaki.hyogo.jp
+aogaki.hyogo.jp
+asago.hyogo.jp
+ashiya.hyogo.jp
+awaji.hyogo.jp
+fukusaki.hyogo.jp
+goshiki.hyogo.jp
+harima.hyogo.jp
+himeji.hyogo.jp
+ichikawa.hyogo.jp
+inagawa.hyogo.jp
+itami.hyogo.jp
+kakogawa.hyogo.jp
+kamigori.hyogo.jp
+kamikawa.hyogo.jp
+kasai.hyogo.jp
+kasuga.hyogo.jp
+kawanishi.hyogo.jp
+miki.hyogo.jp
+minamiawaji.hyogo.jp
+nishinomiya.hyogo.jp
+nishiwaki.hyogo.jp
+ono.hyogo.jp
+sanda.hyogo.jp
+sannan.hyogo.jp
+sasayama.hyogo.jp
+sayo.hyogo.jp
+shingu.hyogo.jp
+shinonsen.hyogo.jp
+shiso.hyogo.jp
+sumoto.hyogo.jp
+taishi.hyogo.jp
+taka.hyogo.jp
+takarazuka.hyogo.jp
+takasago.hyogo.jp
+takino.hyogo.jp
+tamba.hyogo.jp
+tatsuno.hyogo.jp
+toyooka.hyogo.jp
+yabu.hyogo.jp
+yashiro.hyogo.jp
+yoka.hyogo.jp
+yokawa.hyogo.jp
+ami.ibaraki.jp
+asahi.ibaraki.jp
+bando.ibaraki.jp
+chikusei.ibaraki.jp
+daigo.ibaraki.jp
+fujishiro.ibaraki.jp
+hitachi.ibaraki.jp
+hitachinaka.ibaraki.jp
+hitachiomiya.ibaraki.jp
+hitachiota.ibaraki.jp
+ibaraki.ibaraki.jp
+ina.ibaraki.jp
+inashiki.ibaraki.jp
+itako.ibaraki.jp
+iwama.ibaraki.jp
+joso.ibaraki.jp
+kamisu.ibaraki.jp
+kasama.ibaraki.jp
+kashima.ibaraki.jp
+kasumigaura.ibaraki.jp
+koga.ibaraki.jp
+miho.ibaraki.jp
+mito.ibaraki.jp
+moriya.ibaraki.jp
+naka.ibaraki.jp
+namegata.ibaraki.jp
+oarai.ibaraki.jp
+ogawa.ibaraki.jp
+omitama.ibaraki.jp
+ryugasaki.ibaraki.jp
+sakai.ibaraki.jp
+sakuragawa.ibaraki.jp
+shimodate.ibaraki.jp
+shimotsuma.ibaraki.jp
+shirosato.ibaraki.jp
+sowa.ibaraki.jp
+suifu.ibaraki.jp
+takahagi.ibaraki.jp
+tamatsukuri.ibaraki.jp
+tokai.ibaraki.jp
+tomobe.ibaraki.jp
+tone.ibaraki.jp
+toride.ibaraki.jp
+tsuchiura.ibaraki.jp
+tsukuba.ibaraki.jp
+uchihara.ibaraki.jp
+ushiku.ibaraki.jp
+yachiyo.ibaraki.jp
+yamagata.ibaraki.jp
+yawara.ibaraki.jp
+yuki.ibaraki.jp
+anamizu.ishikawa.jp
+hakui.ishikawa.jp
+hakusan.ishikawa.jp
+kaga.ishikawa.jp
+kahoku.ishikawa.jp
+kanazawa.ishikawa.jp
+kawakita.ishikawa.jp
+komatsu.ishikawa.jp
+nakanoto.ishikawa.jp
+nanao.ishikawa.jp
+nomi.ishikawa.jp
+nonoichi.ishikawa.jp
+noto.ishikawa.jp
+shika.ishikawa.jp
+suzu.ishikawa.jp
+tsubata.ishikawa.jp
+tsurugi.ishikawa.jp
+uchinada.ishikawa.jp
+wajima.ishikawa.jp
+fudai.iwate.jp
+fujisawa.iwate.jp
+hanamaki.iwate.jp
+hiraizumi.iwate.jp
+hirono.iwate.jp
+ichinohe.iwate.jp
+ichinoseki.iwate.jp
+iwaizumi.iwate.jp
+iwate.iwate.jp
+joboji.iwate.jp
+kamaishi.iwate.jp
+kanegasaki.iwate.jp
+karumai.iwate.jp
+kawai.iwate.jp
+kitakami.iwate.jp
+kuji.iwate.jp
+kunohe.iwate.jp
+kuzumaki.iwate.jp
+miyako.iwate.jp
+mizusawa.iwate.jp
+morioka.iwate.jp
+ninohe.iwate.jp
+noda.iwate.jp
+ofunato.iwate.jp
+oshu.iwate.jp
+otsuchi.iwate.jp
+rikuzentakata.iwate.jp
+shiwa.iwate.jp
+shizukuishi.iwate.jp
+sumita.iwate.jp
+tanohata.iwate.jp
+tono.iwate.jp
+yahaba.iwate.jp
+yamada.iwate.jp
+ayagawa.kagawa.jp
+higashikagawa.kagawa.jp
+kanonji.kagawa.jp
+kotohira.kagawa.jp
+manno.kagawa.jp
+marugame.kagawa.jp
+mitoyo.kagawa.jp
+naoshima.kagawa.jp
+sanuki.kagawa.jp
+tadotsu.kagawa.jp
+takamatsu.kagawa.jp
+tonosho.kagawa.jp
+uchinomi.kagawa.jp
+utazu.kagawa.jp
+zentsuji.kagawa.jp
+akune.kagoshima.jp
+amami.kagoshima.jp
+hioki.kagoshima.jp
+isa.kagoshima.jp
+isen.kagoshima.jp
+izumi.kagoshima.jp
+kagoshima.kagoshima.jp
+kanoya.kagoshima.jp
+kawanabe.kagoshima.jp
+kinko.kagoshima.jp
+kouyama.kagoshima.jp
+makurazaki.kagoshima.jp
+matsumoto.kagoshima.jp
+minamitane.kagoshima.jp
+nakatane.kagoshima.jp
+nishinoomote.kagoshima.jp
+satsumasendai.kagoshima.jp
+soo.kagoshima.jp
+tarumizu.kagoshima.jp
+yusui.kagoshima.jp
+aikawa.kanagawa.jp
+atsugi.kanagawa.jp
+ayase.kanagawa.jp
+chigasaki.kanagawa.jp
+ebina.kanagawa.jp
+fujisawa.kanagawa.jp
+hadano.kanagawa.jp
+hakone.kanagawa.jp
+hiratsuka.kanagawa.jp
+isehara.kanagawa.jp
+kaisei.kanagawa.jp
+kamakura.kanagawa.jp
+kiyokawa.kanagawa.jp
+matsuda.kanagawa.jp
+minamiashigara.kanagawa.jp
+miura.kanagawa.jp
+nakai.kanagawa.jp
+ninomiya.kanagawa.jp
+odawara.kanagawa.jp
+oi.kanagawa.jp
+oiso.kanagawa.jp
+sagamihara.kanagawa.jp
+samukawa.kanagawa.jp
+tsukui.kanagawa.jp
+yamakita.kanagawa.jp
+yamato.kanagawa.jp
+yokosuka.kanagawa.jp
+yugawara.kanagawa.jp
+zama.kanagawa.jp
+zushi.kanagawa.jp
+aki.kochi.jp
+geisei.kochi.jp
+hidaka.kochi.jp
+higashitsuno.kochi.jp
+ino.kochi.jp
+kagami.kochi.jp
+kami.kochi.jp
+kitagawa.kochi.jp
+kochi.kochi.jp
+mihara.kochi.jp
+motoyama.kochi.jp
+muroto.kochi.jp
+nahari.kochi.jp
+nakamura.kochi.jp
+nankoku.kochi.jp
+nishitosa.kochi.jp
+niyodogawa.kochi.jp
+ochi.kochi.jp
+okawa.kochi.jp
+otoyo.kochi.jp
+otsuki.kochi.jp
+sakawa.kochi.jp
+sukumo.kochi.jp
+susaki.kochi.jp
+tosa.kochi.jp
+tosashimizu.kochi.jp
+toyo.kochi.jp
+tsuno.kochi.jp
+umaji.kochi.jp
+yasuda.kochi.jp
+yusuhara.kochi.jp
+amakusa.kumamoto.jp
+arao.kumamoto.jp
+aso.kumamoto.jp
+choyo.kumamoto.jp
+gyokuto.kumamoto.jp
+kamiamakusa.kumamoto.jp
+kikuchi.kumamoto.jp
+kumamoto.kumamoto.jp
+mashiki.kumamoto.jp
+mifune.kumamoto.jp
+minamata.kumamoto.jp
+minamioguni.kumamoto.jp
+nagasu.kumamoto.jp
+nishihara.kumamoto.jp
+oguni.kumamoto.jp
+ozu.kumamoto.jp
+sumoto.kumamoto.jp
+takamori.kumamoto.jp
+uki.kumamoto.jp
+uto.kumamoto.jp
+yamaga.kumamoto.jp
+yamato.kumamoto.jp
+yatsushiro.kumamoto.jp
+ayabe.kyoto.jp
+fukuchiyama.kyoto.jp
+higashiyama.kyoto.jp
+ide.kyoto.jp
+ine.kyoto.jp
+joyo.kyoto.jp
+kameoka.kyoto.jp
+kamo.kyoto.jp
+kita.kyoto.jp
+kizu.kyoto.jp
+kumiyama.kyoto.jp
+kyotamba.kyoto.jp
+kyotanabe.kyoto.jp
+kyotango.kyoto.jp
+maizuru.kyoto.jp
+minami.kyoto.jp
+minamiyamashiro.kyoto.jp
+miyazu.kyoto.jp
+muko.kyoto.jp
+nagaokakyo.kyoto.jp
+nakagyo.kyoto.jp
+nantan.kyoto.jp
+oyamazaki.kyoto.jp
+sakyo.kyoto.jp
+seika.kyoto.jp
+tanabe.kyoto.jp
+uji.kyoto.jp
+ujitawara.kyoto.jp
+wazuka.kyoto.jp
+yamashina.kyoto.jp
+yawata.kyoto.jp
+asahi.mie.jp
+inabe.mie.jp
+ise.mie.jp
+kameyama.mie.jp
+kawagoe.mie.jp
+kiho.mie.jp
+kisosaki.mie.jp
+kiwa.mie.jp
+komono.mie.jp
+kumano.mie.jp
+kuwana.mie.jp
+matsusaka.mie.jp
+meiwa.mie.jp
+mihama.mie.jp
+minamiise.mie.jp
+misugi.mie.jp
+miyama.mie.jp
+nabari.mie.jp
+shima.mie.jp
+suzuka.mie.jp
+tado.mie.jp
+taiki.mie.jp
+taki.mie.jp
+tamaki.mie.jp
+toba.mie.jp
+tsu.mie.jp
+udono.mie.jp
+ureshino.mie.jp
+watarai.mie.jp
+yokkaichi.mie.jp
+furukawa.miyagi.jp
+higashimatsushima.miyagi.jp
+ishinomaki.miyagi.jp
+iwanuma.miyagi.jp
+kakuda.miyagi.jp
+kami.miyagi.jp
+kawasaki.miyagi.jp
+marumori.miyagi.jp
+matsushima.miyagi.jp
+minamisanriku.miyagi.jp
+misato.miyagi.jp
+murata.miyagi.jp
+natori.miyagi.jp
+ogawara.miyagi.jp
+ohira.miyagi.jp
+onagawa.miyagi.jp
+osaki.miyagi.jp
+rifu.miyagi.jp
+semine.miyagi.jp
+shibata.miyagi.jp
+shichikashuku.miyagi.jp
+shikama.miyagi.jp
+shiogama.miyagi.jp
+shiroishi.miyagi.jp
+tagajo.miyagi.jp
+taiwa.miyagi.jp
+tome.miyagi.jp
+tomiya.miyagi.jp
+wakuya.miyagi.jp
+watari.miyagi.jp
+yamamoto.miyagi.jp
+zao.miyagi.jp
+aya.miyazaki.jp
+ebino.miyazaki.jp
+gokase.miyazaki.jp
+hyuga.miyazaki.jp
+kadogawa.miyazaki.jp
+kawaminami.miyazaki.jp
+kijo.miyazaki.jp
+kitagawa.miyazaki.jp
+kitakata.miyazaki.jp
+kitaura.miyazaki.jp
+kobayashi.miyazaki.jp
+kunitomi.miyazaki.jp
+kushima.miyazaki.jp
+mimata.miyazaki.jp
+miyakonojo.miyazaki.jp
+miyazaki.miyazaki.jp
+morotsuka.miyazaki.jp
+nichinan.miyazaki.jp
+nishimera.miyazaki.jp
+nobeoka.miyazaki.jp
+saito.miyazaki.jp
+shiiba.miyazaki.jp
+shintomi.miyazaki.jp
+takaharu.miyazaki.jp
+takanabe.miyazaki.jp
+takazaki.miyazaki.jp
+tsuno.miyazaki.jp
+achi.nagano.jp
+agematsu.nagano.jp
+anan.nagano.jp
+aoki.nagano.jp
+asahi.nagano.jp
+azumino.nagano.jp
+chikuhoku.nagano.jp
+chikuma.nagano.jp
+chino.nagano.jp
+fujimi.nagano.jp
+hakuba.nagano.jp
+hara.nagano.jp
+hiraya.nagano.jp
+iida.nagano.jp
+iijima.nagano.jp
+iiyama.nagano.jp
+iizuna.nagano.jp
+ikeda.nagano.jp
+ikusaka.nagano.jp
+ina.nagano.jp
+karuizawa.nagano.jp
+kawakami.nagano.jp
+kiso.nagano.jp
+kisofukushima.nagano.jp
+kitaaiki.nagano.jp
+komagane.nagano.jp
+komoro.nagano.jp
+matsukawa.nagano.jp
+matsumoto.nagano.jp
+miasa.nagano.jp
+minamiaiki.nagano.jp
+minamimaki.nagano.jp
+minamiminowa.nagano.jp
+minowa.nagano.jp
+miyada.nagano.jp
+miyota.nagano.jp
+mochizuki.nagano.jp
+nagano.nagano.jp
+nagawa.nagano.jp
+nagiso.nagano.jp
+nakagawa.nagano.jp
+nakano.nagano.jp
+nozawaonsen.nagano.jp
+obuse.nagano.jp
+ogawa.nagano.jp
+okaya.nagano.jp
+omachi.nagano.jp
+omi.nagano.jp
+ookuwa.nagano.jp
+ooshika.nagano.jp
+otaki.nagano.jp
+otari.nagano.jp
+sakae.nagano.jp
+sakaki.nagano.jp
+saku.nagano.jp
+sakuho.nagano.jp
+shimosuwa.nagano.jp
+shinanomachi.nagano.jp
+shiojiri.nagano.jp
+suwa.nagano.jp
+suzaka.nagano.jp
+takagi.nagano.jp
+takamori.nagano.jp
+takayama.nagano.jp
+tateshina.nagano.jp
+tatsuno.nagano.jp
+togakushi.nagano.jp
+togura.nagano.jp
+tomi.nagano.jp
+ueda.nagano.jp
+wada.nagano.jp
+yamagata.nagano.jp
+yamanouchi.nagano.jp
+yasaka.nagano.jp
+yasuoka.nagano.jp
+chijiwa.nagasaki.jp
+futsu.nagasaki.jp
+goto.nagasaki.jp
+hasami.nagasaki.jp
+hirado.nagasaki.jp
+iki.nagasaki.jp
+isahaya.nagasaki.jp
+kawatana.nagasaki.jp
+kuchinotsu.nagasaki.jp
+matsuura.nagasaki.jp
+nagasaki.nagasaki.jp
+obama.nagasaki.jp
+omura.nagasaki.jp
+oseto.nagasaki.jp
+saikai.nagasaki.jp
+sasebo.nagasaki.jp
+seihi.nagasaki.jp
+shimabara.nagasaki.jp
+shinkamigoto.nagasaki.jp
+togitsu.nagasaki.jp
+tsushima.nagasaki.jp
+unzen.nagasaki.jp
+ando.nara.jp
+gose.nara.jp
+heguri.nara.jp
+higashiyoshino.nara.jp
+ikaruga.nara.jp
+ikoma.nara.jp
+kamikitayama.nara.jp
+kanmaki.nara.jp
+kashiba.nara.jp
+kashihara.nara.jp
+katsuragi.nara.jp
+kawai.nara.jp
+kawakami.nara.jp
+kawanishi.nara.jp
+koryo.nara.jp
+kurotaki.nara.jp
+mitsue.nara.jp
+miyake.nara.jp
+nara.nara.jp
+nosegawa.nara.jp
+oji.nara.jp
+ouda.nara.jp
+oyodo.nara.jp
+sakurai.nara.jp
+sango.nara.jp
+shimoichi.nara.jp
+shimokitayama.nara.jp
+shinjo.nara.jp
+soni.nara.jp
+takatori.nara.jp
+tawaramoto.nara.jp
+tenkawa.nara.jp
+tenri.nara.jp
+uda.nara.jp
+yamatokoriyama.nara.jp
+yamatotakada.nara.jp
+yamazoe.nara.jp
+yoshino.nara.jp
+aga.niigata.jp
+agano.niigata.jp
+gosen.niigata.jp
+itoigawa.niigata.jp
+izumozaki.niigata.jp
+joetsu.niigata.jp
+kamo.niigata.jp
+kariwa.niigata.jp
+kashiwazaki.niigata.jp
+minamiuonuma.niigata.jp
+mitsuke.niigata.jp
+muika.niigata.jp
+murakami.niigata.jp
+myoko.niigata.jp
+nagaoka.niigata.jp
+niigata.niigata.jp
+ojiya.niigata.jp
+omi.niigata.jp
+sado.niigata.jp
+sanjo.niigata.jp
+seiro.niigata.jp
+seirou.niigata.jp
+sekikawa.niigata.jp
+shibata.niigata.jp
+tagami.niigata.jp
+tainai.niigata.jp
+tochio.niigata.jp
+tokamachi.niigata.jp
+tsubame.niigata.jp
+tsunan.niigata.jp
+uonuma.niigata.jp
+yahiko.niigata.jp
+yoita.niigata.jp
+yuzawa.niigata.jp
+beppu.oita.jp
+bungoono.oita.jp
+bungotakada.oita.jp
+hasama.oita.jp
+hiji.oita.jp
+himeshima.oita.jp
+hita.oita.jp
+kamitsue.oita.jp
+kokonoe.oita.jp
+kuju.oita.jp
+kunisaki.oita.jp
+kusu.oita.jp
+oita.oita.jp
+saiki.oita.jp
+taketa.oita.jp
+tsukumi.oita.jp
+usa.oita.jp
+usuki.oita.jp
+yufu.oita.jp
+akaiwa.okayama.jp
+asakuchi.okayama.jp
+bizen.okayama.jp
+hayashima.okayama.jp
+ibara.okayama.jp
+kagamino.okayama.jp
+kasaoka.okayama.jp
+kibichuo.okayama.jp
+kumenan.okayama.jp
+kurashiki.okayama.jp
+maniwa.okayama.jp
+misaki.okayama.jp
+nagi.okayama.jp
+niimi.okayama.jp
+nishiawakura.okayama.jp
+okayama.okayama.jp
+satosho.okayama.jp
+setouchi.okayama.jp
+shinjo.okayama.jp
+shoo.okayama.jp
+soja.okayama.jp
+takahashi.okayama.jp
+tamano.okayama.jp
+tsuyama.okayama.jp
+wake.okayama.jp
+yakage.okayama.jp
+aguni.okinawa.jp
+ginowan.okinawa.jp
+ginoza.okinawa.jp
+gushikami.okinawa.jp
+haebaru.okinawa.jp
+higashi.okinawa.jp
+hirara.okinawa.jp
+iheya.okinawa.jp
+ishigaki.okinawa.jp
+ishikawa.okinawa.jp
+itoman.okinawa.jp
+izena.okinawa.jp
+kadena.okinawa.jp
+kin.okinawa.jp
+kitadaito.okinawa.jp
+kitanakagusuku.okinawa.jp
+kumejima.okinawa.jp
+kunigami.okinawa.jp
+minamidaito.okinawa.jp
+motobu.okinawa.jp
+nago.okinawa.jp
+naha.okinawa.jp
+nakagusuku.okinawa.jp
+nakijin.okinawa.jp
+nanjo.okinawa.jp
+nishihara.okinawa.jp
+ogimi.okinawa.jp
+okinawa.okinawa.jp
+onna.okinawa.jp
+shimoji.okinawa.jp
+taketomi.okinawa.jp
+tarama.okinawa.jp
+tokashiki.okinawa.jp
+tomigusuku.okinawa.jp
+tonaki.okinawa.jp
+urasoe.okinawa.jp
+uruma.okinawa.jp
+yaese.okinawa.jp
+yomitan.okinawa.jp
+yonabaru.okinawa.jp
+yonaguni.okinawa.jp
+zamami.okinawa.jp
+abeno.osaka.jp
+chihayaakasaka.osaka.jp
+chuo.osaka.jp
+daito.osaka.jp
+fujiidera.osaka.jp
+habikino.osaka.jp
+hannan.osaka.jp
+higashiosaka.osaka.jp
+higashisumiyoshi.osaka.jp
+higashiyodogawa.osaka.jp
+hirakata.osaka.jp
+ibaraki.osaka.jp
+ikeda.osaka.jp
+izumi.osaka.jp
+izumiotsu.osaka.jp
+izumisano.osaka.jp
+kadoma.osaka.jp
+kaizuka.osaka.jp
+kanan.osaka.jp
+kashiwara.osaka.jp
+katano.osaka.jp
+kawachinagano.osaka.jp
+kishiwada.osaka.jp
+kita.osaka.jp
+kumatori.osaka.jp
+matsubara.osaka.jp
+minato.osaka.jp
+minoh.osaka.jp
+misaki.osaka.jp
+moriguchi.osaka.jp
+neyagawa.osaka.jp
+nishi.osaka.jp
+nose.osaka.jp
+osakasayama.osaka.jp
+sakai.osaka.jp
+sayama.osaka.jp
+sennan.osaka.jp
+settsu.osaka.jp
+shijonawate.osaka.jp
+shimamoto.osaka.jp
+suita.osaka.jp
+tadaoka.osaka.jp
+taishi.osaka.jp
+tajiri.osaka.jp
+takaishi.osaka.jp
+takatsuki.osaka.jp
+tondabayashi.osaka.jp
+toyonaka.osaka.jp
+toyono.osaka.jp
+yao.osaka.jp
+ariake.saga.jp
+arita.saga.jp
+fukudomi.saga.jp
+genkai.saga.jp
+hamatama.saga.jp
+hizen.saga.jp
+imari.saga.jp
+kamimine.saga.jp
+kanzaki.saga.jp
+karatsu.saga.jp
+kashima.saga.jp
+kitagata.saga.jp
+kitahata.saga.jp
+kiyama.saga.jp
+kouhoku.saga.jp
+kyuragi.saga.jp
+nishiarita.saga.jp
+ogi.saga.jp
+omachi.saga.jp
+ouchi.saga.jp
+saga.saga.jp
+shiroishi.saga.jp
+taku.saga.jp
+tara.saga.jp
+tosu.saga.jp
+yoshinogari.saga.jp
+arakawa.saitama.jp
+asaka.saitama.jp
+chichibu.saitama.jp
+fujimi.saitama.jp
+fujimino.saitama.jp
+fukaya.saitama.jp
+hanno.saitama.jp
+hanyu.saitama.jp
+hasuda.saitama.jp
+hatogaya.saitama.jp
+hatoyama.saitama.jp
+hidaka.saitama.jp
+higashichichibu.saitama.jp
+higashimatsuyama.saitama.jp
+honjo.saitama.jp
+ina.saitama.jp
+iruma.saitama.jp
+iwatsuki.saitama.jp
+kamiizumi.saitama.jp
+kamikawa.saitama.jp
+kamisato.saitama.jp
+kasukabe.saitama.jp
+kawagoe.saitama.jp
+kawaguchi.saitama.jp
+kawajima.saitama.jp
+kazo.saitama.jp
+kitamoto.saitama.jp
+koshigaya.saitama.jp
+kounosu.saitama.jp
+kuki.saitama.jp
+kumagaya.saitama.jp
+matsubushi.saitama.jp
+minano.saitama.jp
+misato.saitama.jp
+miyashiro.saitama.jp
+miyoshi.saitama.jp
+moroyama.saitama.jp
+nagatoro.saitama.jp
+namegawa.saitama.jp
+niiza.saitama.jp
+ogano.saitama.jp
+ogawa.saitama.jp
+ogose.saitama.jp
+okegawa.saitama.jp
+omiya.saitama.jp
+otaki.saitama.jp
+ranzan.saitama.jp
+ryokami.saitama.jp
+saitama.saitama.jp
+sakado.saitama.jp
+satte.saitama.jp
+sayama.saitama.jp
+shiki.saitama.jp
+shiraoka.saitama.jp
+soka.saitama.jp
+sugito.saitama.jp
+toda.saitama.jp
+tokigawa.saitama.jp
+tokorozawa.saitama.jp
+tsurugashima.saitama.jp
+urawa.saitama.jp
+warabi.saitama.jp
+yashio.saitama.jp
+yokoze.saitama.jp
+yono.saitama.jp
+yorii.saitama.jp
+yoshida.saitama.jp
+yoshikawa.saitama.jp
+yoshimi.saitama.jp
+aisho.shiga.jp
+gamo.shiga.jp
+higashiomi.shiga.jp
+hikone.shiga.jp
+koka.shiga.jp
+konan.shiga.jp
+kosei.shiga.jp
+koto.shiga.jp
+kusatsu.shiga.jp
+maibara.shiga.jp
+moriyama.shiga.jp
+nagahama.shiga.jp
+nishiazai.shiga.jp
+notogawa.shiga.jp
+omihachiman.shiga.jp
+otsu.shiga.jp
+ritto.shiga.jp
+ryuoh.shiga.jp
+takashima.shiga.jp
+takatsuki.shiga.jp
+torahime.shiga.jp
+toyosato.shiga.jp
+yasu.shiga.jp
+akagi.shimane.jp
+ama.shimane.jp
+gotsu.shimane.jp
+hamada.shimane.jp
+higashiizumo.shimane.jp
+hikawa.shimane.jp
+hikimi.shimane.jp
+izumo.shimane.jp
+kakinoki.shimane.jp
+masuda.shimane.jp
+matsue.shimane.jp
+misato.shimane.jp
+nishinoshima.shimane.jp
+ohda.shimane.jp
+okinoshima.shimane.jp
+okuizumo.shimane.jp
+shimane.shimane.jp
+tamayu.shimane.jp
+tsuwano.shimane.jp
+unnan.shimane.jp
+yakumo.shimane.jp
+yasugi.shimane.jp
+yatsuka.shimane.jp
+arai.shizuoka.jp
+atami.shizuoka.jp
+fuji.shizuoka.jp
+fujieda.shizuoka.jp
+fujikawa.shizuoka.jp
+fujinomiya.shizuoka.jp
+fukuroi.shizuoka.jp
+gotemba.shizuoka.jp
+haibara.shizuoka.jp
+hamamatsu.shizuoka.jp
+higashiizu.shizuoka.jp
+ito.shizuoka.jp
+iwata.shizuoka.jp
+izu.shizuoka.jp
+izunokuni.shizuoka.jp
+kakegawa.shizuoka.jp
+kannami.shizuoka.jp
+kawanehon.shizuoka.jp
+kawazu.shizuoka.jp
+kikugawa.shizuoka.jp
+kosai.shizuoka.jp
+makinohara.shizuoka.jp
+matsuzaki.shizuoka.jp
+minamiizu.shizuoka.jp
+mishima.shizuoka.jp
+morimachi.shizuoka.jp
+nishiizu.shizuoka.jp
+numazu.shizuoka.jp
+omaezaki.shizuoka.jp
+shimada.shizuoka.jp
+shimizu.shizuoka.jp
+shimoda.shizuoka.jp
+shizuoka.shizuoka.jp
+susono.shizuoka.jp
+yaizu.shizuoka.jp
+yoshida.shizuoka.jp
+ashikaga.tochigi.jp
+bato.tochigi.jp
+haga.tochigi.jp
+ichikai.tochigi.jp
+iwafune.tochigi.jp
+kaminokawa.tochigi.jp
+kanuma.tochigi.jp
+karasuyama.tochigi.jp
+kuroiso.tochigi.jp
+mashiko.tochigi.jp
+mibu.tochigi.jp
+moka.tochigi.jp
+motegi.tochigi.jp
+nasu.tochigi.jp
+nasushiobara.tochigi.jp
+nikko.tochigi.jp
+nishikata.tochigi.jp
+nogi.tochigi.jp
+ohira.tochigi.jp
+ohtawara.tochigi.jp
+oyama.tochigi.jp
+sakura.tochigi.jp
+sano.tochigi.jp
+shimotsuke.tochigi.jp
+shioya.tochigi.jp
+takanezawa.tochigi.jp
+tochigi.tochigi.jp
+tsuga.tochigi.jp
+ujiie.tochigi.jp
+utsunomiya.tochigi.jp
+yaita.tochigi.jp
+aizumi.tokushima.jp
+anan.tokushima.jp
+ichiba.tokushima.jp
+itano.tokushima.jp
+kainan.tokushima.jp
+komatsushima.tokushima.jp
+matsushige.tokushima.jp
+mima.tokushima.jp
+minami.tokushima.jp
+miyoshi.tokushima.jp
+mugi.tokushima.jp
+nakagawa.tokushima.jp
+naruto.tokushima.jp
+sanagochi.tokushima.jp
+shishikui.tokushima.jp
+tokushima.tokushima.jp
+wajiki.tokushima.jp
+adachi.tokyo.jp
+akiruno.tokyo.jp
+akishima.tokyo.jp
+aogashima.tokyo.jp
+arakawa.tokyo.jp
+bunkyo.tokyo.jp
+chiyoda.tokyo.jp
+chofu.tokyo.jp
+chuo.tokyo.jp
+edogawa.tokyo.jp
+fuchu.tokyo.jp
+fussa.tokyo.jp
+hachijo.tokyo.jp
+hachioji.tokyo.jp
+hamura.tokyo.jp
+higashikurume.tokyo.jp
+higashimurayama.tokyo.jp
+higashiyamato.tokyo.jp
+hino.tokyo.jp
+hinode.tokyo.jp
+hinohara.tokyo.jp
+inagi.tokyo.jp
+itabashi.tokyo.jp
+katsushika.tokyo.jp
+kita.tokyo.jp
+kiyose.tokyo.jp
+kodaira.tokyo.jp
+koganei.tokyo.jp
+kokubunji.tokyo.jp
+komae.tokyo.jp
+koto.tokyo.jp
+kouzushima.tokyo.jp
+kunitachi.tokyo.jp
+machida.tokyo.jp
+meguro.tokyo.jp
+minato.tokyo.jp
+mitaka.tokyo.jp
+mizuho.tokyo.jp
+musashimurayama.tokyo.jp
+musashino.tokyo.jp
+nakano.tokyo.jp
+nerima.tokyo.jp
+ogasawara.tokyo.jp
+okutama.tokyo.jp
+ome.tokyo.jp
+oshima.tokyo.jp
+ota.tokyo.jp
+setagaya.tokyo.jp
+shibuya.tokyo.jp
+shinagawa.tokyo.jp
+shinjuku.tokyo.jp
+suginami.tokyo.jp
+sumida.tokyo.jp
+tachikawa.tokyo.jp
+taito.tokyo.jp
+tama.tokyo.jp
+toshima.tokyo.jp
+chizu.tottori.jp
+hino.tottori.jp
+kawahara.tottori.jp
+koge.tottori.jp
+kotoura.tottori.jp
+misasa.tottori.jp
+nanbu.tottori.jp
+nichinan.tottori.jp
+sakaiminato.tottori.jp
+tottori.tottori.jp
+wakasa.tottori.jp
+yazu.tottori.jp
+yonago.tottori.jp
+asahi.toyama.jp
+fuchu.toyama.jp
+fukumitsu.toyama.jp
+funahashi.toyama.jp
+himi.toyama.jp
+imizu.toyama.jp
+inami.toyama.jp
+johana.toyama.jp
+kamiichi.toyama.jp
+kurobe.toyama.jp
+nakaniikawa.toyama.jp
+namerikawa.toyama.jp
+nanto.toyama.jp
+nyuzen.toyama.jp
+oyabe.toyama.jp
+taira.toyama.jp
+takaoka.toyama.jp
+tateyama.toyama.jp
+toga.toyama.jp
+tonami.toyama.jp
+toyama.toyama.jp
+unazuki.toyama.jp
+uozu.toyama.jp
+yamada.toyama.jp
+arida.wakayama.jp
+aridagawa.wakayama.jp
+gobo.wakayama.jp
+hashimoto.wakayama.jp
+hidaka.wakayama.jp
+hirogawa.wakayama.jp
+inami.wakayama.jp
+iwade.wakayama.jp
+kainan.wakayama.jp
+kamitonda.wakayama.jp
+katsuragi.wakayama.jp
+kimino.wakayama.jp
+kinokawa.wakayama.jp
+kitayama.wakayama.jp
+koya.wakayama.jp
+koza.wakayama.jp
+kozagawa.wakayama.jp
+kudoyama.wakayama.jp
+kushimoto.wakayama.jp
+mihama.wakayama.jp
+misato.wakayama.jp
+nachikatsuura.wakayama.jp
+shingu.wakayama.jp
+shirahama.wakayama.jp
+taiji.wakayama.jp
+tanabe.wakayama.jp
+wakayama.wakayama.jp
+yuasa.wakayama.jp
+yura.wakayama.jp
+asahi.yamagata.jp
+funagata.yamagata.jp
+higashine.yamagata.jp
+iide.yamagata.jp
+kahoku.yamagata.jp
+kaminoyama.yamagata.jp
+kaneyama.yamagata.jp
+kawanishi.yamagata.jp
+mamurogawa.yamagata.jp
+mikawa.yamagata.jp
+murayama.yamagata.jp
+nagai.yamagata.jp
+nakayama.yamagata.jp
+nanyo.yamagata.jp
+nishikawa.yamagata.jp
+obanazawa.yamagata.jp
+oe.yamagata.jp
+oguni.yamagata.jp
+ohkura.yamagata.jp
+oishida.yamagata.jp
+sagae.yamagata.jp
+sakata.yamagata.jp
+sakegawa.yamagata.jp
+shinjo.yamagata.jp
+shirataka.yamagata.jp
+shonai.yamagata.jp
+takahata.yamagata.jp
+tendo.yamagata.jp
+tozawa.yamagata.jp
+tsuruoka.yamagata.jp
+yamagata.yamagata.jp
+yamanobe.yamagata.jp
+yonezawa.yamagata.jp
+yuza.yamagata.jp
+abu.yamaguchi.jp
+hagi.yamaguchi.jp
+hikari.yamaguchi.jp
+hofu.yamaguchi.jp
+iwakuni.yamaguchi.jp
+kudamatsu.yamaguchi.jp
+mitou.yamaguchi.jp
+nagato.yamaguchi.jp
+oshima.yamaguchi.jp
+shimonoseki.yamaguchi.jp
+shunan.yamaguchi.jp
+tabuse.yamaguchi.jp
+tokuyama.yamaguchi.jp
+toyota.yamaguchi.jp
+ube.yamaguchi.jp
+yuu.yamaguchi.jp
+chuo.yamanashi.jp
+doshi.yamanashi.jp
+fuefuki.yamanashi.jp
+fujikawa.yamanashi.jp
+fujikawaguchiko.yamanashi.jp
+fujiyoshida.yamanashi.jp
+hayakawa.yamanashi.jp
+hokuto.yamanashi.jp
+ichikawamisato.yamanashi.jp
+kai.yamanashi.jp
+kofu.yamanashi.jp
+koshu.yamanashi.jp
+kosuge.yamanashi.jp
+minami-alps.yamanashi.jp
+minobu.yamanashi.jp
+nakamichi.yamanashi.jp
+nanbu.yamanashi.jp
+narusawa.yamanashi.jp
+nirasaki.yamanashi.jp
+nishikatsura.yamanashi.jp
+oshino.yamanashi.jp
+otsuki.yamanashi.jp
+showa.yamanashi.jp
+tabayama.yamanashi.jp
+tsuru.yamanashi.jp
+uenohara.yamanashi.jp
+yamanakako.yamanashi.jp
+yamanashi.yamanashi.jp
+
+// ke : http://www.kenic.or.ke/index.php/en/ke-domains/ke-domains
+ke
+ac.ke
+co.ke
+go.ke
+info.ke
+me.ke
+mobi.ke
+ne.ke
+or.ke
+sc.ke
+
+// kg : http://www.domain.kg/dmn_n.html
+kg
+com.kg
+edu.kg
+gov.kg
+mil.kg
+net.kg
+org.kg
+
+// kh : http://www.mptc.gov.kh/dns_registration.htm
+*.kh
+
+// ki : https://www.iana.org/domains/root/db/ki.html
+ki
+biz.ki
+com.ki
+edu.ki
+gov.ki
+info.ki
+net.ki
+org.ki
+
+// km : https://www.iana.org/domains/root/db/km.html
+// http://www.domaine.km/documents/charte.doc
+km
+ass.km
+com.km
+edu.km
+gov.km
+mil.km
+nom.km
+org.km
+prd.km
+tm.km
+// These are only mentioned as proposed suggestions at domaine.km, but
+// https://www.iana.org/domains/root/db/km.html says they're available for registration:
+asso.km
+coop.km
+gouv.km
+medecin.km
+notaires.km
+pharmaciens.km
+presse.km
+veterinaire.km
+
+// kn : https://www.iana.org/domains/root/db/kn.html
+// http://www.dot.kn/domainRules.html
+kn
+edu.kn
+gov.kn
+net.kn
+org.kn
+
+// kp : http://www.kcce.kp/en_index.php
+kp
+com.kp
+edu.kp
+gov.kp
+org.kp
+rep.kp
+tra.kp
+
+// kr : https://www.iana.org/domains/root/db/kr.html
+// see also: https://krnic.kisa.or.kr/jsp/infoboard/law/domBylawsReg.jsp
+kr
+ac.kr
+ai.kr
+co.kr
+es.kr
+go.kr
+hs.kr
+io.kr
+it.kr
+kg.kr
+me.kr
+mil.kr
+ms.kr
+ne.kr
+or.kr
+pe.kr
+re.kr
+sc.kr
+// kr geographical names
+busan.kr
+chungbuk.kr
+chungnam.kr
+daegu.kr
+daejeon.kr
+gangwon.kr
+gwangju.kr
+gyeongbuk.kr
+gyeonggi.kr
+gyeongnam.kr
+incheon.kr
+jeju.kr
+jeonbuk.kr
+jeonnam.kr
+seoul.kr
+ulsan.kr
+
+// kw : https://www.nic.kw/policies/
+// Confirmed by registry
+kw
+com.kw
+edu.kw
+emb.kw
+gov.kw
+ind.kw
+net.kw
+org.kw
+
+// ky : http://www.icta.ky/da_ky_reg_dom.php
+// Confirmed by registry 2008-06-17
+ky
+com.ky
+edu.ky
+net.ky
+org.ky
+
+// kz : https://www.iana.org/domains/root/db/kz.html
+// see also: http://www.nic.kz/rules/index.jsp
+kz
+com.kz
+edu.kz
+gov.kz
+mil.kz
+net.kz
+org.kz
+
+// la : https://www.iana.org/domains/root/db/la.html
+// Submitted by registry
+la
+com.la
+edu.la
+gov.la
+info.la
+int.la
+net.la
+org.la
+per.la
+
+// lb : https://www.iana.org/domains/root/db/lb.html
+// Submitted by registry
+lb
+com.lb
+edu.lb
+gov.lb
+net.lb
+org.lb
+
+// lc : https://www.iana.org/domains/root/db/lc.html
+// see also: http://www.nic.lc/rules.htm
+lc
+co.lc
+com.lc
+edu.lc
+gov.lc
+net.lc
+org.lc
+
+// li : https://www.iana.org/domains/root/db/li.html
+li
+
+// lk : https://www.iana.org/domains/root/db/lk.html
+lk
+ac.lk
+assn.lk
+com.lk
+edu.lk
+gov.lk
+grp.lk
+hotel.lk
+int.lk
+ltd.lk
+net.lk
+ngo.lk
+org.lk
+sch.lk
+soc.lk
+web.lk
+
+// lr : http://psg.com/dns/lr/lr.txt
+// Submitted by registry
+lr
+com.lr
+edu.lr
+gov.lr
+net.lr
+org.lr
+
+// ls : http://www.nic.ls/
+// Confirmed by registry
+ls
+ac.ls
+biz.ls
+co.ls
+edu.ls
+gov.ls
+info.ls
+net.ls
+org.ls
+sc.ls
+
+// lt : https://www.iana.org/domains/root/db/lt.html
+lt
+// gov.lt : http://www.gov.lt/index_en.php
+gov.lt
+
+// lu : http://www.dns.lu/en/
+lu
+
+// lv : https://www.iana.org/domains/root/db/lv.html
+lv
+asn.lv
+com.lv
+conf.lv
+edu.lv
+gov.lv
+id.lv
+mil.lv
+net.lv
+org.lv
+
+// ly : http://www.nic.ly/regulations.php
+ly
+com.ly
+edu.ly
+gov.ly
+id.ly
+med.ly
+net.ly
+org.ly
+plc.ly
+sch.ly
+
+// ma : https://www.iana.org/domains/root/db/ma.html
+// http://www.anrt.ma/fr/admin/download/upload/file_fr782.pdf
+ma
+ac.ma
+co.ma
+gov.ma
+net.ma
+org.ma
+press.ma
+
+// mc : http://www.nic.mc/
+mc
+asso.mc
+tm.mc
+
+// md : https://www.iana.org/domains/root/db/md.html
+md
+
+// me : https://www.iana.org/domains/root/db/me.html
+me
+ac.me
+co.me
+edu.me
+gov.me
+its.me
+net.me
+org.me
+priv.me
+
+// mg : https://nic.mg
+mg
+co.mg
+com.mg
+edu.mg
+gov.mg
+mil.mg
+nom.mg
+org.mg
+prd.mg
+
+// mh : https://www.iana.org/domains/root/db/mh.html
+mh
+
+// mil : https://www.iana.org/domains/root/db/mil.html
+mil
+
+// mk : https://www.iana.org/domains/root/db/mk.html
+// see also: http://dns.marnet.net.mk/postapka.php
+mk
+com.mk
+edu.mk
+gov.mk
+inf.mk
+name.mk
+net.mk
+org.mk
+
+// ml : https://www.iana.org/domains/root/db/ml.html
+// Confirmed by Boubacar NDIAYE 2024-12-31
+ml
+ac.ml
+art.ml
+asso.ml
+com.ml
+edu.ml
+gouv.ml
+gov.ml
+info.ml
+inst.ml
+net.ml
+org.ml
+pr.ml
+presse.ml
+
+// mm : https://www.iana.org/domains/root/db/mm.html
+*.mm
+
+// mn : https://www.iana.org/domains/root/db/mn.html
+mn
+edu.mn
+gov.mn
+org.mn
+
+// mo : http://www.monic.net.mo/
+mo
+com.mo
+edu.mo
+gov.mo
+net.mo
+org.mo
+
+// mobi : https://www.iana.org/domains/root/db/mobi.html
+mobi
+
+// mp : http://www.dot.mp/
+// Confirmed by registry 2008-06-17
+mp
+
+// mq : https://www.iana.org/domains/root/db/mq.html
+mq
+
+// mr : https://www.iana.org/domains/root/db/mr.html
+mr
+gov.mr
+
+// ms : https://www.iana.org/domains/root/db/ms.html
+ms
+com.ms
+edu.ms
+gov.ms
+net.ms
+org.ms
+
+// mt : https://www.nic.org.mt/go/policy
+// Submitted by registry
+mt
+com.mt
+edu.mt
+net.mt
+org.mt
+
+// mu : https://www.iana.org/domains/root/db/mu.html
+mu
+ac.mu
+co.mu
+com.mu
+gov.mu
+net.mu
+or.mu
+org.mu
+
+// museum : https://welcome.museum/wp-content/uploads/2018/05/20180525-Registration-Policy-MUSEUM-EN_VF-2.pdf https://welcome.museum/buy-your-dot-museum-2/
+museum
+
+// mv : https://www.iana.org/domains/root/db/mv.html
+// "mv" included because, contra Wikipedia, google.mv exists.
+mv
+aero.mv
+biz.mv
+com.mv
+coop.mv
+edu.mv
+gov.mv
+info.mv
+int.mv
+mil.mv
+museum.mv
+name.mv
+net.mv
+org.mv
+pro.mv
+
+// mw : http://www.registrar.mw/
+mw
+ac.mw
+biz.mw
+co.mw
+com.mw
+coop.mw
+edu.mw
+gov.mw
+int.mw
+net.mw
+org.mw
+
+// mx : http://www.nic.mx/
+// Submitted by registry
+mx
+com.mx
+edu.mx
+gob.mx
+net.mx
+org.mx
+
+// my : http://www.mynic.my/
+// Available strings: https://mynic.my/resources/domains/buying-a-domain/
+my
+biz.my
+com.my
+edu.my
+gov.my
+mil.my
+name.my
+net.my
+org.my
+
+// mz : http://www.uem.mz/
+// Submitted by registry
+mz
+ac.mz
+adv.mz
+co.mz
+edu.mz
+gov.mz
+mil.mz
+net.mz
+org.mz
+
+// na : http://www.na-nic.com.na/
+na
+alt.na
+co.na
+com.na
+gov.na
+net.na
+org.na
+
+// name : http://www.nic.name/
+// Regarding 2LDs: https://github.com/publicsuffix/list/issues/2306
+name
+
+// nc : http://www.cctld.nc/
+nc
+asso.nc
+nom.nc
+
+// ne : https://www.iana.org/domains/root/db/ne.html
+ne
+
+// net : https://www.iana.org/domains/root/db/net.html
+net
+
+// nf : https://www.iana.org/domains/root/db/nf.html
+nf
+arts.nf
+com.nf
+firm.nf
+info.nf
+net.nf
+other.nf
+per.nf
+rec.nf
+store.nf
+web.nf
+
+// ng : http://www.nira.org.ng/index.php/join-us/register-ng-domain/189-nira-slds
+ng
+com.ng
+edu.ng
+gov.ng
+i.ng
+mil.ng
+mobi.ng
+name.ng
+net.ng
+org.ng
+sch.ng
+
+// ni : http://www.nic.ni/
+ni
+ac.ni
+biz.ni
+co.ni
+com.ni
+edu.ni
+gob.ni
+in.ni
+info.ni
+int.ni
+mil.ni
+net.ni
+nom.ni
+org.ni
+web.ni
+
+// nl : https://www.iana.org/domains/root/db/nl.html
+// https://www.sidn.nl/
+nl
+
+// no : https://www.norid.no/en/om-domenenavn/regelverk-for-no/
+// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
+// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
+// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
+// RSS feed: https://teknisk.norid.no/en/feed/
+no
+// Norid category second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-c/
+fhs.no
+folkebibl.no
+fylkesbibl.no
+idrett.no
+museum.no
+priv.no
+vgs.no
+// Norid category second-level domains managed by parties other than Norid : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-d/
+dep.no
+herad.no
+kommune.no
+mil.no
+stat.no
+// Norid geographical second level domains : https://www.norid.no/en/om-domenenavn/regelverk-for-no/vedlegg-b/
+// counties
+aa.no
+ah.no
+bu.no
+fm.no
+hl.no
+hm.no
+jan-mayen.no
+mr.no
+nl.no
+nt.no
+of.no
+ol.no
+oslo.no
+rl.no
+sf.no
+st.no
+svalbard.no
+tm.no
+tr.no
+va.no
+vf.no
+// primary and lower secondary schools per county
+gs.aa.no
+gs.ah.no
+gs.bu.no
+gs.fm.no
+gs.hl.no
+gs.hm.no
+gs.jan-mayen.no
+gs.mr.no
+gs.nl.no
+gs.nt.no
+gs.of.no
+gs.ol.no
+gs.oslo.no
+gs.rl.no
+gs.sf.no
+gs.st.no
+gs.svalbard.no
+gs.tm.no
+gs.tr.no
+gs.va.no
+gs.vf.no
+// cities
+akrehamn.no
+åkrehamn.no
+algard.no
+ålgård.no
+arna.no
+bronnoysund.no
+brønnøysund.no
+brumunddal.no
+bryne.no
+drobak.no
+drøbak.no
+egersund.no
+fetsund.no
+floro.no
+florø.no
+fredrikstad.no
+hokksund.no
+honefoss.no
+hønefoss.no
+jessheim.no
+jorpeland.no
+jørpeland.no
+kirkenes.no
+kopervik.no
+krokstadelva.no
+langevag.no
+langevåg.no
+leirvik.no
+mjondalen.no
+mjøndalen.no
+mo-i-rana.no
+mosjoen.no
+mosjøen.no
+nesoddtangen.no
+orkanger.no
+osoyro.no
+osøyro.no
+raholt.no
+råholt.no
+sandnessjoen.no
+sandnessjøen.no
+skedsmokorset.no
+slattum.no
+spjelkavik.no
+stathelle.no
+stavern.no
+stjordalshalsen.no
+stjørdalshalsen.no
+tananger.no
+tranby.no
+vossevangen.no
+// communities
+aarborte.no
+aejrie.no
+afjord.no
+åfjord.no
+agdenes.no
+nes.akershus.no
+aknoluokta.no
+ákŋoluokta.no
+al.no
+ål.no
+alaheadju.no
+álaheadju.no
+alesund.no
+ålesund.no
+alstahaug.no
+alta.no
+áltá.no
+alvdal.no
+amli.no
+åmli.no
+amot.no
+åmot.no
+andasuolo.no
+andebu.no
+andoy.no
+andøy.no
+ardal.no
+årdal.no
+aremark.no
+arendal.no
+ås.no
+aseral.no
+åseral.no
+asker.no
+askim.no
+askoy.no
+askøy.no
+askvoll.no
+asnes.no
+åsnes.no
+audnedaln.no
+aukra.no
+aure.no
+aurland.no
+aurskog-holand.no
+aurskog-høland.no
+austevoll.no
+austrheim.no
+averoy.no
+averøy.no
+badaddja.no
+bådåddjå.no
+bærum.no
+bahcavuotna.no
+báhcavuotna.no
+bahccavuotna.no
+báhccavuotna.no
+baidar.no
+báidár.no
+bajddar.no
+bájddar.no
+balat.no
+bálát.no
+balestrand.no
+ballangen.no
+balsfjord.no
+bamble.no
+bardu.no
+barum.no
+batsfjord.no
+båtsfjord.no
+bearalvahki.no
+bearalváhki.no
+beardu.no
+beiarn.no
+berg.no
+bergen.no
+berlevag.no
+berlevåg.no
+bievat.no
+bievát.no
+bindal.no
+birkenes.no
+bjerkreim.no
+bjugn.no
+bodo.no
+bodø.no
+bokn.no
+bomlo.no
+bømlo.no
+bremanger.no
+bronnoy.no
+brønnøy.no
+budejju.no
+nes.buskerud.no
+bygland.no
+bykle.no
+cahcesuolo.no
+čáhcesuolo.no
+davvenjarga.no
+davvenjárga.no
+davvesiida.no
+deatnu.no
+dielddanuorri.no
+divtasvuodna.no
+divttasvuotna.no
+donna.no
+dønna.no
+dovre.no
+drammen.no
+drangedal.no
+dyroy.no
+dyrøy.no
+eid.no
+eidfjord.no
+eidsberg.no
+eidskog.no
+eidsvoll.no
+eigersund.no
+elverum.no
+enebakk.no
+engerdal.no
+etne.no
+etnedal.no
+evenassi.no
+evenášši.no
+evenes.no
+evje-og-hornnes.no
+farsund.no
+fauske.no
+fedje.no
+fet.no
+finnoy.no
+finnøy.no
+fitjar.no
+fjaler.no
+fjell.no
+fla.no
+flå.no
+flakstad.no
+flatanger.no
+flekkefjord.no
+flesberg.no
+flora.no
+folldal.no
+forde.no
+førde.no
+forsand.no
+fosnes.no
+fræna.no
+frana.no
+frei.no
+frogn.no
+froland.no
+frosta.no
+froya.no
+frøya.no
+fuoisku.no
+fuossko.no
+fusa.no
+fyresdal.no
+gaivuotna.no
+gáivuotna.no
+galsa.no
+gálsá.no
+gamvik.no
+gangaviika.no
+gáŋgaviika.no
+gaular.no
+gausdal.no
+giehtavuoatna.no
+gildeskal.no
+gildeskål.no
+giske.no
+gjemnes.no
+gjerdrum.no
+gjerstad.no
+gjesdal.no
+gjovik.no
+gjøvik.no
+gloppen.no
+gol.no
+gran.no
+grane.no
+granvin.no
+gratangen.no
+grimstad.no
+grong.no
+grue.no
+gulen.no
+guovdageaidnu.no
+ha.no
+hå.no
+habmer.no
+hábmer.no
+hadsel.no
+hægebostad.no
+hagebostad.no
+halden.no
+halsa.no
+hamar.no
+hamaroy.no
+hammarfeasta.no
+hámmárfeasta.no
+hammerfest.no
+hapmir.no
+hápmir.no
+haram.no
+hareid.no
+harstad.no
+hasvik.no
+hattfjelldal.no
+haugesund.no
+os.hedmark.no
+valer.hedmark.no
+våler.hedmark.no
+hemne.no
+hemnes.no
+hemsedal.no
+hitra.no
+hjartdal.no
+hjelmeland.no
+hobol.no
+hobøl.no
+hof.no
+hol.no
+hole.no
+holmestrand.no
+holtalen.no
+holtålen.no
+os.hordaland.no
+hornindal.no
+horten.no
+hoyanger.no
+høyanger.no
+hoylandet.no
+høylandet.no
+hurdal.no
+hurum.no
+hvaler.no
+hyllestad.no
+ibestad.no
+inderoy.no
+inderøy.no
+iveland.no
+ivgu.no
+jevnaker.no
+jolster.no
+jølster.no
+jondal.no
+kafjord.no
+kåfjord.no
+karasjohka.no
+kárášjohka.no
+karasjok.no
+karlsoy.no
+karmoy.no
+karmøy.no
+kautokeino.no
+klabu.no
+klæbu.no
+klepp.no
+kongsberg.no
+kongsvinger.no
+kraanghke.no
+kråanghke.no
+kragero.no
+kragerø.no
+kristiansand.no
+kristiansund.no
+krodsherad.no
+krødsherad.no
+kvæfjord.no
+kvænangen.no
+kvafjord.no
+kvalsund.no
+kvam.no
+kvanangen.no
+kvinesdal.no
+kvinnherad.no
+kviteseid.no
+kvitsoy.no
+kvitsøy.no
+laakesvuemie.no
+lærdal.no
+lahppi.no
+láhppi.no
+lardal.no
+larvik.no
+lavagis.no
+lavangen.no
+leangaviika.no
+leaŋgaviika.no
+lebesby.no
+leikanger.no
+leirfjord.no
+leka.no
+leksvik.no
+lenvik.no
+lerdal.no
+lesja.no
+levanger.no
+lier.no
+lierne.no
+lillehammer.no
+lillesand.no
+lindas.no
+lindås.no
+lindesnes.no
+loabat.no
+loabát.no
+lodingen.no
+lødingen.no
+lom.no
+loppa.no
+lorenskog.no
+lørenskog.no
+loten.no
+løten.no
+lund.no
+lunner.no
+luroy.no
+lurøy.no
+luster.no
+lyngdal.no
+lyngen.no
+malatvuopmi.no
+málatvuopmi.no
+malselv.no
+målselv.no
+malvik.no
+mandal.no
+marker.no
+marnardal.no
+masfjorden.no
+masoy.no
+måsøy.no
+matta-varjjat.no
+mátta-várjjat.no
+meland.no
+meldal.no
+melhus.no
+meloy.no
+meløy.no
+meraker.no
+meråker.no
+midsund.no
+midtre-gauldal.no
+moareke.no
+moåreke.no
+modalen.no
+modum.no
+molde.no
+heroy.more-og-romsdal.no
+sande.more-og-romsdal.no
+herøy.møre-og-romsdal.no
+sande.møre-og-romsdal.no
+moskenes.no
+moss.no
+muosat.no
+muosát.no
+naamesjevuemie.no
+nååmesjevuemie.no
+nærøy.no
+namdalseid.no
+namsos.no
+namsskogan.no
+nannestad.no
+naroy.no
+narviika.no
+narvik.no
+naustdal.no
+navuotna.no
+návuotna.no
+nedre-eiker.no
+nesna.no
+nesodden.no
+nesseby.no
+nesset.no
+nissedal.no
+nittedal.no
+nord-aurdal.no
+nord-fron.no
+nord-odal.no
+norddal.no
+nordkapp.no
+bo.nordland.no
+bø.nordland.no
+heroy.nordland.no
+herøy.nordland.no
+nordre-land.no
+nordreisa.no
+nore-og-uvdal.no
+notodden.no
+notteroy.no
+nøtterøy.no
+odda.no
+oksnes.no
+øksnes.no
+omasvuotna.no
+oppdal.no
+oppegard.no
+oppegård.no
+orkdal.no
+orland.no
+ørland.no
+orskog.no
+ørskog.no
+orsta.no
+ørsta.no
+osen.no
+osteroy.no
+osterøy.no
+valer.ostfold.no
+våler.østfold.no
+ostre-toten.no
+østre-toten.no
+overhalla.no
+ovre-eiker.no
+øvre-eiker.no
+oyer.no
+øyer.no
+oygarden.no
+øygarden.no
+oystre-slidre.no
+øystre-slidre.no
+porsanger.no
+porsangu.no
+porsáŋgu.no
+porsgrunn.no
+rade.no
+råde.no
+radoy.no
+radøy.no
+rælingen.no
+rahkkeravju.no
+ráhkkerávju.no
+raisa.no
+ráisa.no
+rakkestad.no
+ralingen.no
+rana.no
+randaberg.no
+rauma.no
+rendalen.no
+rennebu.no
+rennesoy.no
+rennesøy.no
+rindal.no
+ringebu.no
+ringerike.no
+ringsaker.no
+risor.no
+risør.no
+rissa.no
+roan.no
+rodoy.no
+rødøy.no
+rollag.no
+romsa.no
+romskog.no
+rømskog.no
+roros.no
+røros.no
+rost.no
+røst.no
+royken.no
+røyken.no
+royrvik.no
+røyrvik.no
+ruovat.no
+rygge.no
+salangen.no
+salat.no
+sálat.no
+sálát.no
+saltdal.no
+samnanger.no
+sandefjord.no
+sandnes.no
+sandoy.no
+sandøy.no
+sarpsborg.no
+sauda.no
+sauherad.no
+sel.no
+selbu.no
+selje.no
+seljord.no
+siellak.no
+sigdal.no
+siljan.no
+sirdal.no
+skanit.no
+skánit.no
+skanland.no
+skånland.no
+skaun.no
+skedsmo.no
+ski.no
+skien.no
+skierva.no
+skiervá.no
+skiptvet.no
+skjak.no
+skjåk.no
+skjervoy.no
+skjervøy.no
+skodje.no
+smola.no
+smøla.no
+snaase.no
+snåase.no
+snasa.no
+snåsa.no
+snillfjord.no
+snoasa.no
+sogndal.no
+sogne.no
+søgne.no
+sokndal.no
+sola.no
+solund.no
+somna.no
+sømna.no
+sondre-land.no
+søndre-land.no
+songdalen.no
+sor-aurdal.no
+sør-aurdal.no
+sor-fron.no
+sør-fron.no
+sor-odal.no
+sør-odal.no
+sor-varanger.no
+sør-varanger.no
+sorfold.no
+sørfold.no
+sorreisa.no
+sørreisa.no
+sortland.no
+sorum.no
+sørum.no
+spydeberg.no
+stange.no
+stavanger.no
+steigen.no
+steinkjer.no
+stjordal.no
+stjørdal.no
+stokke.no
+stor-elvdal.no
+stord.no
+stordal.no
+storfjord.no
+strand.no
+stranda.no
+stryn.no
+sula.no
+suldal.no
+sund.no
+sunndal.no
+surnadal.no
+sveio.no
+svelvik.no
+sykkylven.no
+tana.no
+bo.telemark.no
+bø.telemark.no
+time.no
+tingvoll.no
+tinn.no
+tjeldsund.no
+tjome.no
+tjøme.no
+tokke.no
+tolga.no
+tonsberg.no
+tønsberg.no
+torsken.no
+træna.no
+trana.no
+tranoy.no
+tranøy.no
+troandin.no
+trogstad.no
+trøgstad.no
+tromsa.no
+tromso.no
+tromsø.no
+trondheim.no
+trysil.no
+tvedestrand.no
+tydal.no
+tynset.no
+tysfjord.no
+tysnes.no
+tysvær.no
+tysvar.no
+ullensaker.no
+ullensvang.no
+ulvik.no
+unjarga.no
+unjárga.no
+utsira.no
+vaapste.no
+vadso.no
+vadsø.no
+værøy.no
+vaga.no
+vågå.no
+vagan.no
+vågan.no
+vagsoy.no
+vågsøy.no
+vaksdal.no
+valle.no
+vang.no
+vanylven.no
+vardo.no
+vardø.no
+varggat.no
+várggát.no
+varoy.no
+vefsn.no
+vega.no
+vegarshei.no
+vegårshei.no
+vennesla.no
+verdal.no
+verran.no
+vestby.no
+sande.vestfold.no
+vestnes.no
+vestre-slidre.no
+vestre-toten.no
+vestvagoy.no
+vestvågøy.no
+vevelstad.no
+vik.no
+vikna.no
+vindafjord.no
+voagat.no
+volda.no
+voss.no
+
+// np : http://www.mos.com.np/register.html
+*.np
+
+// nr : http://cenpac.net.nr/dns/index.html
+// Submitted by registry
+nr
+biz.nr
+com.nr
+edu.nr
+gov.nr
+info.nr
+net.nr
+org.nr
+
+// nu : https://www.iana.org/domains/root/db/nu.html
+nu
+
+// nz : https://www.iana.org/domains/root/db/nz.html
+// Submitted by registry
+nz
+ac.nz
+co.nz
+cri.nz
+geek.nz
+gen.nz
+govt.nz
+health.nz
+iwi.nz
+kiwi.nz
+maori.nz
+māori.nz
+mil.nz
+net.nz
+org.nz
+parliament.nz
+school.nz
+
+// om : https://www.iana.org/domains/root/db/om.html
+om
+co.om
+com.om
+edu.om
+gov.om
+med.om
+museum.om
+net.om
+org.om
+pro.om
+
+// onion : https://tools.ietf.org/html/rfc7686
+onion
+
+// org : https://www.iana.org/domains/root/db/org.html
+org
+
+// pa : http://www.nic.pa/
+// Some additional second level "domains" resolve directly as hostnames, such as
+// pannet.pa, so we add a rule for "pa".
+pa
+abo.pa
+ac.pa
+com.pa
+edu.pa
+gob.pa
+ing.pa
+med.pa
+net.pa
+nom.pa
+org.pa
+sld.pa
+
+// pe : https://www.nic.pe/InformeFinalComision.pdf
+pe
+com.pe
+edu.pe
+gob.pe
+mil.pe
+net.pe
+nom.pe
+org.pe
+
+// pf : http://www.gobin.info/domainname/formulaire-pf.pdf
+pf
+com.pf
+edu.pf
+org.pf
+
+// pg : https://www.iana.org/domains/root/db/pg.html
+*.pg
+
+// ph : https://www.iana.org/domains/root/db/ph.html
+// Submitted by registry
+ph
+com.ph
+edu.ph
+gov.ph
+i.ph
+mil.ph
+net.ph
+ngo.ph
+org.ph
+
+// pk : https://pk5.pknic.net.pk/pk5/msgNamepk.PK
+// Contact Email: staff@pknic.net.pk
+pk
+ac.pk
+biz.pk
+com.pk
+edu.pk
+fam.pk
+gkp.pk
+gob.pk
+gog.pk
+gok.pk
+gop.pk
+gos.pk
+gov.pk
+net.pk
+org.pk
+web.pk
+
+// pl : https://www.dns.pl/en/
+// Confirmed by registry 2024-11-18
+pl
+com.pl
+net.pl
+org.pl
+// pl functional domains : https://www.dns.pl/en/list_of_functional_domain_names
+agro.pl
+aid.pl
+atm.pl
+auto.pl
+biz.pl
+edu.pl
+gmina.pl
+gsm.pl
+info.pl
+mail.pl
+media.pl
+miasta.pl
+mil.pl
+nieruchomosci.pl
+nom.pl
+pc.pl
+powiat.pl
+priv.pl
+realestate.pl
+rel.pl
+sex.pl
+shop.pl
+sklep.pl
+sos.pl
+szkola.pl
+targi.pl
+tm.pl
+tourism.pl
+travel.pl
+turystyka.pl
+// Government domains : https://www.dns.pl/informacje_o_rejestracji_domen_gov_pl
+// In accordance with the .gov.pl Domain Name Regulations : https://www.dns.pl/regulamin_gov_pl
+gov.pl
+ap.gov.pl
+griw.gov.pl
+ic.gov.pl
+is.gov.pl
+kmpsp.gov.pl
+konsulat.gov.pl
+kppsp.gov.pl
+kwp.gov.pl
+kwpsp.gov.pl
+mup.gov.pl
+mw.gov.pl
+oia.gov.pl
+oirm.gov.pl
+oke.gov.pl
+oow.gov.pl
+oschr.gov.pl
+oum.gov.pl
+pa.gov.pl
+pinb.gov.pl
+piw.gov.pl
+po.gov.pl
+pr.gov.pl
+psp.gov.pl
+psse.gov.pl
+pup.gov.pl
+rzgw.gov.pl
+sa.gov.pl
+sdn.gov.pl
+sko.gov.pl
+so.gov.pl
+sr.gov.pl
+starostwo.gov.pl
+ug.gov.pl
+ugim.gov.pl
+um.gov.pl
+umig.gov.pl
+upow.gov.pl
+uppo.gov.pl
+us.gov.pl
+uw.gov.pl
+uzs.gov.pl
+wif.gov.pl
+wiih.gov.pl
+winb.gov.pl
+wios.gov.pl
+witd.gov.pl
+wiw.gov.pl
+wkz.gov.pl
+wsa.gov.pl
+wskr.gov.pl
+wsse.gov.pl
+wuoz.gov.pl
+wzmiuw.gov.pl
+zp.gov.pl
+zpisdn.gov.pl
+// pl regional domains : https://www.dns.pl/en/list_of_regional_domain_names
+augustow.pl
+babia-gora.pl
+bedzin.pl
+beskidy.pl
+bialowieza.pl
+bialystok.pl
+bielawa.pl
+bieszczady.pl
+boleslawiec.pl
+bydgoszcz.pl
+bytom.pl
+cieszyn.pl
+czeladz.pl
+czest.pl
+dlugoleka.pl
+elblag.pl
+elk.pl
+glogow.pl
+gniezno.pl
+gorlice.pl
+grajewo.pl
+ilawa.pl
+jaworzno.pl
+jelenia-gora.pl
+jgora.pl
+kalisz.pl
+karpacz.pl
+kartuzy.pl
+kaszuby.pl
+katowice.pl
+kazimierz-dolny.pl
+kepno.pl
+ketrzyn.pl
+klodzko.pl
+kobierzyce.pl
+kolobrzeg.pl
+konin.pl
+konskowola.pl
+kutno.pl
+lapy.pl
+lebork.pl
+legnica.pl
+lezajsk.pl
+limanowa.pl
+lomza.pl
+lowicz.pl
+lubin.pl
+lukow.pl
+malbork.pl
+malopolska.pl
+mazowsze.pl
+mazury.pl
+mielec.pl
+mielno.pl
+mragowo.pl
+naklo.pl
+nowaruda.pl
+nysa.pl
+olawa.pl
+olecko.pl
+olkusz.pl
+olsztyn.pl
+opoczno.pl
+opole.pl
+ostroda.pl
+ostroleka.pl
+ostrowiec.pl
+ostrowwlkp.pl
+pila.pl
+pisz.pl
+podhale.pl
+podlasie.pl
+polkowice.pl
+pomorskie.pl
+pomorze.pl
+prochowice.pl
+pruszkow.pl
+przeworsk.pl
+pulawy.pl
+radom.pl
+rawa-maz.pl
+rybnik.pl
+rzeszow.pl
+sanok.pl
+sejny.pl
+skoczow.pl
+slask.pl
+slupsk.pl
+sosnowiec.pl
+stalowa-wola.pl
+starachowice.pl
+stargard.pl
+suwalki.pl
+swidnica.pl
+swiebodzin.pl
+swinoujscie.pl
+szczecin.pl
+szczytno.pl
+tarnobrzeg.pl
+tgory.pl
+turek.pl
+tychy.pl
+ustka.pl
+walbrzych.pl
+warmia.pl
+warszawa.pl
+waw.pl
+wegrow.pl
+wielun.pl
+wlocl.pl
+wloclawek.pl
+wodzislaw.pl
+wolomin.pl
+wroclaw.pl
+zachpomor.pl
+zagan.pl
+zarow.pl
+zgora.pl
+zgorzelec.pl
+
+// pm : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+pm
+
+// pn : https://www.iana.org/domains/root/db/pn.html
+pn
+co.pn
+edu.pn
+gov.pn
+net.pn
+org.pn
+
+// post : https://www.iana.org/domains/root/db/post.html
+post
+
+// pr : http://www.nic.pr/index.asp?f=1
+pr
+biz.pr
+com.pr
+edu.pr
+gov.pr
+info.pr
+isla.pr
+name.pr
+net.pr
+org.pr
+pro.pr
+// these aren't mentioned on nic.pr, but on https://www.iana.org/domains/root/db/pr.html
+ac.pr
+est.pr
+prof.pr
+
+// pro : http://registry.pro/get-pro
+pro
+aaa.pro
+aca.pro
+acct.pro
+avocat.pro
+bar.pro
+cpa.pro
+eng.pro
+jur.pro
+law.pro
+med.pro
+recht.pro
+
+// ps : https://www.iana.org/domains/root/db/ps.html
+// http://www.nic.ps/registration/policy.html#reg
+ps
+com.ps
+edu.ps
+gov.ps
+net.ps
+org.ps
+plo.ps
+sec.ps
+
+// pt : https://www.dns.pt/en/domain/pt-terms-and-conditions-registration-rules/
+pt
+com.pt
+edu.pt
+gov.pt
+int.pt
+net.pt
+nome.pt
+org.pt
+publ.pt
+
+// pw : https://www.iana.org/domains/root/db/pw.html
+// Confirmed by registry in private correspondence with @dnsguru 2024-12-09
+pw
+gov.pw
+
+// py : https://www.iana.org/domains/root/db/py.html
+// Submitted by registry
+py
+com.py
+coop.py
+edu.py
+gov.py
+mil.py
+net.py
+org.py
+
+// qa : http://domains.qa/en/
+qa
+com.qa
+edu.qa
+gov.qa
+mil.qa
+name.qa
+net.qa
+org.qa
+sch.qa
+
+// re : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+// Confirmed by registry 2024-11-18
+re
+// Closed for registration on 2013-03-15 but domains are still maintained
+asso.re
+com.re
+
+// ro : http://www.rotld.ro/
+ro
+arts.ro
+com.ro
+firm.ro
+info.ro
+nom.ro
+nt.ro
+org.ro
+rec.ro
+store.ro
+tm.ro
+www.ro
+
+// rs : https://www.rnids.rs/en/domains/national-domains
+rs
+ac.rs
+co.rs
+edu.rs
+gov.rs
+in.rs
+org.rs
+
+// ru : https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
+// Submitted by George Georgievsky
+ru
+
+// rw : https://www.iana.org/domains/root/db/rw.html
+rw
+ac.rw
+co.rw
+coop.rw
+gov.rw
+mil.rw
+net.rw
+org.rw
+
+// sa : http://www.nic.net.sa/
+sa
+com.sa
+edu.sa
+gov.sa
+med.sa
+net.sa
+org.sa
+pub.sa
+sch.sa
+
+// sb : http://www.sbnic.net.sb/
+// Submitted by registry
+sb
+com.sb
+edu.sb
+gov.sb
+net.sb
+org.sb
+
+// sc : http://www.nic.sc/
+sc
+com.sc
+edu.sc
+gov.sc
+net.sc
+org.sc
+
+// sd : https://www.iana.org/domains/root/db/sd.html
+// Submitted by registry
+sd
+com.sd
+edu.sd
+gov.sd
+info.sd
+med.sd
+net.sd
+org.sd
+tv.sd
+
+// se : https://www.iana.org/domains/root/db/se.html
+// https://data.internetstiftelsen.se/barred_domains_list.txt -> Second level domains & Sub-domains
+// Confirmed by Registry Services 2024-11-20
+se
+a.se
+ac.se
+b.se
+bd.se
+brand.se
+c.se
+d.se
+e.se
+f.se
+fh.se
+fhsk.se
+fhv.se
+g.se
+h.se
+i.se
+k.se
+komforb.se
+kommunalforbund.se
+komvux.se
+l.se
+lanbib.se
+m.se
+n.se
+naturbruksgymn.se
+o.se
+org.se
+p.se
+parti.se
+pp.se
+press.se
+r.se
+s.se
+t.se
+tm.se
+u.se
+w.se
+x.se
+y.se
+z.se
+
+// sg : https://www.sgnic.sg/domain-registration/sg-categories-rules
+// Confirmed by registry 2024-11-19
+sg
+com.sg
+edu.sg
+gov.sg
+net.sg
+org.sg
+
+// sh : http://nic.sh/rules.htm
+sh
+com.sh
+gov.sh
+mil.sh
+net.sh
+org.sh
+
+// si : https://www.iana.org/domains/root/db/si.html
+si
+
+// sj : No registrations at this time.
+// Submitted by registry
+sj
+
+// sk : https://www.iana.org/domains/root/db/sk.html
+// https://sk-nic.sk/
+sk
+org.sk
+
+// sl : http://www.nic.sl
+// Submitted by registry
+sl
+com.sl
+edu.sl
+gov.sl
+net.sl
+org.sl
+
+// sm : https://www.iana.org/domains/root/db/sm.html
+sm
+
+// sn : https://www.iana.org/domains/root/db/sn.html
+sn
+art.sn
+com.sn
+edu.sn
+gouv.sn
+org.sn
+univ.sn
+
+// so : http://sonic.so/policies/
+so
+com.so
+edu.so
+gov.so
+me.so
+net.so
+org.so
+
+// sr : https://www.iana.org/domains/root/db/sr.html
+sr
+
+// ss : https://registry.nic.ss/
+// Submitted by registry
+ss
+biz.ss
+co.ss
+com.ss
+edu.ss
+gov.ss
+me.ss
+net.ss
+org.ss
+sch.ss
+
+// st : http://www.nic.st/html/policyrules/
+st
+co.st
+com.st
+consulado.st
+edu.st
+embaixada.st
+mil.st
+net.st
+org.st
+principe.st
+saotome.st
+store.st
+
+// su : https://www.iana.org/domains/root/db/su.html
+su
+
+// sv : https://www.iana.org/domains/root/db/sv.html
+sv
+com.sv
+edu.sv
+gob.sv
+org.sv
+red.sv
+
+// sx : https://www.iana.org/domains/root/db/sx.html
+// Submitted by registry
+sx
+gov.sx
+
+// sy : https://www.iana.org/domains/root/db/sy.html
+sy
+com.sy
+edu.sy
+gov.sy
+mil.sy
+net.sy
+org.sy
+
+// sz : https://www.iana.org/domains/root/db/sz.html
+// http://www.sispa.org.sz/
+sz
+ac.sz
+co.sz
+org.sz
+
+// tc : https://www.iana.org/domains/root/db/tc.html
+tc
+
+// td : https://www.iana.org/domains/root/db/td.html
+td
+
+// tel : https://www.iana.org/domains/root/db/tel.html
+// http://www.telnic.org/
+tel
+
+// tf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+tf
+
+// tg : https://www.iana.org/domains/root/db/tg.html
+// http://www.nic.tg/
+tg
+
+// th : https://www.iana.org/domains/root/db/th.html
+// Submitted by registry
+th
+ac.th
+co.th
+go.th
+in.th
+mi.th
+net.th
+or.th
+
+// tj : http://www.nic.tj/policy.html
+tj
+ac.tj
+biz.tj
+co.tj
+com.tj
+edu.tj
+go.tj
+gov.tj
+int.tj
+mil.tj
+name.tj
+net.tj
+nic.tj
+org.tj
+test.tj
+web.tj
+
+// tk : https://www.iana.org/domains/root/db/tk.html
+tk
+
+// tl : https://www.iana.org/domains/root/db/tl.html
+tl
+gov.tl
+
+// tm : https://www.nic.tm/local.html
+// Confirmed by registry 2024-11-19
+tm
+co.tm
+com.tm
+edu.tm
+gov.tm
+mil.tm
+net.tm
+nom.tm
+org.tm
+
+// tn : http://www.registre.tn/fr/
+// https://whois.ati.tn/
+tn
+com.tn
+ens.tn
+fin.tn
+gov.tn
+ind.tn
+info.tn
+intl.tn
+mincom.tn
+nat.tn
+net.tn
+org.tn
+perso.tn
+tourism.tn
+
+// to : https://www.iana.org/domains/root/db/to.html
+// Submitted by registry
+to
+com.to
+edu.to
+gov.to
+mil.to
+net.to
+org.to
+
+// tr : https://nic.tr/
+// https://nic.tr/forms/eng/policies.pdf
+// https://nic.tr/index.php?USRACTN=PRICELST
+tr
+av.tr
+bbs.tr
+bel.tr
+biz.tr
+com.tr
+dr.tr
+edu.tr
+gen.tr
+gov.tr
+info.tr
+k12.tr
+kep.tr
+mil.tr
+name.tr
+net.tr
+org.tr
+pol.tr
+tel.tr
+tsk.tr
+tv.tr
+web.tr
+// Used by Northern Cyprus
+nc.tr
+// Used by government agencies of Northern Cyprus
+gov.nc.tr
+
+// tt : https://www.nic.tt/
+// Confirmed by registry 2024-11-19
+tt
+biz.tt
+co.tt
+com.tt
+edu.tt
+gov.tt
+info.tt
+mil.tt
+name.tt
+net.tt
+org.tt
+pro.tt
+
+// tv : https://www.iana.org/domains/root/db/tv.html
+// Not listing any 2LDs as reserved since none seem to exist in practice,
+// Wikipedia notwithstanding.
+tv
+
+// tw : https://www.iana.org/domains/root/db/tw.html
+// https://twnic.tw/dnservice_catag.php
+// Confirmed by registry 2024-11-26
+tw
+club.tw
+com.tw
+ebiz.tw
+edu.tw
+game.tw
+gov.tw
+idv.tw
+mil.tw
+net.tw
+org.tw
+
+// tz : http://www.tznic.or.tz/index.php/domains
+// Submitted by registry
+tz
+ac.tz
+co.tz
+go.tz
+hotel.tz
+info.tz
+me.tz
+mil.tz
+mobi.tz
+ne.tz
+or.tz
+sc.tz
+tv.tz
+
+// ua : https://hostmaster.ua/policy/?ua
+// Submitted by registry
+ua
+// ua 2LD
+com.ua
+edu.ua
+gov.ua
+in.ua
+net.ua
+org.ua
+// ua geographic names
+// https://hostmaster.ua/2ld/
+cherkassy.ua
+cherkasy.ua
+chernigov.ua
+chernihiv.ua
+chernivtsi.ua
+chernovtsy.ua
+ck.ua
+cn.ua
+cr.ua
+crimea.ua
+cv.ua
+dn.ua
+dnepropetrovsk.ua
+dnipropetrovsk.ua
+donetsk.ua
+dp.ua
+if.ua
+ivano-frankivsk.ua
+kh.ua
+kharkiv.ua
+kharkov.ua
+kherson.ua
+khmelnitskiy.ua
+khmelnytskyi.ua
+kiev.ua
+kirovograd.ua
+km.ua
+kr.ua
+kropyvnytskyi.ua
+krym.ua
+ks.ua
+kv.ua
+kyiv.ua
+lg.ua
+lt.ua
+lugansk.ua
+luhansk.ua
+lutsk.ua
+lv.ua
+lviv.ua
+mk.ua
+mykolaiv.ua
+nikolaev.ua
+od.ua
+odesa.ua
+odessa.ua
+pl.ua
+poltava.ua
+rivne.ua
+rovno.ua
+rv.ua
+sb.ua
+sebastopol.ua
+sevastopol.ua
+sm.ua
+sumy.ua
+te.ua
+ternopil.ua
+uz.ua
+uzhgorod.ua
+uzhhorod.ua
+vinnica.ua
+vinnytsia.ua
+vn.ua
+volyn.ua
+yalta.ua
+zakarpattia.ua
+zaporizhzhe.ua
+zaporizhzhia.ua
+zhitomir.ua
+zhytomyr.ua
+zp.ua
+zt.ua
+
+// ug : https://www.registry.co.ug/
+// https://www.registry.co.ug, https://whois.co.ug
+// Confirmed by registry 2025-01-20
+ug
+ac.ug
+co.ug
+com.ug
+edu.ug
+go.ug
+gov.ug
+mil.ug
+ne.ug
+or.ug
+org.ug
+sc.ug
+us.ug
+
+// uk : https://www.iana.org/domains/root/db/uk.html
+// Submitted by registry
+uk
+ac.uk
+co.uk
+gov.uk
+ltd.uk
+me.uk
+net.uk
+nhs.uk
+org.uk
+plc.uk
+police.uk
+*.sch.uk
+
+// us : https://www.iana.org/domains/root/db/us.html
+// Confirmed via the .us zone file by William Harrison 2024-12-10
+us
+dni.us
+isa.us
+nsn.us
+// Geographic Names
+ak.us
+al.us
+ar.us
+as.us
+az.us
+ca.us
+co.us
+ct.us
+dc.us
+de.us
+fl.us
+ga.us
+gu.us
+hi.us
+ia.us
+id.us
+il.us
+in.us
+ks.us
+ky.us
+la.us
+ma.us
+md.us
+me.us
+mi.us
+mn.us
+mo.us
+ms.us
+mt.us
+nc.us
+nd.us
+ne.us
+nh.us
+nj.us
+nm.us
+nv.us
+ny.us
+oh.us
+ok.us
+or.us
+pa.us
+pr.us
+ri.us
+sc.us
+sd.us
+tn.us
+tx.us
+ut.us
+va.us
+vi.us
+vt.us
+wa.us
+wi.us
+wv.us
+wy.us
+// The registrar notes several more specific domains available in each state,
+// such as state.*.us, dst.*.us, etc., but resolution of these is somewhat
+// haphazard; in some states these domains resolve as addresses, while in others
+// only subdomains are available, or even nothing at all. We include the
+// most common ones where it's clear that different sites are different
+// entities.
+k12.ak.us
+k12.al.us
+k12.ar.us
+k12.as.us
+k12.az.us
+k12.ca.us
+k12.co.us
+k12.ct.us
+k12.dc.us
+k12.fl.us
+k12.ga.us
+k12.gu.us
+// k12.hi.us - Bug 614565 - Hawaii has a state-wide DOE login
+k12.ia.us
+k12.id.us
+k12.il.us
+k12.in.us
+k12.ks.us
+k12.ky.us
+k12.la.us
+k12.ma.us
+k12.md.us
+k12.me.us
+k12.mi.us
+k12.mn.us
+k12.mo.us
+k12.ms.us
+k12.mt.us
+k12.nc.us
+// k12.nd.us - Bug 1028347 - Removed at request of Travis Rosso
+k12.ne.us
+k12.nh.us
+k12.nj.us
+k12.nm.us
+k12.nv.us
+k12.ny.us
+k12.oh.us
+k12.ok.us
+k12.or.us
+k12.pa.us
+k12.pr.us
+// k12.ri.us - Removed at request of Kim Cournoyer
+k12.sc.us
+// k12.sd.us - Bug 934131 - Removed at request of James Booze
+k12.tn.us
+k12.tx.us
+k12.ut.us
+k12.va.us
+k12.vi.us
+k12.vt.us
+k12.wa.us
+k12.wi.us
+// k12.wv.us - Bug 947705 - Removed at request of Verne Britton
+cc.ak.us
+lib.ak.us
+cc.al.us
+lib.al.us
+cc.ar.us
+lib.ar.us
+cc.as.us
+lib.as.us
+cc.az.us
+lib.az.us
+cc.ca.us
+lib.ca.us
+cc.co.us
+lib.co.us
+cc.ct.us
+lib.ct.us
+cc.dc.us
+lib.dc.us
+cc.de.us
+cc.fl.us
+lib.fl.us
+cc.ga.us
+lib.ga.us
+cc.gu.us
+lib.gu.us
+cc.hi.us
+lib.hi.us
+cc.ia.us
+lib.ia.us
+cc.id.us
+lib.id.us
+cc.il.us
+lib.il.us
+cc.in.us
+lib.in.us
+cc.ks.us
+lib.ks.us
+cc.ky.us
+lib.ky.us
+cc.la.us
+lib.la.us
+cc.ma.us
+lib.ma.us
+cc.md.us
+lib.md.us
+cc.me.us
+lib.me.us
+cc.mi.us
+lib.mi.us
+cc.mn.us
+lib.mn.us
+cc.mo.us
+lib.mo.us
+cc.ms.us
+cc.mt.us
+lib.mt.us
+cc.nc.us
+lib.nc.us
+cc.nd.us
+lib.nd.us
+cc.ne.us
+lib.ne.us
+cc.nh.us
+lib.nh.us
+cc.nj.us
+lib.nj.us
+cc.nm.us
+lib.nm.us
+cc.nv.us
+lib.nv.us
+cc.ny.us
+lib.ny.us
+cc.oh.us
+lib.oh.us
+cc.ok.us
+lib.ok.us
+cc.or.us
+lib.or.us
+cc.pa.us
+lib.pa.us
+cc.pr.us
+lib.pr.us
+cc.ri.us
+lib.ri.us
+cc.sc.us
+lib.sc.us
+cc.sd.us
+lib.sd.us
+cc.tn.us
+lib.tn.us
+cc.tx.us
+lib.tx.us
+cc.ut.us
+lib.ut.us
+cc.va.us
+lib.va.us
+cc.vi.us
+lib.vi.us
+cc.vt.us
+lib.vt.us
+cc.wa.us
+lib.wa.us
+cc.wi.us
+lib.wi.us
+cc.wv.us
+cc.wy.us
+k12.wy.us
+// lib.wv.us - Bug 941670 - Removed at request of Larry W Arnold
+lib.wy.us
+// k12.ma.us contains school districts in Massachusetts. The 4LDs are
+// managed independently except for private (PVT), charter (CHTR) and
+// parochial (PAROCH) schools. Those are delegated directly to the
+// 5LD operators.
+chtr.k12.ma.us
+paroch.k12.ma.us
+pvt.k12.ma.us
+// Merit Network, Inc. maintains the registry for =~ /(k12|cc|lib).mi.us/ and the following
+// see also: https://domreg.merit.edu : domreg@merit.edu
+// see also: whois -h whois.domreg.merit.edu help
+ann-arbor.mi.us
+cog.mi.us
+dst.mi.us
+eaton.mi.us
+gen.mi.us
+mus.mi.us
+tec.mi.us
+washtenaw.mi.us
+
+// uy : http://www.nic.org.uy/
+uy
+com.uy
+edu.uy
+gub.uy
+mil.uy
+net.uy
+org.uy
+
+// uz : http://www.reg.uz/
+uz
+co.uz
+com.uz
+net.uz
+org.uz
+
+// va : https://www.iana.org/domains/root/db/va.html
+va
+
+// vc : https://www.iana.org/domains/root/db/vc.html
+// Submitted by registry
+vc
+com.vc
+edu.vc
+gov.vc
+mil.vc
+net.vc
+org.vc
+
+// ve : https://registro.nic.ve/
+// https://nic.ve/site/user-agreement -> under "III. Clasificación de Nombres de Dominio"
+// Submitted by registry nic@nic.ve and nicve@conatel.gob.ve
+ve
+arts.ve
+bib.ve
+co.ve
+com.ve
+e12.ve
+edu.ve
+emprende.ve
+firm.ve
+gob.ve
+gov.ve
+ia.ve
+info.ve
+int.ve
+mil.ve
+net.ve
+nom.ve
+org.ve
+rar.ve
+rec.ve
+store.ve
+tec.ve
+web.ve
+
+// vg : https://www.iana.org/domains/root/db/vg.html
+// Confirmed by registry 2025-01-10
+vg
+edu.vg
+
+// vi : https://www.iana.org/domains/root/db/vi.html
+vi
+co.vi
+com.vi
+k12.vi
+net.vi
+org.vi
+
+// vn : https://www.vnnic.vn/en/domain/cctld-vn
+// https://vnnic.vn/sites/default/files/tailieu/vn.cctld.domains.txt
+vn
+ac.vn
+ai.vn
+biz.vn
+com.vn
+edu.vn
+gov.vn
+health.vn
+id.vn
+info.vn
+int.vn
+io.vn
+name.vn
+net.vn
+org.vn
+pro.vn
+
+// vn geographical names
+angiang.vn
+bacgiang.vn
+backan.vn
+baclieu.vn
+bacninh.vn
+baria-vungtau.vn
+bentre.vn
+binhdinh.vn
+binhduong.vn
+binhphuoc.vn
+binhthuan.vn
+camau.vn
+cantho.vn
+caobang.vn
+daklak.vn
+daknong.vn
+danang.vn
+dienbien.vn
+dongnai.vn
+dongthap.vn
+gialai.vn
+hagiang.vn
+haiduong.vn
+haiphong.vn
+hanam.vn
+hanoi.vn
+hatinh.vn
+haugiang.vn
+hoabinh.vn
+hungyen.vn
+khanhhoa.vn
+kiengiang.vn
+kontum.vn
+laichau.vn
+lamdong.vn
+langson.vn
+laocai.vn
+longan.vn
+namdinh.vn
+nghean.vn
+ninhbinh.vn
+ninhthuan.vn
+phutho.vn
+phuyen.vn
+quangbinh.vn
+quangnam.vn
+quangngai.vn
+quangninh.vn
+quangtri.vn
+soctrang.vn
+sonla.vn
+tayninh.vn
+thaibinh.vn
+thainguyen.vn
+thanhhoa.vn
+thanhphohochiminh.vn
+thuathienhue.vn
+tiengiang.vn
+travinh.vn
+tuyenquang.vn
+vinhlong.vn
+vinhphuc.vn
+yenbai.vn
+
+// vu : https://www.iana.org/domains/root/db/vu.html
+// http://www.vunic.vu/
+vu
+com.vu
+edu.vu
+net.vu
+org.vu
+
+// wf : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+wf
+
+// ws : https://www.iana.org/domains/root/db/ws.html
+// http://samoanic.ws/index.dhtml
+ws
+com.ws
+edu.ws
+gov.ws
+net.ws
+org.ws
+
+// yt : https://www.afnic.fr/wp-media/uploads/2022/12/afnic-naming-policy-2023-01-01.pdf
+yt
+
+// IDN ccTLDs
+// When submitting patches, please maintain a sort by ISO 3166 ccTLD, then
+// U-label, and follow this format:
+// // A-Label ("", [, variant info]) :
+// // [sponsoring org]
+// U-Label
+
+// xn--mgbaam7a8h ("Emerat", Arabic) : AE
+// http://nic.ae/english/arabicdomain/rules.jsp
+امارات
+
+// xn--y9a3aq ("hye", Armenian) : AM
+// ISOC AM (operated by .am Registry)
+հայ
+
+// xn--54b7fta0cc ("Bangla", Bangla) : BD
+বাংলা
+
+// xn--90ae ("bg", Bulgarian) : BG
+бг
+
+// xn--mgbcpq6gpa1a ("albahrain", Arabic) : BH
+البحرين
+
+// xn--90ais ("bel", Belarusian/Russian Cyrillic) : BY
+// Operated by .by registry
+бел
+
+// xn--fiqs8s ("Zhongguo/China", Chinese, Simplified) : CN
+// CNNIC
+// https://www.cnnic.cn/11/192/index.html
+中国
+
+// xn--fiqz9s ("Zhongguo/China", Chinese, Traditional) : CN
+// CNNIC
+// https://www.cnnic.com.cn/AU/MediaC/Announcement/201609/t20160905_54470.htm
+中國
+
+// xn--lgbbat1ad8j ("Algeria/Al Jazair", Arabic) : DZ
+الجزائر
+
+// xn--wgbh1c ("Egypt/Masr", Arabic) : EG
+// http://www.dotmasr.eg/
+مصر
+
+// xn--e1a4c ("eu", Cyrillic) : EU
+// https://eurid.eu
+ею
+
+// xn--qxa6a ("eu", Greek) : EU
+// https://eurid.eu
+ευ
+
+// xn--mgbah1a3hjkrd ("Mauritania", Arabic) : MR
+موريتانيا
+
+// xn--node ("ge", Georgian Mkhedruli) : GE
+გე
+
+// xn--qxam ("el", Greek) : GR
+// Hellenic Ministry of Infrastructure, Transport, and Networks
+ελ
+
+// xn--j6w193g ("Hong Kong", Chinese) : HK
+// https://www.hkirc.hk
+// Submitted by registry
+// https://www.hkirc.hk/content.jsp?id=30#!/34
+香港
+個人.香港
+公司.香港
+政府.香港
+教育.香港
+組織.香港
+網絡.香港
+
+// xn--2scrj9c ("Bharat", Kannada) : IN
+// India
+ಭಾರತ
+
+// xn--3hcrj9c ("Bharat", Oriya) : IN
+// India
+ଭାରତ
+
+// xn--45br5cyl ("Bharatam", Assamese) : IN
+// India
+ভাৰত
+
+// xn--h2breg3eve ("Bharatam", Sanskrit) : IN
+// India
+भारतम्
+
+// xn--h2brj9c8c ("Bharot", Santali) : IN
+// India
+भारोत
+
+// xn--mgbgu82a ("Bharat", Sindhi) : IN
+// India
+ڀارت
+
+// xn--rvc1e0am3e ("Bharatam", Malayalam) : IN
+// India
+ഭാരതം
+
+// xn--h2brj9c ("Bharat", Devanagari) : IN
+// India
+भारत
+
+// xn--mgbbh1a ("Bharat", Kashmiri) : IN
+// India
+بارت
+
+// xn--mgbbh1a71e ("Bharat", Arabic) : IN
+// India
+بھارت
+
+// xn--fpcrj9c3d ("Bharat", Telugu) : IN
+// India
+భారత్
+
+// xn--gecrj9c ("Bharat", Gujarati) : IN
+// India
+ભારત
+
+// xn--s9brj9c ("Bharat", Gurmukhi) : IN
+// India
+ਭਾਰਤ
+
+// xn--45brj9c ("Bharat", Bengali) : IN
+// India
+ভারত
+
+// xn--xkc2dl3a5ee0h ("India", Tamil) : IN
+// India
+இந்தியா
+
+// xn--mgba3a4f16a ("Iran", Persian) : IR
+ایران
+
+// xn--mgba3a4fra ("Iran", Arabic) : IR
+ايران
+
+// xn--mgbtx2b ("Iraq", Arabic) : IQ
+// Communications and Media Commission
+عراق
+
+// xn--mgbayh7gpa ("al-Ordon", Arabic) : JO
+// National Information Technology Center (NITC)
+// Royal Scientific Society, Al-Jubeiha
+الاردن
+
+// xn--3e0b707e ("Republic of Korea", Hangul) : KR
+한국
+
+// xn--80ao21a ("Kaz", Kazakh) : KZ
+қаз
+
+// xn--q7ce6a ("Lao", Lao) : LA
+ລາວ
+
+// xn--fzc2c9e2c ("Lanka", Sinhalese-Sinhala) : LK
+// https://nic.lk
+ලංකා
+
+// xn--xkc2al3hye2a ("Ilangai", Tamil) : LK
+// https://nic.lk
+இலங்கை
+
+// xn--mgbc0a9azcg ("Morocco/al-Maghrib", Arabic) : MA
+المغرب
+
+// xn--d1alf ("mkd", Macedonian) : MK
+// MARnet
+мкд
+
+// xn--l1acc ("mon", Mongolian) : MN
+мон
+
+// xn--mix891f ("Macao", Chinese, Traditional) : MO
+// MONIC / HNET Asia (Registry Operator for .mo)
+澳門
+
+// xn--mix082f ("Macao", Chinese, Simplified) : MO
+澳门
+
+// xn--mgbx4cd0ab ("Malaysia", Malay) : MY
+مليسيا
+
+// xn--mgb9awbf ("Oman", Arabic) : OM
+عمان
+
+// xn--mgbai9azgqp6j ("Pakistan", Urdu/Arabic) : PK
+پاکستان
+
+// xn--mgbai9a5eva00b ("Pakistan", Urdu/Arabic, variant) : PK
+پاكستان
+
+// xn--ygbi2ammx ("Falasteen", Arabic) : PS
+// The Palestinian National Internet Naming Authority (PNINA)
+// http://www.pnina.ps
+فلسطين
+
+// xn--90a3ac ("srb", Cyrillic) : RS
+// https://www.rnids.rs/en/domains/national-domains
+срб
+ак.срб
+обр.срб
+од.срб
+орг.срб
+пр.срб
+упр.срб
+
+// xn--p1ai ("rf", Russian-Cyrillic) : RU
+// https://cctld.ru/files/pdf/docs/en/rules_ru-rf.pdf
+// Submitted by George Georgievsky
+рф
+
+// xn--wgbl6a ("Qatar", Arabic) : QA
+// http://www.ict.gov.qa/
+قطر
+
+// xn--mgberp4a5d4ar ("AlSaudiah", Arabic) : SA
+// http://www.nic.net.sa/
+السعودية
+
+// xn--mgberp4a5d4a87g ("AlSaudiah", Arabic, variant): SA
+السعودیة
+
+// xn--mgbqly7c0a67fbc ("AlSaudiah", Arabic, variant) : SA
+السعودیۃ
+
+// xn--mgbqly7cvafr ("AlSaudiah", Arabic, variant) : SA
+السعوديه
+
+// xn--mgbpl2fh ("sudan", Arabic) : SD
+// Operated by .sd registry
+سودان
+
+// xn--yfro4i67o Singapore ("Singapore", Chinese) : SG
+新加坡
+
+// xn--clchc0ea0b2g2a9gcd ("Singapore", Tamil) : SG
+சிங்கப்பூர்
+
+// xn--ogbpf8fl ("Syria", Arabic) : SY
+سورية
+
+// xn--mgbtf8fl ("Syria", Arabic, variant) : SY
+سوريا
+
+// xn--o3cw4h ("Thai", Thai) : TH
+// http://www.thnic.co.th
+ไทย
+ทหาร.ไทย
+ธุรกิจ.ไทย
+เน็ต.ไทย
+รัฐบาล.ไทย
+ศึกษา.ไทย
+องค์กร.ไทย
+
+// xn--pgbs0dh ("Tunisia", Arabic) : TN
+// http://nic.tn
+تونس
+
+// xn--kpry57d ("Taiwan", Chinese, Traditional) : TW
+// https://twnic.tw/dnservice_catag.php
+台灣
+
+// xn--kprw13d ("Taiwan", Chinese, Simplified) : TW
+// http://www.twnic.net/english/dn/dn_07a.htm
+台湾
+
+// xn--nnx388a ("Taiwan", Chinese, variant) : TW
+臺灣
+
+// xn--j1amh ("ukr", Cyrillic) : UA
+укр
+
+// xn--mgb2ddes ("AlYemen", Arabic) : YE
+اليمن
+
+// xxx : http://icmregistry.com
+xxx
+
+// ye : http://www.y.net.ye/services/domain_name.htm
+ye
+com.ye
+edu.ye
+gov.ye
+mil.ye
+net.ye
+org.ye
+
+// za : https://www.iana.org/domains/root/db/za.html
+ac.za
+agric.za
+alt.za
+co.za
+edu.za
+gov.za
+grondar.za
+law.za
+mil.za
+net.za
+ngo.za
+nic.za
+nis.za
+nom.za
+org.za
+school.za
+tm.za
+web.za
+
+// zm : https://zicta.zm/
+// Submitted by registry
+zm
+ac.zm
+biz.zm
+co.zm
+com.zm
+edu.zm
+gov.zm
+info.zm
+mil.zm
+net.zm
+org.zm
+sch.zm
+
+// zw : https://www.potraz.gov.zw/
+// Confirmed by registry 2017-01-25
+zw
+ac.zw
+co.zw
+gov.zw
+mil.zw
+org.zw
+
+// newGTLDs
+
+// List of new gTLDs imported from https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 2026-01-29T15:35:06Z
+// This list is auto-generated, don't edit it manually.
+// aaa : American Automobile Association, Inc.
+// https://www.iana.org/domains/root/db/aaa.html
+aaa
+
+// aarp : AARP
+// https://www.iana.org/domains/root/db/aarp.html
+aarp
+
+// abb : ABB Ltd
+// https://www.iana.org/domains/root/db/abb.html
+abb
+
+// abbott : Abbott Laboratories, Inc.
+// https://www.iana.org/domains/root/db/abbott.html
+abbott
+
+// abbvie : AbbVie Inc.
+// https://www.iana.org/domains/root/db/abbvie.html
+abbvie
+
+// abc : Disney Enterprises, Inc.
+// https://www.iana.org/domains/root/db/abc.html
+abc
+
+// able : Able Inc.
+// https://www.iana.org/domains/root/db/able.html
+able
+
+// abogado : Registry Services, LLC
+// https://www.iana.org/domains/root/db/abogado.html
+abogado
+
+// abudhabi : Abu Dhabi Systems and Information Centre
+// https://www.iana.org/domains/root/db/abudhabi.html
+abudhabi
+
+// academy : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/academy.html
+academy
+
+// accenture : Accenture plc
+// https://www.iana.org/domains/root/db/accenture.html
+accenture
+
+// accountant : dot Accountant Limited
+// https://www.iana.org/domains/root/db/accountant.html
+accountant
+
+// accountants : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/accountants.html
+accountants
+
+// aco : ACO Severin Ahlmann GmbH & Co. KG
+// https://www.iana.org/domains/root/db/aco.html
+aco
+
+// actor : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/actor.html
+actor
+
+// ads : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/ads.html
+ads
+
+// adult : ICM Registry AD LLC
+// https://www.iana.org/domains/root/db/adult.html
+adult
+
+// aeg : Aktiebolaget Electrolux
+// https://www.iana.org/domains/root/db/aeg.html
+aeg
+
+// aetna : Aetna Life Insurance Company
+// https://www.iana.org/domains/root/db/aetna.html
+aetna
+
+// afl : Australian Football League
+// https://www.iana.org/domains/root/db/afl.html
+afl
+
+// africa : ZA Central Registry NPC trading as Registry.Africa
+// https://www.iana.org/domains/root/db/africa.html
+africa
+
+// agakhan : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/agakhan.html
+agakhan
+
+// agency : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/agency.html
+agency
+
+// aig : American International Group, Inc.
+// https://www.iana.org/domains/root/db/aig.html
+aig
+
+// airbus : Airbus S.A.S.
+// https://www.iana.org/domains/root/db/airbus.html
+airbus
+
+// airforce : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/airforce.html
+airforce
+
+// airtel : Bharti Airtel Limited
+// https://www.iana.org/domains/root/db/airtel.html
+airtel
+
+// akdn : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/akdn.html
+akdn
+
+// alibaba : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/alibaba.html
+alibaba
+
+// alipay : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/alipay.html
+alipay
+
+// allfinanz : Allfinanz Deutsche Vermögensberatung Aktiengesellschaft
+// https://www.iana.org/domains/root/db/allfinanz.html
+allfinanz
+
+// allstate : Allstate Fire and Casualty Insurance Company
+// https://www.iana.org/domains/root/db/allstate.html
+allstate
+
+// ally : Ally Financial Inc.
+// https://www.iana.org/domains/root/db/ally.html
+ally
+
+// alsace : Region Grand Est
+// https://www.iana.org/domains/root/db/alsace.html
+alsace
+
+// alstom : ALSTOM
+// https://www.iana.org/domains/root/db/alstom.html
+alstom
+
+// amazon : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/amazon.html
+amazon
+
+// americanexpress : American Express Travel Related Services Company, Inc.
+// https://www.iana.org/domains/root/db/americanexpress.html
+americanexpress
+
+// americanfamily : AmFam, Inc.
+// https://www.iana.org/domains/root/db/americanfamily.html
+americanfamily
+
+// amex : American Express Travel Related Services Company, Inc.
+// https://www.iana.org/domains/root/db/amex.html
+amex
+
+// amfam : AmFam, Inc.
+// https://www.iana.org/domains/root/db/amfam.html
+amfam
+
+// amica : Amica Mutual Insurance Company
+// https://www.iana.org/domains/root/db/amica.html
+amica
+
+// amsterdam : Gemeente Amsterdam
+// https://www.iana.org/domains/root/db/amsterdam.html
+amsterdam
+
+// analytics : Campus IP LLC
+// https://www.iana.org/domains/root/db/analytics.html
+analytics
+
+// android : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/android.html
+android
+
+// anquan : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/anquan.html
+anquan
+
+// anz : Australia and New Zealand Banking Group Limited
+// https://www.iana.org/domains/root/db/anz.html
+anz
+
+// aol : Yahoo Inc.
+// https://www.iana.org/domains/root/db/aol.html
+aol
+
+// apartments : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/apartments.html
+apartments
+
+// app : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/app.html
+app
+
+// apple : Apple Inc.
+// https://www.iana.org/domains/root/db/apple.html
+apple
+
+// aquarelle : Aquarelle.com
+// https://www.iana.org/domains/root/db/aquarelle.html
+aquarelle
+
+// arab : League of Arab States
+// https://www.iana.org/domains/root/db/arab.html
+arab
+
+// aramco : Aramco Services Company
+// https://www.iana.org/domains/root/db/aramco.html
+aramco
+
+// archi : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/archi.html
+archi
+
+// army : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/army.html
+army
+
+// art : UK Creative Ideas Limited
+// https://www.iana.org/domains/root/db/art.html
+art
+
+// arte : Association Relative à la Télévision Européenne G.E.I.E.
+// https://www.iana.org/domains/root/db/arte.html
+arte
+
+// asda : Asda Stores Limited
+// https://www.iana.org/domains/root/db/asda.html
+asda
+
+// associates : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/associates.html
+associates
+
+// athleta : The Gap, Inc.
+// https://www.iana.org/domains/root/db/athleta.html
+athleta
+
+// attorney : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/attorney.html
+attorney
+
+// auction : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/auction.html
+auction
+
+// audi : AUDI Aktiengesellschaft
+// https://www.iana.org/domains/root/db/audi.html
+audi
+
+// audible : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/audible.html
+audible
+
+// audio : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/audio.html
+audio
+
+// auspost : Australian Postal Corporation
+// https://www.iana.org/domains/root/db/auspost.html
+auspost
+
+// author : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/author.html
+author
+
+// auto : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/auto.html
+auto
+
+// autos : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/autos.html
+autos
+
+// aws : AWS Registry LLC
+// https://www.iana.org/domains/root/db/aws.html
+aws
+
+// axa : AXA Group Operations SAS
+// https://www.iana.org/domains/root/db/axa.html
+axa
+
+// azure : Microsoft Corporation
+// https://www.iana.org/domains/root/db/azure.html
+azure
+
+// baby : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/baby.html
+baby
+
+// baidu : Baidu, Inc.
+// https://www.iana.org/domains/root/db/baidu.html
+baidu
+
+// banamex : Citigroup Inc.
+// https://www.iana.org/domains/root/db/banamex.html
+banamex
+
+// band : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/band.html
+band
+
+// bank : fTLD Registry Services LLC
+// https://www.iana.org/domains/root/db/bank.html
+bank
+
+// bar : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+// https://www.iana.org/domains/root/db/bar.html
+bar
+
+// barcelona : Municipi de Barcelona
+// https://www.iana.org/domains/root/db/barcelona.html
+barcelona
+
+// barclaycard : Barclays Bank PLC
+// https://www.iana.org/domains/root/db/barclaycard.html
+barclaycard
+
+// barclays : Barclays Bank PLC
+// https://www.iana.org/domains/root/db/barclays.html
+barclays
+
+// barefoot : Gallo Vineyards, Inc.
+// https://www.iana.org/domains/root/db/barefoot.html
+barefoot
+
+// bargains : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/bargains.html
+bargains
+
+// baseball : MLB Advanced Media DH, LLC
+// https://www.iana.org/domains/root/db/baseball.html
+baseball
+
+// basketball : Fédération Internationale de Basketball (FIBA)
+// https://www.iana.org/domains/root/db/basketball.html
+basketball
+
+// bauhaus : Werkhaus GmbH
+// https://www.iana.org/domains/root/db/bauhaus.html
+bauhaus
+
+// bayern : Bayern Connect GmbH
+// https://www.iana.org/domains/root/db/bayern.html
+bayern
+
+// bbc : British Broadcasting Corporation
+// https://www.iana.org/domains/root/db/bbc.html
+bbc
+
+// bbt : BB&T Corporation
+// https://www.iana.org/domains/root/db/bbt.html
+bbt
+
+// bbva : BANCO BILBAO VIZCAYA ARGENTARIA, S.A.
+// https://www.iana.org/domains/root/db/bbva.html
+bbva
+
+// bcg : The Boston Consulting Group, Inc.
+// https://www.iana.org/domains/root/db/bcg.html
+bcg
+
+// bcn : Municipi de Barcelona
+// https://www.iana.org/domains/root/db/bcn.html
+bcn
+
+// beats : Beats Electronics, LLC
+// https://www.iana.org/domains/root/db/beats.html
+beats
+
+// beauty : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/beauty.html
+beauty
+
+// beer : Registry Services, LLC
+// https://www.iana.org/domains/root/db/beer.html
+beer
+
+// berlin : dotBERLIN GmbH & Co. KG
+// https://www.iana.org/domains/root/db/berlin.html
+berlin
+
+// best : BestTLD Pty Ltd
+// https://www.iana.org/domains/root/db/best.html
+best
+
+// bestbuy : BBY Solutions, Inc.
+// https://www.iana.org/domains/root/db/bestbuy.html
+bestbuy
+
+// bet : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/bet.html
+bet
+
+// bharti : Bharti Enterprises (Holding) Private Limited
+// https://www.iana.org/domains/root/db/bharti.html
+bharti
+
+// bible : American Bible Society
+// https://www.iana.org/domains/root/db/bible.html
+bible
+
+// bid : dot Bid Limited
+// https://www.iana.org/domains/root/db/bid.html
+bid
+
+// bike : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/bike.html
+bike
+
+// bing : Microsoft Corporation
+// https://www.iana.org/domains/root/db/bing.html
+bing
+
+// bingo : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/bingo.html
+bingo
+
+// bio : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/bio.html
+bio
+
+// black : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/black.html
+black
+
+// blackfriday : Registry Services, LLC
+// https://www.iana.org/domains/root/db/blackfriday.html
+blackfriday
+
+// blockbuster : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/blockbuster.html
+blockbuster
+
+// blog : Knock Knock WHOIS There, LLC
+// https://www.iana.org/domains/root/db/blog.html
+blog
+
+// bloomberg : Bloomberg IP Holdings LLC
+// https://www.iana.org/domains/root/db/bloomberg.html
+bloomberg
+
+// blue : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/blue.html
+blue
+
+// bms : Bristol-Myers Squibb Company
+// https://www.iana.org/domains/root/db/bms.html
+bms
+
+// bmw : Bayerische Motoren Werke Aktiengesellschaft
+// https://www.iana.org/domains/root/db/bmw.html
+bmw
+
+// bnpparibas : BNP Paribas
+// https://www.iana.org/domains/root/db/bnpparibas.html
+bnpparibas
+
+// boats : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/boats.html
+boats
+
+// boehringer : Boehringer Ingelheim International GmbH
+// https://www.iana.org/domains/root/db/boehringer.html
+boehringer
+
+// bofa : Bank of America Corporation
+// https://www.iana.org/domains/root/db/bofa.html
+bofa
+
+// bom : Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+// https://www.iana.org/domains/root/db/bom.html
+bom
+
+// bond : ShortDot SA
+// https://www.iana.org/domains/root/db/bond.html
+bond
+
+// boo : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/boo.html
+boo
+
+// book : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/book.html
+book
+
+// booking : Booking.com B.V.
+// https://www.iana.org/domains/root/db/booking.html
+booking
+
+// bosch : Robert Bosch GMBH
+// https://www.iana.org/domains/root/db/bosch.html
+bosch
+
+// bostik : Bostik SA
+// https://www.iana.org/domains/root/db/bostik.html
+bostik
+
+// boston : Registry Services, LLC
+// https://www.iana.org/domains/root/db/boston.html
+boston
+
+// bot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/bot.html
+bot
+
+// boutique : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/boutique.html
+boutique
+
+// box : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/box.html
+box
+
+// bradesco : Banco Bradesco S.A.
+// https://www.iana.org/domains/root/db/bradesco.html
+bradesco
+
+// bridgestone : Bridgestone Corporation
+// https://www.iana.org/domains/root/db/bridgestone.html
+bridgestone
+
+// broadway : Celebrate Broadway, Inc.
+// https://www.iana.org/domains/root/db/broadway.html
+broadway
+
+// broker : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/broker.html
+broker
+
+// brother : Brother Industries, Ltd.
+// https://www.iana.org/domains/root/db/brother.html
+brother
+
+// brussels : DNS.be vzw
+// https://www.iana.org/domains/root/db/brussels.html
+brussels
+
+// build : Plan Bee LLC
+// https://www.iana.org/domains/root/db/build.html
+build
+
+// builders : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/builders.html
+builders
+
+// business : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/business.html
+business
+
+// buy : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/buy.html
+buy
+
+// buzz : DOTSTRATEGY CO.
+// https://www.iana.org/domains/root/db/buzz.html
+buzz
+
+// bzh : Association www.bzh
+// https://www.iana.org/domains/root/db/bzh.html
+bzh
+
+// cab : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cab.html
+cab
+
+// cafe : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cafe.html
+cafe
+
+// cal : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/cal.html
+cal
+
+// call : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/call.html
+call
+
+// calvinklein : PVH gTLD Holdings LLC
+// https://www.iana.org/domains/root/db/calvinklein.html
+calvinklein
+
+// cam : Cam Connecting SARL
+// https://www.iana.org/domains/root/db/cam.html
+cam
+
+// camera : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/camera.html
+camera
+
+// camp : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/camp.html
+camp
+
+// canon : Canon Inc.
+// https://www.iana.org/domains/root/db/canon.html
+canon
+
+// capetown : ZA Central Registry NPC trading as ZA Central Registry
+// https://www.iana.org/domains/root/db/capetown.html
+capetown
+
+// capital : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/capital.html
+capital
+
+// capitalone : Capital One Financial Corporation
+// https://www.iana.org/domains/root/db/capitalone.html
+capitalone
+
+// car : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/car.html
+car
+
+// caravan : Caravan International, Inc.
+// https://www.iana.org/domains/root/db/caravan.html
+caravan
+
+// cards : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cards.html
+cards
+
+// care : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/care.html
+care
+
+// career : dotCareer LLC
+// https://www.iana.org/domains/root/db/career.html
+career
+
+// careers : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/careers.html
+careers
+
+// cars : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/cars.html
+cars
+
+// casa : Registry Services, LLC
+// https://www.iana.org/domains/root/db/casa.html
+casa
+
+// case : Digity, LLC
+// https://www.iana.org/domains/root/db/case.html
+case
+
+// cash : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cash.html
+cash
+
+// casino : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/casino.html
+casino
+
+// catering : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/catering.html
+catering
+
+// catholic : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/catholic.html
+catholic
+
+// cba : COMMONWEALTH BANK OF AUSTRALIA
+// https://www.iana.org/domains/root/db/cba.html
+cba
+
+// cbn : The Christian Broadcasting Network, Inc.
+// https://www.iana.org/domains/root/db/cbn.html
+cbn
+
+// cbre : CBRE, Inc.
+// https://www.iana.org/domains/root/db/cbre.html
+cbre
+
+// center : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/center.html
+center
+
+// ceo : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/ceo.html
+ceo
+
+// cern : European Organization for Nuclear Research ("CERN")
+// https://www.iana.org/domains/root/db/cern.html
+cern
+
+// cfa : CFA Institute
+// https://www.iana.org/domains/root/db/cfa.html
+cfa
+
+// cfd : ShortDot SA
+// https://www.iana.org/domains/root/db/cfd.html
+cfd
+
+// chanel : Chanel International B.V.
+// https://www.iana.org/domains/root/db/chanel.html
+chanel
+
+// channel : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/channel.html
+channel
+
+// charity : Public Interest Registry
+// https://www.iana.org/domains/root/db/charity.html
+charity
+
+// chase : JPMorgan Chase Bank, National Association
+// https://www.iana.org/domains/root/db/chase.html
+chase
+
+// chat : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/chat.html
+chat
+
+// cheap : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cheap.html
+cheap
+
+// chintai : CHINTAI Corporation
+// https://www.iana.org/domains/root/db/chintai.html
+chintai
+
+// christmas : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/christmas.html
+christmas
+
+// chrome : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/chrome.html
+chrome
+
+// church : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/church.html
+church
+
+// cipriani : Hotel Cipriani Srl
+// https://www.iana.org/domains/root/db/cipriani.html
+cipriani
+
+// circle : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/circle.html
+circle
+
+// cisco : Cisco Technology, Inc.
+// https://www.iana.org/domains/root/db/cisco.html
+cisco
+
+// citadel : Citadel Domain LLC
+// https://www.iana.org/domains/root/db/citadel.html
+citadel
+
+// citi : Citigroup Inc.
+// https://www.iana.org/domains/root/db/citi.html
+citi
+
+// citic : CITIC Group Corporation
+// https://www.iana.org/domains/root/db/citic.html
+citic
+
+// city : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/city.html
+city
+
+// claims : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/claims.html
+claims
+
+// cleaning : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cleaning.html
+cleaning
+
+// click : Waterford Limited
+// https://www.iana.org/domains/root/db/click.html
+click
+
+// clinic : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/clinic.html
+clinic
+
+// clinique : The Estée Lauder Companies Inc.
+// https://www.iana.org/domains/root/db/clinique.html
+clinique
+
+// clothing : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/clothing.html
+clothing
+
+// cloud : Aruba PEC S.p.A.
+// https://www.iana.org/domains/root/db/cloud.html
+cloud
+
+// club : Registry Services, LLC
+// https://www.iana.org/domains/root/db/club.html
+club
+
+// clubmed : Club Méditerranée S.A.
+// https://www.iana.org/domains/root/db/clubmed.html
+clubmed
+
+// coach : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/coach.html
+coach
+
+// codes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/codes.html
+codes
+
+// coffee : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/coffee.html
+coffee
+
+// college : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/college.html
+college
+
+// cologne : dotKoeln GmbH
+// https://www.iana.org/domains/root/db/cologne.html
+cologne
+
+// commbank : COMMONWEALTH BANK OF AUSTRALIA
+// https://www.iana.org/domains/root/db/commbank.html
+commbank
+
+// community : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/community.html
+community
+
+// company : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/company.html
+company
+
+// compare : Registry Services, LLC
+// https://www.iana.org/domains/root/db/compare.html
+compare
+
+// computer : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/computer.html
+computer
+
+// comsec : VeriSign, Inc.
+// https://www.iana.org/domains/root/db/comsec.html
+comsec
+
+// condos : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/condos.html
+condos
+
+// construction : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/construction.html
+construction
+
+// consulting : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/consulting.html
+consulting
+
+// contact : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/contact.html
+contact
+
+// contractors : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/contractors.html
+contractors
+
+// cooking : Registry Services, LLC
+// https://www.iana.org/domains/root/db/cooking.html
+cooking
+
+// cool : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cool.html
+cool
+
+// corsica : Collectivité de Corse
+// https://www.iana.org/domains/root/db/corsica.html
+corsica
+
+// country : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/country.html
+country
+
+// coupon : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/coupon.html
+coupon
+
+// coupons : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/coupons.html
+coupons
+
+// courses : Registry Services, LLC
+// https://www.iana.org/domains/root/db/courses.html
+courses
+
+// cpa : American Institute of Certified Public Accountants
+// https://www.iana.org/domains/root/db/cpa.html
+cpa
+
+// credit : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/credit.html
+credit
+
+// creditcard : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/creditcard.html
+creditcard
+
+// creditunion : DotCooperation LLC
+// https://www.iana.org/domains/root/db/creditunion.html
+creditunion
+
+// cricket : dot Cricket Limited
+// https://www.iana.org/domains/root/db/cricket.html
+cricket
+
+// crown : Crown Equipment Corporation
+// https://www.iana.org/domains/root/db/crown.html
+crown
+
+// crs : Federated Co-operatives Limited
+// https://www.iana.org/domains/root/db/crs.html
+crs
+
+// cruise : Viking River Cruises (Bermuda) Ltd.
+// https://www.iana.org/domains/root/db/cruise.html
+cruise
+
+// cruises : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/cruises.html
+cruises
+
+// cuisinella : SCHMIDT GROUPE S.A.S.
+// https://www.iana.org/domains/root/db/cuisinella.html
+cuisinella
+
+// cymru : Nominet UK
+// https://www.iana.org/domains/root/db/cymru.html
+cymru
+
+// cyou : ShortDot SA
+// https://www.iana.org/domains/root/db/cyou.html
+cyou
+
+// dad : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/dad.html
+dad
+
+// dance : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/dance.html
+dance
+
+// data : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/data.html
+data
+
+// date : dot Date Limited
+// https://www.iana.org/domains/root/db/date.html
+date
+
+// dating : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/dating.html
+dating
+
+// datsun : NISSAN MOTOR CO., LTD.
+// https://www.iana.org/domains/root/db/datsun.html
+datsun
+
+// day : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/day.html
+day
+
+// dclk : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/dclk.html
+dclk
+
+// dds : Registry Services, LLC
+// https://www.iana.org/domains/root/db/dds.html
+dds
+
+// deal : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/deal.html
+deal
+
+// dealer : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/dealer.html
+dealer
+
+// deals : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/deals.html
+deals
+
+// degree : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/degree.html
+degree
+
+// delivery : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/delivery.html
+delivery
+
+// dell : Dell Inc.
+// https://www.iana.org/domains/root/db/dell.html
+dell
+
+// deloitte : Deloitte Touche Tohmatsu
+// https://www.iana.org/domains/root/db/deloitte.html
+deloitte
+
+// delta : Delta Air Lines, Inc.
+// https://www.iana.org/domains/root/db/delta.html
+delta
+
+// democrat : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/democrat.html
+democrat
+
+// dental : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/dental.html
+dental
+
+// dentist : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/dentist.html
+dentist
+
+// desi
+// https://www.iana.org/domains/root/db/desi.html
+desi
+
+// design : Registry Services, LLC
+// https://www.iana.org/domains/root/db/design.html
+design
+
+// dev : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/dev.html
+dev
+
+// dhl : Deutsche Post AG
+// https://www.iana.org/domains/root/db/dhl.html
+dhl
+
+// diamonds : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/diamonds.html
+diamonds
+
+// diet : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/diet.html
+diet
+
+// digital : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/digital.html
+digital
+
+// direct : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/direct.html
+direct
+
+// directory : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/directory.html
+directory
+
+// discount : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/discount.html
+discount
+
+// discover : Discover Financial Services
+// https://www.iana.org/domains/root/db/discover.html
+discover
+
+// dish : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/dish.html
+dish
+
+// diy : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/diy.html
+diy
+
+// dnp : Dai Nippon Printing Co., Ltd.
+// https://www.iana.org/domains/root/db/dnp.html
+dnp
+
+// docs : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/docs.html
+docs
+
+// doctor : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/doctor.html
+doctor
+
+// dog : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/dog.html
+dog
+
+// domains : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/domains.html
+domains
+
+// dot : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/dot.html
+dot
+
+// download : dot Support Limited
+// https://www.iana.org/domains/root/db/download.html
+download
+
+// drive : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/drive.html
+drive
+
+// dtv : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/dtv.html
+dtv
+
+// dubai : Dubai Smart Government Department
+// https://www.iana.org/domains/root/db/dubai.html
+dubai
+
+// dupont : DuPont Specialty Products USA, LLC
+// https://www.iana.org/domains/root/db/dupont.html
+dupont
+
+// durban : ZA Central Registry NPC trading as ZA Central Registry
+// https://www.iana.org/domains/root/db/durban.html
+durban
+
+// dvag : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/dvag.html
+dvag
+
+// dvr : DISH Technologies L.L.C.
+// https://www.iana.org/domains/root/db/dvr.html
+dvr
+
+// earth : Interlink Systems Innovation Institute K.K.
+// https://www.iana.org/domains/root/db/earth.html
+earth
+
+// eat : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/eat.html
+eat
+
+// eco : Big Room Inc.
+// https://www.iana.org/domains/root/db/eco.html
+eco
+
+// edeka : EDEKA Verband kaufmännischer Genossenschaften e.V.
+// https://www.iana.org/domains/root/db/edeka.html
+edeka
+
+// education : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/education.html
+education
+
+// email : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/email.html
+email
+
+// emerck : Merck KGaA
+// https://www.iana.org/domains/root/db/emerck.html
+emerck
+
+// energy : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/energy.html
+energy
+
+// engineer : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/engineer.html
+engineer
+
+// engineering : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/engineering.html
+engineering
+
+// enterprises : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/enterprises.html
+enterprises
+
+// epson : Seiko Epson Corporation
+// https://www.iana.org/domains/root/db/epson.html
+epson
+
+// equipment : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/equipment.html
+equipment
+
+// ericsson : Telefonaktiebolaget L M Ericsson
+// https://www.iana.org/domains/root/db/ericsson.html
+ericsson
+
+// erni : ERNI Group Holding AG
+// https://www.iana.org/domains/root/db/erni.html
+erni
+
+// esq : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/esq.html
+esq
+
+// estate : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/estate.html
+estate
+
+// eurovision : European Broadcasting Union (EBU)
+// https://www.iana.org/domains/root/db/eurovision.html
+eurovision
+
+// eus : Puntueus Fundazioa
+// https://www.iana.org/domains/root/db/eus.html
+eus
+
+// events : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/events.html
+events
+
+// exchange : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/exchange.html
+exchange
+
+// expert : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/expert.html
+expert
+
+// exposed : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/exposed.html
+exposed
+
+// express : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/express.html
+express
+
+// extraspace : Extra Space Storage LLC
+// https://www.iana.org/domains/root/db/extraspace.html
+extraspace
+
+// fage : Fage International S.A.
+// https://www.iana.org/domains/root/db/fage.html
+fage
+
+// fail : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fail.html
+fail
+
+// fairwinds : FairWinds Partners, LLC
+// https://www.iana.org/domains/root/db/fairwinds.html
+fairwinds
+
+// faith : dot Faith Limited
+// https://www.iana.org/domains/root/db/faith.html
+faith
+
+// family : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/family.html
+family
+
+// fan : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/fan.html
+fan
+
+// fans : ZDNS International Limited
+// https://www.iana.org/domains/root/db/fans.html
+fans
+
+// farm : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/farm.html
+farm
+
+// farmers : Farmers Insurance Exchange
+// https://www.iana.org/domains/root/db/farmers.html
+farmers
+
+// fashion : Registry Services, LLC
+// https://www.iana.org/domains/root/db/fashion.html
+fashion
+
+// fast : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/fast.html
+fast
+
+// fedex : Federal Express Corporation
+// https://www.iana.org/domains/root/db/fedex.html
+fedex
+
+// feedback : Top Level Spectrum, Inc.
+// https://www.iana.org/domains/root/db/feedback.html
+feedback
+
+// ferrari : Fiat Chrysler Automobiles N.V.
+// https://www.iana.org/domains/root/db/ferrari.html
+ferrari
+
+// ferrero : Ferrero Trading Lux S.A.
+// https://www.iana.org/domains/root/db/ferrero.html
+ferrero
+
+// fidelity : Fidelity Brokerage Services LLC
+// https://www.iana.org/domains/root/db/fidelity.html
+fidelity
+
+// fido : Rogers Communications Canada Inc.
+// https://www.iana.org/domains/root/db/fido.html
+fido
+
+// film : Motion Picture Domain Registry Pty Ltd
+// https://www.iana.org/domains/root/db/film.html
+film
+
+// final : Núcleo de Informação e Coordenação do Ponto BR - NIC.br
+// https://www.iana.org/domains/root/db/final.html
+final
+
+// finance : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/finance.html
+finance
+
+// financial : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/financial.html
+financial
+
+// fire : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/fire.html
+fire
+
+// firestone : Bridgestone Licensing Services, Inc
+// https://www.iana.org/domains/root/db/firestone.html
+firestone
+
+// firmdale : Firmdale Holdings Limited
+// https://www.iana.org/domains/root/db/firmdale.html
+firmdale
+
+// fish : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fish.html
+fish
+
+// fishing : Registry Services, LLC
+// https://www.iana.org/domains/root/db/fishing.html
+fishing
+
+// fit : Registry Services, LLC
+// https://www.iana.org/domains/root/db/fit.html
+fit
+
+// fitness : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fitness.html
+fitness
+
+// flickr : Flickr, Inc.
+// https://www.iana.org/domains/root/db/flickr.html
+flickr
+
+// flights : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/flights.html
+flights
+
+// flir : FLIR Systems, Inc.
+// https://www.iana.org/domains/root/db/flir.html
+flir
+
+// florist : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/florist.html
+florist
+
+// flowers : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/flowers.html
+flowers
+
+// fly : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/fly.html
+fly
+
+// foo : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/foo.html
+foo
+
+// food : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/food.html
+food
+
+// football : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/football.html
+football
+
+// ford : Ford Motor Company
+// https://www.iana.org/domains/root/db/ford.html
+ford
+
+// forex : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/forex.html
+forex
+
+// forsale : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/forsale.html
+forsale
+
+// forum : Waterford Limited
+// https://www.iana.org/domains/root/db/forum.html
+forum
+
+// foundation : Public Interest Registry
+// https://www.iana.org/domains/root/db/foundation.html
+foundation
+
+// fox : FOX Registry, LLC
+// https://www.iana.org/domains/root/db/fox.html
+fox
+
+// free : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/free.html
+free
+
+// fresenius : Fresenius Immobilien-Verwaltungs-GmbH
+// https://www.iana.org/domains/root/db/fresenius.html
+fresenius
+
+// frl : FRLregistry B.V.
+// https://www.iana.org/domains/root/db/frl.html
+frl
+
+// frogans : OP3FT
+// https://www.iana.org/domains/root/db/frogans.html
+frogans
+
+// frontier : Frontier Communications Corporation
+// https://www.iana.org/domains/root/db/frontier.html
+frontier
+
+// ftr : Frontier Communications Corporation
+// https://www.iana.org/domains/root/db/ftr.html
+ftr
+
+// fujitsu : Fujitsu Limited
+// https://www.iana.org/domains/root/db/fujitsu.html
+fujitsu
+
+// fun : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/fun.html
+fun
+
+// fund : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fund.html
+fund
+
+// furniture : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/furniture.html
+furniture
+
+// futbol : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/futbol.html
+futbol
+
+// fyi : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/fyi.html
+fyi
+
+// gal : Asociación puntoGAL
+// https://www.iana.org/domains/root/db/gal.html
+gal
+
+// gallery : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gallery.html
+gallery
+
+// gallo : Gallo Vineyards, Inc.
+// https://www.iana.org/domains/root/db/gallo.html
+gallo
+
+// gallup : Gallup, Inc.
+// https://www.iana.org/domains/root/db/gallup.html
+gallup
+
+// game : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/game.html
+game
+
+// games : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/games.html
+games
+
+// gap : The Gap, Inc.
+// https://www.iana.org/domains/root/db/gap.html
+gap
+
+// garden : Registry Services, LLC
+// https://www.iana.org/domains/root/db/garden.html
+garden
+
+// gay : Registry Services, LLC
+// https://www.iana.org/domains/root/db/gay.html
+gay
+
+// gbiz : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/gbiz.html
+gbiz
+
+// gdn : Joint Stock Company "Navigation-information systems"
+// https://www.iana.org/domains/root/db/gdn.html
+gdn
+
+// gea : GEA Group Aktiengesellschaft
+// https://www.iana.org/domains/root/db/gea.html
+gea
+
+// gent : Easyhost BV
+// https://www.iana.org/domains/root/db/gent.html
+gent
+
+// genting : Resorts World Inc Pte. Ltd.
+// https://www.iana.org/domains/root/db/genting.html
+genting
+
+// george : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/george.html
+george
+
+// ggee : GMO Internet, Inc.
+// https://www.iana.org/domains/root/db/ggee.html
+ggee
+
+// gift : DotGift, LLC
+// https://www.iana.org/domains/root/db/gift.html
+gift
+
+// gifts : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gifts.html
+gifts
+
+// gives : Public Interest Registry
+// https://www.iana.org/domains/root/db/gives.html
+gives
+
+// giving : Public Interest Registry
+// https://www.iana.org/domains/root/db/giving.html
+giving
+
+// glass : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/glass.html
+glass
+
+// gle : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/gle.html
+gle
+
+// global : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/global.html
+global
+
+// globo : Globo Comunicação e Participações S.A
+// https://www.iana.org/domains/root/db/globo.html
+globo
+
+// gmail : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/gmail.html
+gmail
+
+// gmbh : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gmbh.html
+gmbh
+
+// gmo : GMO Internet, Inc.
+// https://www.iana.org/domains/root/db/gmo.html
+gmo
+
+// gmx : 1&1 Mail & Media GmbH
+// https://www.iana.org/domains/root/db/gmx.html
+gmx
+
+// godaddy : Go Daddy East, LLC
+// https://www.iana.org/domains/root/db/godaddy.html
+godaddy
+
+// gold : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gold.html
+gold
+
+// goldpoint : YODOBASHI CAMERA CO.,LTD.
+// https://www.iana.org/domains/root/db/goldpoint.html
+goldpoint
+
+// golf : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/golf.html
+golf
+
+// goo : NTT DOCOMO, INC.
+// https://www.iana.org/domains/root/db/goo.html
+goo
+
+// goodyear : The Goodyear Tire & Rubber Company
+// https://www.iana.org/domains/root/db/goodyear.html
+goodyear
+
+// goog : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/goog.html
+goog
+
+// google : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/google.html
+google
+
+// gop : Republican State Leadership Committee, Inc.
+// https://www.iana.org/domains/root/db/gop.html
+gop
+
+// got : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/got.html
+got
+
+// grainger : Grainger Registry Services, LLC
+// https://www.iana.org/domains/root/db/grainger.html
+grainger
+
+// graphics : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/graphics.html
+graphics
+
+// gratis : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gratis.html
+gratis
+
+// green : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/green.html
+green
+
+// gripe : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/gripe.html
+gripe
+
+// grocery : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/grocery.html
+grocery
+
+// group : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/group.html
+group
+
+// gucci : Guccio Gucci S.p.a.
+// https://www.iana.org/domains/root/db/gucci.html
+gucci
+
+// guge : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/guge.html
+guge
+
+// guide : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/guide.html
+guide
+
+// guitars : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/guitars.html
+guitars
+
+// guru : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/guru.html
+guru
+
+// hair : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/hair.html
+hair
+
+// hamburg : Hamburg Top-Level-Domain GmbH
+// https://www.iana.org/domains/root/db/hamburg.html
+hamburg
+
+// hangout : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/hangout.html
+hangout
+
+// haus : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/haus.html
+haus
+
+// hbo : HBO Registry Services, Inc.
+// https://www.iana.org/domains/root/db/hbo.html
+hbo
+
+// hdfc : HDFC BANK LIMITED
+// https://www.iana.org/domains/root/db/hdfc.html
+hdfc
+
+// hdfcbank : HDFC BANK LIMITED
+// https://www.iana.org/domains/root/db/hdfcbank.html
+hdfcbank
+
+// health : Registry Services, LLC
+// https://www.iana.org/domains/root/db/health.html
+health
+
+// healthcare : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/healthcare.html
+healthcare
+
+// help : Innovation service Limited
+// https://www.iana.org/domains/root/db/help.html
+help
+
+// helsinki : City of Helsinki
+// https://www.iana.org/domains/root/db/helsinki.html
+helsinki
+
+// here : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/here.html
+here
+
+// hermes : HERMES INTERNATIONAL
+// https://www.iana.org/domains/root/db/hermes.html
+hermes
+
+// hiphop : Dot Hip Hop, LLC
+// https://www.iana.org/domains/root/db/hiphop.html
+hiphop
+
+// hisamitsu : Hisamitsu Pharmaceutical Co.,Inc.
+// https://www.iana.org/domains/root/db/hisamitsu.html
+hisamitsu
+
+// hitachi : Hitachi, Ltd.
+// https://www.iana.org/domains/root/db/hitachi.html
+hitachi
+
+// hiv : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/hiv.html
+hiv
+
+// hkt : PCCW-HKT DataCom Services Limited
+// https://www.iana.org/domains/root/db/hkt.html
+hkt
+
+// hockey : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/hockey.html
+hockey
+
+// holdings : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/holdings.html
+holdings
+
+// holiday : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/holiday.html
+holiday
+
+// homedepot : Home Depot Product Authority, LLC
+// https://www.iana.org/domains/root/db/homedepot.html
+homedepot
+
+// homegoods : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/homegoods.html
+homegoods
+
+// homes : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/homes.html
+homes
+
+// homesense : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/homesense.html
+homesense
+
+// honda : Honda Motor Co., Ltd.
+// https://www.iana.org/domains/root/db/honda.html
+honda
+
+// horse : Registry Services, LLC
+// https://www.iana.org/domains/root/db/horse.html
+horse
+
+// hospital : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/hospital.html
+hospital
+
+// host : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/host.html
+host
+
+// hosting : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/hosting.html
+hosting
+
+// hot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/hot.html
+hot
+
+// hotel : HOTEL Top-Level-Domain S.a.r.l
+// https://www.iana.org/domains/root/db/hotel.html
+hotel
+
+// hotels : Booking.com B.V.
+// https://www.iana.org/domains/root/db/hotels.html
+hotels
+
+// hotmail : Microsoft Corporation
+// https://www.iana.org/domains/root/db/hotmail.html
+hotmail
+
+// house : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/house.html
+house
+
+// how : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/how.html
+how
+
+// hsbc : HSBC Global Services (UK) Limited
+// https://www.iana.org/domains/root/db/hsbc.html
+hsbc
+
+// hughes : Hughes Satellite Systems Corporation
+// https://www.iana.org/domains/root/db/hughes.html
+hughes
+
+// hyatt : Hyatt GTLD, L.L.C.
+// https://www.iana.org/domains/root/db/hyatt.html
+hyatt
+
+// hyundai : Hyundai Motor Company
+// https://www.iana.org/domains/root/db/hyundai.html
+hyundai
+
+// ibm : International Business Machines Corporation
+// https://www.iana.org/domains/root/db/ibm.html
+ibm
+
+// icbc : Industrial and Commercial Bank of China Limited
+// https://www.iana.org/domains/root/db/icbc.html
+icbc
+
+// ice : IntercontinentalExchange, Inc.
+// https://www.iana.org/domains/root/db/ice.html
+ice
+
+// icu : ShortDot SA
+// https://www.iana.org/domains/root/db/icu.html
+icu
+
+// ieee : IEEE Global LLC
+// https://www.iana.org/domains/root/db/ieee.html
+ieee
+
+// ifm : ifm electronic gmbh
+// https://www.iana.org/domains/root/db/ifm.html
+ifm
+
+// ikano : Ikano S.A.
+// https://www.iana.org/domains/root/db/ikano.html
+ikano
+
+// imamat : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/imamat.html
+imamat
+
+// imdb : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/imdb.html
+imdb
+
+// immo : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/immo.html
+immo
+
+// immobilien : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/immobilien.html
+immobilien
+
+// inc : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/inc.html
+inc
+
+// industries : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/industries.html
+industries
+
+// infiniti : NISSAN MOTOR CO., LTD.
+// https://www.iana.org/domains/root/db/infiniti.html
+infiniti
+
+// ing : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/ing.html
+ing
+
+// ink : Registry Services, LLC
+// https://www.iana.org/domains/root/db/ink.html
+ink
+
+// institute : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/institute.html
+institute
+
+// insurance : fTLD Registry Services LLC
+// https://www.iana.org/domains/root/db/insurance.html
+insurance
+
+// insure : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/insure.html
+insure
+
+// international : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/international.html
+international
+
+// intuit : Intuit Administrative Services, Inc.
+// https://www.iana.org/domains/root/db/intuit.html
+intuit
+
+// investments : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/investments.html
+investments
+
+// ipiranga : Ipiranga Produtos de Petroleo S.A.
+// https://www.iana.org/domains/root/db/ipiranga.html
+ipiranga
+
+// irish : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/irish.html
+irish
+
+// ismaili : Fondation Aga Khan (Aga Khan Foundation)
+// https://www.iana.org/domains/root/db/ismaili.html
+ismaili
+
+// ist : Istanbul Metropolitan Municipality
+// https://www.iana.org/domains/root/db/ist.html
+ist
+
+// istanbul : Istanbul Metropolitan Municipality
+// https://www.iana.org/domains/root/db/istanbul.html
+istanbul
+
+// itau : Itau Unibanco Holding S.A.
+// https://www.iana.org/domains/root/db/itau.html
+itau
+
+// itv : ITV Services Limited
+// https://www.iana.org/domains/root/db/itv.html
+itv
+
+// jaguar : Jaguar Land Rover Ltd
+// https://www.iana.org/domains/root/db/jaguar.html
+jaguar
+
+// java : Oracle Corporation
+// https://www.iana.org/domains/root/db/java.html
+java
+
+// jcb : JCB Co., Ltd.
+// https://www.iana.org/domains/root/db/jcb.html
+jcb
+
+// jeep : FCA US LLC.
+// https://www.iana.org/domains/root/db/jeep.html
+jeep
+
+// jetzt : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/jetzt.html
+jetzt
+
+// jewelry : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/jewelry.html
+jewelry
+
+// jio : Reliance Industries Limited
+// https://www.iana.org/domains/root/db/jio.html
+jio
+
+// jll : Jones Lang LaSalle Incorporated
+// https://www.iana.org/domains/root/db/jll.html
+jll
+
+// jmp : Matrix IP LLC
+// https://www.iana.org/domains/root/db/jmp.html
+jmp
+
+// jnj : Johnson & Johnson Services, Inc.
+// https://www.iana.org/domains/root/db/jnj.html
+jnj
+
+// joburg : ZA Central Registry NPC trading as ZA Central Registry
+// https://www.iana.org/domains/root/db/joburg.html
+joburg
+
+// jot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/jot.html
+jot
+
+// joy : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/joy.html
+joy
+
+// jpmorgan : JPMorgan Chase Bank, National Association
+// https://www.iana.org/domains/root/db/jpmorgan.html
+jpmorgan
+
+// jprs : Japan Registry Services Co., Ltd.
+// https://www.iana.org/domains/root/db/jprs.html
+jprs
+
+// juegos : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/juegos.html
+juegos
+
+// juniper : JUNIPER NETWORKS, INC.
+// https://www.iana.org/domains/root/db/juniper.html
+juniper
+
+// kaufen : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/kaufen.html
+kaufen
+
+// kddi : KDDI CORPORATION
+// https://www.iana.org/domains/root/db/kddi.html
+kddi
+
+// kerryhotels : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kerryhotels.html
+kerryhotels
+
+// kerryproperties : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kerryproperties.html
+kerryproperties
+
+// kfh : Kuwait Finance House
+// https://www.iana.org/domains/root/db/kfh.html
+kfh
+
+// kia : KIA MOTORS CORPORATION
+// https://www.iana.org/domains/root/db/kia.html
+kia
+
+// kids : DotKids Foundation Limited
+// https://www.iana.org/domains/root/db/kids.html
+kids
+
+// kim : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/kim.html
+kim
+
+// kindle : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/kindle.html
+kindle
+
+// kitchen : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/kitchen.html
+kitchen
+
+// kiwi : DOT KIWI LIMITED
+// https://www.iana.org/domains/root/db/kiwi.html
+kiwi
+
+// koeln : dotKoeln GmbH
+// https://www.iana.org/domains/root/db/koeln.html
+koeln
+
+// komatsu : Komatsu Ltd.
+// https://www.iana.org/domains/root/db/komatsu.html
+komatsu
+
+// kosher : Kosher Marketing Assets LLC
+// https://www.iana.org/domains/root/db/kosher.html
+kosher
+
+// kpmg : KPMG International Cooperative (KPMG International Genossenschaft)
+// https://www.iana.org/domains/root/db/kpmg.html
+kpmg
+
+// kpn : Koninklijke KPN N.V.
+// https://www.iana.org/domains/root/db/kpn.html
+kpn
+
+// krd : KRG Department of Information Technology
+// https://www.iana.org/domains/root/db/krd.html
+krd
+
+// kred : KredTLD Pty Ltd
+// https://www.iana.org/domains/root/db/kred.html
+kred
+
+// kuokgroup : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/kuokgroup.html
+kuokgroup
+
+// kyoto : Academic Institution: Kyoto Jyoho Gakuen
+// https://www.iana.org/domains/root/db/kyoto.html
+kyoto
+
+// lacaixa : Fundación Bancaria Caixa d’Estalvis i Pensions de Barcelona, “la Caixa”
+// https://www.iana.org/domains/root/db/lacaixa.html
+lacaixa
+
+// lamborghini : Automobili Lamborghini S.p.A.
+// https://www.iana.org/domains/root/db/lamborghini.html
+lamborghini
+
+// lamer : The Estée Lauder Companies Inc.
+// https://www.iana.org/domains/root/db/lamer.html
+lamer
+
+// land : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/land.html
+land
+
+// landrover : Jaguar Land Rover Ltd
+// https://www.iana.org/domains/root/db/landrover.html
+landrover
+
+// lanxess : LANXESS Corporation
+// https://www.iana.org/domains/root/db/lanxess.html
+lanxess
+
+// lasalle : Jones Lang LaSalle Incorporated
+// https://www.iana.org/domains/root/db/lasalle.html
+lasalle
+
+// lat : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/lat.html
+lat
+
+// latino : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/latino.html
+latino
+
+// latrobe : La Trobe University
+// https://www.iana.org/domains/root/db/latrobe.html
+latrobe
+
+// law : Registry Services, LLC
+// https://www.iana.org/domains/root/db/law.html
+law
+
+// lawyer : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/lawyer.html
+lawyer
+
+// lds : IRI Domain Management, LLC
+// https://www.iana.org/domains/root/db/lds.html
+lds
+
+// lease : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/lease.html
+lease
+
+// leclerc : A.C.D. LEC Association des Centres Distributeurs Edouard Leclerc
+// https://www.iana.org/domains/root/db/leclerc.html
+leclerc
+
+// lefrak : LeFrak Organization, Inc.
+// https://www.iana.org/domains/root/db/lefrak.html
+lefrak
+
+// legal : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/legal.html
+legal
+
+// lego : LEGO Juris A/S
+// https://www.iana.org/domains/root/db/lego.html
+lego
+
+// lexus : TOYOTA MOTOR CORPORATION
+// https://www.iana.org/domains/root/db/lexus.html
+lexus
+
+// lgbt : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/lgbt.html
+lgbt
+
+// lidl : Schwarz Domains und Services GmbH & Co. KG
+// https://www.iana.org/domains/root/db/lidl.html
+lidl
+
+// life : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/life.html
+life
+
+// lifeinsurance : American Council of Life Insurers
+// https://www.iana.org/domains/root/db/lifeinsurance.html
+lifeinsurance
+
+// lifestyle : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/lifestyle.html
+lifestyle
+
+// lighting : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/lighting.html
+lighting
+
+// like : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/like.html
+like
+
+// lilly : Eli Lilly and Company
+// https://www.iana.org/domains/root/db/lilly.html
+lilly
+
+// limited : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/limited.html
+limited
+
+// limo : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/limo.html
+limo
+
+// lincoln : Ford Motor Company
+// https://www.iana.org/domains/root/db/lincoln.html
+lincoln
+
+// link : Nova Registry Ltd
+// https://www.iana.org/domains/root/db/link.html
+link
+
+// live : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/live.html
+live
+
+// living : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/living.html
+living
+
+// llc : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/llc.html
+llc
+
+// llp : Intercap Registry Inc.
+// https://www.iana.org/domains/root/db/llp.html
+llp
+
+// loan : dot Loan Limited
+// https://www.iana.org/domains/root/db/loan.html
+loan
+
+// loans : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/loans.html
+loans
+
+// locker : Orange Domains LLC
+// https://www.iana.org/domains/root/db/locker.html
+locker
+
+// locus : Locus Analytics LLC
+// https://www.iana.org/domains/root/db/locus.html
+locus
+
+// lol : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/lol.html
+lol
+
+// london : Dot London Domains Limited
+// https://www.iana.org/domains/root/db/london.html
+london
+
+// lotte : Lotte Holdings Co., Ltd.
+// https://www.iana.org/domains/root/db/lotte.html
+lotte
+
+// lotto : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/lotto.html
+lotto
+
+// love : Waterford Limited
+// https://www.iana.org/domains/root/db/love.html
+love
+
+// lpl : LPL Holdings, Inc.
+// https://www.iana.org/domains/root/db/lpl.html
+lpl
+
+// lplfinancial : LPL Holdings, Inc.
+// https://www.iana.org/domains/root/db/lplfinancial.html
+lplfinancial
+
+// ltd : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/ltd.html
+ltd
+
+// ltda : InterNetX, Corp
+// https://www.iana.org/domains/root/db/ltda.html
+ltda
+
+// lundbeck : H. Lundbeck A/S
+// https://www.iana.org/domains/root/db/lundbeck.html
+lundbeck
+
+// luxe : Registry Services, LLC
+// https://www.iana.org/domains/root/db/luxe.html
+luxe
+
+// luxury : Luxury Partners, LLC
+// https://www.iana.org/domains/root/db/luxury.html
+luxury
+
+// madrid : Comunidad de Madrid
+// https://www.iana.org/domains/root/db/madrid.html
+madrid
+
+// maif : Mutuelle Assurance Instituteur France (MAIF)
+// https://www.iana.org/domains/root/db/maif.html
+maif
+
+// maison : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/maison.html
+maison
+
+// makeup : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/makeup.html
+makeup
+
+// man : MAN Truck & Bus SE
+// https://www.iana.org/domains/root/db/man.html
+man
+
+// management : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/management.html
+management
+
+// mango : PUNTO FA S.L.
+// https://www.iana.org/domains/root/db/mango.html
+mango
+
+// map : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/map.html
+map
+
+// market : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/market.html
+market
+
+// marketing : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/marketing.html
+marketing
+
+// markets : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/markets.html
+markets
+
+// marriott : Marriott Worldwide Corporation
+// https://www.iana.org/domains/root/db/marriott.html
+marriott
+
+// marshalls : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/marshalls.html
+marshalls
+
+// mattel : Mattel IT Services, Inc.
+// https://www.iana.org/domains/root/db/mattel.html
+mattel
+
+// mba : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/mba.html
+mba
+
+// mckinsey : McKinsey Holdings, Inc.
+// https://www.iana.org/domains/root/db/mckinsey.html
+mckinsey
+
+// med : Medistry LLC
+// https://www.iana.org/domains/root/db/med.html
+med
+
+// media : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/media.html
+media
+
+// meet : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/meet.html
+meet
+
+// melbourne : The Crown in right of the State of Victoria, represented by its Department of State Development, Business and Innovation
+// https://www.iana.org/domains/root/db/melbourne.html
+melbourne
+
+// meme : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/meme.html
+meme
+
+// memorial : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/memorial.html
+memorial
+
+// men : Exclusive Registry Limited
+// https://www.iana.org/domains/root/db/men.html
+men
+
+// menu : Dot Menu Registry, LLC
+// https://www.iana.org/domains/root/db/menu.html
+menu
+
+// merck : Merck Registry Holdings, Inc.
+// https://www.iana.org/domains/root/db/merck.html
+merck
+
+// merckmsd : MSD Registry Holdings, Inc.
+// https://www.iana.org/domains/root/db/merckmsd.html
+merckmsd
+
+// miami : Registry Services, LLC
+// https://www.iana.org/domains/root/db/miami.html
+miami
+
+// microsoft : Microsoft Corporation
+// https://www.iana.org/domains/root/db/microsoft.html
+microsoft
+
+// mini : Bayerische Motoren Werke Aktiengesellschaft
+// https://www.iana.org/domains/root/db/mini.html
+mini
+
+// mint : Intuit Administrative Services, Inc.
+// https://www.iana.org/domains/root/db/mint.html
+mint
+
+// mit : Massachusetts Institute of Technology
+// https://www.iana.org/domains/root/db/mit.html
+mit
+
+// mitsubishi : Mitsubishi Corporation
+// https://www.iana.org/domains/root/db/mitsubishi.html
+mitsubishi
+
+// mlb : MLB Advanced Media DH, LLC
+// https://www.iana.org/domains/root/db/mlb.html
+mlb
+
+// mls : The Canadian Real Estate Association
+// https://www.iana.org/domains/root/db/mls.html
+mls
+
+// mma : MMA IARD
+// https://www.iana.org/domains/root/db/mma.html
+mma
+
+// mobile : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/mobile.html
+mobile
+
+// moda : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/moda.html
+moda
+
+// moe : Interlink Systems Innovation Institute K.K.
+// https://www.iana.org/domains/root/db/moe.html
+moe
+
+// moi : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/moi.html
+moi
+
+// mom : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/mom.html
+mom
+
+// monash : Monash University
+// https://www.iana.org/domains/root/db/monash.html
+monash
+
+// money : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/money.html
+money
+
+// monster : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/monster.html
+monster
+
+// mormon : IRI Domain Management, LLC
+// https://www.iana.org/domains/root/db/mormon.html
+mormon
+
+// mortgage : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/mortgage.html
+mortgage
+
+// moscow : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+// https://www.iana.org/domains/root/db/moscow.html
+moscow
+
+// moto : Motorola Trademark Holdings, LLC
+// https://www.iana.org/domains/root/db/moto.html
+moto
+
+// motorcycles : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/motorcycles.html
+motorcycles
+
+// mov : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/mov.html
+mov
+
+// movie : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/movie.html
+movie
+
+// msd : MSD Registry Holdings, Inc.
+// https://www.iana.org/domains/root/db/msd.html
+msd
+
+// mtn : MTN Dubai Limited
+// https://www.iana.org/domains/root/db/mtn.html
+mtn
+
+// mtr : MTR Corporation Limited
+// https://www.iana.org/domains/root/db/mtr.html
+mtr
+
+// music : DotMusic Limited
+// https://www.iana.org/domains/root/db/music.html
+music
+
+// nab : National Australia Bank Limited
+// https://www.iana.org/domains/root/db/nab.html
+nab
+
+// nagoya : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/nagoya.html
+nagoya
+
+// navy : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/navy.html
+navy
+
+// nba : NBA REGISTRY, LLC
+// https://www.iana.org/domains/root/db/nba.html
+nba
+
+// nec : NEC Corporation
+// https://www.iana.org/domains/root/db/nec.html
+nec
+
+// netbank : COMMONWEALTH BANK OF AUSTRALIA
+// https://www.iana.org/domains/root/db/netbank.html
+netbank
+
+// netflix : Netflix, Inc.
+// https://www.iana.org/domains/root/db/netflix.html
+netflix
+
+// network : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/network.html
+network
+
+// neustar : NeuStar, Inc.
+// https://www.iana.org/domains/root/db/neustar.html
+neustar
+
+// new : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/new.html
+new
+
+// news : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/news.html
+news
+
+// next : Next plc
+// https://www.iana.org/domains/root/db/next.html
+next
+
+// nextdirect : Next plc
+// https://www.iana.org/domains/root/db/nextdirect.html
+nextdirect
+
+// nexus : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/nexus.html
+nexus
+
+// nfl : NFL Reg Ops LLC
+// https://www.iana.org/domains/root/db/nfl.html
+nfl
+
+// ngo : Public Interest Registry
+// https://www.iana.org/domains/root/db/ngo.html
+ngo
+
+// nhk : Japan Broadcasting Corporation (NHK)
+// https://www.iana.org/domains/root/db/nhk.html
+nhk
+
+// nico : DWANGO Co., Ltd.
+// https://www.iana.org/domains/root/db/nico.html
+nico
+
+// nike : NIKE, Inc.
+// https://www.iana.org/domains/root/db/nike.html
+nike
+
+// nikon : NIKON CORPORATION
+// https://www.iana.org/domains/root/db/nikon.html
+nikon
+
+// ninja : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/ninja.html
+ninja
+
+// nissan : NISSAN MOTOR CO., LTD.
+// https://www.iana.org/domains/root/db/nissan.html
+nissan
+
+// nissay : Nippon Life Insurance Company
+// https://www.iana.org/domains/root/db/nissay.html
+nissay
+
+// nokia : Nokia Corporation
+// https://www.iana.org/domains/root/db/nokia.html
+nokia
+
+// norton : Gen Digital Inc.
+// https://www.iana.org/domains/root/db/norton.html
+norton
+
+// now : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/now.html
+now
+
+// nowruz
+// https://www.iana.org/domains/root/db/nowruz.html
+nowruz
+
+// nowtv : Starbucks (HK) Limited
+// https://www.iana.org/domains/root/db/nowtv.html
+nowtv
+
+// nra : National Rifle Association of America
+// https://www.iana.org/domains/root/db/nra.html
+nra
+
+// nrw : Minds + Machines GmbH
+// https://www.iana.org/domains/root/db/nrw.html
+nrw
+
+// ntt : NIPPON TELEGRAPH AND TELEPHONE CORPORATION
+// https://www.iana.org/domains/root/db/ntt.html
+ntt
+
+// nyc : The City of New York by and through the New York City Department of Information Technology & Telecommunications
+// https://www.iana.org/domains/root/db/nyc.html
+nyc
+
+// obi : OBI Group Holding SE & Co. KGaA
+// https://www.iana.org/domains/root/db/obi.html
+obi
+
+// observer : Fegistry, LLC
+// https://www.iana.org/domains/root/db/observer.html
+observer
+
+// office : Microsoft Corporation
+// https://www.iana.org/domains/root/db/office.html
+office
+
+// okinawa : BRregistry, Inc.
+// https://www.iana.org/domains/root/db/okinawa.html
+okinawa
+
+// olayan : Competrol (Luxembourg) Sarl
+// https://www.iana.org/domains/root/db/olayan.html
+olayan
+
+// olayangroup : Competrol (Luxembourg) Sarl
+// https://www.iana.org/domains/root/db/olayangroup.html
+olayangroup
+
+// ollo : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/ollo.html
+ollo
+
+// omega : The Swatch Group Ltd
+// https://www.iana.org/domains/root/db/omega.html
+omega
+
+// one : One.com A/S
+// https://www.iana.org/domains/root/db/one.html
+one
+
+// ong : Public Interest Registry
+// https://www.iana.org/domains/root/db/ong.html
+ong
+
+// onl : iRegistry GmbH
+// https://www.iana.org/domains/root/db/onl.html
+onl
+
+// online : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/online.html
+online
+
+// ooo : INFIBEAM AVENUES LIMITED
+// https://www.iana.org/domains/root/db/ooo.html
+ooo
+
+// open : American Express Travel Related Services Company, Inc.
+// https://www.iana.org/domains/root/db/open.html
+open
+
+// oracle : Oracle Corporation
+// https://www.iana.org/domains/root/db/oracle.html
+oracle
+
+// orange : Orange Brand Services Limited
+// https://www.iana.org/domains/root/db/orange.html
+orange
+
+// organic : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/organic.html
+organic
+
+// origins : The Estée Lauder Companies Inc.
+// https://www.iana.org/domains/root/db/origins.html
+origins
+
+// osaka : Osaka Registry Co., Ltd.
+// https://www.iana.org/domains/root/db/osaka.html
+osaka
+
+// otsuka : Otsuka Holdings Co., Ltd.
+// https://www.iana.org/domains/root/db/otsuka.html
+otsuka
+
+// ott : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/ott.html
+ott
+
+// ovh : MédiaBC
+// https://www.iana.org/domains/root/db/ovh.html
+ovh
+
+// page : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/page.html
+page
+
+// panasonic : Panasonic Holdings Corporation
+// https://www.iana.org/domains/root/db/panasonic.html
+panasonic
+
+// paris : City of Paris
+// https://www.iana.org/domains/root/db/paris.html
+paris
+
+// pars
+// https://www.iana.org/domains/root/db/pars.html
+pars
+
+// partners : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/partners.html
+partners
+
+// parts : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/parts.html
+parts
+
+// party : Blue Sky Registry Limited
+// https://www.iana.org/domains/root/db/party.html
+party
+
+// pay : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/pay.html
+pay
+
+// pccw : PCCW Enterprises Limited
+// https://www.iana.org/domains/root/db/pccw.html
+pccw
+
+// pet : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/pet.html
+pet
+
+// pfizer : Pfizer Inc.
+// https://www.iana.org/domains/root/db/pfizer.html
+pfizer
+
+// pharmacy : National Association of Boards of Pharmacy
+// https://www.iana.org/domains/root/db/pharmacy.html
+pharmacy
+
+// phd : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/phd.html
+phd
+
+// philips : Koninklijke Philips N.V.
+// https://www.iana.org/domains/root/db/philips.html
+philips
+
+// phone : Dish DBS Corporation
+// https://www.iana.org/domains/root/db/phone.html
+phone
+
+// photo : Registry Services, LLC
+// https://www.iana.org/domains/root/db/photo.html
+photo
+
+// photography : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/photography.html
+photography
+
+// photos : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/photos.html
+photos
+
+// physio : PhysBiz Pty Ltd
+// https://www.iana.org/domains/root/db/physio.html
+physio
+
+// pics : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/pics.html
+pics
+
+// pictet : Banque Pictet & Cie SA
+// https://www.iana.org/domains/root/db/pictet.html
+pictet
+
+// pictures : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/pictures.html
+pictures
+
+// pid : Top Level Spectrum, Inc.
+// https://www.iana.org/domains/root/db/pid.html
+pid
+
+// pin : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/pin.html
+pin
+
+// ping : Ping Registry Provider, Inc.
+// https://www.iana.org/domains/root/db/ping.html
+ping
+
+// pink : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/pink.html
+pink
+
+// pioneer : Pioneer Corporation
+// https://www.iana.org/domains/root/db/pioneer.html
+pioneer
+
+// pizza : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/pizza.html
+pizza
+
+// place : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/place.html
+place
+
+// play : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/play.html
+play
+
+// playstation : Sony Interactive Entertainment Inc.
+// https://www.iana.org/domains/root/db/playstation.html
+playstation
+
+// plumbing : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/plumbing.html
+plumbing
+
+// plus : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/plus.html
+plus
+
+// pnc : PNC Domain Co., LLC
+// https://www.iana.org/domains/root/db/pnc.html
+pnc
+
+// pohl : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/pohl.html
+pohl
+
+// poker : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/poker.html
+poker
+
+// politie : Politie Nederland
+// https://www.iana.org/domains/root/db/politie.html
+politie
+
+// porn : ICM Registry PN LLC
+// https://www.iana.org/domains/root/db/porn.html
+porn
+
+// praxi : Praxi S.p.A.
+// https://www.iana.org/domains/root/db/praxi.html
+praxi
+
+// press : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/press.html
+press
+
+// prime : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/prime.html
+prime
+
+// prod : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/prod.html
+prod
+
+// productions : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/productions.html
+productions
+
+// prof : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/prof.html
+prof
+
+// progressive : Progressive Casualty Insurance Company
+// https://www.iana.org/domains/root/db/progressive.html
+progressive
+
+// promo : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/promo.html
+promo
+
+// properties : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/properties.html
+properties
+
+// property : Digital Property Infrastructure Limited
+// https://www.iana.org/domains/root/db/property.html
+property
+
+// protection : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/protection.html
+protection
+
+// pru : Prudential Financial, Inc.
+// https://www.iana.org/domains/root/db/pru.html
+pru
+
+// prudential : Prudential Financial, Inc.
+// https://www.iana.org/domains/root/db/prudential.html
+prudential
+
+// pub : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/pub.html
+pub
+
+// pwc : PricewaterhouseCoopers LLP
+// https://www.iana.org/domains/root/db/pwc.html
+pwc
+
+// qpon : dotQPON LLC
+// https://www.iana.org/domains/root/db/qpon.html
+qpon
+
+// quebec : PointQuébec Inc
+// https://www.iana.org/domains/root/db/quebec.html
+quebec
+
+// quest : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/quest.html
+quest
+
+// racing : Premier Registry Limited
+// https://www.iana.org/domains/root/db/racing.html
+racing
+
+// radio : Digity, LLC
+// https://www.iana.org/domains/root/db/radio.html
+radio
+
+// read : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/read.html
+read
+
+// realestate : dotRealEstate LLC
+// https://www.iana.org/domains/root/db/realestate.html
+realestate
+
+// realtor : Real Estate Domains LLC
+// https://www.iana.org/domains/root/db/realtor.html
+realtor
+
+// realty : Waterford Limited
+// https://www.iana.org/domains/root/db/realty.html
+realty
+
+// recipes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/recipes.html
+recipes
+
+// red : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/red.html
+red
+
+// redumbrella : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/redumbrella.html
+redumbrella
+
+// rehab : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/rehab.html
+rehab
+
+// reise : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/reise.html
+reise
+
+// reisen : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/reisen.html
+reisen
+
+// reit : National Association of Real Estate Investment Trusts, Inc.
+// https://www.iana.org/domains/root/db/reit.html
+reit
+
+// reliance : Reliance Industries Limited
+// https://www.iana.org/domains/root/db/reliance.html
+reliance
+
+// ren : ZDNS International Limited
+// https://www.iana.org/domains/root/db/ren.html
+ren
+
+// rent : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/rent.html
+rent
+
+// rentals : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/rentals.html
+rentals
+
+// repair : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/repair.html
+repair
+
+// report : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/report.html
+report
+
+// republican : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/republican.html
+republican
+
+// rest : Punto 2012 Sociedad Anonima Promotora de Inversion de Capital Variable
+// https://www.iana.org/domains/root/db/rest.html
+rest
+
+// restaurant : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/restaurant.html
+restaurant
+
+// review : dot Review Limited
+// https://www.iana.org/domains/root/db/review.html
+review
+
+// reviews : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/reviews.html
+reviews
+
+// rexroth : Robert Bosch GMBH
+// https://www.iana.org/domains/root/db/rexroth.html
+rexroth
+
+// rich : iRegistry GmbH
+// https://www.iana.org/domains/root/db/rich.html
+rich
+
+// richardli : Pacific Century Asset Management (HK) Limited
+// https://www.iana.org/domains/root/db/richardli.html
+richardli
+
+// ricoh : Ricoh Company, Ltd.
+// https://www.iana.org/domains/root/db/ricoh.html
+ricoh
+
+// ril : Reliance Industries Limited
+// https://www.iana.org/domains/root/db/ril.html
+ril
+
+// rio : Empresa Municipal de Informática SA - IPLANRIO
+// https://www.iana.org/domains/root/db/rio.html
+rio
+
+// rip : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/rip.html
+rip
+
+// rocks : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/rocks.html
+rocks
+
+// rodeo : Registry Services, LLC
+// https://www.iana.org/domains/root/db/rodeo.html
+rodeo
+
+// rogers : Rogers Communications Canada Inc.
+// https://www.iana.org/domains/root/db/rogers.html
+rogers
+
+// room : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/room.html
+room
+
+// rsvp : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/rsvp.html
+rsvp
+
+// rugby : World Rugby Strategic Developments Limited
+// https://www.iana.org/domains/root/db/rugby.html
+rugby
+
+// ruhr : dotSaarland GmbH
+// https://www.iana.org/domains/root/db/ruhr.html
+ruhr
+
+// run : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/run.html
+run
+
+// rwe : RWE AG
+// https://www.iana.org/domains/root/db/rwe.html
+rwe
+
+// ryukyu : BRregistry, Inc.
+// https://www.iana.org/domains/root/db/ryukyu.html
+ryukyu
+
+// saarland : dotSaarland GmbH
+// https://www.iana.org/domains/root/db/saarland.html
+saarland
+
+// safe : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/safe.html
+safe
+
+// safety : Safety Registry Services, LLC.
+// https://www.iana.org/domains/root/db/safety.html
+safety
+
+// sakura : SAKURA Internet Inc.
+// https://www.iana.org/domains/root/db/sakura.html
+sakura
+
+// sale : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/sale.html
+sale
+
+// salon : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/salon.html
+salon
+
+// samsclub : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/samsclub.html
+samsclub
+
+// samsung : SAMSUNG SDS CO., LTD
+// https://www.iana.org/domains/root/db/samsung.html
+samsung
+
+// sandvik : Sandvik AB
+// https://www.iana.org/domains/root/db/sandvik.html
+sandvik
+
+// sandvikcoromant : Sandvik AB
+// https://www.iana.org/domains/root/db/sandvikcoromant.html
+sandvikcoromant
+
+// sanofi : Sanofi
+// https://www.iana.org/domains/root/db/sanofi.html
+sanofi
+
+// sap : SAP AG
+// https://www.iana.org/domains/root/db/sap.html
+sap
+
+// sarl : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/sarl.html
+sarl
+
+// sas : Research IP LLC
+// https://www.iana.org/domains/root/db/sas.html
+sas
+
+// save : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/save.html
+save
+
+// saxo : Saxo Bank A/S
+// https://www.iana.org/domains/root/db/saxo.html
+saxo
+
+// sbi : STATE BANK OF INDIA
+// https://www.iana.org/domains/root/db/sbi.html
+sbi
+
+// sbs : ShortDot SA
+// https://www.iana.org/domains/root/db/sbs.html
+sbs
+
+// scb : The Siam Commercial Bank Public Company Limited ("SCB")
+// https://www.iana.org/domains/root/db/scb.html
+scb
+
+// schaeffler : Schaeffler Technologies AG & Co. KG
+// https://www.iana.org/domains/root/db/schaeffler.html
+schaeffler
+
+// schmidt : SCHMIDT GROUPE S.A.S.
+// https://www.iana.org/domains/root/db/schmidt.html
+schmidt
+
+// scholarships : Scholarships.com, LLC
+// https://www.iana.org/domains/root/db/scholarships.html
+scholarships
+
+// school : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/school.html
+school
+
+// schule : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/schule.html
+schule
+
+// schwarz : Schwarz Domains und Services GmbH & Co. KG
+// https://www.iana.org/domains/root/db/schwarz.html
+schwarz
+
+// science : dot Science Limited
+// https://www.iana.org/domains/root/db/science.html
+science
+
+// scot : Dot Scot Registry Limited
+// https://www.iana.org/domains/root/db/scot.html
+scot
+
+// search : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/search.html
+search
+
+// seat : SEAT, S.A. (Sociedad Unipersonal)
+// https://www.iana.org/domains/root/db/seat.html
+seat
+
+// secure : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/secure.html
+secure
+
+// security : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/security.html
+security
+
+// seek : Seek Limited
+// https://www.iana.org/domains/root/db/seek.html
+seek
+
+// select : Registry Services, LLC
+// https://www.iana.org/domains/root/db/select.html
+select
+
+// sener : Sener Ingeniería y Sistemas, S.A.
+// https://www.iana.org/domains/root/db/sener.html
+sener
+
+// services : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/services.html
+services
+
+// seven : Seven West Media Ltd
+// https://www.iana.org/domains/root/db/seven.html
+seven
+
+// sew : SEW-EURODRIVE GmbH & Co KG
+// https://www.iana.org/domains/root/db/sew.html
+sew
+
+// sex : ICM Registry SX LLC
+// https://www.iana.org/domains/root/db/sex.html
+sex
+
+// sexy : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/sexy.html
+sexy
+
+// sfr : Societe Francaise du Radiotelephone - SFR
+// https://www.iana.org/domains/root/db/sfr.html
+sfr
+
+// shangrila : Shangri‐La International Hotel Management Limited
+// https://www.iana.org/domains/root/db/shangrila.html
+shangrila
+
+// sharp : Sharp Corporation
+// https://www.iana.org/domains/root/db/sharp.html
+sharp
+
+// shell : Shell Information Technology International Inc
+// https://www.iana.org/domains/root/db/shell.html
+shell
+
+// shia
+// https://www.iana.org/domains/root/db/shia.html
+shia
+
+// shiksha : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/shiksha.html
+shiksha
+
+// shoes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/shoes.html
+shoes
+
+// shop : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/shop.html
+shop
+
+// shopping : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/shopping.html
+shopping
+
+// shouji : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/shouji.html
+shouji
+
+// show : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/show.html
+show
+
+// silk : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/silk.html
+silk
+
+// sina : Sina Corporation
+// https://www.iana.org/domains/root/db/sina.html
+sina
+
+// singles : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/singles.html
+singles
+
+// site : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/site.html
+site
+
+// ski : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/ski.html
+ski
+
+// skin : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/skin.html
+skin
+
+// sky : Sky UK Limited
+// https://www.iana.org/domains/root/db/sky.html
+sky
+
+// skype : Microsoft Corporation
+// https://www.iana.org/domains/root/db/skype.html
+skype
+
+// sling : DISH Technologies L.L.C.
+// https://www.iana.org/domains/root/db/sling.html
+sling
+
+// smart : Smart Communications, Inc. (SMART)
+// https://www.iana.org/domains/root/db/smart.html
+smart
+
+// smile : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/smile.html
+smile
+
+// sncf : Société Nationale SNCF
+// https://www.iana.org/domains/root/db/sncf.html
+sncf
+
+// soccer : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/soccer.html
+soccer
+
+// social : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/social.html
+social
+
+// softbank : SoftBank Group Corp.
+// https://www.iana.org/domains/root/db/softbank.html
+softbank
+
+// software : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/software.html
+software
+
+// sohu : Sohu.com Limited
+// https://www.iana.org/domains/root/db/sohu.html
+sohu
+
+// solar : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/solar.html
+solar
+
+// solutions : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/solutions.html
+solutions
+
+// song : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/song.html
+song
+
+// sony : Sony Group Corporation
+// https://www.iana.org/domains/root/db/sony.html
+sony
+
+// soy : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/soy.html
+soy
+
+// spa : Asia Spa and Wellness Promotion Council Limited
+// https://www.iana.org/domains/root/db/spa.html
+spa
+
+// space : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/space.html
+space
+
+// sport : SportAccord
+// https://www.iana.org/domains/root/db/sport.html
+sport
+
+// spot : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/spot.html
+spot
+
+// srl : InterNetX, Corp
+// https://www.iana.org/domains/root/db/srl.html
+srl
+
+// stada : STADA Arzneimittel AG
+// https://www.iana.org/domains/root/db/stada.html
+stada
+
+// staples : Staples, Inc.
+// https://www.iana.org/domains/root/db/staples.html
+staples
+
+// star : Star India Private Limited
+// https://www.iana.org/domains/root/db/star.html
+star
+
+// statebank : STATE BANK OF INDIA
+// https://www.iana.org/domains/root/db/statebank.html
+statebank
+
+// statefarm : State Farm Mutual Automobile Insurance Company
+// https://www.iana.org/domains/root/db/statefarm.html
+statefarm
+
+// stc : Saudi Telecom Company
+// https://www.iana.org/domains/root/db/stc.html
+stc
+
+// stcgroup : Saudi Telecom Company
+// https://www.iana.org/domains/root/db/stcgroup.html
+stcgroup
+
+// stockholm : Stockholms kommun
+// https://www.iana.org/domains/root/db/stockholm.html
+stockholm
+
+// storage : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/storage.html
+storage
+
+// store : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/store.html
+store
+
+// stream : dot Stream Limited
+// https://www.iana.org/domains/root/db/stream.html
+stream
+
+// studio : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/studio.html
+studio
+
+// study : Registry Services, LLC
+// https://www.iana.org/domains/root/db/study.html
+study
+
+// style : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/style.html
+style
+
+// sucks : Vox Populi Registry Ltd.
+// https://www.iana.org/domains/root/db/sucks.html
+sucks
+
+// supplies : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/supplies.html
+supplies
+
+// supply : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/supply.html
+supply
+
+// support : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/support.html
+support
+
+// surf : Registry Services, LLC
+// https://www.iana.org/domains/root/db/surf.html
+surf
+
+// surgery : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/surgery.html
+surgery
+
+// suzuki : SUZUKI MOTOR CORPORATION
+// https://www.iana.org/domains/root/db/suzuki.html
+suzuki
+
+// swatch : The Swatch Group Ltd
+// https://www.iana.org/domains/root/db/swatch.html
+swatch
+
+// swiss : Swiss Confederation
+// https://www.iana.org/domains/root/db/swiss.html
+swiss
+
+// sydney : State of New South Wales, Department of Premier and Cabinet
+// https://www.iana.org/domains/root/db/sydney.html
+sydney
+
+// systems : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/systems.html
+systems
+
+// tab : Tabcorp Holdings Limited
+// https://www.iana.org/domains/root/db/tab.html
+tab
+
+// taipei : Taipei City Government
+// https://www.iana.org/domains/root/db/taipei.html
+taipei
+
+// talk : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/talk.html
+talk
+
+// taobao : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/taobao.html
+taobao
+
+// target : Target Domain Holdings, LLC
+// https://www.iana.org/domains/root/db/target.html
+target
+
+// tatamotors : Tata Motors Ltd
+// https://www.iana.org/domains/root/db/tatamotors.html
+tatamotors
+
+// tatar : Limited Liability Company "Coordination Center of Regional Domain of Tatarstan Republic"
+// https://www.iana.org/domains/root/db/tatar.html
+tatar
+
+// tattoo : Registry Services, LLC
+// https://www.iana.org/domains/root/db/tattoo.html
+tattoo
+
+// tax : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tax.html
+tax
+
+// taxi : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/taxi.html
+taxi
+
+// tci
+// https://www.iana.org/domains/root/db/tci.html
+tci
+
+// tdk : TDK Corporation
+// https://www.iana.org/domains/root/db/tdk.html
+tdk
+
+// team : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/team.html
+team
+
+// tech : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/tech.html
+tech
+
+// technology : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/technology.html
+technology
+
+// temasek : Temasek Holdings (Private) Limited
+// https://www.iana.org/domains/root/db/temasek.html
+temasek
+
+// tennis : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tennis.html
+tennis
+
+// teva : Teva Pharmaceutical Industries Limited
+// https://www.iana.org/domains/root/db/teva.html
+teva
+
+// thd : Home Depot Product Authority, LLC
+// https://www.iana.org/domains/root/db/thd.html
+thd
+
+// theater : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/theater.html
+theater
+
+// theatre : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/theatre.html
+theatre
+
+// tiaa : Teachers Insurance and Annuity Association of America
+// https://www.iana.org/domains/root/db/tiaa.html
+tiaa
+
+// tickets : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/tickets.html
+tickets
+
+// tienda : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tienda.html
+tienda
+
+// tips : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tips.html
+tips
+
+// tires : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tires.html
+tires
+
+// tirol : punkt Tirol GmbH
+// https://www.iana.org/domains/root/db/tirol.html
+tirol
+
+// tjmaxx : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/tjmaxx.html
+tjmaxx
+
+// tjx : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/tjx.html
+tjx
+
+// tkmaxx : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/tkmaxx.html
+tkmaxx
+
+// tmall : Alibaba Group Holding Limited
+// https://www.iana.org/domains/root/db/tmall.html
+tmall
+
+// today : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/today.html
+today
+
+// tokyo : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/tokyo.html
+tokyo
+
+// tools : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tools.html
+tools
+
+// top : Hong Kong Zhongze International Limited
+// https://www.iana.org/domains/root/db/top.html
+top
+
+// toray : Toray Industries, Inc.
+// https://www.iana.org/domains/root/db/toray.html
+toray
+
+// toshiba : TOSHIBA Corporation
+// https://www.iana.org/domains/root/db/toshiba.html
+toshiba
+
+// total : TotalEnergies SE
+// https://www.iana.org/domains/root/db/total.html
+total
+
+// tours : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/tours.html
+tours
+
+// town : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/town.html
+town
+
+// toyota : TOYOTA MOTOR CORPORATION
+// https://www.iana.org/domains/root/db/toyota.html
+toyota
+
+// toys : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/toys.html
+toys
+
+// trade : Elite Registry Limited
+// https://www.iana.org/domains/root/db/trade.html
+trade
+
+// trading : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/trading.html
+trading
+
+// training : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/training.html
+training
+
+// travel : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/travel.html
+travel
+
+// travelers : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/travelers.html
+travelers
+
+// travelersinsurance : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/travelersinsurance.html
+travelersinsurance
+
+// trust : Internet Naming Company LLC
+// https://www.iana.org/domains/root/db/trust.html
+trust
+
+// trv : Travelers TLD, LLC
+// https://www.iana.org/domains/root/db/trv.html
+trv
+
+// tube : Latin American Telecom LLC
+// https://www.iana.org/domains/root/db/tube.html
+tube
+
+// tui : TUI AG
+// https://www.iana.org/domains/root/db/tui.html
+tui
+
+// tunes : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/tunes.html
+tunes
+
+// tushu : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/tushu.html
+tushu
+
+// tvs : T V SUNDRAM IYENGAR & SONS LIMITED
+// https://www.iana.org/domains/root/db/tvs.html
+tvs
+
+// ubank : National Australia Bank Limited
+// https://www.iana.org/domains/root/db/ubank.html
+ubank
+
+// ubs : UBS AG
+// https://www.iana.org/domains/root/db/ubs.html
+ubs
+
+// unicom : China United Network Communications Corporation Limited
+// https://www.iana.org/domains/root/db/unicom.html
+unicom
+
+// university : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/university.html
+university
+
+// uno : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/uno.html
+uno
+
+// uol : UBN INTERNET LTDA.
+// https://www.iana.org/domains/root/db/uol.html
+uol
+
+// ups : UPS Market Driver, Inc.
+// https://www.iana.org/domains/root/db/ups.html
+ups
+
+// vacations : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/vacations.html
+vacations
+
+// vana : D3 Registry LLC
+// https://www.iana.org/domains/root/db/vana.html
+vana
+
+// vanguard : The Vanguard Group, Inc.
+// https://www.iana.org/domains/root/db/vanguard.html
+vanguard
+
+// vegas : Dot Vegas, Inc.
+// https://www.iana.org/domains/root/db/vegas.html
+vegas
+
+// ventures : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/ventures.html
+ventures
+
+// verisign : VeriSign, Inc.
+// https://www.iana.org/domains/root/db/verisign.html
+verisign
+
+// versicherung : tldbox GmbH
+// https://www.iana.org/domains/root/db/versicherung.html
+versicherung
+
+// vet : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/vet.html
+vet
+
+// viajes : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/viajes.html
+viajes
+
+// video : Dog Beach, LLC
+// https://www.iana.org/domains/root/db/video.html
+video
+
+// vig : VIENNA INSURANCE GROUP AG Wiener Versicherung Gruppe
+// https://www.iana.org/domains/root/db/vig.html
+vig
+
+// viking : Viking River Cruises (Bermuda) Ltd.
+// https://www.iana.org/domains/root/db/viking.html
+viking
+
+// villas : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/villas.html
+villas
+
+// vin : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/vin.html
+vin
+
+// vip : Registry Services, LLC
+// https://www.iana.org/domains/root/db/vip.html
+vip
+
+// virgin : Virgin Enterprises Limited
+// https://www.iana.org/domains/root/db/virgin.html
+virgin
+
+// visa : Visa Worldwide Pte. Limited
+// https://www.iana.org/domains/root/db/visa.html
+visa
+
+// vision : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/vision.html
+vision
+
+// viva : Saudi Telecom Company
+// https://www.iana.org/domains/root/db/viva.html
+viva
+
+// vivo : Telefonica Brasil S.A.
+// https://www.iana.org/domains/root/db/vivo.html
+vivo
+
+// vlaanderen : DNS.be vzw
+// https://www.iana.org/domains/root/db/vlaanderen.html
+vlaanderen
+
+// vodka : Registry Services, LLC
+// https://www.iana.org/domains/root/db/vodka.html
+vodka
+
+// volvo : Volvo Holding Sverige Aktiebolag
+// https://www.iana.org/domains/root/db/volvo.html
+volvo
+
+// vote : Monolith Registry LLC
+// https://www.iana.org/domains/root/db/vote.html
+vote
+
+// voting : Valuetainment Corp.
+// https://www.iana.org/domains/root/db/voting.html
+voting
+
+// voto : Monolith Registry LLC
+// https://www.iana.org/domains/root/db/voto.html
+voto
+
+// voyage : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/voyage.html
+voyage
+
+// wales : Nominet UK
+// https://www.iana.org/domains/root/db/wales.html
+wales
+
+// walmart : Wal-Mart Stores, Inc.
+// https://www.iana.org/domains/root/db/walmart.html
+walmart
+
+// walter : Sandvik AB
+// https://www.iana.org/domains/root/db/walter.html
+walter
+
+// wang : Zodiac Wang Limited
+// https://www.iana.org/domains/root/db/wang.html
+wang
+
+// wanggou : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/wanggou.html
+wanggou
+
+// watch : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/watch.html
+watch
+
+// watches : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/watches.html
+watches
+
+// weather : The Weather Company, LLC
+// https://www.iana.org/domains/root/db/weather.html
+weather
+
+// weatherchannel : The Weather Company, LLC
+// https://www.iana.org/domains/root/db/weatherchannel.html
+weatherchannel
+
+// webcam : dot Webcam Limited
+// https://www.iana.org/domains/root/db/webcam.html
+webcam
+
+// weber : Saint-Gobain Weber SA
+// https://www.iana.org/domains/root/db/weber.html
+weber
+
+// website : Radix Technologies Inc SEZC
+// https://www.iana.org/domains/root/db/website.html
+website
+
+// wed
+// https://www.iana.org/domains/root/db/wed.html
+wed
+
+// wedding : Registry Services, LLC
+// https://www.iana.org/domains/root/db/wedding.html
+wedding
+
+// weibo : Sina Corporation
+// https://www.iana.org/domains/root/db/weibo.html
+weibo
+
+// weir : Weir Group IP Limited
+// https://www.iana.org/domains/root/db/weir.html
+weir
+
+// whoswho : Who's Who Registry
+// https://www.iana.org/domains/root/db/whoswho.html
+whoswho
+
+// wien : punkt.wien GmbH
+// https://www.iana.org/domains/root/db/wien.html
+wien
+
+// wiki : Registry Services, LLC
+// https://www.iana.org/domains/root/db/wiki.html
+wiki
+
+// williamhill : William Hill Organization Limited
+// https://www.iana.org/domains/root/db/williamhill.html
+williamhill
+
+// win : First Registry Limited
+// https://www.iana.org/domains/root/db/win.html
+win
+
+// windows : Microsoft Corporation
+// https://www.iana.org/domains/root/db/windows.html
+windows
+
+// wine : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/wine.html
+wine
+
+// winners : The TJX Companies, Inc.
+// https://www.iana.org/domains/root/db/winners.html
+winners
+
+// wme : William Morris Endeavor Entertainment, LLC
+// https://www.iana.org/domains/root/db/wme.html
+wme
+
+// wolterskluwer : Wolters Kluwer N.V.
+// https://www.iana.org/domains/root/db/wolterskluwer.html
+wolterskluwer
+
+// woodside : Woodside Petroleum Limited
+// https://www.iana.org/domains/root/db/woodside.html
+woodside
+
+// work : Registry Services, LLC
+// https://www.iana.org/domains/root/db/work.html
+work
+
+// works : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/works.html
+works
+
+// world : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/world.html
+world
+
+// wow : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/wow.html
+wow
+
+// wtc : World Trade Centers Association, Inc.
+// https://www.iana.org/domains/root/db/wtc.html
+wtc
+
+// wtf : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/wtf.html
+wtf
+
+// xbox : Microsoft Corporation
+// https://www.iana.org/domains/root/db/xbox.html
+xbox
+
+// xerox : Xerox DNHC LLC
+// https://www.iana.org/domains/root/db/xerox.html
+xerox
+
+// xihuan : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/xihuan.html
+xihuan
+
+// xin : Elegant Leader Limited
+// https://www.iana.org/domains/root/db/xin.html
+xin
+
+// xn--11b4c3d : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--11b4c3d.html
+कॉम
+
+// xn--1ck2e1b : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--1ck2e1b.html
+セール
+
+// xn--1qqw23a : Guangzhou YU Wei Information Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--1qqw23a.html
+佛山
+
+// xn--30rr7y : Excellent First Limited
+// https://www.iana.org/domains/root/db/xn--30rr7y.html
+慈善
+
+// xn--3bst00m : Eagle Horizon Limited
+// https://www.iana.org/domains/root/db/xn--3bst00m.html
+集团
+
+// xn--3ds443g : Beijing TLD Registry Technology Limited
+// https://www.iana.org/domains/root/db/xn--3ds443g.html
+在线
+
+// xn--3pxu8k : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--3pxu8k.html
+点看
+
+// xn--42c2d9a : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--42c2d9a.html
+คอม
+
+// xn--45q11c : Zodiac Gemini Ltd
+// https://www.iana.org/domains/root/db/xn--45q11c.html
+八卦
+
+// xn--4gbrim : Helium TLDs Ltd
+// https://www.iana.org/domains/root/db/xn--4gbrim.html
+موقع
+
+// xn--55qw42g : China Organizational Name Administration Center
+// https://www.iana.org/domains/root/db/xn--55qw42g.html
+公益
+
+// xn--55qx5d : China Internet Network Information Center (CNNIC)
+// https://www.iana.org/domains/root/db/xn--55qx5d.html
+公司
+
+// xn--5su34j936bgsg : Shangri‐La International Hotel Management Limited
+// https://www.iana.org/domains/root/db/xn--5su34j936bgsg.html
+香格里拉
+
+// xn--5tzm5g : Global Website TLD Asia Limited
+// https://www.iana.org/domains/root/db/xn--5tzm5g.html
+网站
+
+// xn--6frz82g : Identity Digital Domains Limited
+// https://www.iana.org/domains/root/db/xn--6frz82g.html
+移动
+
+// xn--6qq986b3xl : Tycoon Treasure Limited
+// https://www.iana.org/domains/root/db/xn--6qq986b3xl.html
+我爱你
+
+// xn--80adxhks : Foundation for Assistance for Internet Technologies and Infrastructure Development (FAITID)
+// https://www.iana.org/domains/root/db/xn--80adxhks.html
+москва
+
+// xn--80aqecdr1a : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/xn--80aqecdr1a.html
+католик
+
+// xn--80asehdb : CORE Association
+// https://www.iana.org/domains/root/db/xn--80asehdb.html
+онлайн
+
+// xn--80aswg : CORE Association
+// https://www.iana.org/domains/root/db/xn--80aswg.html
+сайт
+
+// xn--8y0a063a : China United Network Communications Corporation Limited
+// https://www.iana.org/domains/root/db/xn--8y0a063a.html
+联通
+
+// xn--9dbq2a : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--9dbq2a.html
+קום
+
+// xn--9et52u : RISE VICTORY LIMITED
+// https://www.iana.org/domains/root/db/xn--9et52u.html
+时尚
+
+// xn--9krt00a : Sina Corporation
+// https://www.iana.org/domains/root/db/xn--9krt00a.html
+微博
+
+// xn--b4w605ferd : Temasek Holdings (Private) Limited
+// https://www.iana.org/domains/root/db/xn--b4w605ferd.html
+淡马锡
+
+// xn--bck1b9a5dre4c : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--bck1b9a5dre4c.html
+ファッション
+
+// xn--c1avg : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--c1avg.html
+орг
+
+// xn--c2br7g : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--c2br7g.html
+नेट
+
+// xn--cck2b3b : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--cck2b3b.html
+ストア
+
+// xn--cckwcxetd : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--cckwcxetd.html
+アマゾン
+
+// xn--cg4bki : SAMSUNG SDS CO., LTD
+// https://www.iana.org/domains/root/db/xn--cg4bki.html
+삼성
+
+// xn--czr694b : Internet DotTrademark Organisation Limited
+// https://www.iana.org/domains/root/db/xn--czr694b.html
+商标
+
+// xn--czrs0t : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--czrs0t.html
+商店
+
+// xn--czru2d : Zodiac Aquarius Limited
+// https://www.iana.org/domains/root/db/xn--czru2d.html
+商城
+
+// xn--d1acj3b : The Foundation for Network Initiatives “The Smart Internet”
+// https://www.iana.org/domains/root/db/xn--d1acj3b.html
+дети
+
+// xn--eckvdtc9d : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--eckvdtc9d.html
+ポイント
+
+// xn--efvy88h : Guangzhou YU Wei Information Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--efvy88h.html
+新闻
+
+// xn--fct429k : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--fct429k.html
+家電
+
+// xn--fhbei : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--fhbei.html
+كوم
+
+// xn--fiq228c5hs : Beijing TLD Registry Technology Limited
+// https://www.iana.org/domains/root/db/xn--fiq228c5hs.html
+中文网
+
+// xn--fiq64b : CITIC Group Corporation
+// https://www.iana.org/domains/root/db/xn--fiq64b.html
+中信
+
+// xn--fjq720a : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--fjq720a.html
+娱乐
+
+// xn--flw351e : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/xn--flw351e.html
+谷歌
+
+// xn--fzys8d69uvgm : PCCW Enterprises Limited
+// https://www.iana.org/domains/root/db/xn--fzys8d69uvgm.html
+電訊盈科
+
+// xn--g2xx48c : Nawang Heli(Xiamen) Network Service Co., LTD.
+// https://www.iana.org/domains/root/db/xn--g2xx48c.html
+购物
+
+// xn--gckr3f0f : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--gckr3f0f.html
+クラウド
+
+// xn--gk3at1e : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--gk3at1e.html
+通販
+
+// xn--hxt814e : Zodiac Taurus Limited
+// https://www.iana.org/domains/root/db/xn--hxt814e.html
+网店
+
+// xn--i1b6b1a6a2e : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--i1b6b1a6a2e.html
+संगठन
+
+// xn--imr513n : Internet DotTrademark Organisation Limited
+// https://www.iana.org/domains/root/db/xn--imr513n.html
+餐厅
+
+// xn--io0a7i : China Internet Network Information Center (CNNIC)
+// https://www.iana.org/domains/root/db/xn--io0a7i.html
+网络
+
+// xn--j1aef : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--j1aef.html
+ком
+
+// xn--jlq480n2rg : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--jlq480n2rg.html
+亚马逊
+
+// xn--jvr189m : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--jvr189m.html
+食品
+
+// xn--kcrx77d1x4a : Koninklijke Philips N.V.
+// https://www.iana.org/domains/root/db/xn--kcrx77d1x4a.html
+飞利浦
+
+// xn--kput3i : Beijing RITT-Net Technology Development Co., Ltd
+// https://www.iana.org/domains/root/db/xn--kput3i.html
+手机
+
+// xn--mgba3a3ejt : Aramco Services Company
+// https://www.iana.org/domains/root/db/xn--mgba3a3ejt.html
+ارامكو
+
+// xn--mgba7c0bbn0a : Competrol (Luxembourg) Sarl
+// https://www.iana.org/domains/root/db/xn--mgba7c0bbn0a.html
+العليان
+
+// xn--mgbab2bd : CORE Association
+// https://www.iana.org/domains/root/db/xn--mgbab2bd.html
+بازار
+
+// xn--mgbca7dzdo : Abu Dhabi Systems and Information Centre
+// https://www.iana.org/domains/root/db/xn--mgbca7dzdo.html
+ابوظبي
+
+// xn--mgbi4ecexp : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/xn--mgbi4ecexp.html
+كاثوليك
+
+// xn--mgbt3dhd
+// https://www.iana.org/domains/root/db/xn--mgbt3dhd.html
+همراه
+
+// xn--mk1bu44c : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--mk1bu44c.html
+닷컴
+
+// xn--mxtq1m : Net-Chinese Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--mxtq1m.html
+政府
+
+// xn--ngbc5azd : International Domain Registry Pty. Ltd.
+// https://www.iana.org/domains/root/db/xn--ngbc5azd.html
+شبكة
+
+// xn--ngbe9e0a : Kuwait Finance House
+// https://www.iana.org/domains/root/db/xn--ngbe9e0a.html
+بيتك
+
+// xn--ngbrx : League of Arab States
+// https://www.iana.org/domains/root/db/xn--ngbrx.html
+عرب
+
+// xn--nqv7f : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--nqv7f.html
+机构
+
+// xn--nqv7fs00ema : Public Interest Registry
+// https://www.iana.org/domains/root/db/xn--nqv7fs00ema.html
+组织机构
+
+// xn--nyqy26a : Stable Tone Limited
+// https://www.iana.org/domains/root/db/xn--nyqy26a.html
+健康
+
+// xn--otu796d : Jiang Yu Liang Cai Technology Company Limited
+// https://www.iana.org/domains/root/db/xn--otu796d.html
+招聘
+
+// xn--p1acf : Rusnames Limited
+// https://www.iana.org/domains/root/db/xn--p1acf.html
+рус
+
+// xn--pssy2u : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--pssy2u.html
+大拿
+
+// xn--q9jyb4c : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/xn--q9jyb4c.html
+みんな
+
+// xn--qcka1pmc : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/xn--qcka1pmc.html
+グーグル
+
+// xn--rhqv96g : Stable Tone Limited
+// https://www.iana.org/domains/root/db/xn--rhqv96g.html
+世界
+
+// xn--rovu88b : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/xn--rovu88b.html
+書籍
+
+// xn--ses554g : KNET Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--ses554g.html
+网址
+
+// xn--t60b56a : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--t60b56a.html
+닷넷
+
+// xn--tckwe : VeriSign Sarl
+// https://www.iana.org/domains/root/db/xn--tckwe.html
+コム
+
+// xn--tiq49xqyj : Pontificium Consilium de Comunicationibus Socialibus (PCCS) (Pontifical Council for Social Communication)
+// https://www.iana.org/domains/root/db/xn--tiq49xqyj.html
+天主教
+
+// xn--unup4y : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--unup4y.html
+游戏
+
+// xn--vermgensberater-ctb : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/xn--vermgensberater-ctb.html
+vermögensberater
+
+// xn--vermgensberatung-pwb : Deutsche Vermögensberatung Aktiengesellschaft DVAG
+// https://www.iana.org/domains/root/db/xn--vermgensberatung-pwb.html
+vermögensberatung
+
+// xn--vhquv : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/xn--vhquv.html
+企业
+
+// xn--vuq861b : Beijing Tele-info Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--vuq861b.html
+信息
+
+// xn--w4r85el8fhu5dnra : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/xn--w4r85el8fhu5dnra.html
+嘉里大酒店
+
+// xn--w4rs40l : Kerry Trading Co. Limited
+// https://www.iana.org/domains/root/db/xn--w4rs40l.html
+嘉里
+
+// xn--xhq521b : Guangzhou YU Wei Information Technology Co., Ltd.
+// https://www.iana.org/domains/root/db/xn--xhq521b.html
+广东
+
+// xn--zfr164b : China Organizational Name Administration Center
+// https://www.iana.org/domains/root/db/xn--zfr164b.html
+政务
+
+// xyz : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/xyz.html
+xyz
+
+// yachts : XYZ.COM LLC
+// https://www.iana.org/domains/root/db/yachts.html
+yachts
+
+// yahoo : Yahoo Inc.
+// https://www.iana.org/domains/root/db/yahoo.html
+yahoo
+
+// yamaxun : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/yamaxun.html
+yamaxun
+
+// yandex : YANDEX, LLC
+// https://www.iana.org/domains/root/db/yandex.html
+yandex
+
+// yodobashi : YODOBASHI CAMERA CO.,LTD.
+// https://www.iana.org/domains/root/db/yodobashi.html
+yodobashi
+
+// yoga : Registry Services, LLC
+// https://www.iana.org/domains/root/db/yoga.html
+yoga
+
+// yokohama : GMO Registry, Inc.
+// https://www.iana.org/domains/root/db/yokohama.html
+yokohama
+
+// you : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/you.html
+you
+
+// youtube : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/youtube.html
+youtube
+
+// yun : Beijing Qihu Keji Co., Ltd.
+// https://www.iana.org/domains/root/db/yun.html
+yun
+
+// zappos : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/zappos.html
+zappos
+
+// zara : Industria de Diseño Textil, S.A. (INDITEX, S.A.)
+// https://www.iana.org/domains/root/db/zara.html
+zara
+
+// zero : Amazon Registry Services, Inc.
+// https://www.iana.org/domains/root/db/zero.html
+zero
+
+// zip : Charleston Road Registry Inc.
+// https://www.iana.org/domains/root/db/zip.html
+zip
+
+// zone : Binky Moon, LLC
+// https://www.iana.org/domains/root/db/zone.html
+zone
+
+// zuerich : Kanton Zürich (Canton of Zurich)
+// https://www.iana.org/domains/root/db/zuerich.html
+zuerich
+
+// ===END ICANN DOMAINS===
diff --git a/client/src/test/java/org/asynchttpclient/AsyncHttpClientDefaultsTest.java b/client/src/test/java/org/asynchttpclient/AsyncHttpClientDefaultsTest.java
index 8b7d172a45..6dcd27ad39 100644
--- a/client/src/test/java/org/asynchttpclient/AsyncHttpClientDefaultsTest.java
+++ b/client/src/test/java/org/asynchttpclient/AsyncHttpClientDefaultsTest.java
@@ -125,6 +125,11 @@ public void testDefaultHashedWheelTimerSize() {
testIntegerSystemProperty("hashedWheelTimerSize", "defaultHashedWheelTimerSize", "512");
}
+ public void testDefaultMaxDecompressedResponseSize() {
+ Assert.assertEquals(AsyncHttpClientConfigDefaults.defaultMaxDecompressedResponseSize(), 256 * 1024 * 1024);
+ testIntegerSystemProperty("maxDecompressedResponseSize", "defaultMaxDecompressedResponseSize", "1024");
+ }
+
private void testIntegerSystemProperty(String propertyName, String methodName, String value) {
String previous = System.getProperty(ASYNC_CLIENT_CONFIG_ROOT + propertyName);
System.setProperty(ASYNC_CLIENT_CONFIG_ROOT + propertyName, value);
diff --git a/client/src/test/java/org/asynchttpclient/CookieStoreTest.java b/client/src/test/java/org/asynchttpclient/CookieStoreTest.java
index e248e9a0c4..c5ad915487 100644
--- a/client/src/test/java/org/asynchttpclient/CookieStoreTest.java
+++ b/client/src/test/java/org/asynchttpclient/CookieStoreTest.java
@@ -28,14 +28,14 @@
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;
import static org.testng.Assert.assertTrue;
-import com.google.common.collect.Sets;
-
public class CookieStoreTest {
private final Logger logger = LoggerFactory.getLogger(getClass());
@@ -55,6 +55,7 @@ public void tearDownGlobal() {
public void runAllSequentiallyBecauseNotThreadSafe() throws Exception {
addCookieWithEmptyPath();
dontReturnCookieForAnotherDomain();
+ dontStoreCookieForUnrelatedDomainAttribute();
returnCookieWhenItWasSetOnSamePath();
returnCookieWhenItWasSetOnParentPath();
dontReturnCookieWhenDomainMatchesButPathIsDifferent();
@@ -93,6 +94,14 @@ private void addCookieWithEmptyPath() {
assertTrue(store.get(uri).size() > 0);
}
+ // rfc6265#section-5.3 step 6: a host must not be able to set a cookie for an unrelated domain
+ private void dontStoreCookieForUnrelatedDomainAttribute() {
+ CookieStore store = new ThreadSafeCookieStore();
+ store.add(Uri.create("http://www.evil.com/"), ClientCookieDecoder.LAX.decode("SID=attacker; Domain=victim.com"));
+ assertTrue(store.get(Uri.create("https://victim.com/account")).isEmpty());
+ assertTrue(store.getAll().isEmpty());
+ }
+
private void dontReturnCookieForAnotherDomain() {
CookieStore store = new ThreadSafeCookieStore();
store.add(Uri.create("http://www.foo.com"), ClientCookieDecoder.LAX.decode("ALPHA=VALUE1; path="));
@@ -359,7 +368,7 @@ private void shouldCleanExpiredCookieFromUnderlyingDataStructure() throws Except
store.evictExpired();
assertTrue(store.getUnderlying().size() == 2);
Collection unexpiredCookieNames = store.getAll().stream().map(Cookie::name).collect(Collectors.toList());
- assertTrue(unexpiredCookieNames.containsAll(Sets.newHashSet("UNEXPIRED_BAR", "UNEXPIRED_FOOBAR")));
+ assertTrue(unexpiredCookieNames.containsAll(new HashSet<>(Arrays.asList("UNEXPIRED_BAR", "UNEXPIRED_FOOBAR"))));
}
private static Cookie getCookie(String key, String value, int maxAge) {
diff --git a/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientConfigTest.java b/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientConfigTest.java
new file mode 100644
index 0000000000..e069b89c6f
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/DefaultAsyncHttpClientConfigTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015-2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient;
+
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+public class DefaultAsyncHttpClientConfigTest {
+
+ @Test
+ public void testStripAuthorizationOnRedirect_DefaultIsFalse() {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build();
+ assertFalse(config.isStripAuthorizationOnRedirect(), "Default should be false");
+ }
+
+ @Test
+ public void testStripAuthorizationOnRedirect_SetTrue() {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setStripAuthorizationOnRedirect(true)
+ .build();
+ assertTrue(config.isStripAuthorizationOnRedirect(), "Should be true when set");
+ }
+
+ @Test
+ public void testStripAuthorizationOnRedirect_SetFalse() {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setStripAuthorizationOnRedirect(false)
+ .build();
+ assertFalse(config.isStripAuthorizationOnRedirect(), "Should be false when set to false");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/DigestCnonceTest.java b/client/src/test/java/org/asynchttpclient/DigestCnonceTest.java
new file mode 100644
index 0000000000..3a7f81bfe0
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/DigestCnonceTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient;
+
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.asynchttpclient.Dsl.digestAuthRealm;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * RFC 7616 section 3.3 requires the client nonce to be unpredictable: it is what stops a hostile or
+ * compromised server from choosing the whole digest input and precomputing responses. A non-cryptographic
+ * PRNG such as {@link java.util.concurrent.ThreadLocalRandom} does not provide that, so the cnonce must be
+ * drawn from a {@link SecureRandom}.
+ */
+public class DigestCnonceTest {
+
+ /**
+ * Structural check: whatever generator {@code Realm.Builder} holds for the cnonce, it must be a
+ * {@link SecureRandom}. Deliberately does not hard-code the field name so a rename does not break it.
+ */
+ @Test
+ public void cnonceIsDrawnFromASecureRandom() throws Exception {
+ SecureRandom found = null;
+ for (Field field : Realm.Builder.class.getDeclaredFields()) {
+ if (!Modifier.isStatic(field.getModifiers())) {
+ continue;
+ }
+ field.setAccessible(true);
+ Object value = field.get(null);
+ if (value instanceof SecureRandom) {
+ found = (SecureRandom) value;
+ } else if (value instanceof ThreadLocal) {
+ Object supplied = ((ThreadLocal>) value).get();
+ if (supplied instanceof SecureRandom) {
+ found = (SecureRandom) supplied;
+ }
+ }
+ }
+
+ assertNotNull(found, "Realm.Builder must draw the Digest cnonce from a SecureRandom (RFC 7616 section 3.3)");
+ }
+
+ @Test
+ public void everyDigestRealmGetsItsOwnCnonce() {
+ Set cnonces = new HashSet<>();
+ for (int i = 0; i < 64; i++) {
+ Realm realm = digestAuthRealm("user", "password")
+ .setRealmName("realm")
+ .setNonce("aabbccddeeff")
+ .setQop("auth")
+ .build();
+ assertNotNull(realm.getCnonce(), "a Digest realm built against a server nonce must carry a cnonce");
+ cnonces.add(realm.getCnonce());
+ }
+
+ assertTrue(cnonces.size() == 64, "each Digest realm must get a fresh cnonce, got " + cnonces.size() + " distinct out of 64");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/Http1DecompressionLimitTest.java b/client/src/test/java/org/asynchttpclient/Http1DecompressionLimitTest.java
new file mode 100644
index 0000000000..894a6713ab
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/Http1DecompressionLimitTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import io.netty.handler.codec.compression.DecompressionException;
+import io.netty.handler.codec.http.HttpContentDecompressor;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import org.asynchttpclient.netty.channel.ChannelManager;
+import org.asynchttpclient.netty.handler.BoundedHttpContentDecompressor;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.GZIPOutputStream;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+/**
+ * HTTP/1.1 automatic response decompression must honour
+ * {@link AsyncHttpClientConfig#getMaxDecompressedResponseSize()} cumulatively over the whole
+ * response, so a small, highly compressible body cannot inflate without bound.
+ *
+ * The distinction matters. Netty's {@code HttpContentDecompressor(int maxAllocation)} caps the output
+ * of one {@code ZlibDecoder.decode()} call and keeps no counter across calls, and the HTTP codec
+ * feeds the decompressor at most {@code httpClientCodecMaxChunkSize} (8 KiB) at a time. A body with a
+ * merely ordinary compression ratio therefore sails straight through such a "limit" no matter how large
+ * its total becomes - which is what {@link #cumulativeLimitFiresEvenWhenNoSingleChunkExceedsIt()} pins
+ * down. Passing a non-zero maxAllocation is also unsafe: Netty 4.1 forwards it to
+ * {@code new BrotliDecoder(int)}, an input buffer size that is eagerly allocated as native
+ * memory, so a 256 MiB "ceiling" would mean a 256 MiB direct allocation per {@code br} response - see
+ * {@link #decompressorIsBuiltWithoutANettyMaxAllocation()}.
+ */
+public class Http1DecompressionLimitTest {
+
+ // 4 MiB of a single repeated byte: gzips to a few KiB but inflates to 4 MiB.
+ private static final byte[] LARGE_PAYLOAD = new byte[4 * 1024 * 1024];
+
+ /**
+ * 8 MiB that gzips at a ratio of roughly 20:1. Deliberately NOT a run of one byte: at 20:1 the largest
+ * amount a single 8 KiB codec chunk can inflate to is about 160 KiB, which is far below both the 1 MiB
+ * ceiling this payload is served against and Netty's 256 MiB maxAllocation default. Only a counter that
+ * spans the whole response can stop it.
+ */
+ private static final byte[] RATIO_20_PAYLOAD = ratio20Payload(8 * 1024 * 1024);
+
+ private static final int LOW_RATIO_LIMIT = 1024 * 1024;
+
+ static {
+ Arrays.fill(LARGE_PAYLOAD, (byte) 'a');
+ }
+
+ private static byte[] ratio20Payload(int size) {
+ byte[] bytes = new byte[size];
+ Arrays.fill(bytes, (byte) 'a');
+ Random random = new Random(42); // fixed seed: the compression ratio must not vary between runs
+ for (int i = 0; i < size; i++) {
+ if (random.nextInt(48) == 0) {
+ bytes[i] = (byte) ('b' + random.nextInt(20));
+ }
+ }
+ return bytes;
+ }
+
+ private static byte[] gzip(byte[] payload) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ GZIPOutputStream gzip = new GZIPOutputStream(out);
+ gzip.write(payload);
+ gzip.finish();
+ gzip.close();
+ return out.toByteArray();
+ }
+
+ private static HttpServer httpServer;
+
+ @BeforeClass
+ public static void setupServer() throws Exception {
+ httpServer = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
+ httpServer.createContext("/gzip-bomb").setHandler(gzipHandler(LARGE_PAYLOAD));
+ httpServer.createContext("/gzip-ratio-20").setHandler(gzipHandler(RATIO_20_PAYLOAD));
+ httpServer.start();
+ }
+
+ private static HttpHandler gzipHandler(final byte[] payload) {
+ return new HttpHandler() {
+ @Override
+ public void handle(HttpExchange exchange) throws IOException {
+ exchange.getResponseHeaders().set("Content-Encoding", "gzip");
+ exchange.sendResponseHeaders(200, 0);
+ OutputStream out = exchange.getResponseBody();
+ try {
+ GZIPOutputStream gzip = new GZIPOutputStream(out);
+ gzip.write(payload);
+ gzip.finish();
+ gzip.close();
+ } finally {
+ out.close();
+ }
+ }
+ };
+ }
+
+ @AfterClass
+ public static void stopServer() {
+ if (httpServer != null) {
+ httpServer.stop(0);
+ }
+ }
+
+ private static AsyncHttpClient clientWithLimit(int maxDecompressedResponseSize) {
+ return clientWithLimit(maxDecompressedResponseSize, false);
+ }
+
+ private static AsyncHttpClient clientWithLimit(int maxDecompressedResponseSize, boolean keepEncodingHeader) {
+ AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setCompressionEnforced(true)
+ .setKeepEncodingHeader(keepEncodingHeader)
+ .setMaxDecompressedResponseSize(maxDecompressedResponseSize)
+ .build();
+ return new DefaultAsyncHttpClient(config);
+ }
+
+ private static String url(String path) {
+ return "http://localhost:" + httpServer.getAddress().getPort() + path;
+ }
+
+ private static boolean hasDecompressionCause(Throwable t) {
+ for (Throwable c = t; c != null; c = c.getCause()) {
+ if (c instanceof DecompressionException) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The core of the fix. The body inflates to 8 MiB against a 1 MiB ceiling, but no single decode step
+ * produces more than about 160 KiB, so nothing that looks only at one chunk - including Netty's
+ * maxAllocation - can catch it. The arithmetic is asserted, not assumed, so the payload cannot silently
+ * drift into being a trivially compressible one.
+ */
+ @Test
+ public void cumulativeLimitFiresEvenWhenNoSingleChunkExceedsIt() throws Exception {
+ byte[] compressed = gzip(RATIO_20_PAYLOAD);
+ double ratio = (double) RATIO_20_PAYLOAD.length / compressed.length;
+ long largestSingleChunkInflation = (long) Math.ceil(ratio * 8192);
+
+ assertTrue(RATIO_20_PAYLOAD.length > LOW_RATIO_LIMIT,
+ "the payload must exceed the ceiling in total, else the test proves nothing");
+ assertTrue(largestSingleChunkInflation < LOW_RATIO_LIMIT,
+ "no single 8 KiB codec chunk may reach the ceiling on its own, else a per-chunk limit would "
+ + "also pass this test; ratio=" + ratio + ", per-chunk=" + largestSingleChunkInflation);
+
+ try (AsyncHttpClient client = clientWithLimit(LOW_RATIO_LIMIT)) {
+ try {
+ client.prepareGet(url("/gzip-ratio-20")).execute().get(30, TimeUnit.SECONDS);
+ fail("a body inflating past the configured ceiling must not be delivered, even in small steps");
+ } catch (ExecutionException ex) {
+ assertTrue(hasDecompressionCause(ex),
+ "expected a DecompressionException in the cause chain but got: " + ex.getCause());
+ }
+ }
+ }
+
+ @Test
+ public void decompressionBeyondLimitFails() throws Exception {
+ // 256 KiB ceiling, but the body inflates to 4 MiB.
+ try (AsyncHttpClient client = clientWithLimit(256 * 1024)) {
+ try {
+ client.prepareGet(url("/gzip-bomb")).execute().get(30, TimeUnit.SECONDS);
+ fail("a body inflating past the configured ceiling must not be delivered");
+ } catch (ExecutionException ex) {
+ assertTrue(hasDecompressionCause(ex),
+ "expected a DecompressionException in the cause chain but got: " + ex.getCause());
+ }
+ }
+ }
+
+ @Test
+ public void decompressionWithinLimitSucceeds() throws Exception {
+ // A generous ceiling comfortably above the 4 MiB inflated size lets the same body through unchanged.
+ try (AsyncHttpClient client = clientWithLimit(64 * 1024 * 1024)) {
+ Response response = client.prepareGet(url("/gzip-bomb")).execute().get(30, TimeUnit.SECONDS);
+ assertEquals(response.getStatusCode(), 200);
+ assertEquals(response.getResponseBodyAsBytes().length, LARGE_PAYLOAD.length);
+ }
+ }
+
+ /**
+ * The counter must restart per response, so a client whose ceiling is above a single body's inflated
+ * size can serve an unbounded number of them on a pooled connection.
+ */
+ @Test
+ public void limitIsPerResponseNotPerConnection() throws Exception {
+ try (AsyncHttpClient client = clientWithLimit(8 * 1024 * 1024)) {
+ for (int i = 0; i < 3; i++) {
+ Response response = client.prepareGet(url("/gzip-bomb")).execute().get(30, TimeUnit.SECONDS);
+ assertEquals(response.getStatusCode(), 200);
+ assertEquals(response.getResponseBodyAsBytes().length, LARGE_PAYLOAD.length,
+ "response " + i + " must decompress fully; the counter must not carry over");
+ }
+ }
+ }
+
+ @Test
+ public void decompressionBeyondLimitFailsWithKeepEncodingHeader() throws Exception {
+ // keepEncodingHeader used to select a separate anonymous HttpContentDecompressor subclass; it is now a
+ // constructor flag, and this covers the half-fix where only one of the two branches was bounded.
+ try (AsyncHttpClient client = clientWithLimit(256 * 1024, true)) {
+ try {
+ client.prepareGet(url("/gzip-bomb")).execute().get(30, TimeUnit.SECONDS);
+ fail("the keepEncodingHeader decompressor must honour the ceiling too");
+ } catch (ExecutionException ex) {
+ assertTrue(hasDecompressionCause(ex),
+ "expected a DecompressionException in the cause chain but got: " + ex.getCause());
+ }
+ }
+ }
+
+ @Test
+ public void keepEncodingHeaderStillKeepsTheEncodingHeader() throws Exception {
+ try (AsyncHttpClient client = clientWithLimit(64 * 1024 * 1024, true)) {
+ Response response = client.prepareGet(url("/gzip-bomb")).execute().get(30, TimeUnit.SECONDS);
+ assertEquals(response.getStatusCode(), 200);
+ assertEquals(response.getHeader("Content-Encoding"), "gzip",
+ "keepEncodingHeader must leave the response advertising the encoding it arrived with");
+ assertEquals(response.getResponseBodyAsBytes().length, LARGE_PAYLOAD.length);
+ }
+ }
+
+ @Test
+ public void zeroDisablesTheLimit() throws Exception {
+ // 0 keeps the unbounded behaviour for callers that deliberately opt out.
+ try (AsyncHttpClient client = clientWithLimit(0)) {
+ Response response = client.prepareGet(url("/gzip-bomb")).execute().get(30, TimeUnit.SECONDS);
+ assertEquals(response.getStatusCode(), 200);
+ assertEquals(response.getResponseBodyAsBytes().length, LARGE_PAYLOAD.length);
+ }
+ }
+
+ /**
+ * brotli4j is not on the 2.x classpath, so a {@code br} response cannot be exercised end to end here.
+ * What can be pinned - and is the part that matters - is that AHC never hands Netty a non-zero
+ * maxAllocation. In Netty 4.1, {@code HttpContentDecompressor.newContentDecoder} passes maxAllocation to
+ * {@code new BrotliDecoder(int)}, whose one-argument form is {@code inputBufferSize}, and
+ * {@code BrotliDecoder.handlerAdded} eagerly allocates {@code new DecoderJNI.Wrapper(inputBufferSize)} in
+ * direct memory. With maxAllocation left at 256 MiB, any peer that answers
+ * {@code Content-Encoding: br} - even with an empty body - would cost the client 256 MiB of native
+ * memory per concurrent response: cheaper for an attacker than the bomb this limit exists to stop.
+ */
+ @Test
+ public void decompressorIsBuiltWithoutANettyMaxAllocation() throws Exception {
+ AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setMaxDecompressedResponseSize(256 * 1024 * 1024)
+ .build();
+ Timer timer = new HashedWheelTimer();
+ ChannelManager channelManager = new ChannelManager(config, timer);
+ try {
+ Method factory = ChannelManager.class.getDeclaredMethod("newHttpContentDecompressor");
+ factory.setAccessible(true);
+ HttpContentDecompressor decompressor = (HttpContentDecompressor) factory.invoke(channelManager);
+
+ assertTrue(decompressor instanceof BoundedHttpContentDecompressor,
+ "the pipeline must use the counting decompressor, not a bare Netty one: " + decompressor.getClass());
+
+ Field maxAllocation = HttpContentDecompressor.class.getDeclaredField("maxAllocation");
+ maxAllocation.setAccessible(true);
+ assertEquals(maxAllocation.getInt(decompressor), 0,
+ "Netty's maxAllocation must stay 0: it does not bound a response, and a non-zero value is "
+ + "used as the eagerly allocated BrotliDecoder input buffer size");
+ } finally {
+ channelManager.close();
+ timer.stop();
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/HttpsDowngradeRedirectTest.java b/client/src/test/java/org/asynchttpclient/HttpsDowngradeRedirectTest.java
new file mode 100644
index 0000000000..a7d25b32bc
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/HttpsDowngradeRedirectTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2015-2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.asynchttpclient.test.TestUtils.addHttpConnector;
+import static org.asynchttpclient.test.TestUtils.addHttpsConnector;
+import static org.testng.Assert.assertNull;
+
+/**
+ * Verifies that credentials are stripped when a redirect downgrades the scheme
+ * from HTTPS to HTTP, even if host and port would otherwise match.
+ */
+public class HttpsDowngradeRedirectTest {
+
+ private static Server server;
+ private static int httpPort;
+ private static int httpsPort;
+ private static final AtomicReference authOnHttpTarget = new AtomicReference<>();
+ private static final AtomicReference cookieOnHttpTarget = new AtomicReference<>();
+
+ @BeforeClass(alwaysRun = true)
+ public static void setUp() throws Exception {
+ server = new Server();
+ ServerConnector httpConnector = addHttpConnector(server);
+ ServerConnector httpsConnector = addHttpsConnector(server);
+
+ server.setHandler(new AbstractHandler() {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) {
+ if ("/redirect".equals(target)) {
+ // Redirect from HTTPS to plain HTTP on the same host — a scheme downgrade.
+ response.setStatus(302);
+ response.setHeader("Location", "http://localhost:" + httpPort + "/target");
+ } else if ("/target".equals(target)) {
+ authOnHttpTarget.set(request.getHeader("Authorization"));
+ cookieOnHttpTarget.set(request.getHeader("Cookie"));
+ response.setStatus(200);
+ }
+ baseRequest.setHandled(true);
+ }
+ });
+
+ server.start();
+ httpPort = httpConnector.getLocalPort();
+ httpsPort = httpsConnector.getLocalPort();
+ }
+
+ @AfterClass(alwaysRun = true)
+ public static void tearDown() throws Exception {
+ if (server != null) {
+ server.stop();
+ }
+ }
+
+ @Test
+ public void httpsToHttpDowngradeStripsAuthorization() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setUseInsecureTrustManager(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ authOnHttpTarget.set(null);
+
+ client.prepareGet("https://localhost:" + httpsPort + "/redirect")
+ .setHeader("Authorization", "Bearer secret-token")
+ .execute()
+ .get(10, TimeUnit.SECONDS);
+
+ // HTTPS -> HTTP is a scheme downgrade: credentials must not leak to the plain-HTTP target.
+ assertNull(authOnHttpTarget.get(),
+ "Authorization header must be stripped when redirect downgrades HTTPS to HTTP");
+ }
+ }
+
+ /**
+ * HTTPS-to-HTTP downgrade also strips the Cookie header. Regression test for GHSA-fmxf-pm6p-7xgm.
+ */
+ @Test
+ public void httpsToHttpDowngradeStripsCookie() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setUseInsecureTrustManager(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ cookieOnHttpTarget.set(null);
+
+ client.prepareGet("https://localhost:" + httpsPort + "/redirect")
+ .setHeader("Cookie", "session=secret-session")
+ .execute()
+ .get(10, TimeUnit.SECONDS);
+
+ assertNull(cookieOnHttpTarget.get(),
+ "Cookie header must be stripped when redirect downgrades HTTPS to HTTP");
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/RealmTest.java b/client/src/test/java/org/asynchttpclient/RealmTest.java
index 5f17a9b6fd..7f278cbb9e 100644
--- a/client/src/test/java/org/asynchttpclient/RealmTest.java
+++ b/client/src/test/java/org/asynchttpclient/RealmTest.java
@@ -24,6 +24,54 @@
import static org.testng.Assert.assertEquals;
public class RealmTest {
+
+ /**
+ * A Digest challenge that yields no nonce must not become a Basic one. Answering it as Basic puts the
+ * password on the wire in the clear, and a server cannot obtain that by offering Basic outright,
+ * because a Digest realm refuses a challenge that is not Digest. Omitting the nonce, or sending it
+ * empty, was enough to get the password out of the client.
+ */
+ @Test
+ public void aDigestChallengeWithNoNonceMustNotDowngradeToBasic() {
+ Realm realm = new Realm.Builder("user", "pass")
+ .parseWWWAuthenticateHeader("Digest realm=\"protected\"")
+ .build();
+
+ assertEquals(realm.getScheme(), Realm.AuthScheme.DIGEST,
+ "an unreadable Digest challenge must fail, not answer in cleartext");
+ }
+
+ @Test
+ public void aDigestChallengeWithAnEmptyNonceMustNotDowngradeToBasic() {
+ Realm realm = new Realm.Builder("user", "pass")
+ .parseWWWAuthenticateHeader("Digest realm=\"protected\", nonce=\"\"")
+ .build();
+
+ assertEquals(realm.getScheme(), Realm.AuthScheme.DIGEST);
+ }
+
+ @Test
+ public void aProxyDigestChallengeWithNoNonceMustNotDowngradeToBasic() {
+ Realm realm = new Realm.Builder("user", "pass")
+ .parseProxyAuthenticateHeader("Digest realm=\"protected\"")
+ .build();
+
+ assertEquals(realm.getScheme(), Realm.AuthScheme.DIGEST,
+ "an unreadable proxy Digest challenge must fail, not answer in cleartext");
+ }
+
+ /**
+ * A genuine Basic challenge is still Basic: the fix must not turn every challenge into Digest.
+ */
+ @Test
+ public void aBasicChallengeIsStillBasic() {
+ Realm realm = new Realm.Builder("user", "pass")
+ .parseWWWAuthenticateHeader("Basic realm=\"protected\"")
+ .build();
+
+ assertEquals(realm.getScheme(), Realm.AuthScheme.BASIC);
+ }
+
@Test
public void testClone() {
Realm orig = basicAuthRealm("user", "pass").setCharset(UTF_16)
diff --git a/client/src/test/java/org/asynchttpclient/RedirectCredentialSecurityTest.java b/client/src/test/java/org/asynchttpclient/RedirectCredentialSecurityTest.java
new file mode 100644
index 0000000000..6b52fbf94c
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/RedirectCredentialSecurityTest.java
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) 2015-2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient;
+
+import com.sun.net.httpserver.HttpServer;
+import io.netty.handler.codec.http.cookie.DefaultCookie;
+import org.asynchttpclient.cookie.ThreadSafeCookieStore;
+import org.asynchttpclient.uri.Uri;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.asynchttpclient.Dsl.basicAuthRealm;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Tests for credential stripping on cross-domain redirects and HTTPS-to-HTTP downgrades.
+ * Verifies that Authorization headers, Cookie headers, and Realm credentials are not leaked
+ * to different origins.
+ */
+public class RedirectCredentialSecurityTest {
+
+ private static HttpServer serverA;
+ private static HttpServer serverB;
+ private static HttpServer serverC;
+ private static int portA;
+ private static int portB;
+ private static int portC;
+ private static final AtomicReference lastAuthHeaderOnA = new AtomicReference<>();
+ private static final AtomicReference lastAuthHeaderOnB = new AtomicReference<>();
+ private static final AtomicReference authAtChainStep2 = new AtomicReference<>();
+ private static final AtomicReference authOnBounceBack = new AtomicReference<>();
+ private static final AtomicReference authOn307Target = new AtomicReference<>();
+ private static final AtomicReference bodyOn307Target = new AtomicReference<>();
+ private static final AtomicReference authOn308Target = new AtomicReference<>();
+ private static final AtomicReference bodyOn308Target = new AtomicReference<>();
+ private static final AtomicReference proxyAuthOnB = new AtomicReference<>();
+ private static final AtomicReference authOnHttpsDowngradeTarget = new AtomicReference<>();
+ private static final AtomicReference lastCookieHeaderOnA = new AtomicReference<>();
+ private static final AtomicReference lastCookieHeaderOnB = new AtomicReference<>();
+ private static final AtomicReference cookieAtChainStep2 = new AtomicReference<>();
+ private static final AtomicReference cookieOnBounceBack = new AtomicReference<>();
+ private static final AtomicReference authOn401Target = new AtomicReference<>();
+
+ @BeforeClass
+ public static void startServers() throws Exception {
+ // Server A: the "origin" server that issues redirects
+ serverA = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ portA = serverA.getAddress().getPort();
+
+ // Server B: the "target" server that receives redirected requests (different port = different origin)
+ serverB = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ portB = serverB.getAddress().getPort();
+
+ // Server C: a third server for multi-hop redirect chains
+ serverC = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ portC = serverC.getAddress().getPort();
+
+ // Server A endpoints
+ serverA.createContext("/redirect-to-b", exchange -> {
+ lastAuthHeaderOnA.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ lastCookieHeaderOnA.set(exchange.getRequestHeaders().getFirst("Cookie"));
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/target");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverA.createContext("/redirect-same-origin", exchange -> {
+ lastAuthHeaderOnA.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ lastCookieHeaderOnA.set(exchange.getRequestHeaders().getFirst("Cookie"));
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portA + "/final");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverA.createContext("/final", exchange -> {
+ lastAuthHeaderOnA.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ lastCookieHeaderOnA.set(exchange.getRequestHeaders().getFirst("Cookie"));
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ // Server B endpoints
+ serverB.createContext("/target", exchange -> {
+ lastAuthHeaderOnB.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ lastCookieHeaderOnB.set(exchange.getRequestHeaders().getFirst("Cookie"));
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ // Multi-hop: A -> A (same-origin) -> B (cross-domain)
+ serverA.createContext("/chain-same-then-cross", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portA + "/chain-step2");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverA.createContext("/chain-step2", exchange -> {
+ authAtChainStep2.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ cookieAtChainStep2.set(exchange.getRequestHeaders().getFirst("Cookie"));
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/target");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ // Multi-hop: A -> B (cross-domain, credentials stripped) -> C (credentials stay stripped)
+ serverA.createContext("/chain-cross-and-back", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/bounce-to-c");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverB.createContext("/bounce-to-c", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portC + "/chain-final");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverC.createContext("/chain-final", exchange -> {
+ authOnBounceBack.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ cookieOnBounceBack.set(exchange.getRequestHeaders().getFirst("Cookie"));
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ // 307 Temporary Redirect: A -> B (body preserved, auth stripped)
+ serverA.createContext("/redirect-307-to-b", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/target-307");
+ exchange.sendResponseHeaders(307, -1);
+ exchange.close();
+ });
+
+ serverB.createContext("/target-307", exchange -> {
+ authOn307Target.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ bodyOn307Target.set(readAll(exchange.getRequestBody()));
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ // 308 Permanent Redirect: A -> B (body preserved, auth stripped)
+ serverA.createContext("/redirect-308-to-b", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/target-308");
+ exchange.sendResponseHeaders(308, -1);
+ exchange.close();
+ });
+
+ serverB.createContext("/target-308", exchange -> {
+ authOn308Target.set(exchange.getRequestHeaders().getFirst("Authorization"));
+ bodyOn308Target.set(readAll(exchange.getRequestBody()));
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ // Proxy-Authorization cross-domain redirect: A -> B
+ serverA.createContext("/redirect-to-b-proxy", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/target-proxy");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverB.createContext("/target-proxy", exchange -> {
+ proxyAuthOnB.set(exchange.getRequestHeaders().getFirst("Proxy-Authorization"));
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ // Cross-domain redirect to a target that answers 401: the target must never receive
+ // credentials, even those configured client-wide via config.setRealm(...).
+ serverA.createContext("/redirect-to-b-401", exchange -> {
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + portB + "/target-401");
+ exchange.sendResponseHeaders(302, -1);
+ exchange.close();
+ });
+
+ serverB.createContext("/target-401", exchange -> {
+ String auth = exchange.getRequestHeaders().getFirst("Authorization");
+ if (auth != null) {
+ authOn401Target.set(auth);
+ }
+ exchange.getResponseHeaders().add("WWW-Authenticate", "Basic realm=\"target\"");
+ // Streamed framing (length 0 plus a closed body), which this server emits as Transfer-Encoding:
+ // chunked with a terminating chunk, so the connection survives the exchange.
+ //
+ // What decides that is the getResponseBody().close() below, not the 0. Measured against
+ // com.sun.net.httpserver on JDK 11.0.31 and JDK 25: with this handler otherwise unchanged,
+ // sendResponseHeaders(401, -1) reuses the connection too, the authenticated retry this test is
+ // watching for still reaches this handler, and the leak is still observed. Only dropping the body
+ // close strands it, and even then nothing is reset - -1 sends Content-Length: 0 and at worst a
+ // clean FIN. On the client side Unauthorized401Interceptor opens a fresh connection whenever the
+ // channel is not reusable, so a closed idle socket would not strand the retry either.
+ exchange.sendResponseHeaders(401, 0);
+ exchange.getResponseBody().close();
+ exchange.close();
+ });
+
+ serverA.start();
+ serverB.start();
+ serverC.start();
+ }
+
+ @AfterClass
+ public static void stopServers() {
+ serverA.stop(0);
+ serverB.stop(0);
+ serverC.stop(0);
+ }
+
+ private static String readAll(InputStream in) throws java.io.IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buf = new byte[4096];
+ int n;
+ while ((n = in.read(buf)) != -1) {
+ out.write(buf, 0, n);
+ }
+ return new String(out.toByteArray(), StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Cross-domain redirect (different port) must strip Authorization header.
+ */
+ @Test
+ public void crossDomainRedirectStripsAuthHeader() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnA.set(null);
+ lastAuthHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b")
+ .setHeader("Authorization", "Bearer secret-token")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Auth header should be present on the original request to server A
+ assertEquals(lastAuthHeaderOnA.get(), "Bearer secret-token",
+ "Authorization header should be present on original request");
+ // Auth header must NOT be forwarded to the cross-domain target (server B)
+ assertNull(lastAuthHeaderOnB.get(),
+ "Authorization header must be stripped on cross-domain redirect");
+ }
+ }
+
+ /**
+ * Same-origin redirect (same host and port) should preserve Authorization header.
+ */
+ @Test
+ public void sameOriginRedirectPreservesAuthHeader() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnA.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-same-origin")
+ .setHeader("Authorization", "Bearer secret-token")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Auth header should still be present after same-origin redirect
+ assertEquals(lastAuthHeaderOnA.get(), "Bearer secret-token",
+ "Authorization header should be preserved on same-origin redirect");
+ }
+ }
+
+ /**
+ * Cross-domain redirect must strip Proxy-Authorization header too.
+ */
+ @Test
+ public void crossDomainRedirectStripsProxyAuthHeader() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ proxyAuthOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b-proxy")
+ .setHeader("Proxy-Authorization", "Basic cHJveHk6cGFzcw==")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertNull(proxyAuthOnB.get(),
+ "Proxy-Authorization header must be stripped on cross-domain redirect");
+ }
+ }
+
+ /**
+ * Realm-based BASIC auth credentials must NOT be propagated on cross-domain redirects.
+ * This tests that the Realm object is cleared, preventing credential regeneration
+ * via NettyRequestFactory.
+ */
+ @Test
+ public void crossDomainRedirectDoesNotPropagateRealm() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b")
+ .setRealm(basicAuthRealm("user", "password").setUsePreemptiveAuth(true).build())
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // The Realm-generated Authorization header must NOT appear on the cross-domain target
+ assertNull(lastAuthHeaderOnB.get(),
+ "Realm-based credentials must not be propagated on cross-domain redirect");
+ }
+ }
+
+ /**
+ * Realm bypass is closed: even with stripAuthorizationOnRedirect=true on a same-origin redirect,
+ * the Realm credential regeneration must be prevented (no Authorization header on target).
+ */
+ @Test
+ public void stripAuthorizationOnRedirectAlsoStripsRealm() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setStripAuthorizationOnRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnA.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-same-origin")
+ .setRealm(basicAuthRealm("user", "password").setUsePreemptiveAuth(true).build())
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Even on same-origin, with stripAuthorizationOnRedirect=true, the Realm-regenerated
+ // Authorization header must NOT appear (this was the bypass bug)
+ assertNull(lastAuthHeaderOnA.get(),
+ "stripAuthorizationOnRedirect=true must also prevent Realm-based credential regeneration");
+ }
+ }
+
+ /**
+ * Same-origin redirect should preserve Realm-based credentials when stripping is not enabled.
+ */
+ @Test
+ public void sameOriginRedirectPreservesRealmCredentials() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnA.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-same-origin")
+ .setRealm(basicAuthRealm("user", "password").setUsePreemptiveAuth(true).build())
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // On same-origin, Realm credentials should be preserved
+ assertEquals(lastAuthHeaderOnA.get(), "Basic dXNlcjpwYXNzd29yZA==",
+ "Realm-based credentials should be preserved on same-origin redirect");
+ }
+ }
+
+ /**
+ * Multi-hop redirect: A -> A (same-origin) -> B (cross-domain).
+ * Credentials should survive the same-origin hop but be stripped on the cross-domain hop.
+ */
+ @Test
+ public void multiHopSameOriginThenCrossDomainStripsCredentials() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ authAtChainStep2.set(null);
+ lastAuthHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/chain-same-then-cross")
+ .setHeader("Authorization", "Bearer secret-token")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Credentials should survive the same-origin intermediate hop (A -> A)
+ assertEquals(authAtChainStep2.get(), "Bearer secret-token",
+ "Authorization header should be preserved on same-origin intermediate redirect");
+ // Credentials must be stripped on the final cross-domain hop (A -> B)
+ assertNull(lastAuthHeaderOnB.get(),
+ "Authorization header must be stripped on cross-domain hop in redirect chain");
+ }
+ }
+
+ /**
+ * Multi-hop redirect: A -> B (cross-domain) -> C (another domain).
+ * Once credentials are stripped at the first cross-domain hop, they must not reappear
+ * on subsequent hops even if the Realm was originally set.
+ */
+ @Test
+ public void multiHopCredentialsStayStrippedAfterCrossDomain() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ authOnBounceBack.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/chain-cross-and-back")
+ .setRealm(basicAuthRealm("user", "password").setUsePreemptiveAuth(true).build())
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Credentials were stripped at A -> B; they must not reappear at B -> C
+ assertNull(authOnBounceBack.get(),
+ "Credentials must not reappear after being stripped at a cross-domain hop");
+ }
+ }
+
+ /**
+ * 307 Temporary Redirect cross-domain: body is preserved but Authorization is stripped.
+ */
+ @Test
+ public void redirect307CrossDomainStripsAuthButPreservesBody() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ authOn307Target.set(null);
+ bodyOn307Target.set(null);
+
+ client.preparePost("http://127.0.0.1:" + portA + "/redirect-307-to-b")
+ .setHeader("Authorization", "Bearer secret-token")
+ .setBody("request-body-content")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertNull(authOn307Target.get(),
+ "Authorization header must be stripped on cross-domain 307 redirect");
+ assertEquals(bodyOn307Target.get(), "request-body-content",
+ "Request body must be preserved on 307 redirect");
+ }
+ }
+
+ /**
+ * 308 Permanent Redirect cross-domain: body is preserved but Authorization is stripped.
+ */
+ @Test
+ public void redirect308CrossDomainStripsAuthButPreservesBody() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ authOn308Target.set(null);
+ bodyOn308Target.set(null);
+
+ client.preparePost("http://127.0.0.1:" + portA + "/redirect-308-to-b")
+ .setHeader("Authorization", "Bearer secret-token")
+ .setBody("request-body-content")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertNull(authOn308Target.get(),
+ "Authorization header must be stripped on cross-domain 308 redirect");
+ assertEquals(bodyOn308Target.get(), "request-body-content",
+ "Request body must be preserved on 308 redirect");
+ }
+ }
+
+ /**
+ * Cross-domain redirect (different port) must strip a user-supplied Cookie header.
+ * Regression test for GHSA-fmxf-pm6p-7xgm.
+ */
+ @Test
+ public void crossDomainRedirectStripsCookieHeader() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastCookieHeaderOnA.set(null);
+ lastCookieHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b")
+ .setHeader("Cookie", "session=abc123; csrf=xyz789")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Cookie should be present on the original request to server A
+ assertEquals(lastCookieHeaderOnA.get(), "session=abc123; csrf=xyz789",
+ "Cookie header should be present on original request");
+ // Cookie must NOT be forwarded to the cross-domain target (server B)
+ assertNull(lastCookieHeaderOnB.get(),
+ "Cookie header must be stripped on cross-domain redirect");
+ }
+ }
+
+ /**
+ * Same-origin redirect (same host and port) should preserve the Cookie header.
+ */
+ @Test
+ public void sameOriginRedirectPreservesCookieHeader() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastCookieHeaderOnA.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-same-origin")
+ .setHeader("Cookie", "session=abc123")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertEquals(lastCookieHeaderOnA.get(), "session=abc123",
+ "Cookie header should be preserved on same-origin redirect");
+ }
+ }
+
+ /**
+ * Cross-domain redirect must strip both Authorization and Cookie when both are set.
+ * Combined regression that mirrors the original PoC.
+ */
+ @Test
+ public void crossDomainRedirectStripsBothCookieAndAuthorization() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnA.set(null);
+ lastAuthHeaderOnB.set(null);
+ lastCookieHeaderOnA.set(null);
+ lastCookieHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b")
+ .setHeader("Authorization", "Bearer token123")
+ .setHeader("Cookie", "session=abc123; api_key=secret")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertEquals(lastAuthHeaderOnA.get(), "Bearer token123",
+ "Authorization header should be present on original request");
+ assertEquals(lastCookieHeaderOnA.get(), "session=abc123; api_key=secret",
+ "Cookie header should be present on original request");
+ assertNull(lastAuthHeaderOnB.get(),
+ "Authorization header must be stripped on cross-domain redirect");
+ assertNull(lastCookieHeaderOnB.get(),
+ "Cookie header must be stripped on cross-domain redirect");
+ }
+ }
+
+ /**
+ * Multi-hop: A -> A (same-origin, Cookie preserved) -> B (cross-domain, Cookie stripped).
+ */
+ @Test
+ public void multiHopChainStripsCookieAtFirstCrossOriginHop() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ cookieAtChainStep2.set(null);
+ lastCookieHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/chain-same-then-cross")
+ .setHeader("Cookie", "session=abc123")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // Cookie should survive the same-origin intermediate hop (A -> A)
+ assertEquals(cookieAtChainStep2.get(), "session=abc123",
+ "Cookie header should be preserved on same-origin intermediate redirect");
+ // Cookie must be stripped on the cross-domain hop (A -> B)
+ assertNull(lastCookieHeaderOnB.get(),
+ "Cookie header must be stripped on cross-domain hop in redirect chain");
+ }
+ }
+
+ /**
+ * Once Cookie is stripped at a cross-domain hop, it must not reappear on subsequent hops.
+ */
+ @Test
+ public void multiHopCookieStaysStrippedAfterCrossDomain() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ cookieOnBounceBack.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/chain-cross-and-back")
+ .setHeader("Cookie", "session=abc123")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertNull(cookieOnBounceBack.get(),
+ "Cookie must not reappear after being stripped at a cross-domain hop");
+ }
+ }
+
+ /**
+ * setStripAuthorizationOnRedirect(true) also strips Cookie on same-origin redirects:
+ * the conditions are coupled, so users opting into strict credential stripping get cookie
+ * stripping on the same-origin path too.
+ */
+ @Test
+ public void stripAuthorizationOnRedirectFlagAlsoStripsCookie() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setStripAuthorizationOnRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastCookieHeaderOnA.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-same-origin")
+ .setHeader("Cookie", "session=abc123")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // With stripAuthorizationOnRedirect=true, even same-origin redirects strip the Cookie
+ assertNull(lastCookieHeaderOnA.get(),
+ "stripAuthorizationOnRedirect=true must also strip Cookie on same-origin redirect");
+ }
+ }
+
+ /**
+ * Regression: a cookie added to the URI-scoped CookieStore for server B is still delivered
+ * to server B after a cross-origin redirect from A -> B. Cookie stripping must not break the
+ * legitimate cookie-store flow (store cookies are added after the strip step in
+ * Redirect30xInterceptor and are URI-matched).
+ */
+ @Test
+ public void cookieStoreManagedCookiesUnaffectedByStrip() throws Exception {
+ ThreadSafeCookieStore store = new ThreadSafeCookieStore();
+ store.add(Uri.create("http://127.0.0.1:" + portB + "/target"),
+ new DefaultCookie("store_cookie", "store_value"));
+
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setCookieStore(store)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastCookieHeaderOnB.set(null);
+
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // The store cookie scoped to server B's URI should reach server B
+ assertNotNull(lastCookieHeaderOnB.get(),
+ "URI-scoped CookieStore cookies should be delivered after cross-domain redirect");
+ assertTrue(lastCookieHeaderOnB.get().contains("store_cookie=store_value"),
+ "Expected store cookie in Cookie header, got: " + lastCookieHeaderOnB.get());
+ }
+ }
+
+ /**
+ * Same host (127.0.0.1) on a different port is treated as cross-origin: both Cookie and
+ * Authorization are stripped. Locks in the port-as-part-of-origin behavior so that a future
+ * change to {@link Uri#isSameBase} cannot accidentally narrow the origin to host-only.
+ */
+ @Test
+ public void portChangeOnSameHostIsTreatedAsCrossOrigin() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeaderOnB.set(null);
+ lastCookieHeaderOnB.set(null);
+
+ // /redirect-to-b: 127.0.0.1:portA -> 127.0.0.1:portB. Same host, different port.
+ client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b")
+ .setHeader("Authorization", "Bearer same-host-token")
+ .setHeader("Cookie", "session=same-host-cookie")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ assertNull(lastAuthHeaderOnB.get(),
+ "Authorization must be stripped when only the port differs (origin includes port)");
+ assertNull(lastCookieHeaderOnB.get(),
+ "Cookie must be stripped when only the port differs (origin includes port)");
+ }
+ }
+
+ /**
+ * Client-wide credentials set via {@code config.setRealm(...)} must not be sent to a cross-domain
+ * redirect target, even when that target answers 401 to solicit them. The redirect clears the
+ * request/future realm, but the config realm must not be re-applied.
+ */
+ @Test
+ public void crossDomainRedirectTo401TargetDoesNotLeakConfigRealm() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setRealm(basicAuthRealm("user", "password").build())
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ authOn401Target.set(null);
+
+ Response response = client.prepareGet("http://127.0.0.1:" + portA + "/redirect-to-b-401")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+
+ // The redirect is followed and the 401 is returned to the caller: the exchange completes either
+ // way, so a leak cannot hide behind a failed request.
+ assertEquals(response.getStatusCode(), 401);
+ assertNull(authOn401Target.get(),
+ "client-wide config Realm must not be sent to a cross-domain 401 target after redirect");
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/StripAuthorizationOnRedirectHttpTest.java b/client/src/test/java/org/asynchttpclient/StripAuthorizationOnRedirectHttpTest.java
new file mode 100644
index 0000000000..722612e1b6
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/StripAuthorizationOnRedirectHttpTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2015-2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient;
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNull;
+
+public class StripAuthorizationOnRedirectHttpTest {
+ private static HttpServer server;
+ private static int port;
+ private static volatile String lastAuthHeader;
+
+ @BeforeClass
+ public static void startServer() throws Exception {
+ server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
+ port = server.getAddress().getPort();
+ server.createContext("/redirect", new RedirectHandler());
+ server.createContext("/final", new FinalHandler());
+ server.start();
+ }
+
+ @AfterClass
+ public static void stopServer() {
+ server.stop(0);
+ }
+
+ static class RedirectHandler implements HttpHandler {
+ @Override
+ public void handle(HttpExchange exchange) {
+ String auth = exchange.getRequestHeaders().getFirst("Authorization");
+ lastAuthHeader = auth;
+ exchange.getResponseHeaders().add("Location", "http://127.0.0.1:" + port + "/final");
+ try {
+ exchange.sendResponseHeaders(302, -1);
+ } catch (Exception ignored) {
+ }
+ exchange.close();
+ }
+ }
+
+ static class FinalHandler implements HttpHandler {
+ @Override
+ public void handle(HttpExchange exchange) {
+ String auth = exchange.getRequestHeaders().getFirst("Authorization");
+ lastAuthHeader = auth;
+ try {
+ exchange.sendResponseHeaders(200, 0);
+ exchange.getResponseBody().close();
+ } catch (Exception ignored) {
+ }
+ exchange.close();
+ }
+ }
+
+ @Test
+ public void testAuthHeaderPropagatedByDefault() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeader = null;
+ client.prepareGet("http://127.0.0.1:" + port + "/redirect")
+ .setHeader("Authorization", "Bearer testtoken")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+ // Same-origin (same host:port): default should preserve Authorization on /final
+ assertEquals(lastAuthHeader, "Bearer testtoken",
+ "Authorization header should be present on same-origin redirect by default");
+ }
+ }
+
+ @Test
+ public void testAuthHeaderStrippedWhenEnabled() throws Exception {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(true)
+ .setStripAuthorizationOnRedirect(true)
+ .build();
+ try (DefaultAsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ lastAuthHeader = null;
+ client.prepareGet("http://127.0.0.1:" + port + "/redirect")
+ .setHeader("Authorization", "Bearer testtoken")
+ .execute()
+ .get(5, TimeUnit.SECONDS);
+ // When enabled, Authorization header must be stripped even on same-origin redirects
+ assertNull(lastAuthHeader, "Authorization header should be stripped on redirect when enabled");
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/cookie/PublicSuffixCookieTest.java b/client/src/test/java/org/asynchttpclient/cookie/PublicSuffixCookieTest.java
new file mode 100644
index 0000000000..7abe39f67e
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/cookie/PublicSuffixCookieTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient.cookie;
+
+import io.netty.handler.codec.http.cookie.DefaultCookie;
+import org.asynchttpclient.uri.Uri;
+import org.testng.annotations.Test;
+
+import java.util.List;
+import java.util.Locale;
+
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * RFC 6265 Section 5.3 step 5: a cookie whose {@code Domain} names a public suffix must be ignored.
+ * Without it, one site under a registry can plant a cookie that every other site under that registry
+ * receives. Step 6, which asks only whether the request host sits under the Domain, does not catch it.
+ */
+public class PublicSuffixCookieTest {
+
+ private static boolean reaches(String setterHost, String domainAttribute, String victimHost) {
+ ThreadSafeCookieStore store = new ThreadSafeCookieStore();
+ DefaultCookie cookie = new DefaultCookie("SID", "planted");
+ if (domainAttribute != null) {
+ cookie.setDomain(domainAttribute);
+ }
+ cookie.setPath("/");
+ store.add(Uri.create("http://" + setterHost + "/"), cookie);
+
+ List got = store.get(Uri.create("http://" + victimHost + "/"));
+ return got.stream().anyMatch(c -> "SID".equals(c.name()));
+ }
+
+ @Test
+ public void aCookieForAPublicSuffixIsNotPlanted() {
+ assertFalse(reaches("evil.co.uk", "co.uk", "bank.co.uk"),
+ "a host under co.uk must not set a cookie for co.uk itself");
+ assertFalse(reaches("evil.co.uk", "uk", "bank.co.uk"),
+ "a host must not set a cookie for a bare TLD");
+ assertFalse(reaches("evil.com", "com", "bank.com"),
+ "a host must not set a cookie for com");
+ }
+
+ /**
+ * The existing cookie-tossing guard must keep working: this is the case #2196 fixed, and it is what
+ * shows the new check is not the only thing standing between these two hosts.
+ */
+ @Test
+ public void theExistingCrossSiteGuardStillHolds() {
+ assertFalse(reaches("evil.co.uk", "bank.co.uk", "bank.co.uk"),
+ "one host must not set a cookie naming an unrelated host");
+ }
+
+ /**
+ * Ordinary cookies must be unaffected, or every user loses their session handling.
+ */
+ @Test
+ public void ordinaryCookiesAreUnaffected() {
+ assertTrue(reaches("bank.co.uk", "bank.co.uk", "www.bank.co.uk"),
+ "a site must still set a cookie for its own registrable domain");
+ assertTrue(reaches("bank.co.uk", null, "bank.co.uk"),
+ "a host-only cookie must still be returned to that host");
+ assertTrue(reaches("www.example.com", "example.com", "api.example.com"),
+ "a site must still share a cookie across its own subdomains");
+ }
+
+ /**
+ * RFC 6265 section 5.3 step 5 keeps a cookie whose Domain equals the request host, as a host-only
+ * cookie. Dropping it would break ordinary single-label hosts, because dev, app, box, cloud and a
+ * dozen more are ICANN suffixes as well as the short names Docker Compose and Kubernetes hand out.
+ */
+ @Test
+ public void aSingleLabelHostCanStillSetItsOwnCookie() {
+ for (String host : new String[]{"dev", "app", "box", "cloud", "build", "run"}) {
+ assertTrue(PublicSuffixList.isPublicSuffix(host), host + " is expected to be an ICANN suffix");
+ assertTrue(reaches(host, host, host),
+ "a host whose own name is a public suffix must still set a cookie for itself: " + host);
+ }
+ }
+
+ /**
+ * The locale must not decide whether the check engages. Under Turkish the default lowercasing turns I
+ * into a dotless i, so every I-initial suffix would stop matching and the guard would fail open.
+ */
+ @Test
+ public void matchingDoesNotDependOnTheDefaultLocale() {
+ Locale original = Locale.getDefault();
+ try {
+ Locale.setDefault(new Locale("tr", "TR"));
+ assertTrue(PublicSuffixList.isPublicSuffix("INFO"), "INFO must match under a Turkish locale");
+ assertTrue(PublicSuffixList.isPublicSuffix("CO.IN"), "CO.IN must match under a Turkish locale");
+ assertFalse(reaches("evil.co.in", "co.in", "bank.co.in"),
+ "the guard must hold under a Turkish locale");
+ } finally {
+ Locale.setDefault(original);
+ }
+ }
+
+ @Test
+ public void theListRecognisesSuffixesAndRegistrableDomains() {
+ assertTrue(PublicSuffixList.isPublicSuffix("co.uk"));
+ assertTrue(PublicSuffixList.isPublicSuffix("com"));
+ assertTrue(PublicSuffixList.isPublicSuffix("com.au"));
+ assertTrue(PublicSuffixList.isPublicSuffix("CO.UK"), "matching must be case-insensitive");
+
+ assertFalse(PublicSuffixList.isPublicSuffix("bank.co.uk"));
+ assertFalse(PublicSuffixList.isPublicSuffix("example.com"));
+ assertFalse(PublicSuffixList.isPublicSuffix(""));
+ assertFalse(PublicSuffixList.isPublicSuffix(null));
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/filter/CrossHostReplayTest.java b/client/src/test/java/org/asynchttpclient/filter/CrossHostReplayTest.java
new file mode 100644
index 0000000000..50e67d70d3
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/filter/CrossHostReplayTest.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.filter;
+
+import org.asynchttpclient.AsyncHttpClient;
+import org.asynchttpclient.Dsl;
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.RequestBuilder;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.testng.annotations.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeUnit;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A {@link ResponseFilter} that replays onto a different host is the documented way to do
+ * failover. The request the future was built for and the request it ends up sending then disagree, and
+ * anything derived from the target has to move with it.
+ *
+ * The existing replay tests all replay to the same host, so nothing here was covered: the future kept
+ * describing the first origin, which sent that origin's credentials to the second one and filed the second
+ * one's socket in the connection pool under the first one's key.
+ */
+public class CrossHostReplayTest {
+
+ private static final String SECRET = "s3cr3t-for-A";
+
+ /**
+ * A tiny origin that records the request line and Authorization header of everything it receives.
+ */
+ private static final class Recorder implements AutoCloseable {
+ final ServerSocket socket = new ServerSocket(0);
+ final List seen = new CopyOnWriteArrayList<>();
+ private final int status;
+
+ Recorder(int status) throws IOException {
+ this.status = status;
+ Thread t = new Thread(this::serve);
+ t.setDaemon(true);
+ t.start();
+ }
+
+ int port() {
+ return socket.getLocalPort();
+ }
+
+ private void serve() {
+ while (!socket.isClosed()) {
+ final Socket s;
+ try {
+ s = socket.accept();
+ } catch (IOException e) {
+ return;
+ }
+ // One thread per connection, and the connection is kept open across requests. Without
+ // keep-alive nothing is ever offered to the connection pool, and a test about pool keys
+ // would pass no matter what the product code did.
+ Thread worker = new Thread(() -> handle(s));
+ worker.setDaemon(true);
+ worker.start();
+ }
+ }
+
+ private void handle(Socket s) {
+ try (Socket conn = s) {
+ BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), ISO_8859_1));
+ while (true) {
+ String requestLine = in.readLine();
+ if (requestLine == null) {
+ return;
+ }
+ String auth = null;
+ String line;
+ while ((line = in.readLine()) != null && !line.isEmpty()) {
+ if (line.toLowerCase().startsWith("authorization:")) {
+ auth = line.substring("authorization:".length()).trim();
+ }
+ }
+ seen.add(requestLine.split(" ")[1] + " auth=" + (auth == null ? "" : auth));
+ conn.getOutputStream().write(
+ ("HTTP/1.1 " + status + " X\r\nContent-Length: 0\r\n\r\n").getBytes(ISO_8859_1));
+ conn.getOutputStream().flush();
+ }
+ } catch (IOException ignored) {
+ // client hung up
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ socket.close();
+ }
+ }
+
+ /**
+ * Replays onto {@code url} when the origin answers 503. ResponseFilter.filter is generic, so this
+ * cannot be a lambda.
+ */
+ private static ResponseFilter failoverTo(String url) {
+ return new ResponseFilter() {
+ @Override
+ public FilterContext filter(FilterContext ctx) {
+ if (ctx.getResponseStatus() != null && ctx.getResponseStatus().getStatusCode() == 503) {
+ return new FilterContext.FilterContextBuilder<>(ctx)
+ .request(new RequestBuilder("GET").setUrl(url).build())
+ .replayRequest(true)
+ .build();
+ }
+ return ctx;
+ }
+ };
+ }
+
+ /**
+ * Replays onto {@code url} through the proxy on {@code proxyPort} when the origin answers 503.
+ */
+ private static ResponseFilter failoverVia(String url, int proxyPort) {
+ return new ResponseFilter() {
+ @Override
+ public FilterContext filter(FilterContext ctx) {
+ if (ctx.getResponseStatus() != null && ctx.getResponseStatus().getStatusCode() == 503) {
+ return new FilterContext.FilterContextBuilder<>(ctx)
+ .request(new RequestBuilder("GET").setUrl(url)
+ .setProxyServer(new ProxyServer.Builder("127.0.0.1", proxyPort).build())
+ .build())
+ .replayRequest(true)
+ .build();
+ }
+ return ctx;
+ }
+ };
+ }
+
+ private static Realm realmForA() {
+ return Dsl.basicAuthRealm("alice", SECRET).setUsePreemptiveAuth(true).build();
+ }
+
+ /**
+ * The credentials configured for the first origin must not be sent to the host the request is replayed
+ * onto. The replay carries no realm of its own, and the previous origin's must not be reused for it.
+ */
+ @Test
+ public void replayToADifferentHostDoesNotCarryTheFirstHostsCredentials() throws Exception {
+ try (Recorder a = new Recorder(503); Recorder b = new Recorder(200)) {
+ String urlB = "http://127.0.0.1:" + b.port() + "/failover";
+ ResponseFilter failover = failoverTo(urlB);
+
+ try (AsyncHttpClient client = Dsl.asyncHttpClient(
+ Dsl.config().setMaxRequestRetry(0).addResponseFilter(failover))) {
+ client.prepareGet("http://127.0.0.1:" + a.port() + "/probe")
+ .setRealm(realmForA())
+ .execute().get(30, TimeUnit.SECONDS);
+ }
+
+ assertEquals(b.seen.size(), 1, "the failover host should have been called exactly once: " + b.seen);
+ assertTrue(b.seen.get(0).endsWith("auth="),
+ "the first host's credentials must not follow the replay: " + b.seen.get(0));
+ }
+ }
+
+ /**
+ * The socket opened for the replay target must be filed in the connection pool under that target, not
+ * under the origin the future was created for. Filed under the wrong key, a later request the
+ * application addresses to the first host is served over the connection to the second one, and the
+ * first host's credentials go there with it.
+ */
+ @Test
+ public void replayToADifferentHostDoesNotFileTheSocketUnderTheFirstHostsKey() throws Exception {
+ try (Recorder a = new Recorder(503); Recorder b = new Recorder(200)) {
+ String urlB = "http://127.0.0.1:" + b.port() + "/failover";
+ ResponseFilter failover = failoverTo(urlB);
+
+ try (AsyncHttpClient client = Dsl.asyncHttpClient(
+ Dsl.config().setMaxRequestRetry(0).addResponseFilter(failover))) {
+ client.prepareGet("http://127.0.0.1:" + a.port() + "/probe")
+ .setRealm(realmForA())
+ .execute().get(30, TimeUnit.SECONDS);
+
+ // Addressed to A. If the replay left B's socket pooled under A's key, this is served by B.
+ client.prepareGet("http://127.0.0.1:" + a.port() + "/admin")
+ .setRealm(realmForA())
+ .execute().get(30, TimeUnit.SECONDS);
+ }
+
+ assertTrue(b.seen.stream().noneMatch(r -> r.startsWith("/admin")),
+ "a request addressed to the first host was served by the failover host: " + b.seen);
+ assertTrue(b.seen.stream().noneMatch(r -> r.contains(SECRET) || r.contains("YWxpY2U6")),
+ "the first host's credentials reached the failover host: " + b.seen);
+ }
+ }
+
+ /**
+ * A replay that routes through a proxy must file its socket under a key naming that proxy. The proxy is
+ * half of the connection pool key and is memoized alongside the target, so moving the target while the
+ * proxy still names the previous route files a proxied connection under the direct key for the new
+ * host. The next request the application sends directly to that host then draws a socket that runs
+ * through the proxy, and its credentials go to the proxy.
+ */
+ @Test
+ public void replayThroughAProxyDoesNotFileTheProxiedSocketAsDirect() throws Exception {
+ try (Recorder a = new Recorder(503); Recorder proxy = new Recorder(200); Recorder b = new Recorder(200)) {
+ String urlB = "http://127.0.0.1:" + b.port() + "/failover";
+
+ try (AsyncHttpClient client = Dsl.asyncHttpClient(
+ Dsl.config().setMaxRequestRetry(0).addResponseFilter(failoverVia(urlB, proxy.port())))) {
+ // Direct to A, which fails over onto B through the proxy.
+ client.prepareGet("http://127.0.0.1:" + a.port() + "/probe")
+ .setRealm(realmForA())
+ .execute().get(30, TimeUnit.SECONDS);
+
+ // Addressed directly to B, no proxy. Must not reuse the connection that runs through one.
+ client.prepareGet("http://127.0.0.1:" + b.port() + "/admin")
+ .setRealm(realmForA())
+ .execute().get(30, TimeUnit.SECONDS);
+ }
+
+ assertTrue(proxy.seen.stream().noneMatch(r -> r.contains("/admin")),
+ "a request addressed directly to the origin was served over the pooled proxy connection: "
+ + proxy.seen);
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessorStoreTest.java b/client/src/test/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessorStoreTest.java
new file mode 100644
index 0000000000..4b907fcddf
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/handler/resumable/PropertiesBasedResumableProcessorStoreTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.handler.resumable;
+
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.Map;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Covers how the resumable index store is created in the shared temp directory. The store sits at a fixed,
+ * predictable path that every local user can write to, so it must be created owner-only and must never be
+ * written or read through something another user planted there.
+ */
+public class PropertiesBasedResumableProcessorStoreTest {
+
+ private static final Path STORE = Paths.get(System.getProperty("java.io.tmpdir"), "ahc", "ResumableAsyncHandler.properties");
+
+ private static void requirePosix() {
+ if (!FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) {
+ throw new SkipException("POSIX file permissions are not supported on this file system");
+ }
+ }
+
+ private static void deleteStore() throws IOException {
+ Files.deleteIfExists(STORE);
+ }
+
+ @Test
+ public void storeIsCreatedOwnerOnly() throws IOException {
+ requirePosix();
+ deleteStore();
+
+ PropertiesBasedResumableProcessor processor = new PropertiesBasedResumableProcessor();
+ processor.put("http://localhost/owner-only.url", 15L);
+ processor.save(null);
+
+ assertEquals(PosixFilePermissions.toString(Files.getPosixFilePermissions(STORE)), "rw-------",
+ "the store must be created owner-readable/writable only");
+ }
+
+ @Test
+ public void saveDoesNotWriteThroughASymlink() throws IOException {
+ requirePosix();
+ deleteStore();
+ Files.createDirectories(STORE.getParent());
+
+ Path target = Files.createTempFile("ahc-symlink-target", ".txt");
+ try {
+ Files.write(target, "untouched".getBytes(StandardCharsets.UTF_8));
+ Files.createSymbolicLink(STORE, target);
+
+ PropertiesBasedResumableProcessor processor = new PropertiesBasedResumableProcessor();
+ processor.put("http://localhost/symlink.url", 15L);
+ processor.save(null);
+
+ assertEquals(new String(Files.readAllBytes(target), StandardCharsets.UTF_8), "untouched",
+ "a symlink planted at the store path must not be followed and its target must not be truncated");
+ } finally {
+ Files.deleteIfExists(target);
+ deleteStore();
+ }
+ }
+
+ @Test
+ public void loadDoesNotReadThroughASymlink() throws IOException {
+ requirePosix();
+ deleteStore();
+ Files.createDirectories(STORE.getParent());
+
+ Path target = Files.createTempFile("ahc-symlink-source", ".txt");
+ try {
+ Files.write(target, "http://localhost/planted.url=42\n".getBytes(StandardCharsets.UTF_8));
+ Files.createSymbolicLink(STORE, target);
+
+ Map loaded = new PropertiesBasedResumableProcessor().load();
+
+ assertTrue(loaded.isEmpty(), "state must not be read back through a planted symlink, got " + loaded);
+ } finally {
+ Files.deleteIfExists(target);
+ deleteStore();
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/channel/PooledConnectionIdentityTest.java b/client/src/test/java/org/asynchttpclient/netty/channel/PooledConnectionIdentityTest.java
new file mode 100644
index 0000000000..bbdd6c33ca
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/channel/PooledConnectionIdentityTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient.netty.channel;
+
+import org.asynchttpclient.AsyncHttpClient;
+import org.asynchttpclient.Dsl;
+import org.asynchttpclient.Realm;
+import org.testng.annotations.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.TimeUnit;
+
+import static java.nio.charset.StandardCharsets.ISO_8859_1;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * A connection authenticated by NTLM or Negotiate belongs to the identity that authenticated it, because
+ * those schemes authenticate the socket rather than the request. Handing it to another principal makes the
+ * server serve that request as the first principal, with nothing on the wire to show it.
+ *
+ * Counting distinct accepted sockets is what makes this observable: reuse and isolation are invisible in
+ * the responses, and the offer and poll sides deriving their key differently shows up only here.
+ */
+public class PooledConnectionIdentityTest {
+
+ /** Keep-alive origin that records how many distinct connections it accepted. */
+ private static final class CountingServer implements AutoCloseable {
+ final ServerSocket socket = new ServerSocket(0);
+ final Set connections = ConcurrentHashMap.newKeySet();
+
+ CountingServer() throws IOException {
+ Thread t = new Thread(this::accept);
+ t.setDaemon(true);
+ t.start();
+ }
+
+ int port() {
+ return socket.getLocalPort();
+ }
+
+ private void accept() {
+ while (!socket.isClosed()) {
+ final Socket s;
+ try {
+ s = socket.accept();
+ } catch (IOException e) {
+ return;
+ }
+ connections.add(s.getPort());
+ Thread w = new Thread(() -> serve(s));
+ w.setDaemon(true);
+ w.start();
+ }
+ }
+
+ private void serve(Socket s) {
+ try (Socket conn = s) {
+ BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), ISO_8859_1));
+ String line;
+ while ((line = in.readLine()) != null) {
+ if (line.isEmpty()) {
+ conn.getOutputStream().write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes(ISO_8859_1));
+ conn.getOutputStream().flush();
+ }
+ }
+ } catch (IOException ignored) {
+ // client hung up
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ socket.close();
+ }
+ }
+
+ private static Realm ntlm(String principal) {
+ return new Realm.Builder(principal, "secret")
+ .setScheme(Realm.AuthScheme.NTLM)
+ .setUsePreemptiveAuth(true)
+ .setNtlmDomain("DOMAIN")
+ .setNtlmHost("host")
+ .build();
+ }
+
+ @Test
+ public void twoPrincipalsDoNotShareAPooledConnection() throws Exception {
+ try (CountingServer server = new CountingServer();
+ AsyncHttpClient client = Dsl.asyncHttpClient(Dsl.config().setMaxRequestRetry(0))) {
+ String url = "http://127.0.0.1:" + server.port() + "/";
+
+ client.prepareGet(url).setRealm(ntlm("alice")).execute().get(30, TimeUnit.SECONDS);
+ client.prepareGet(url).setRealm(ntlm("bob")).execute().get(30, TimeUnit.SECONDS);
+
+ assertEquals(server.connections.size(), 2,
+ "bob must not be served over the socket alice authenticated");
+ }
+ }
+
+ /**
+ * The isolation must not cost every other request its connection reuse, and one identity must keep
+ * reusing its own.
+ */
+ @Test
+ public void oneIdentityStillReusesItsOwnConnection() throws Exception {
+ try (CountingServer server = new CountingServer();
+ AsyncHttpClient client = Dsl.asyncHttpClient(Dsl.config().setMaxRequestRetry(0))) {
+ String url = "http://127.0.0.1:" + server.port() + "/";
+
+ client.prepareGet(url).setRealm(ntlm("alice")).execute().get(30, TimeUnit.SECONDS);
+ client.prepareGet(url).setRealm(ntlm("alice")).execute().get(30, TimeUnit.SECONDS);
+
+ assertEquals(server.connections.size(), 1,
+ "alice's second request must reuse her own pooled connection");
+ }
+ }
+
+ @Test
+ public void requestsWithoutARealmStillReuseAConnection() throws Exception {
+ try (CountingServer server = new CountingServer();
+ AsyncHttpClient client = Dsl.asyncHttpClient(Dsl.config().setMaxRequestRetry(0))) {
+ String url = "http://127.0.0.1:" + server.port() + "/";
+
+ client.prepareGet(url).execute().get(30, TimeUnit.SECONDS);
+ client.prepareGet(url).execute().get(30, TimeUnit.SECONDS);
+
+ assertEquals(server.connections.size(), 1, "ordinary pooling must be unaffected");
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/channel/PrincipalScopedPartitionKeyTest.java b/client/src/test/java/org/asynchttpclient/netty/channel/PrincipalScopedPartitionKeyTest.java
new file mode 100644
index 0000000000..296c51d27f
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/channel/PrincipalScopedPartitionKeyTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.asynchttpclient.netty.channel;
+
+import org.asynchttpclient.Dsl;
+import org.asynchttpclient.Realm;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertSame;
+
+/**
+ * NTLM and Negotiate authenticate the connection rather than the request, so two principals must never
+ * share a pooled socket. Schemes that authenticate each request must keep sharing them, or every one of
+ * them loses connection reuse.
+ */
+public class PrincipalScopedPartitionKeyTest {
+
+ private static final Object BASE = "host:443";
+
+ private static Realm realm(Realm.AuthScheme scheme, String principal) {
+ return new Realm.Builder(principal, "secret").setScheme(scheme).build();
+ }
+
+ @Test
+ public void twoPrincipalsOnAConnectionScopedSchemeDoNotShareAKey() {
+ for (Realm.AuthScheme scheme : new Realm.AuthScheme[]{
+ Realm.AuthScheme.NTLM, Realm.AuthScheme.KERBEROS, Realm.AuthScheme.SPNEGO}) {
+ Object alice = PrincipalScopedPartitionKey.scope(BASE, realm(scheme, "alice"));
+ Object bob = PrincipalScopedPartitionKey.scope(BASE, realm(scheme, "bob"));
+
+ assertNotEquals(alice, bob, scheme + ": one principal's connection must not be reused by another");
+ assertNotEquals(BASE, alice, scheme + ": the scoped key must differ from the unscoped one");
+ }
+ }
+
+ @Test
+ public void theSamePrincipalKeepsTheSameKey() {
+ Object first = PrincipalScopedPartitionKey.scope(BASE, realm(Realm.AuthScheme.NTLM, "alice"));
+ Object second = PrincipalScopedPartitionKey.scope(BASE, realm(Realm.AuthScheme.NTLM, "alice"));
+
+ assertEquals(first, second, "the same principal must keep reusing its own connections");
+ assertEquals(second.hashCode(), first.hashCode());
+ }
+
+ /**
+ * Basic and Digest send credentials with every request, so their connections are not tied to an
+ * identity. Scoping them would cost reuse for no benefit.
+ */
+ @Test
+ public void requestScopedSchemesAreLeftAlone() {
+ assertSame(BASE, PrincipalScopedPartitionKey.scope(BASE, realm(Realm.AuthScheme.BASIC, "alice")));
+ assertSame(BASE, PrincipalScopedPartitionKey.scope(BASE, realm(Realm.AuthScheme.DIGEST, "alice")));
+ assertSame(BASE, PrincipalScopedPartitionKey.scope(BASE, Dsl.basicAuthRealm("alice", "s").build()));
+ }
+
+ @Test
+ public void noRealmAndNoPrincipalAreLeftAlone() {
+ assertSame(BASE, PrincipalScopedPartitionKey.scope(BASE, null));
+ assertSame(BASE, PrincipalScopedPartitionKey.scope(BASE,
+ new Realm.Builder(null, null).setScheme(Realm.AuthScheme.NTLM).build()));
+ }
+
+ /**
+ * Different hosts must stay separate even for one principal, or the scoping would collapse the
+ * distinction the base key exists to make.
+ */
+ @Test
+ public void theBaseKeyStillSeparatesHosts() {
+ Realm alice = realm(Realm.AuthScheme.NTLM, "alice");
+
+ assertNotEquals(PrincipalScopedPartitionKey.scope("host-a:443", alice),
+ PrincipalScopedPartitionKey.scope("host-b:443", alice));
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/handler/WebSocketHandlerDoneFutureTest.java b/client/src/test/java/org/asynchttpclient/netty/handler/WebSocketHandlerDoneFutureTest.java
new file mode 100644
index 0000000000..b7f4a3fcbe
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/handler/WebSocketHandlerDoneFutureTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.handler;
+
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultHttpResponse;
+import io.netty.handler.codec.http.HttpClientCodec;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import org.asynchttpclient.AsyncHttpClientConfig;
+import org.asynchttpclient.AsyncHttpClientState;
+import org.asynchttpclient.DefaultAsyncHttpClientConfig;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.RequestBuilder;
+import org.asynchttpclient.channel.ChannelPoolPartitioning;
+import org.asynchttpclient.netty.NettyResponseFuture;
+import org.asynchttpclient.netty.channel.ChannelManager;
+import org.asynchttpclient.netty.channel.Channels;
+import org.asynchttpclient.netty.request.NettyRequest;
+import org.asynchttpclient.netty.request.NettyRequestSender;
+import org.asynchttpclient.netty.ws.NettyWebSocket;
+import org.asynchttpclient.ws.WebSocket;
+import org.asynchttpclient.ws.WebSocketListener;
+import org.asynchttpclient.ws.WebSocketUpgradeHandler;
+import org.asynchttpclient.ws.WebSocketUtils;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Constructor;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
+import static io.netty.handler.codec.http.HttpHeaderNames.SEC_WEBSOCKET_ACCEPT;
+import static io.netty.handler.codec.http.HttpHeaderNames.SEC_WEBSOCKET_KEY;
+import static io.netty.handler.codec.http.HttpHeaderNames.UPGRADE;
+import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS;
+import static org.asynchttpclient.ws.WebSocketUtils.getAcceptKey;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A WebSocket upgrade response that lands on a future which is already finished must be dropped.
+ * {@link NettyResponseFuture#abort} and {@link NettyResponseFuture#cancel} both mark the future done before
+ * (cancel) or without (a direct abort) marking the channel discarded, so the event loop can still find the
+ * future as the channel attribute and deliver the 101 into {@code handleRead}. Without a guard the handler
+ * upgrades the pipeline and fires {@code onOpen} on a listener that has already had {@code onThrowable}:
+ * the caller is told the request failed and then handed a live WebSocket.
+ *
+ * The window is a race in production, so it is reproduced here at the handler seam, where the state can
+ * be set up exactly. The two upgrade-still-works cases are not decoration: the guard MUST stay scoped to
+ * the {@code HttpResponse} branch, because {@code upgrade()} ends with {@code future.done()} and a
+ * whole-method guard would therefore drop every frame of every healthy WebSocket.
+ */
+public class WebSocketHandlerDoneFutureTest {
+
+ private Timer timer;
+ private ChannelManager channelManager;
+ private WebSocketHandler handler;
+ private EmbeddedChannel channel;
+ private String webSocketKey;
+
+ private final List events = new CopyOnWriteArrayList<>();
+
+ private final WebSocketListener listener = new WebSocketListener() {
+ @Override
+ public void onOpen(WebSocket websocket) {
+ events.add("onOpen");
+ }
+
+ @Override
+ public void onClose(WebSocket websocket, int code, String reason) {
+ events.add("onClose");
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ events.add("onError");
+ }
+
+ @Override
+ public void onTextFrame(String payload, boolean finalFragment, int rsv) {
+ events.add("onTextFrame:" + payload);
+ }
+ };
+
+ // NettyRequest and AsyncHttpClientState are package-private to their own packages; a WebSocketHandler
+ // cannot be exercised in isolation without them.
+ private static NettyRequest newNettyRequest(HttpRequest httpRequest) throws Exception {
+ Constructor constructor = NettyRequest.class.getDeclaredConstructor(HttpRequest.class,
+ Class.forName("org.asynchttpclient.netty.request.body.NettyBody"));
+ constructor.setAccessible(true);
+ return constructor.newInstance(httpRequest, null);
+ }
+
+ private static AsyncHttpClientState newClientState() throws Exception {
+ Constructor constructor = AsyncHttpClientState.class.getDeclaredConstructor(AtomicBoolean.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance(new AtomicBoolean(false));
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build();
+ timer = new HashedWheelTimer();
+ channelManager = new ChannelManager(config, timer);
+ NettyRequestSender requestSender = new NettyRequestSender(config, channelManager, timer, newClientState());
+ handler = new WebSocketHandler(config, channelManager, requestSender);
+
+ channel = new EmbeddedChannel();
+ // upgradePipelineForWebSockets() inserts the WebSocket codecs relative to the HTTP codec.
+ channel.pipeline().addLast(ChannelManager.HTTP_CLIENT_CODEC, new HttpClientCodec());
+
+ webSocketKey = WebSocketUtils.getWebSocketKey();
+ events.clear();
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ if (channel != null) {
+ channel.finishAndReleaseAll();
+ }
+ if (channelManager != null) {
+ channelManager.close();
+ }
+ if (timer != null) {
+ timer.stop();
+ }
+ }
+
+ private NettyResponseFuture newFuture() throws Exception {
+ HttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
+ httpRequest.headers().set(SEC_WEBSOCKET_KEY, webSocketKey);
+
+ Request request = new RequestBuilder("GET").setUrl("ws://localhost:8080/").build();
+ WebSocketUpgradeHandler upgradeHandler = new WebSocketUpgradeHandler.Builder()
+ .addWebSocketListener(listener)
+ .build();
+
+ NettyResponseFuture future = new NettyResponseFuture<>(request,
+ upgradeHandler,
+ newNettyRequest(httpRequest),
+ 0,
+ ChannelPoolPartitioning.PerHostChannelPoolPartitioning.INSTANCE,
+ null,
+ null);
+ Channels.setAttribute(channel, future);
+ return future;
+ }
+
+ /**
+ * A textbook-valid 101: nothing but the state of the future may stop the upgrade.
+ */
+ private HttpResponse newUpgradeResponse() {
+ HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, SWITCHING_PROTOCOLS);
+ response.headers()
+ .set(UPGRADE, "websocket")
+ .set(CONNECTION, "Upgrade")
+ .set(SEC_WEBSOCKET_ACCEPT, getAcceptKey(webSocketKey));
+ return response;
+ }
+
+ @Test
+ public void doesNotUpgradeWhenTheFutureIsAlreadyDone() throws Exception {
+ NettyResponseFuture future = newFuture();
+
+ // What a request timeout leaves behind: the exchange is over and the listener has been told.
+ future.abort(new TimeoutException("Request timeout"));
+ assertTrue(future.isDone());
+ assertEquals(events, java.util.Collections.singletonList("onError"));
+
+ handler.handleRead(channel, future, newUpgradeResponse());
+
+ assertFalse(events.contains("onOpen"),
+ "onOpen must not fire after the future was aborted, but got: " + events);
+ assertFalse(channel.isOpen(), "the orphaned channel must be closed rather than left upgraded");
+ }
+
+ @Test
+ public void stillUpgradesWhenTheFutureIsLive() throws Exception {
+ NettyResponseFuture future = newFuture();
+
+ handler.handleRead(channel, future, newUpgradeResponse());
+
+ assertTrue(events.contains("onOpen"), "a valid 101 on a live future must still upgrade, got: " + events);
+ assertFalse(events.contains("onError"), "the successful upgrade must not report an error: " + events);
+ }
+
+ /**
+ * upgrade() completes the future, so isDone() is true for the entire life of a healthy WebSocket. The
+ * guard must not reach the frame branch.
+ */
+ @Test
+ public void stillDeliversFramesAfterTheUpgradeCompletedTheFuture() throws Exception {
+ NettyResponseFuture future = newFuture();
+ handler.handleRead(channel, future, newUpgradeResponse());
+ assertTrue(future.isDone(), "upgrade() is expected to complete the future");
+
+ handler.handleRead(channel, future, new TextWebSocketFrame("hello"));
+
+ assertTrue(events.contains("onTextFrame:hello"),
+ "frames on an established WebSocket must still be delivered, got: " + events);
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407InterceptorTest.java b/client/src/test/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407InterceptorTest.java
new file mode 100644
index 0000000000..5715f7b135
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/handler/intercept/ProxyUnauthorized407InterceptorTest.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.handler.intercept;
+
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.http.DefaultFullHttpRequest;
+import io.netty.handler.codec.http.DefaultHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpResponse;
+import io.netty.handler.codec.http.HttpResponseStatus;
+import io.netty.handler.codec.http.HttpVersion;
+import io.netty.util.HashedWheelTimer;
+import io.netty.util.Timer;
+import org.asynchttpclient.AsyncHandler;
+import org.asynchttpclient.AsyncHttpClientConfig;
+import org.asynchttpclient.AsyncHttpClientState;
+import org.asynchttpclient.DefaultAsyncHttpClientConfig;
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.RequestBuilder;
+import org.asynchttpclient.Response;
+import org.asynchttpclient.channel.ChannelPoolPartitioning;
+import org.asynchttpclient.AsyncCompletionHandlerBase;
+import org.asynchttpclient.netty.NettyResponseFuture;
+import org.asynchttpclient.netty.channel.ChannelManager;
+import org.asynchttpclient.netty.request.NettyRequest;
+import org.asynchttpclient.netty.request.NettyRequestSender;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.asynchttpclient.proxy.ProxyType;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Constructor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.asynchttpclient.Dsl.basicAuthRealm;
+import static org.asynchttpclient.Dsl.proxyServer;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A 407 is an HTTP-proxy mechanism. A SOCKS proxy negotiates at the transport layer and never writes an
+ * HTTP status line, so a 407 arriving over one was written by the ORIGIN - and answering it hands the
+ * proxy's credentials to that origin.
+ *
+ * The preemptive path is already gated by proxy type in NettyRequestFactory and NettyRequestSender (see
+ * {@code SocksProxyCredentialLeakTest}), but this interceptor bypassed both: its NTLM and Kerberos/SPNEGO
+ * branches write {@code Proxy-Authorization} straight onto the request headers, and {@code newNettyRequest}
+ * copies request headers verbatim afterwards.
+ */
+public class ProxyUnauthorized407InterceptorTest {
+
+ private Timer timer;
+ private ChannelManager channelManager;
+ private ProxyUnauthorized407Interceptor interceptor;
+ private EmbeddedChannel channel;
+
+ private static AsyncHttpClientState newClientState() throws Exception {
+ Constructor constructor = AsyncHttpClientState.class.getDeclaredConstructor(AtomicBoolean.class);
+ constructor.setAccessible(true);
+ return constructor.newInstance(new AtomicBoolean(false));
+ }
+
+ private static NettyRequest newNettyRequest(HttpRequest httpRequest) throws Exception {
+ Constructor constructor = NettyRequest.class.getDeclaredConstructor(HttpRequest.class,
+ Class.forName("org.asynchttpclient.netty.request.body.NettyBody"));
+ constructor.setAccessible(true);
+ return constructor.newInstance(httpRequest, null);
+ }
+
+ @BeforeMethod
+ public void setUp() throws Exception {
+ AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build();
+ timer = new HashedWheelTimer();
+ channelManager = new ChannelManager(config, timer);
+ NettyRequestSender requestSender = new NettyRequestSender(config, channelManager, timer, newClientState());
+ interceptor = new ProxyUnauthorized407Interceptor(channelManager, requestSender);
+ channel = new EmbeddedChannel();
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ if (channel != null) {
+ channel.finishAndReleaseAll();
+ }
+ if (channelManager != null) {
+ channelManager.close();
+ }
+ if (timer != null) {
+ timer.stop();
+ }
+ }
+
+ private static Realm nonPreemptiveProxyRealm() {
+ return basicAuthRealm("proxy-user", "proxy-secret").build();
+ }
+
+ private NettyResponseFuture newFuture(ProxyServer proxyServer, Realm proxyRealm) throws Exception {
+ return newFuture(proxyServer, proxyRealm, "http://origin.example.com/resource", HttpMethod.GET);
+ }
+
+ private NettyResponseFuture newFuture(ProxyServer proxyServer,
+ Realm proxyRealm,
+ String url,
+ HttpMethod method) throws Exception {
+ Request request = new RequestBuilder(method.name()).setUrl(url).build();
+ HttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, method,
+ method == HttpMethod.CONNECT ? "origin.example.com:443" : "/resource");
+ AsyncHandler handler = new AsyncCompletionHandlerBase();
+
+ NettyResponseFuture future = new NettyResponseFuture<>(request,
+ handler,
+ newNettyRequest(httpRequest),
+ 0,
+ ChannelPoolPartitioning.PerHostChannelPoolPartitioning.INSTANCE,
+ null,
+ proxyServer);
+ future.setProxyRealm(proxyRealm);
+ return future;
+ }
+
+ private static HttpResponse new407() {
+ HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.PROXY_AUTHENTICATION_REQUIRED);
+ response.headers().set(HttpHeaderNames.PROXY_AUTHENTICATE, "Basic realm=\"proxy\"");
+ return response;
+ }
+
+ private boolean handle407(ProxyServer proxyServer, Realm proxyRealm) throws Exception {
+ NettyResponseFuture future = newFuture(proxyServer, proxyRealm);
+ return interceptor.exitAfterHandling407(channel,
+ future,
+ new407(),
+ future.getCurrentRequest(),
+ proxyServer,
+ future.getNettyRequest().getHttpRequest());
+ }
+
+ @Test
+ public void socks5Proxy407IsNotAnswered() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer socks = proxyServer("proxy.example.com", 1080).setProxyType(ProxyType.SOCKS_V5).setRealm(proxyRealm).build();
+
+ assertFalse(handle407(socks, proxyRealm),
+ "a 407 over a SOCKS proxy came from the origin and must not be answered with proxy credentials");
+ }
+
+ @Test
+ public void socks4Proxy407IsNotAnswered() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer socks = proxyServer("proxy.example.com", 1080).setProxyType(ProxyType.SOCKS_V4).setRealm(proxyRealm).build();
+
+ assertFalse(handle407(socks, proxyRealm),
+ "a 407 over a SOCKS proxy came from the origin and must not be answered with proxy credentials");
+ }
+
+ @Test
+ public void noProxyAtAllMeans407CameFromTheOrigin() throws Exception {
+ // A realm can be carried on the future without a ProxyServer being in play at all.
+ assertFalse(handle407(null, nonPreemptiveProxyRealm()),
+ "with no proxy configured a 407 can only have come from the origin");
+ }
+
+ /**
+ * The regression guard: an HTTP proxy's 407 is still answered, and the proxy realm is switched to
+ * preemptive so the retried request carries Proxy-Authorization.
+ */
+ @Test
+ public void httpProxy407IsStillAnswered() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer http = proxyServer("proxy.example.com", 8080).setRealm(proxyRealm).build();
+ NettyResponseFuture future = newFuture(http, proxyRealm);
+
+ boolean handled = interceptor.exitAfterHandling407(channel,
+ future,
+ new407(),
+ future.getCurrentRequest(),
+ http,
+ future.getNettyRequest().getHttpRequest());
+
+ assertTrue(handled, "an HTTP proxy's 407 must still be answered");
+ assertTrue(future.getProxyRealm().isUsePreemptiveAuth(),
+ "the retried request must carry the proxy credentials");
+ }
+
+ /**
+ * Once a CONNECT has succeeded the peer on the far end of the socket is the ORIGIN, not the proxy, so a
+ * 407 arriving there was written by the origin. The proxy type is still HTTP, which is why asking what
+ * kind of proxy is configured does not answer the question that matters - who wrote this response.
+ */
+ @Test
+ public void origin407InsideAnEstablishedTunnelIsNotAnswered() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer http = proxyServer("proxy.example.com", 8080).setRealm(proxyRealm).build();
+ NettyResponseFuture future = newFuture(http, proxyRealm, "https://origin.example.com/resource", HttpMethod.GET);
+ future.setTunnelEstablished(true);
+
+ boolean handled = interceptor.exitAfterHandling407(channel,
+ future,
+ new407(),
+ future.getCurrentRequest(),
+ http,
+ future.getNettyRequest().getHttpRequest());
+
+ assertFalse(handled,
+ "a 407 seen inside an established tunnel was written by the origin and must not be answered "
+ + "with the proxy's credentials");
+ assertFalse(future.getProxyRealm().isUsePreemptiveAuth(),
+ "the proxy realm must not be armed to send Proxy-Authorization to the origin");
+ }
+
+ /**
+ * The same request one exchange later. When the tunnelled channel comes from the pool the tunnel was
+ * established by an EARLIER future, so tunnelEstablished is false on this one - but the target still
+ * says what the socket must be: a secured or WebSocket target behind an HTTP proxy is only ever reached
+ * through a CONNECT, so anything that is not the CONNECT itself is talking to the origin.
+ */
+ @Test
+ public void origin407OnATunnelInheritedFromThePoolIsNotAnswered() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer http = proxyServer("proxy.example.com", 8080).setRealm(proxyRealm).build();
+ NettyResponseFuture future = newFuture(http, proxyRealm, "https://origin.example.com/resource", HttpMethod.GET);
+
+ assertFalse(future.isTunnelEstablished(), "this exchange did not build the tunnel itself");
+ assertFalse(interceptor.exitAfterHandling407(channel,
+ future,
+ new407(),
+ future.getCurrentRequest(),
+ http,
+ future.getNettyRequest().getHttpRequest()),
+ "a secured target behind an HTTP proxy is reached through a tunnel, so a 407 on anything but "
+ + "the CONNECT came from the origin");
+ }
+
+ /**
+ * The regression guard for the flow the 407 mechanism exists for: the proxy challenges the CONNECT
+ * itself, which is addressed to it and travels in the clear.
+ */
+ @Test
+ public void proxy407OnTheConnectIsStillAnswered() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer http = proxyServer("proxy.example.com", 8080).setRealm(proxyRealm).build();
+ NettyResponseFuture future = newFuture(http, proxyRealm, "https://origin.example.com/resource", HttpMethod.CONNECT);
+
+ assertTrue(interceptor.exitAfterHandling407(channel,
+ future,
+ new407(),
+ future.getCurrentRequest(),
+ http,
+ future.getNettyRequest().getHttpRequest()),
+ "the proxy's challenge to the CONNECT must still be answered");
+ assertTrue(future.getProxyRealm().isUsePreemptiveAuth(),
+ "the retried CONNECT must carry the proxy credentials");
+ }
+
+ /**
+ * A 407 this interceptor refuses to answer must not consume the exchange's one-shot proxy-auth latch
+ * either. Burning it means a later, legitimate proxy challenge on the same exchange is declined with
+ * "auth was already performed" when no proxy auth was ever performed at all.
+ */
+ @Test
+ public void a407ThisInterceptorRefusesDoesNotBurnTheProxyAuthLatch() throws Exception {
+ Realm proxyRealm = nonPreemptiveProxyRealm();
+ ProxyServer socks = proxyServer("proxy.example.com", 1080).setProxyType(ProxyType.SOCKS_V5).setRealm(proxyRealm).build();
+ NettyResponseFuture future = newFuture(socks, proxyRealm);
+
+ assertFalse(interceptor.exitAfterHandling407(channel,
+ future,
+ new407(),
+ future.getCurrentRequest(),
+ socks,
+ future.getNettyRequest().getHttpRequest()));
+
+ assertFalse(future.isInProxyAuth(),
+ "declining a 407 must leave the proxy-auth latch untouched, since no proxy auth was performed");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/request/ConnectRequestAuthorizationTest.java b/client/src/test/java/org/asynchttpclient/netty/request/ConnectRequestAuthorizationTest.java
new file mode 100644
index 0000000000..4743da73d9
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/request/ConnectRequestAuthorizationTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.request;
+
+import io.netty.handler.codec.http.HttpHeaderNames;
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.testng.annotations.Test;
+
+import static org.asynchttpclient.Dsl.basicAuthRealm;
+import static org.asynchttpclient.Dsl.config;
+import static org.asynchttpclient.Dsl.get;
+import static org.asynchttpclient.Dsl.proxyServer;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A CONNECT request opens the proxy tunnel and is sent to the proxy in the clear, so it must not carry the
+ * origin {@code Authorization} header (which would expose the origin credentials to the proxy). The header
+ * belongs only on the request sent through the established tunnel.
+ */
+public class ConnectRequestAuthorizationTest {
+
+ private static NettyRequestFactory factory() {
+ return new NettyRequestFactory(config().build());
+ }
+
+ @Test
+ public void connectRequestDoesNotCarryOriginAuthorization() {
+ Request request = get("https://origin.example.com/resource").build();
+ Realm realm = basicAuthRealm("user", "secret").setUsePreemptiveAuth(true).build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest connect = factory().newNettyRequest(request, true, proxy, realm, null);
+
+ assertFalse(connect.getHttpRequest().headers().contains(HttpHeaderNames.AUTHORIZATION),
+ "CONNECT request must not expose the origin Authorization to the proxy");
+ }
+
+ @Test
+ public void tunneledRequestKeepsOriginAuthorization() {
+ Request request = get("https://origin.example.com/resource").build();
+ Realm realm = basicAuthRealm("user", "secret").setUsePreemptiveAuth(true).build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest tunneled = factory().newNettyRequest(request, false, proxy, realm, null);
+
+ assertTrue(tunneled.getHttpRequest().headers().contains(HttpHeaderNames.AUTHORIZATION),
+ "the request sent through the tunnel must still carry the origin Authorization");
+ }
+
+ @Test
+ public void connectRequestDoesNotCarryOriginAuthorizationSetAsAPlainHeader() {
+ Request request = get("https://origin.example.com/resource")
+ .setHeader(HttpHeaderNames.AUTHORIZATION, "Bearer origin-token")
+ .build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest connect = factory().newNettyRequest(request, true, proxy, null, null);
+
+ assertFalse(connect.getHttpRequest().headers().contains(HttpHeaderNames.AUTHORIZATION),
+ "an explicitly set origin Authorization must not ride the plaintext CONNECT either");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/request/ProxyRequestUriUserInfoTest.java b/client/src/test/java/org/asynchttpclient/netty/request/ProxyRequestUriUserInfoTest.java
new file mode 100644
index 0000000000..c115516165
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/request/ProxyRequestUriUserInfoTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.request;
+
+import org.asynchttpclient.Request;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.testng.annotations.Test;
+
+import static org.asynchttpclient.Dsl.config;
+import static org.asynchttpclient.Dsl.get;
+import static org.asynchttpclient.Dsl.proxyServer;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+/**
+ * A plaintext request routed through an HTTP proxy carries an absolute-form request target, which the proxy
+ * sees and logs in the clear. RFC 9110 section 4.2.4 forbids the userinfo subcomponent in a generated
+ * request target, so the URL credentials must not appear on that request line.
+ */
+public class ProxyRequestUriUserInfoTest {
+
+ private static NettyRequestFactory factory() {
+ return new NettyRequestFactory(config().build());
+ }
+
+ @Test
+ public void proxiedRequestTargetDropsUserInfo() {
+ Request request = get("http://user:secret@origin.example.com/resource?a=b").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest proxied = factory().newNettyRequest(request, false, proxy, null, null);
+ String requestTarget = proxied.getHttpRequest().uri();
+
+ assertFalse(requestTarget.contains("secret"),
+ "the absolute-form request target must not expose the URL credentials to the proxy");
+ assertEquals(requestTarget, "http://origin.example.com/resource?a=b");
+ }
+
+ @Test
+ public void proxiedRequestTargetDropsUserInfoOnAnExplicitPort() {
+ Request request = get("http://user:secret@origin.example.com:8081/resource").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest proxied = factory().newNettyRequest(request, false, proxy, null, null);
+
+ assertEquals(proxied.getHttpRequest().uri(), "http://origin.example.com:8081/resource");
+ }
+
+ @Test
+ public void proxiedRequestTargetWithoutUserInfoIsUnchanged() {
+ Request request = get("http://origin.example.com/resource?a=b").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest proxied = factory().newNettyRequest(request, false, proxy, null, null);
+
+ assertEquals(proxied.getHttpRequest().uri(), "http://origin.example.com/resource?a=b");
+ }
+
+ @Test
+ public void directRequestTargetStaysRelative() {
+ Request request = get("http://user:secret@origin.example.com/resource?a=b").build();
+
+ NettyRequest direct = factory().newNettyRequest(request, false, null, null, null);
+
+ assertEquals(direct.getHttpRequest().uri(), "/resource?a=b");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/request/SocksProxyCredentialLeakTest.java b/client/src/test/java/org/asynchttpclient/netty/request/SocksProxyCredentialLeakTest.java
new file mode 100644
index 0000000000..f33f9dc362
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/request/SocksProxyCredentialLeakTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.request;
+
+import io.netty.handler.codec.http.HttpHeaderNames;
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.asynchttpclient.proxy.ProxyType;
+import org.testng.annotations.Test;
+
+import static org.asynchttpclient.Dsl.basicAuthRealm;
+import static org.asynchttpclient.Dsl.config;
+import static org.asynchttpclient.Dsl.get;
+import static org.asynchttpclient.Dsl.proxyServer;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A SOCKS proxy tunnels at the transport layer, so the HTTP request it carries reaches the ORIGIN, not the
+ * proxy. Attaching a {@code Proxy-Authorization} header for a SOCKS proxy therefore leaks the proxy
+ * credentials to the origin server. The origin request must carry the header only for an HTTP proxy.
+ */
+public class SocksProxyCredentialLeakTest {
+
+ private static NettyRequestFactory factory() {
+ return new NettyRequestFactory(config().build());
+ }
+
+ private static Realm preemptiveBasicProxyRealm() {
+ return basicAuthRealm("proxy-user", "proxy-secret").setUsePreemptiveAuth(true).build();
+ }
+
+ private static ProxyServer proxy(ProxyType type, Realm proxyRealm) {
+ return proxyServer("proxy.example.com", 1080).setProxyType(type).setRealm(proxyRealm).build();
+ }
+
+ private static boolean hasProxyAuthorization(NettyRequest nettyRequest) {
+ return nettyRequest.getHttpRequest().headers().contains(HttpHeaderNames.PROXY_AUTHORIZATION);
+ }
+
+ @Test
+ public void socks5ProxyDoesNotAttachProxyAuthorizationToPlaintextOrigin() {
+ Request request = get("http://origin.example.com/resource").build();
+ Realm proxyRealm = preemptiveBasicProxyRealm();
+ ProxyServer socksProxy = proxy(ProxyType.SOCKS_V5, proxyRealm);
+
+ NettyRequest nettyRequest = factory().newNettyRequest(request, false, socksProxy, null, proxyRealm);
+
+ assertFalse(hasProxyAuthorization(nettyRequest),
+ "SOCKS proxy tunnels to the origin; Proxy-Authorization must not be sent to the origin");
+ }
+
+ @Test
+ public void socks4ProxyDoesNotAttachProxyAuthorizationToPlaintextOrigin() {
+ Request request = get("http://origin.example.com/resource").build();
+ Realm proxyRealm = preemptiveBasicProxyRealm();
+ ProxyServer socksProxy = proxy(ProxyType.SOCKS_V4, proxyRealm);
+
+ NettyRequest nettyRequest = factory().newNettyRequest(request, false, socksProxy, null, proxyRealm);
+
+ assertFalse(hasProxyAuthorization(nettyRequest),
+ "SOCKS proxy tunnels to the origin; Proxy-Authorization must not be sent to the origin");
+ }
+
+ @Test
+ public void socks5ProxyDoesNotAttachProxyAuthorizationToSecuredOrigin() {
+ // A https:// origin behind a SOCKS proxy never gets a CONNECT (needConnect requires an HTTP proxy),
+ // so the request itself is tunnelled straight to the origin.
+ Request request = get("https://origin.example.com/resource").build();
+ Realm proxyRealm = preemptiveBasicProxyRealm();
+ ProxyServer socksProxy = proxy(ProxyType.SOCKS_V5, proxyRealm);
+
+ NettyRequest nettyRequest = factory().newNettyRequest(request, false, socksProxy, null, proxyRealm);
+
+ assertFalse(hasProxyAuthorization(nettyRequest),
+ "SOCKS proxy tunnels to the origin; Proxy-Authorization must not be sent to the origin");
+ }
+
+ @Test
+ public void httpProxyStillAttachesProxyAuthorizationToPlaintextOrigin() {
+ Request request = get("http://origin.example.com/resource").build();
+ Realm proxyRealm = preemptiveBasicProxyRealm();
+ // Default proxy type is HTTP.
+ ProxyServer httpProxy = proxyServer("proxy.example.com", 8080).setRealm(proxyRealm).build();
+
+ NettyRequest nettyRequest = factory().newNettyRequest(request, false, httpProxy, null, proxyRealm);
+
+ assertTrue(hasProxyAuthorization(nettyRequest),
+ "HTTP proxy receives the origin request directly; Proxy-Authorization must be sent");
+ assertTrue(nettyRequest.getHttpRequest().headers().get(HttpHeaderNames.PROXY_AUTHORIZATION).startsWith("Basic "),
+ "expected a preemptive Basic Proxy-Authorization header");
+ }
+
+ @Test
+ public void connectRequestThroughHttpProxyStillCarriesProxyAuthorization() {
+ // A CONNECT to open a tunnel for an HTTPS origin is sent to the proxy in the clear, so it legitimately
+ // carries the proxy credentials.
+ Request request = get("https://origin.example.com/resource").build();
+ Realm proxyRealm = preemptiveBasicProxyRealm();
+ ProxyServer httpProxy = proxyServer("proxy.example.com", 8080).setRealm(proxyRealm).build();
+
+ NettyRequest connect = factory().newNettyRequest(request, true, httpProxy, null, proxyRealm);
+
+ assertTrue(hasProxyAuthorization(connect),
+ "CONNECT is sent to the proxy to open the tunnel; Proxy-Authorization must be sent");
+ }
+
+ @Test
+ public void directRequestWithoutAProxyCarriesNoProxyAuthorization() {
+ Request request = get("http://origin.example.com/resource").build();
+ Realm proxyRealm = preemptiveBasicProxyRealm();
+
+ NettyRequest nettyRequest = factory().newNettyRequest(request, false, null, null, proxyRealm);
+
+ assertFalse(hasProxyAuthorization(nettyRequest),
+ "there is no proxy to authenticate to");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/netty/request/WebSocketTunnelProxyAuthTest.java b/client/src/test/java/org/asynchttpclient/netty/request/WebSocketTunnelProxyAuthTest.java
new file mode 100644
index 0000000000..a27db86ae6
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/netty/request/WebSocketTunnelProxyAuthTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.netty.request;
+
+import io.netty.handler.codec.http.HttpHeaderNames;
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.testng.annotations.Test;
+
+import static org.asynchttpclient.Dsl.basicAuthRealm;
+import static org.asynchttpclient.Dsl.config;
+import static org.asynchttpclient.Dsl.get;
+import static org.asynchttpclient.Dsl.proxyServer;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A ws:// request through an HTTP proxy is tunnelled with CONNECT (NettyRequestSender's needConnect check
+ * treats ws:// like https://), so the upgrade request that follows travels through the tunnel to the origin,
+ * not to the proxy. It must therefore be treated like wss://: the proxy credentials belong only on the
+ * CONNECT (which the proxy actually receives), and the request target must be origin-form.
+ */
+public class WebSocketTunnelProxyAuthTest {
+
+ private static NettyRequestFactory factory() {
+ return new NettyRequestFactory(config().build());
+ }
+
+ private static Realm preemptiveBasicProxyRealm() {
+ return basicAuthRealm("proxy-user", "proxy-secret").setUsePreemptiveAuth(true).build();
+ }
+
+ @Test
+ public void connectRequestForWebSocketCarriesProxyAuthorization() {
+ Request request = get("ws://origin.example.com/socket").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest connect = factory().newNettyRequest(request, true, proxy, null, preemptiveBasicProxyRealm());
+
+ assertTrue(connect.getHttpRequest().headers().contains(HttpHeaderNames.PROXY_AUTHORIZATION),
+ "the CONNECT request must still authenticate to the proxy");
+ }
+
+ @Test
+ public void tunneledWebSocketUpgradeDoesNotCarryProxyAuthorization() {
+ Request request = get("ws://origin.example.com/socket").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest tunneled = factory().newNettyRequest(request, false, proxy, null, preemptiveBasicProxyRealm());
+
+ assertFalse(tunneled.getHttpRequest().headers().contains(HttpHeaderNames.PROXY_AUTHORIZATION),
+ "the tunnelled ws:// upgrade reaches the origin, not the proxy, and must not expose the proxy credentials");
+ assertEquals(tunneled.getHttpRequest().uri(), "/socket",
+ "the tunnelled ws:// upgrade must use an origin-form request target, not the proxy's absolute-form");
+ }
+
+ @Test
+ public void tunneledSecureWebSocketUpgradeDoesNotCarryProxyAuthorization() {
+ Request request = get("wss://origin.example.com/socket").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest tunneled = factory().newNettyRequest(request, false, proxy, null, preemptiveBasicProxyRealm());
+
+ assertFalse(tunneled.getHttpRequest().headers().contains(HttpHeaderNames.PROXY_AUTHORIZATION),
+ "wss:// was already tunnel-safe; this guards against a regression");
+ }
+
+ @Test
+ public void plainHttpRequestToProxyKeepsProxyAuthorization() {
+ Request request = get("http://origin.example.com/resource").build();
+ ProxyServer proxy = proxyServer("proxy.example.com", 8080).build();
+
+ NettyRequest direct = factory().newNettyRequest(request, false, proxy, null, preemptiveBasicProxyRealm());
+
+ assertTrue(direct.getHttpRequest().headers().contains(HttpHeaderNames.PROXY_AUTHORIZATION),
+ "a plaintext http:// request sent directly to the proxy still authenticates to it");
+ assertEquals(direct.getHttpRequest().uri(), "http://origin.example.com/resource",
+ "a plaintext http:// request to the proxy still uses an absolute-form request target");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/proxy/ConnectTunnelStateTest.java b/client/src/test/java/org/asynchttpclient/proxy/ConnectTunnelStateTest.java
new file mode 100644
index 0000000000..fe24db6440
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/proxy/ConnectTunnelStateTest.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.proxy;
+
+import org.asynchttpclient.AsyncHttpClient;
+import org.asynchttpclient.DefaultAsyncHttpClient;
+import org.asynchttpclient.DefaultAsyncHttpClientConfig;
+import org.asynchttpclient.Response;
+import org.asynchttpclient.ws.WebSocketUpgradeHandler;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.asynchttpclient.Dsl.basicAuthRealm;
+import static org.asynchttpclient.Dsl.proxyServer;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * A CONNECT is sent to the proxy over a socket that is still plaintext; the tunnel exists only once the
+ * proxy has answered 2xx. AHC used to infer "the tunnel is up" from nothing more than "the last request was
+ * a CONNECT", which is equally true when the proxy REJECTED it. On a 401 or a redirect the origin-request
+ * interceptors then rebuilt the exchange as the ORIGIN request with {@code setReuseChannel(true)}, and
+ * because that request is not a CONNECT the request factory attached the origin's {@code Authorization} to
+ * it - handing the origin's credentials to the proxy, in the clear, on demand. A proxy needed only to
+ * answer 401 (a non-preemptive realm, which is the default) or 302 to collect them.
+ *
+ * The proxy here is a raw socket that records every request head it is sent, so the assertion is made on
+ * the actual bytes on the wire rather than on any client-side state.
+ */
+public class ConnectTunnelStateTest {
+
+ private static final String ORIGIN_USER = "origin-user";
+ private static final String ORIGIN_PASSWORD = "origin-secret";
+ // Base64("origin-user:origin-secret"), i.e. what a leak looks like on the wire.
+ private static final String ORIGIN_BASIC = "b3JpZ2luLXVzZXI6b3JpZ2luLXNlY3JldA==";
+
+ private RecordingProxy proxy;
+
+ @AfterMethod(alwaysRun = true)
+ public void tearDown() throws IOException {
+ if (proxy != null) {
+ proxy.close();
+ proxy = null;
+ }
+ }
+
+ /**
+ * One request head as the proxy saw it, together with the identity of the TCP connection it arrived on.
+ * The connection is the point: "the socket was closed" and "the socket was poisoned and left in the
+ * pool" are indistinguishable from a single request, and only the second one leaks.
+ */
+ private static final class RecordedHead {
+
+ final int connectionId;
+ final String head;
+
+ RecordedHead(int connectionId, String head) {
+ this.connectionId = connectionId;
+ this.head = head;
+ }
+
+ @Override
+ public String toString() {
+ return "[conn#" + connectionId + "] " + head;
+ }
+ }
+
+ /**
+ * A minimal HTTP proxy that answers each request head it receives with the next scripted response and
+ * keeps the connection open, recording everything the client sends on it afterwards. Each accepted
+ * connection is served on its own thread and numbered, so a client that opens a second connection is
+ * told apart from one that reuses the first.
+ */
+ private static final class RecordingProxy implements Closeable {
+
+ private final ServerSocket serverSocket;
+ private final String[] responses;
+ private final List requestHeads = new CopyOnWriteArrayList<>();
+ private final List accepted = new CopyOnWriteArrayList<>();
+ private final AtomicInteger connectionIds = new AtomicInteger();
+ /**
+ * Counts down to the number of request heads the UNFIXED client would send, so a run that reproduces
+ * the leak does not have to wait out a request timeout to record it.
+ */
+ private final CountDownLatch expectedHeads;
+ private final Thread thread;
+
+ RecordingProxy(int expectedHeads, String... responses) throws IOException {
+ this.responses = responses;
+ this.expectedHeads = new CountDownLatch(expectedHeads);
+ serverSocket = new ServerSocket(0, 4, InetAddress.getByName("localhost"));
+ thread = new Thread(this::serve);
+ thread.setDaemon(true);
+ thread.start();
+ }
+
+ int port() {
+ return serverSocket.getLocalPort();
+ }
+
+ private void serve() {
+ while (!serverSocket.isClosed()) {
+ try {
+ Socket socket = serverSocket.accept();
+ accepted.add(socket);
+ int connectionId = connectionIds.incrementAndGet();
+ Thread connectionThread = new Thread(() -> serveConnection(socket, connectionId));
+ connectionThread.setDaemon(true);
+ connectionThread.start();
+ } catch (Exception ignored) {
+ // accept() throws once the server socket is closed in tearDown.
+ return;
+ }
+ }
+ }
+
+ private void serveConnection(Socket socket, int connectionId) {
+ try (Socket toClose = socket) {
+ toClose.setSoTimeout(5000);
+ InputStream in = toClose.getInputStream();
+ OutputStream out = toClose.getOutputStream();
+ int answered = 0;
+ String head;
+ while ((head = readHead(in)) != null) {
+ requestHeads.add(new RecordedHead(connectionId, head));
+ expectedHeads.countDown();
+ if (answered < responses.length) {
+ out.write(responses[answered++].getBytes(StandardCharsets.US_ASCII));
+ out.flush();
+ }
+ }
+ } catch (Exception ignored) {
+ // A read times out or fails once the client has nothing more to say on this connection. Either
+ // way there is nothing left to record on it.
+ }
+ }
+
+ /**
+ * Reads one request head, i.e. up to and including the CRLFCRLF. Returns null at end of stream. Note
+ * that a TLS ClientHello (which is what a client sends once a tunnel really is established) contains no
+ * CRLFCRLF, so it is never mistaken for a request.
+ */
+ private static String readHead(InputStream in) throws IOException {
+ StringBuilder head = new StringBuilder();
+ int b3 = -1, b2 = -1, b1 = -1, b;
+ while ((b = in.read()) != -1) {
+ head.append((char) b);
+ if (b3 == '\r' && b2 == '\n' && b1 == '\r' && b == '\n') {
+ return head.toString();
+ }
+ b3 = b2;
+ b2 = b1;
+ b1 = b;
+ }
+ return null;
+ }
+
+ void awaitExpectedHeads() throws InterruptedException {
+ expectedHeads.await(6, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void close() throws IOException {
+ serverSocket.close();
+ for (Socket socket : accepted) {
+ try {
+ socket.close();
+ } catch (IOException ignored) {
+ // Already closed by the client or by the connection thread.
+ }
+ }
+ thread.interrupt();
+ }
+ }
+
+ private static boolean isConnect(String head) {
+ return head.startsWith("CONNECT ");
+ }
+
+ /**
+ * The origin's Authorization, as opposed to Proxy-Authorization which legitimately goes to the proxy.
+ */
+ private static boolean carriesOriginAuthorization(String head) {
+ for (String line : head.split("\r\n")) {
+ String lower = line.toLowerCase();
+ if (lower.startsWith("authorization:") && line.contains(ORIGIN_BASIC)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Any Authorization at all, whatever it carries. Nothing addressed to the origin has any business on a
+ * socket the proxy refused to turn into a tunnel.
+ */
+ private static boolean carriesAnyAuthorization(String head) {
+ for (String line : head.split("\r\n")) {
+ if (line.toLowerCase().startsWith("authorization:")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * The contract for a proxy that never established a tunnel: the only thing it may ever be sent is a
+ * CONNECT, and certainly not the origin's credentials.
+ */
+ private void assertOnlyConnectsReachedTheProxy() {
+ assertFalse(proxy.requestHeads.isEmpty(), "the proxy should have received the CONNECT");
+ // The credential leak is checked across every head before the weaker "it was not even a CONNECT"
+ // check, so that a failure names the secret that escaped rather than the first symptom on the wire.
+ for (RecordedHead recorded : proxy.requestHeads) {
+ assertFalse(carriesAnyAuthorization(recorded.head),
+ "the origin's credentials must never be written to a socket on which no tunnel was "
+ + "established; the proxy received:\n" + recorded);
+ }
+ for (RecordedHead recorded : proxy.requestHeads) {
+ assertTrue(isConnect(recorded.head),
+ "a proxy that has not established a tunnel must only ever be sent CONNECTs, but got:\n" + recorded);
+ }
+ }
+
+ private AsyncHttpClient clientWithOriginRealm(boolean followRedirect) {
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setFollowRedirect(followRedirect)
+ .setRequestTimeout(4000)
+ // Non-preemptive, which is the default: the credentials are sent only in answer to a
+ // challenge. That is precisely what makes a 401 from the proxy enough to solicit them.
+ .setRealm(basicAuthRealm(ORIGIN_USER, ORIGIN_PASSWORD).build())
+ .setProxyServer(proxyServer("localhost", proxy.port()).build())
+ .build();
+ return new DefaultAsyncHttpClient(config);
+ }
+
+ /**
+ * The proxy rejects the CONNECT with a 401. The tunnel does not exist, so nothing that follows may carry
+ * the origin's credentials down that socket.
+ */
+ @Test(timeOut = 30000)
+ public void proxyRejectingConnectWith401DoesNotHarvestOriginCredentials() throws Exception {
+ // Two heads are scripted because an UNFIXED client sends a second request - the origin GET, bearing
+ // the credentials the 401 solicited - and answering it keeps the reproduction fast.
+ proxy = new RecordingProxy(2,
+ "HTTP/1.1 401 Unauthorized\r\n"
+ + "WWW-Authenticate: Basic realm=\"origin\"\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n",
+ "HTTP/1.1 200 OK\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n");
+
+ Response response = null;
+ try (AsyncHttpClient client = clientWithOriginRealm(false)) {
+ try {
+ response = client.prepareGet("https://origin.example.com/secret").execute().get(10, TimeUnit.SECONDS);
+ } catch (Exception ignored) {
+ // What was written is the contract; whether the exchange completed is asserted separately below.
+ }
+ } finally {
+ proxy.awaitExpectedHeads();
+ }
+
+ assertOnlyConnectsReachedTheProxy();
+ // The 401 is the proxy's answer to the CONNECT. It is surfaced to the caller as-is, and must never be
+ // mistaken for an origin challenge to answer.
+ assertEquals(response == null ? null : response.getStatusCode(), (Integer) 401,
+ "the proxy's rejection of the CONNECT must be surfaced to the caller");
+ }
+
+ /**
+ * Same, with a redirect instead of a challenge. The Location deliberately stays on the same origin, so
+ * the redirect is not cross-origin and the credential-stripping path does not mask the bug.
+ */
+ @Test(timeOut = 30000)
+ public void proxyRejectingConnectWith302DoesNotHarvestOriginCredentials() throws Exception {
+ // An unfixed client follows the redirect on the same plaintext socket, is challenged there, and sends
+ // the credentials on a third request.
+ proxy = new RecordingProxy(3,
+ "HTTP/1.1 302 Found\r\n"
+ + "Location: https://origin.example.com/secret2\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n",
+ "HTTP/1.1 401 Unauthorized\r\n"
+ + "WWW-Authenticate: Basic realm=\"origin\"\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n",
+ "HTTP/1.1 200 OK\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n");
+
+ try (AsyncHttpClient client = clientWithOriginRealm(true)) {
+ try {
+ client.prepareGet("https://origin.example.com/secret").execute().get(10, TimeUnit.SECONDS);
+ } catch (Exception ignored) {
+ // Whether the exchange completes is not the contract under test; what was written is.
+ }
+ } finally {
+ proxy.awaitExpectedHeads();
+ }
+
+ assertOnlyConnectsReachedTheProxy();
+ }
+
+ /**
+ * The regression guard for the other direction: once the proxy DOES establish the tunnel, the exchange
+ * must proceed exactly as before - the CONNECT carries Proxy-Authorization and the tunnelled request
+ * carries the origin's Authorization. A {@code ws://} target is used so the tunnelled hop is cleartext
+ * and can be inspected; the {@code https://} equivalent is covered end to end by HttpsProxyTest.
+ */
+ @Test(timeOut = 30000)
+ public void establishedTunnelStillCarriesOriginAuthOnTheTunnelledRequest() throws Exception {
+ proxy = new RecordingProxy(2, "HTTP/1.1 200 Connection Established\r\n\r\n");
+
+ DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setRequestTimeout(4000)
+ .setRealm(basicAuthRealm(ORIGIN_USER, ORIGIN_PASSWORD).setUsePreemptiveAuth(true).build())
+ .setProxyServer(proxyServer("localhost", proxy.port())
+ .setRealm(basicAuthRealm("proxy-user", "proxy-secret").setUsePreemptiveAuth(true))
+ .build())
+ .build();
+
+ try (AsyncHttpClient client = new DefaultAsyncHttpClient(config)) {
+ try {
+ client.prepareGet("ws://origin.example.com/socket")
+ .execute(new WebSocketUpgradeHandler.Builder().build())
+ .get(10, TimeUnit.SECONDS);
+ } catch (Exception ignored) {
+ // The fake proxy never completes the WebSocket handshake; only what was written matters here.
+ }
+ } finally {
+ proxy.awaitExpectedHeads();
+ }
+
+ assertTrue(proxy.requestHeads.size() >= 2,
+ "the tunnelled request must be sent on the same socket after the 200, but got: " + proxy.requestHeads);
+
+ RecordedHead connect = proxy.requestHeads.get(0);
+ assertTrue(isConnect(connect.head), "the first request must be a CONNECT: " + connect);
+ assertTrue(connect.head.toLowerCase().contains("proxy-authorization: basic "),
+ "the CONNECT is addressed to the proxy and must carry its credentials:\n" + connect);
+ assertFalse(carriesOriginAuthorization(connect.head),
+ "the CONNECT travels in the clear and must NOT carry the origin's credentials:\n" + connect);
+
+ RecordedHead tunnelled = proxy.requestHeads.get(1);
+ assertFalse(isConnect(tunnelled.head), "the second request must be the tunnelled one: " + tunnelled);
+ assertEquals(tunnelled.connectionId, connect.connectionId,
+ "the tunnelled request must go down the very socket the CONNECT established: " + proxy.requestHeads);
+ assertTrue(carriesOriginAuthorization(tunnelled.head),
+ "once the tunnel is established the origin's credentials must be sent through it:\n" + tunnelled);
+ }
+
+ /**
+ * Two requests, one client. Asserting on a single request is not enough: "the socket was closed" and
+ * "the socket was left in the pool, still plaintext, under the https-origin partition key" produce the
+ * same one request head, and only the second one leaks. It leaks on the request AFTER the rejected
+ * CONNECT, because {@code NettyRequestSender.sendRequestThroughProxy} takes a channel polled under that
+ * key to be a tunnel that is already up and therefore sends the ORIGIN request on it rather than a new
+ * CONNECT - at which point the proxy's next 401 collects the origin's credentials.
+ *
+ * So the second request must arrive on a NEW connection, identified server-side, and no Authorization
+ * may appear anywhere on the wire.
+ */
+ @Test(timeOut = 30000)
+ public void proxyRejectingConnectDoesNotLeavePoisonedConnectionInThePool() throws Exception {
+ String unauthorized = "HTTP/1.1 401 Unauthorized\r\n"
+ + "WWW-Authenticate: Basic realm=\"origin\"\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n";
+ // Three heads are scripted because that is what the UNFIXED client sends: the CONNECT, then the origin
+ // GET down the pooled plaintext socket, then the same GET carrying the credentials the second 401
+ // solicited. Answering all three keeps the reproduction fast and lets the leak be recorded in full.
+ proxy = new RecordingProxy(3, unauthorized, unauthorized,
+ "HTTP/1.1 200 OK\r\n"
+ + "Content-Length: 0\r\n"
+ + "\r\n");
+
+ try (AsyncHttpClient client = clientWithOriginRealm(false)) {
+ for (int i = 0; i < 2; i++) {
+ try {
+ client.prepareGet("https://origin.example.com/secret").execute().get(10, TimeUnit.SECONDS);
+ } catch (Exception ignored) {
+ // What was written is the contract; the per-request status is asserted by the tests above.
+ }
+ }
+ } finally {
+ // Give a client that would still leak every chance to send the third head before asserting.
+ proxy.awaitExpectedHeads();
+ }
+
+ assertOnlyConnectsReachedTheProxy();
+
+ assertTrue(proxy.requestHeads.size() >= 2,
+ "the second request must have reached the proxy, but it only saw: " + proxy.requestHeads);
+ RecordedHead first = proxy.requestHeads.get(0);
+ for (int i = 1; i < proxy.requestHeads.size(); i++) {
+ assertNotEquals(proxy.requestHeads.get(i).connectionId, first.connectionId,
+ "the socket on which the proxy REFUSED the CONNECT is not a tunnel and must never be pooled; "
+ + "the client kept using it: " + proxy.requestHeads);
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/request/body/InputStreamTest.java b/client/src/test/java/org/asynchttpclient/request/body/InputStreamTest.java
index d7c21b618b..eb17cea627 100644
--- a/client/src/test/java/org/asynchttpclient/request/body/InputStreamTest.java
+++ b/client/src/test/java/org/asynchttpclient/request/body/InputStreamTest.java
@@ -57,7 +57,7 @@ public void testInvalidInputStream() throws IOException, ExecutionException, Int
@Override
public int available() {
- return 1; // Fake
+ return readAllowed < 3 ? 1 : 0;
}
@Override
diff --git a/client/src/test/java/org/asynchttpclient/spnego/SpnegoEngineTest.java b/client/src/test/java/org/asynchttpclient/spnego/SpnegoEngineTest.java
index 92ff4a4d78..2370e6d855 100644
--- a/client/src/test/java/org/asynchttpclient/spnego/SpnegoEngineTest.java
+++ b/client/src/test/java/org/asynchttpclient/spnego/SpnegoEngineTest.java
@@ -3,6 +3,11 @@
import org.apache.commons.io.FileUtils;
import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
import org.asynchttpclient.AbstractBasicTest;
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.RequestBuilder;
+import org.asynchttpclient.proxy.ProxyServer;
+import org.asynchttpclient.util.AuthenticatorUtils;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -51,6 +56,10 @@ public static void startServers() throws Exception {
kerbyServer.createPrincipal(alice, "alice");
kerbyServer.createPrincipal(bob, "bob");
+ // Host based service principal for the origin, so a token can be obtained for HTTP@localhost and
+ // only for that host. negotiateTokenTargetsTheOriginEvenBehindAProxy relies on there being no such
+ // principal for the proxy host.
+ kerbyServer.createPrincipal("HTTP/localhost@service.ws.apache.org", "httppwd");
aliceKeytab = new File(basedir + File.separator + "target" + File.separator + "alice.keytab");
bobKeytab = new File(basedir + File.separator + "target" + File.separator + "bob.keytab");
@@ -77,6 +86,40 @@ public void testSpnegoGenerateTokenWithUsernamePassword() throws Exception {
Assert.assertTrue(token.startsWith("YII"));
}
+ /**
+ * The origin realm's Negotiate token must be minted for the origin service even when a proxy is
+ * configured. Building it against the proxy host produced a service ticket for the proxy's SPN: a
+ * confused deputy where the origin's credential is delivered to, and only usable by, the proxy, while
+ * origin authentication fails.
+ *
+ * This drives the real {@code perConnectionAuthorizationHeader} rather than the host-selection helper,
+ * so that restoring the proxy host in the product code makes it fail. The proxy host below has no
+ * service principal in the KDC, so a token can only be produced if the origin was chosen. It lives in
+ * this class because obtaining a token initialises the JVM's Kerberos configuration process-wide, and
+ * this is the class that already stands up a KDC and installs a krb5.conf for exactly that.
+ */
+ @Test
+ public void negotiateTokenTargetsTheOriginEvenBehindAProxy() throws Exception {
+ Request request = new RequestBuilder("GET").setUrl("http://localhost:8080/resource").build();
+ ProxyServer proxyServer = new ProxyServer.Builder("no-such-proxy.invalid", 3128).build();
+ Realm realm = new Realm.Builder("alice", "alice")
+ .setScheme(Realm.AuthScheme.KERBEROS)
+ .setUsePreemptiveAuth(true)
+ // No explicit service principal name on purpose: setting one makes the SPN a constant and the
+ // host irrelevant, which is what made an earlier version of this test pass against both arms.
+ // Left unset, the name is HTTP@, so only the origin has a principal the KDC will issue for.
+ .setRealmName("service.ws.apache.org")
+ .setUseCanonicalHostname(false)
+ .setLoginContextName("alice")
+ .build();
+
+ String header = AuthenticatorUtils.perConnectionAuthorizationHeader(request, proxyServer, realm);
+
+ Assert.assertNotNull(header, "no Negotiate header was produced for the origin");
+ Assert.assertTrue(header.startsWith("Negotiate YII"),
+ "the origin credential must be minted for the origin service, not the proxy's SPN: " + header);
+ }
+
@Test(expectedExceptions = SpnegoEngineException.class)
public void testSpnegoGenerateTokenWithUsernamePasswordFail() throws Exception {
SpnegoEngine spnegoEngine = new SpnegoEngine("alice",
diff --git a/client/src/test/java/org/asynchttpclient/test/TestUtils.java b/client/src/test/java/org/asynchttpclient/test/TestUtils.java
index 7d90b2c9b1..83e0c46e46 100644
--- a/client/src/test/java/org/asynchttpclient/test/TestUtils.java
+++ b/client/src/test/java/org/asynchttpclient/test/TestUtils.java
@@ -154,7 +154,8 @@ public static ServerConnector addHttpConnector(Server server) {
public static ServerConnector addHttpsConnector(Server server) throws IOException, URISyntaxException {
String keyStoreFile = resourceAsFile("ssltest-keystore.jks").getAbsolutePath();
- SslContextFactory sslContextFactory = new SslContextFactory(keyStoreFile);
+ SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
+ sslContextFactory.setKeyStorePath(keyStoreFile);
sslContextFactory.setKeyStorePassword("changeit");
String trustStoreFile = resourceAsFile("ssltest-cacerts.jks").getAbsolutePath();
diff --git a/client/src/test/java/org/asynchttpclient/uri/UriTest.java b/client/src/test/java/org/asynchttpclient/uri/UriTest.java
index 7ead2a6528..1d4bf41789 100644
--- a/client/src/test/java/org/asynchttpclient/uri/UriTest.java
+++ b/client/src/test/java/org/asynchttpclient/uri/UriTest.java
@@ -137,6 +137,22 @@ public void testToUrlWithUserInfoPortPathAndQuery() {
assertEquals(uri.toUrl(), "http://user@example.com:44/path/path2?query=4", "toUrl returned incorrect url");
}
+ @Test
+ public void testToUrlWithoutUserInfoDropsUserInfo() {
+ Uri uri = new Uri("http", "user:secret", "example.com", 44, "/path/path2", "query=4", null);
+ assertEquals(uri.toUrlWithoutUserInfo(), "http://example.com:44/path/path2?query=4",
+ "toUrlWithoutUserInfo must not emit the userinfo subcomponent");
+ assertEquals(uri.toUrl(), "http://user:secret@example.com:44/path/path2?query=4",
+ "toUrl must keep the userinfo for the caller-visible URL");
+ }
+
+ @Test
+ public void testToUrlWithoutUserInfoWithoutUserInfoMatchesToUrl() {
+ Uri uri = new Uri("http", null, "example.com", -1, "/path", "query=4", null);
+ assertEquals(uri.toUrlWithoutUserInfo(), uri.toUrl(),
+ "toUrlWithoutUserInfo must be identical to toUrl when there is no userinfo");
+ }
+
@Test
public void testQueryWithNonRootPath() {
Uri uri = Uri.create("http://hello.com/foo?query=value");
diff --git a/client/src/test/java/org/asynchttpclient/util/AuthenticatorUtilsTest.java b/client/src/test/java/org/asynchttpclient/util/AuthenticatorUtilsTest.java
new file mode 100644
index 0000000000..7d67ac2348
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/util/AuthenticatorUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.util;
+
+import org.asynchttpclient.Realm;
+import org.asynchttpclient.Request;
+import org.asynchttpclient.RequestBuilder;
+import org.asynchttpclient.uri.Uri;
+import org.testng.annotations.Test;
+
+import static org.asynchttpclient.util.AuthenticatorUtils.computeRealmURI;
+import static org.asynchttpclient.util.AuthenticatorUtils.perRequestAuthorizationHeader;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+public class AuthenticatorUtilsTest {
+
+ private static final Uri URI_WITH_USERINFO = Uri.create("http://user:secret@example.com:8080/path?q=1");
+
+ /**
+ * The Digest {@code uri=} parameter goes on the wire and is covered by the digest itself. RFC 9110
+ * section 4.2.4 forbids generating the userinfo subcomponent in a request target, and rendering it here
+ * would put the password in the clear on the very hop Digest exists to protect.
+ */
+ @Test
+ public void computeRealmURIAbsoluteDropsUserInfo() {
+ assertEquals(computeRealmURI(URI_WITH_USERINFO, true, false), "http://example.com:8080/path?q=1");
+ }
+
+ @Test
+ public void computeRealmURIAbsoluteOmitQueryDropsUserInfo() {
+ // omitQuery rebuilds the Uri via withNewQuery(null), which carries the userinfo over: a second,
+ // independent path to the same leak.
+ assertEquals(computeRealmURI(URI_WITH_USERINFO, true, true), "http://example.com:8080/path");
+ }
+
+ @Test
+ public void computeRealmURIAbsoluteWithoutUserInfoIsUnchanged() {
+ Uri uri = Uri.create("http://example.com:8080/path?q=1");
+ assertEquals(computeRealmURI(uri, true, false), "http://example.com:8080/path?q=1");
+ assertEquals(computeRealmURI(uri, true, true), "http://example.com:8080/path");
+ }
+
+ @Test
+ public void computeRealmURIRelativeIsUnchanged() {
+ assertEquals(computeRealmURI(URI_WITH_USERINFO, false, false), "/path?q=1");
+ assertEquals(computeRealmURI(URI_WITH_USERINFO, false, true), "/path");
+ }
+
+ /**
+ * End-to-end over the header the client actually emits: no fragment of the userinfo may appear anywhere
+ * in the Authorization value.
+ */
+ @Test
+ public void digestAuthorizationHeaderDoesNotCarryUserInfo() {
+ Realm realm = new Realm.Builder("user", "secret")
+ .setScheme(Realm.AuthScheme.DIGEST)
+ .setUri(URI_WITH_USERINFO)
+ .setMethodName("GET")
+ .setUsePreemptiveAuth(true)
+ .setUseAbsoluteURI(true)
+ .setRealmName("realm")
+ .setNonce("nonce")
+ .build();
+
+ Request request = new RequestBuilder("GET").setUri(URI_WITH_USERINFO).build();
+ String header = perRequestAuthorizationHeader(request, realm);
+
+ assertTrue(header.startsWith("Digest "), "expected a Digest header but got: " + header);
+ assertTrue(header.contains("uri=\"http://example.com:8080/path?q=1\""),
+ "expected a userinfo-free uri parameter but got: " + header);
+ assertFalse(header.contains("secret@"), "password must not appear in the Digest header: " + header);
+ assertFalse(header.contains("user:secret"), "credentials must not appear in the Digest header: " + header);
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/util/HttpUtilsTest.java b/client/src/test/java/org/asynchttpclient/util/HttpUtilsTest.java
index aa9235101c..9bc71d0e15 100644
--- a/client/src/test/java/org/asynchttpclient/util/HttpUtilsTest.java
+++ b/client/src/test/java/org/asynchttpclient/util/HttpUtilsTest.java
@@ -23,11 +23,16 @@
import org.asynchttpclient.uri.Uri;
import org.testng.annotations.Test;
+import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
+import java.security.SecureRandom;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Random;
+import java.util.Set;
import static io.netty.handler.codec.http.HttpHeaderValues.APPLICATION_JSON;
import static java.nio.charset.StandardCharsets.*;
@@ -113,6 +118,37 @@ public void testGetFollowRedirectPriorityGivenToRequest() {
assertFalse(followRedirect, "Follow redirect value set in request should be given priority");
}
+ /**
+ * The multipart boundary is the only thing separating parts whose content is never escaped, so a caller
+ * that can predict it can close a part early and append a forged Content-Disposition. It is also the
+ * widest window a peer gets onto the generator - 30 to 40 consecutive draws echoed in the clear - so
+ * anything else drawn from the same generator (the Digest cnonce) inherits the weakness. It must come
+ * from a cryptographically strong source, not from ThreadLocalRandom.
+ */
+ @Test
+ public void multipartBoundaryComesFromASecureGenerator() throws Exception {
+ Field field = HttpUtils.class.getDeclaredField("BOUNDARY_RANDOM");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ ThreadLocal holder = (ThreadLocal) field.get(null);
+ assertTrue(holder.get() instanceof SecureRandom,
+ "the multipart boundary generator must be a SecureRandom but was: " + holder.get().getClass());
+ }
+
+ @Test
+ public void computeMultipartBoundaryRespectsItsContract() {
+ String allowed = "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ Set boundaries = new HashSet<>();
+ for (int i = 0; i < 1000; i++) {
+ String boundary = new String(HttpUtils.computeMultipartBoundary(), US_ASCII);
+ assertTrue(boundary.length() >= 30 && boundary.length() <= 40, "Unexpected boundary length: " + boundary.length());
+ for (int j = 0; j < boundary.length(); j++) {
+ assertTrue(allowed.indexOf(boundary.charAt(j)) != -1, "Illegal boundary char: " + boundary.charAt(j));
+ }
+ assertTrue(boundaries.add(boundary), "Boundary was generated twice: " + boundary);
+ }
+ }
+
private void formUrlEncoding(Charset charset) throws Exception {
String key = "key";
String value = "中文";
diff --git a/client/src/test/java/org/asynchttpclient/util/PerConnectionAuthorizationHeaderTest.java b/client/src/test/java/org/asynchttpclient/util/PerConnectionAuthorizationHeaderTest.java
new file mode 100644
index 0000000000..7c6e0c61e6
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/util/PerConnectionAuthorizationHeaderTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.util;
+
+import org.asynchttpclient.Request;
+import org.asynchttpclient.RequestBuilder;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * The contract of the host that names the service a Negotiate token is minted for.
+ *
+ * That the origin is chosen rather than the proxy is asserted by
+ * {@code SpnegoEngineTest.negotiateTokenTargetsTheOriginEvenBehindAProxy}, which drives the real
+ * {@code perConnectionAuthorizationHeader} against a KDC and so fails if the product code regresses. These
+ * are the cheap unit-level companions to it, covering the two inputs that test does not vary. They mint no
+ * token and touch no Kerberos configuration.
+ */
+public class PerConnectionAuthorizationHeaderTest {
+
+ @Test
+ public void negotiateTokenPrefersTheVirtualHost() {
+ Request request = new RequestBuilder("GET")
+ .setUrl("http://origin.example.com/resource")
+ .setVirtualHost("virtual.example.com")
+ .build();
+
+ assertEquals(AuthenticatorUtils.negotiateHost(request), "virtual.example.com",
+ "a configured virtual host names the service the request is really for");
+ }
+
+ @Test
+ public void negotiateHostIgnoresThePort() {
+ Request request = new RequestBuilder("GET").setUrl("http://origin.example.com:8443/resource").build();
+
+ assertEquals(AuthenticatorUtils.negotiateHost(request), "origin.example.com",
+ "the service principal name is built from the host alone");
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/ws/TextMessageTest.java b/client/src/test/java/org/asynchttpclient/ws/TextMessageTest.java
index 72c3e1d244..feaa3748e4 100644
--- a/client/src/test/java/org/asynchttpclient/ws/TextMessageTest.java
+++ b/client/src/test/java/org/asynchttpclient/ws/TextMessageTest.java
@@ -76,9 +76,6 @@ public void onFailureTest() throws Throwable {
try (AsyncHttpClient c = asyncHttpClient()) {
c.prepareGet("ws://abcdefg").execute(new WebSocketUpgradeHandler.Builder().build()).get();
} catch (ExecutionException e) {
-
- String expectedMessage = "DNS name not found";
- assertTrue(e.getCause().toString().contains(expectedMessage));
throw e.getCause();
}
}
diff --git a/client/src/test/java/org/asynchttpclient/ws/WebSocketHandshakeValidationTest.java b/client/src/test/java/org/asynchttpclient/ws/WebSocketHandshakeValidationTest.java
new file mode 100644
index 0000000000..9525d90141
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/ws/WebSocketHandshakeValidationTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.ws;
+
+import org.asynchttpclient.AsyncHttpClient;
+import org.asynchttpclient.AsyncHttpClientConfig;
+import org.asynchttpclient.DefaultAsyncHttpClientConfig;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import static org.asynchttpclient.Dsl.asyncHttpClient;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+/**
+ * RFC 6455 section 4.1: if the {@code Sec-WebSocket-Accept} value in the server's opening handshake does
+ * not match the expected base64 of SHA-1 of the nonce, the client MUST fail the WebSocket connection. It
+ * must not go on to upgrade the pipeline or notify the listener of an open socket.
+ */
+public class WebSocketHandshakeValidationTest {
+
+ private ServerSocket serverSocket;
+ private Thread serverThread;
+
+ @AfterMethod
+ public void tearDown() throws Exception {
+ if (serverSocket != null) {
+ serverSocket.close();
+ }
+ if (serverThread != null) {
+ serverThread.interrupt();
+ }
+ }
+
+ // Completes the HTTP upgrade with a 101 but a Sec-WebSocket-Accept that does not match the key.
+ private int startServerWithBadAccept() throws IOException {
+ serverSocket = new ServerSocket(0, 1, InetAddress.getByName("localhost"));
+ int port = serverSocket.getLocalPort();
+ serverThread = new Thread(() -> {
+ try (Socket socket = serverSocket.accept()) {
+ InputStream in = socket.getInputStream();
+ int b3 = -1, b2 = -1, b1 = -1, b;
+ while ((b = in.read()) != -1) {
+ if (b3 == '\r' && b2 == '\n' && b1 == '\r' && b == '\n') {
+ break;
+ }
+ b3 = b2;
+ b2 = b1;
+ b1 = b;
+ }
+ OutputStream out = socket.getOutputStream();
+ out.write(("HTTP/1.1 101 Switching Protocols\r\n"
+ + "Upgrade: websocket\r\n"
+ + "Connection: Upgrade\r\n"
+ + "Sec-WebSocket-Accept: this-is-not-the-expected-accept\r\n"
+ + "\r\n").getBytes(StandardCharsets.US_ASCII));
+ out.flush();
+ Thread.sleep(5000);
+ } catch (Exception ignored) {
+ }
+ });
+ serverThread.setDaemon(true);
+ serverThread.start();
+ return port;
+ }
+
+ @Test(timeOut = 30000)
+ public void doesNotUpgradeOnInvalidAcceptKey() throws Exception {
+ int port = startServerWithBadAccept();
+ CountDownLatch openLatch = new CountDownLatch(1);
+
+ try (AsyncHttpClient c = asyncHttpClient()) {
+ try {
+ c.prepareGet("ws://localhost:" + port + "/")
+ .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() {
+ @Override
+ public void onOpen(WebSocket websocket) {
+ openLatch.countDown();
+ }
+
+ @Override
+ public void onClose(WebSocket websocket, int code, String reason) {
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ }
+ }).build()).get();
+ fail("the handshake must fail when Sec-WebSocket-Accept is invalid");
+ } catch (ExecutionException expected) {
+ // the request is aborted with "Invalid challenge"
+ }
+
+ assertFalse(openLatch.await(2, TimeUnit.SECONDS),
+ "onOpen must not fire when the server Sec-WebSocket-Accept is invalid");
+ }
+ }
+
+ // Reads the request, then stalls well past the client's request timeout before sending a perfectly
+ // valid 101, so the upgrade response lands on a future the timeout has already aborted.
+ private int startServerWithLateHandshake(long delayMillis) throws IOException {
+ serverSocket = new ServerSocket(0, 1, InetAddress.getByName("localhost"));
+ int port = serverSocket.getLocalPort();
+ serverThread = new Thread(() -> {
+ try (Socket socket = serverSocket.accept()) {
+ InputStream in = socket.getInputStream();
+ StringBuilder request = new StringBuilder();
+ int b3 = -1, b2 = -1, b1 = -1, b;
+ while ((b = in.read()) != -1) {
+ request.append((char) b);
+ if (b3 == '\r' && b2 == '\n' && b1 == '\r' && b == '\n') {
+ break;
+ }
+ b3 = b2;
+ b2 = b1;
+ b1 = b;
+ }
+
+ // Echo back the accept value the client expects, so the only reason not to upgrade is that the
+ // exchange is already over.
+ String key = null;
+ for (String line : request.toString().split("\r\n")) {
+ if (line.regionMatches(true, 0, "Sec-WebSocket-Key:", 0, "Sec-WebSocket-Key:".length())) {
+ key = line.substring("Sec-WebSocket-Key:".length()).trim();
+ }
+ }
+
+ Thread.sleep(delayMillis);
+
+ OutputStream out = socket.getOutputStream();
+ out.write(("HTTP/1.1 101 Switching Protocols\r\n"
+ + "Upgrade: websocket\r\n"
+ + "Connection: Upgrade\r\n"
+ + "Sec-WebSocket-Accept: " + WebSocketUtils.getAcceptKey(key) + "\r\n"
+ + "\r\n").getBytes(StandardCharsets.US_ASCII));
+ out.flush();
+ Thread.sleep(5000);
+ } catch (Exception ignored) {
+ }
+ });
+ serverThread.setDaemon(true);
+ serverThread.start();
+ return port;
+ }
+
+ /**
+ * End-to-end contract: a 101 that arrives after the request timeout must never surface as onOpen. Two
+ * independent things uphold it - NettyRequestSender.abort marks the channel discarded before it completes
+ * the future, and WebSocketHandler drops an upgrade response landing on a finished future. The second is
+ * what covers the paths where the first does not run in that order (see
+ * {@code WebSocketHandlerDoneFutureTest}, which pins that guard directly).
+ */
+ @Test(timeOut = 30000)
+ public void doesNotUpgradeAfterTheFutureWasAborted() throws Exception {
+ int port = startServerWithLateHandshake(2000);
+
+ CountDownLatch openLatch = new CountDownLatch(1);
+ CountDownLatch errorLatch = new CountDownLatch(1);
+
+ AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
+ .setRequestTimeout(500)
+ .build();
+ try (AsyncHttpClient c = asyncHttpClient(config)) {
+ try {
+ c.prepareGet("ws://localhost:" + port + "/")
+ .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketListener() {
+ @Override
+ public void onOpen(WebSocket websocket) {
+ openLatch.countDown();
+ }
+
+ @Override
+ public void onClose(WebSocket websocket, int code, String reason) {
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ errorLatch.countDown();
+ }
+ }).build()).get();
+ fail("the request must fail on the request timeout");
+ } catch (ExecutionException expected) {
+ // TimeoutException
+ }
+
+ assertTrue(errorLatch.await(2, TimeUnit.SECONDS), "onError must fire when the request times out");
+ assertFalse(openLatch.await(4, TimeUnit.SECONDS),
+ "onOpen must not fire on a 101 that arrives after the future was aborted");
+ }
+ }
+}
diff --git a/client/src/test/java/org/asynchttpclient/ws/WebSocketUtilsTest.java b/client/src/test/java/org/asynchttpclient/ws/WebSocketUtilsTest.java
new file mode 100644
index 0000000000..5dae180d7d
--- /dev/null
+++ b/client/src/test/java/org/asynchttpclient/ws/WebSocketUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2026 AsyncHttpClient Project. All rights reserved.
+ *
+ * This program is licensed to you under the Apache License Version 2.0,
+ * and you may not use this file except in compliance with the Apache License Version 2.0.
+ * You may obtain a copy of the Apache License Version 2.0 at
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the Apache License Version 2.0 is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
+ */
+package org.asynchttpclient.ws;
+
+import org.testng.annotations.Test;
+
+import java.lang.reflect.Field;
+import java.security.SecureRandom;
+import java.util.Base64;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+public class WebSocketUtilsTest {
+
+ /**
+ * RFC 6455 section 10.3: "the nonce MUST be selected randomly for each connection" from a source that
+ * "cannot be guessed", because Sec-WebSocket-Accept is a pure function of the key. An off-path party who
+ * can predict the key can precompute a valid accept value, so the handshake check stops proving the 101
+ * came from a peer that saw the request. Netty's ThreadLocalRandom is a 48-bit LCG.
+ */
+ @Test
+ public void webSocketKeyComesFromASecureGenerator() throws Exception {
+ Field field = WebSocketUtils.class.getDeclaredField("KEY_RANDOM");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ ThreadLocal holder = (ThreadLocal) field.get(null);
+ assertTrue(holder.get() instanceof SecureRandom,
+ "the Sec-WebSocket-Key generator must be a SecureRandom but was: " + holder.get().getClass());
+ }
+
+ @Test
+ public void webSocketKeyIsSixteenFreshBytes() {
+ Set keys = new HashSet<>();
+ for (int i = 0; i < 1000; i++) {
+ String key = WebSocketUtils.getWebSocketKey();
+ assertEquals(Base64.getDecoder().decode(key).length, 16, "RFC 6455 requires a 16-byte nonce: " + key);
+ assertTrue(keys.add(key), "Sec-WebSocket-Key was generated twice: " + key);
+ }
+ }
+
+ @Test
+ public void acceptKeyMatchesTheRfcExample() {
+ // RFC 6455 section 1.3.
+ assertEquals(WebSocketUtils.getAcceptKey("dGhlIHNhbXBsZSBub25jZQ=="), "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=");
+ }
+}
diff --git a/example/pom.xml b/example/pom.xml
index 5643feaab9..dd73d7e765 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -2,7 +2,7 @@
org.asynchttpclient
async-http-client-project
- 2.12.3
+ 2.16.1
4.0.0
async-http-client-example
diff --git a/extras/guava/pom.xml b/extras/guava/pom.xml
index 39fd913a5f..1f30fa4d4d 100644
--- a/extras/guava/pom.xml
+++ b/extras/guava/pom.xml
@@ -2,7 +2,7 @@
org.asynchttpclient
async-http-client-extras-parent
- 2.12.3
+ 2.16.1
4.0.0
async-http-client-extras-guava
diff --git a/extras/jdeferred/pom.xml b/extras/jdeferred/pom.xml
index d3c7d6a9e4..8df187d565 100644
--- a/extras/jdeferred/pom.xml
+++ b/extras/jdeferred/pom.xml
@@ -18,7 +18,7 @@
async-http-client-extras-parent
org.asynchttpclient
- 2.12.3
+ 2.16.1
async-http-client-extras-jdeferred
Asynchronous Http Client JDeferred Extras
diff --git a/extras/pom.xml b/extras/pom.xml
index 5fccc3bce6..7fda2624da 100644
--- a/extras/pom.xml
+++ b/extras/pom.xml
@@ -2,7 +2,7 @@
org.asynchttpclient
async-http-client-project
- 2.12.3
+ 2.16.1
4.0.0
async-http-client-extras-parent
diff --git a/extras/registry/pom.xml b/extras/registry/pom.xml
index 492ef41f65..835e1e4874 100644
--- a/extras/registry/pom.xml
+++ b/extras/registry/pom.xml
@@ -2,7 +2,7 @@
org.asynchttpclient
async-http-client-extras-parent
- 2.12.3
+ 2.16.1
4.0.0
async-http-client-extras-registry
diff --git a/extras/retrofit2/pom.xml b/extras/retrofit2/pom.xml
index f95bd3a092..86a4e9e672 100644
--- a/extras/retrofit2/pom.xml
+++ b/extras/retrofit2/pom.xml
@@ -4,7 +4,7 @@
async-http-client-extras-parent
org.asynchttpclient
- 2.12.3
+ 2.16.1
async-http-client-extras-retrofit2
diff --git a/extras/rxjava/pom.xml b/extras/rxjava/pom.xml
index 06680338a4..236a22c6c2 100644
--- a/extras/rxjava/pom.xml
+++ b/extras/rxjava/pom.xml
@@ -3,7 +3,7 @@
async-http-client-extras-parent
org.asynchttpclient
- 2.12.3
+ 2.16.1
async-http-client-extras-rxjava
Asynchronous Http Client RxJava Extras
diff --git a/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/AsyncHttpObservableTest.java b/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/AsyncHttpObservableTest.java
index 8adbecd3af..0b7784c534 100644
--- a/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/AsyncHttpObservableTest.java
+++ b/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/AsyncHttpObservableTest.java
@@ -26,7 +26,7 @@
public class AsyncHttpObservableTest {
- @Test
+ @Test(groups = "online")
public void testToObservableNoError() {
final TestSubscriber tester = new TestSubscriber<>();
@@ -46,7 +46,7 @@ public void testToObservableNoError() {
}
}
- @Test
+ @Test(groups = "online")
public void testToObservableError() {
final TestSubscriber tester = new TestSubscriber<>();
@@ -66,7 +66,7 @@ public void testToObservableError() {
}
}
- @Test
+ @Test(groups = "online")
public void testObserveNoError() {
final TestSubscriber tester = new TestSubscriber<>();
@@ -86,7 +86,7 @@ public void testObserveNoError() {
}
}
- @Test
+ @Test(groups = "online")
public void testObserveError() {
final TestSubscriber tester = new TestSubscriber<>();
@@ -106,7 +106,7 @@ public void testObserveError() {
}
}
- @Test
+ @Test(groups = "online")
public void testObserveMultiple() {
final TestSubscriber tester = new TestSubscriber<>();
diff --git a/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/single/AsyncHttpSingleTest.java b/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/single/AsyncHttpSingleTest.java
index 018da8044c..e25c5ad18c 100644
--- a/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/single/AsyncHttpSingleTest.java
+++ b/extras/rxjava/src/test/java/org/asynchttpclient/extras/rxjava/single/AsyncHttpSingleTest.java
@@ -261,7 +261,7 @@ public void testErrorInOnThrowablePropagation() {
assertEquals(error.getExceptions(), Arrays.asList(processingException, thrownException));
}
- @Test
+ @Test(groups = "online")
public void testAbort() throws Exception {
final TestSubscriber subscriber = new TestSubscriber<>();
diff --git a/extras/rxjava2/pom.xml b/extras/rxjava2/pom.xml
index e1c7af8f3d..fbb1b94a30 100644
--- a/extras/rxjava2/pom.xml
+++ b/extras/rxjava2/pom.xml
@@ -3,7 +3,7 @@
async-http-client-extras-parent
org.asynchttpclient
- 2.12.3
+ 2.16.1
async-http-client-extras-rxjava2
Asynchronous Http Client RxJava2 Extras
diff --git a/extras/simple/pom.xml b/extras/simple/pom.xml
index 92ee8730e3..99c61c77bc 100644
--- a/extras/simple/pom.xml
+++ b/extras/simple/pom.xml
@@ -3,7 +3,7 @@
async-http-client-extras-parent
org.asynchttpclient
- 2.12.3
+ 2.16.1
async-http-client-extras-simple
Asynchronous Http Simple Client
diff --git a/extras/typesafeconfig/pom.xml b/extras/typesafeconfig/pom.xml
index 437b657438..8c144715cf 100644
--- a/extras/typesafeconfig/pom.xml
+++ b/extras/typesafeconfig/pom.xml
@@ -4,7 +4,7 @@
async-http-client-extras-parent
org.asynchttpclient
- 2.12.3
+ 2.16.1
async-http-client-extras-typesafe-config
diff --git a/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java b/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java
index fa5d87bcf3..b0dbbddddd 100644
--- a/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java
+++ b/extras/typesafeconfig/src/main/java/org/asynchttpclient/extras/typesafeconfig/AsyncHttpClientTypesafeConfig.java
@@ -289,6 +289,11 @@ public boolean isKeepEncodingHeader() {
return getBooleanOpt(KEEP_ENCODING_HEADER_CONFIG).orElse(defaultKeepEncodingHeader());
}
+ @Override
+ public int getMaxDecompressedResponseSize() {
+ return getIntegerOpt(MAX_DECOMPRESSED_RESPONSE_SIZE_CONFIG).orElse(defaultMaxDecompressedResponseSize());
+ }
+
@Override
public int getShutdownQuietPeriod() {
return getIntegerOpt(SHUTDOWN_QUIET_PERIOD_CONFIG).orElse(defaultShutdownQuietPeriod());
@@ -364,6 +369,11 @@ public boolean isValidateResponseHeaders() {
return getBooleanOpt(VALIDATE_RESPONSE_HEADERS_CONFIG).orElse(defaultValidateResponseHeaders());
}
+ @Override
+ public boolean isStripAuthorizationOnRedirect() {
+ return false;
+ }
+
@Override
public boolean isAggregateWebSocketFrameFragments() {
return getBooleanOpt(AGGREGATE_WEBSOCKET_FRAME_FRAGMENTS_CONFIG).orElse(defaultAggregateWebSocketFrameFragments());
diff --git a/netty-utils/pom.xml b/netty-utils/pom.xml
index d2be381f14..f2479fd9e4 100644
--- a/netty-utils/pom.xml
+++ b/netty-utils/pom.xml
@@ -2,7 +2,7 @@
org.asynchttpclient
async-http-client-project
- 2.12.3
+ 2.16.1
4.0.0
async-http-client-netty-utils
diff --git a/pom.xml b/pom.xml
index 0ab1e952ec..95b1ca6be6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
org.asynchttpclient
async-http-client-project
- 2.12.3
+ 2.16.1
pom
Asynchronous Http Client Project
@@ -24,9 +24,9 @@
- slandelle
- Stephane Landelle
- slandelle@gatling.io
+ hyperxpro
+ Aayush Atharva
+ aayush@shieldblaze.com
@@ -34,17 +34,17 @@
scm:git:git@github.com:AsyncHttpClient/async-http-client.git
scm:git:git@github.com:AsyncHttpClient/async-http-client.git
https://github.com/AsyncHttpClient/async-http-client/tree/master
- async-http-client-project-2.12.3
+ async-http-client-project-2.16.1
- sonatype-nexus-staging
- https://oss.sonatype.org/content/repositories/snapshots
+ central
+ https://central.sonatype.com/repository/maven-snapshots/
- sonatype-nexus-staging
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
+ central
+ https://central.sonatype.com
@@ -220,42 +220,38 @@
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.10.0
+ true
+
+ central
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 3.2.7
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
-
-
- release-sign-artifacts
-
-
- performRelease
- true
-
-
-
-
-
- maven-gpg-plugin
- 1.6
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
- test-output
-
- false
-
-
-
bom
@@ -466,22 +462,22 @@
true
1.8
1.8
- 4.1.60.Final
- 1.7.30
- 1.0.3
+ 4.1.136.Final
+ 1.7.36
+ 1.0.4
1.2.2
- 2.0.4
+ 2.0.17
1.3.8
- 2.2.19
- 1.2.3
- 7.1.0
+ 2.2.21
+ 1.2.13
+ 7.5.1
9.4.18.v20190429
9.0.31
- 2.6
- 1.3.3
+ 2.21.0
+ 1.6.0
1.2.2
- 3.4.6
+ 3.12.4
2.2
- 2.0.0
+ 2.1.1