mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Allow only normalized paths in requests (#43869)
* Allow only normalized paths in requests Closes #43763 Signed-off-by: Martin Bartoš <mabartos@redhat.com> Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com> Co-authored-by: Martin Bartoš <mabartos@redhat.com> * Remove the trailing slash for base url in the account and admin tests Closes #43863 Signed-off-by: rmartinc <rmartinc@redhat.com> # Conflicts: # js/apps/account-ui/test/account-security/linked-accounts.spec.ts --------- Signed-off-by: Martin Bartoš <mabartos@redhat.com> Signed-off-by: Alexander Schwartz <alexander.schwartz@ibm.com> Signed-off-by: rmartinc <rmartinc@redhat.com> Co-authored-by: Martin Bartoš <mabartos@redhat.com> Co-authored-by: Ricardo Martin <rmartinc@redhat.com>
This commit is contained in:
parent
4357fc43c7
commit
34b9ede377
@ -4,7 +4,14 @@
|
||||
Breaking changes are identified as those that might require changes for existing users to their configurations or applications.
|
||||
In minor or patch releases, {project_name} will only introduce breaking changes to fix bugs.
|
||||
|
||||
=== <TODO>
|
||||
=== Accepting only normalized paths in requests
|
||||
|
||||
Previously {project_name} accepted HTTP requests with paths containing double dots (`..`) or double slashes (`//`). When processing them, it normalized the path by collapsing double slashes and normalized the path according to RFC3986.
|
||||
As this has led to a hard-to-configure URL filtering, for example, in reverse proxies, the normalization is now disabled, and {project_name} responds with an HTTP 400 response code.
|
||||
|
||||
To analyze rejected requests in the server log, enable debug logging for `org.keycloak.quarkus.runtime.services.RejectNonNormalizedPathFilter`.
|
||||
|
||||
To revert to the previous behavior and to accept non-normalized URLs, set the option `http-accept-non-normalized-paths` to `true`. With this configuration, enable and review the HTTP access log to identify problematic requests.
|
||||
|
||||
// ------------------------ Notable changes ------------------------ //
|
||||
== Notable changes
|
||||
@ -29,12 +36,17 @@ For more information, see link:{adminguide_link}#_fine_grained_permissions[Deleg
|
||||
|
||||
The following sections provide details on deprecated features.
|
||||
|
||||
=== <TODO>
|
||||
=== Accepting HTTP requests with non-normalized paths
|
||||
|
||||
The option `http-accept-non-normalized-paths` was introduced to restore the previous behavior where {project_name} accepted non-normalized URLs.
|
||||
|
||||
As this behavior can be problematic for URL filtering, it is deprecated and will be removed in a future release.
|
||||
|
||||
// ------------------------ Removed features ------------------------ //
|
||||
////
|
||||
== Removed features
|
||||
|
||||
The following features have been removed from this release.
|
||||
|
||||
=== <TODO>
|
||||
|
||||
////
|
||||
|
||||
@ -1,29 +1,36 @@
|
||||
// ------------------------ Breaking changes ------------------------ //
|
||||
////
|
||||
== Breaking changes
|
||||
|
||||
Breaking changes are identified as those that might require changes for existing users to their configurations or applications.
|
||||
In minor or patch releases, {project_name} will only introduce breaking changes to fix bugs.
|
||||
|
||||
=== <TODO>
|
||||
////
|
||||
|
||||
// ------------------------ Notable changes ------------------------ //
|
||||
////
|
||||
== Notable changes
|
||||
|
||||
Notable changes may include internal behavior changes that prevent common misconfigurations, bugs that are fixed, or changes to simplify running {project_name}.
|
||||
|
||||
=== <TODO>
|
||||
////
|
||||
|
||||
// ------------------------ Deprecated features ------------------------ //
|
||||
////
|
||||
== Deprecated features
|
||||
|
||||
The following sections provide details on deprecated features.
|
||||
|
||||
=== <TODO>
|
||||
////
|
||||
|
||||
// ------------------------ Removed features ------------------------ //
|
||||
////
|
||||
== Removed features
|
||||
|
||||
The following features have been removed from this release.
|
||||
|
||||
=== <TODO>
|
||||
|
||||
////
|
||||
|
||||
@ -58,12 +58,12 @@ test.describe("Linked accounts", () => {
|
||||
clientId: "groups-idp",
|
||||
clientSecret: "H0JaTc7VBu3HJR26vrzMxgidfJmgI5Dw",
|
||||
validateSignature: "false",
|
||||
tokenUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/token`,
|
||||
jwksUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/certs`,
|
||||
issuer: `${SERVER_URL}realms/${externalRealm}`,
|
||||
authorizationUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/auth`,
|
||||
logoutUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/logout`,
|
||||
userInfoUrl: `${SERVER_URL}realms/${externalRealm}/protocol/openid-connect/userinfo`,
|
||||
tokenUrl: `${SERVER_URL}/realms/${externalRealm}/protocol/openid-connect/token`,
|
||||
jwksUrl: `${SERVER_URL}/realms/${externalRealm}/protocol/openid-connect/certs`,
|
||||
issuer: `${SERVER_URL}/realms/${externalRealm}`,
|
||||
authorizationUrl: `${SERVER_URL}/realms/${externalRealm}/protocol/openid-connect/auth`,
|
||||
logoutUrl: `${SERVER_URL}/realms/${externalRealm}/protocol/openid-connect/logout`,
|
||||
userInfoUrl: `${SERVER_URL}/realms/${externalRealm}/protocol/openid-connect/userinfo`,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/us
|
||||
import { ADMIN_PASSWORD, ADMIN_USERNAME, SERVER_URL } from "./common.ts";
|
||||
|
||||
export const adminClient = new AdminClient({
|
||||
baseUrl: SERVER_URL.toString(),
|
||||
baseUrl: SERVER_URL,
|
||||
});
|
||||
|
||||
await adminClient.auth({
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation.js";
|
||||
import { generatePath } from "react-router-dom";
|
||||
|
||||
export const SERVER_URL = new URL("http://localhost:8080");
|
||||
export const SERVER_URL = "http://localhost:8080";
|
||||
export const ACCOUNT_ROOT_PATH = "/realms/:realm/account" as const;
|
||||
export const ADMIN_ROOT_PATH = "/admin/:realm/console" as const;
|
||||
export const DEFAULT_REALM = "master";
|
||||
|
||||
@ -15,7 +15,7 @@ import { merge } from "lodash-es";
|
||||
|
||||
class AdminClient {
|
||||
readonly #client = new KeycloakAdminClient({
|
||||
baseUrl: "http://localhost:8080/",
|
||||
baseUrl: "http://localhost:8080",
|
||||
realmName: "master",
|
||||
});
|
||||
|
||||
|
||||
@ -151,4 +151,11 @@ public class HttpOptions {
|
||||
"Specify a list of comma-separated values defined in milliseconds. Example with buckets from 5ms to 10s: 5,10,25,50,250,500,1000,2500,5000,10000")
|
||||
.build();
|
||||
|
||||
public static final Option<Boolean> HTTP_ACCEPT_NON_NORMALIZED_PATHS = new OptionBuilder<>("http-accept-non-normalized-paths", Boolean.class)
|
||||
.category(OptionCategory.HTTP)
|
||||
.description("If the server should accept paths that are not normalized according to RFC3986 or that contain a double slash ('//'). While accepting those requests might be relevant for legacy applications, it is recommended to disable it to allow for more concise URL filtering.")
|
||||
.deprecated()
|
||||
.defaultValue(Boolean.FALSE)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@ -49,9 +49,12 @@ import io.quarkus.narayana.jta.runtime.TransactionManagerBuildTimeConfig.UnsafeM
|
||||
import io.quarkus.resteasy.reactive.server.spi.MethodScannerBuildItem;
|
||||
import io.quarkus.resteasy.reactive.server.spi.PreExceptionMapperHandlerBuildItem;
|
||||
import io.quarkus.runtime.configuration.ConfigurationException;
|
||||
import io.quarkus.vertx.http.deployment.FilterBuildItem;
|
||||
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
|
||||
import io.quarkus.vertx.http.deployment.ManagementInterfaceFilterBuildItem;
|
||||
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
|
||||
import io.quarkus.vertx.http.deployment.RouteBuildItem;
|
||||
import io.quarkus.vertx.http.runtime.security.SecurityHandlerPriorities;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.PersistenceUnitTransactionType;
|
||||
import org.eclipse.microprofile.config.spi.ConfigSource;
|
||||
@ -272,6 +275,26 @@ class KeycloakProcessor {
|
||||
);
|
||||
}
|
||||
|
||||
@Record(ExecutionTime.STATIC_INIT)
|
||||
@BuildStep
|
||||
@Consume(ConfigBuildItem.class)
|
||||
void filterAllRequests(BuildProducer<FilterBuildItem> filters, KeycloakRecorder recorder) {
|
||||
var filter = recorder.getRejectNonNormalizedPathFilter();
|
||||
if (filter != null) {
|
||||
filters.produce(new FilterBuildItem(filter, SecurityHandlerPriorities.CORS + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Record(ExecutionTime.STATIC_INIT)
|
||||
@BuildStep(onlyIf = IsManagementEnabled.class)
|
||||
@Consume(ConfigBuildItem.class)
|
||||
void filterAllManagementRequests(BuildProducer<ManagementInterfaceFilterBuildItem> filters, KeycloakRecorder recorder) {
|
||||
var filter = recorder.getRejectNonNormalizedPathFilter();
|
||||
if (filter != null) {
|
||||
filters.produce(new ManagementInterfaceFilterBuildItem(filter, SecurityHandlerPriorities.CORS + 1));
|
||||
}
|
||||
}
|
||||
|
||||
@Record(ExecutionTime.STATIC_INIT)
|
||||
@BuildStep(onlyIf = IsManagementEnabled.class)
|
||||
@Consume(ConfigBuildItem.class)
|
||||
|
||||
@ -32,6 +32,7 @@ import org.keycloak.common.crypto.CryptoIntegration;
|
||||
import org.keycloak.common.crypto.CryptoProvider;
|
||||
import org.keycloak.common.crypto.FipsMode;
|
||||
import org.keycloak.config.DatabaseOptions;
|
||||
import org.keycloak.config.HttpOptions;
|
||||
import org.keycloak.config.TruststoreOptions;
|
||||
import org.keycloak.marshalling.Marshalling;
|
||||
import org.keycloak.provider.Provider;
|
||||
@ -40,6 +41,7 @@ import org.keycloak.provider.Spi;
|
||||
import org.keycloak.quarkus.runtime.configuration.Configuration;
|
||||
import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||
import org.keycloak.quarkus.runtime.services.RejectNonNormalizedPathFilter;
|
||||
import org.keycloak.quarkus.runtime.storage.database.liquibase.FastServiceLocator;
|
||||
import org.keycloak.representations.userprofile.config.UPConfig;
|
||||
import org.keycloak.theme.ClasspathThemeProviderFactory;
|
||||
@ -78,6 +80,10 @@ public class KeycloakRecorder {
|
||||
return routingContext -> routingContext.response().end("Keycloak Management Interface");
|
||||
}
|
||||
|
||||
public Handler<RoutingContext> getRejectNonNormalizedPathFilter() {
|
||||
return !Configuration.isTrue(HttpOptions.HTTP_ACCEPT_NON_NORMALIZED_PATHS) ? new RejectNonNormalizedPathFilter() : null;
|
||||
}
|
||||
|
||||
public void configureTruststore() {
|
||||
String[] truststores = Configuration.getOptionalKcValue(TruststoreOptions.TRUSTSTORE_PATHS.getKey())
|
||||
.map(s -> s.split(",")).orElse(new String[0]);
|
||||
|
||||
@ -154,6 +154,8 @@ public final class HttpPropertyMappers implements PropertyMapperGrouping {
|
||||
fromOption(HttpOptions.HTTP_METRICS_SLOS)
|
||||
.isEnabled(MetricsPropertyMappers::metricsEnabled, MetricsPropertyMappers.METRICS_ENABLED_MSG)
|
||||
.paramLabel("list of buckets")
|
||||
.build(),
|
||||
fromOption(HttpOptions.HTTP_ACCEPT_NON_NORMALIZED_PATHS)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2025 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* 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.keycloak.quarkus.runtime.services;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||
import org.keycloak.services.util.ObjectMapperResolver;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This filter rejects all paths that need normalization as of RFC3986 or that have double slashes.
|
||||
* This prevents path traversal that would circumvent path filtering applied by a proxy if that proxy would not apply
|
||||
* normalization of the path. In addition to that, the reverse proxy might not be aware of the additional path
|
||||
* of the double slashes that Keycloak performs.
|
||||
*/
|
||||
public class RejectNonNormalizedPathFilter implements Handler<RoutingContext> {
|
||||
private static final Logger LOGGER = Logger.getLogger(RejectNonNormalizedPathFilter.class);
|
||||
private final ObjectMapper MAPPER = ObjectMapperResolver.createStreamSerializer();
|
||||
|
||||
@Override
|
||||
public void handle(RoutingContext routingContext) {
|
||||
if (!Objects.equals(routingContext.request().path(), routingContext.normalizedPath())) {
|
||||
LOGGER.debugf("Request with a non-normalized path blocked: %s vs. %s", routingContext.request().path(), routingContext.normalizedPath());
|
||||
OAuth2ErrorRepresentation error = new OAuth2ErrorRepresentation("missingNormalization", "Request path not normalized");
|
||||
routingContext.response().headers().add("Content-Type", "application/json; charset=UTF-8");
|
||||
String jsonString;
|
||||
try {
|
||||
jsonString = MAPPER.writeValueAsString(error);
|
||||
} catch (JsonProcessingException e) {
|
||||
jsonString = "";
|
||||
}
|
||||
routingContext.response().setStatusCode(400).end(jsonString);
|
||||
} else {
|
||||
routingContext.next();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -59,7 +59,21 @@ public class HttpDistTest {
|
||||
assertThat("Some of the requests should be properly rejected", statusCodes, hasItem(503));
|
||||
assertThat("None of the requests should throw an unhandled exception", statusCodes, not(hasItem(500)));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Launch({"start-dev", "--log-level=INFO,org.keycloak.quarkus.runtime.services.RejectNonNormalizedPathFilter:debug", "--http-access-log-enabled=true"})
|
||||
public void preventNonNormalizedURLs() {
|
||||
when().get("/realms/master").then().statusCode(200);
|
||||
when().get("/realms/xxx/../master").then().statusCode(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({"start-dev", "--http-access-log-enabled=true", "--http-accept-non-normalized-paths=true"})
|
||||
public void allowNonNormalizedURLs() {
|
||||
when().get("/realms/master").then().statusCode(200);
|
||||
when().get("/realms/xxx/../master").then().statusCode(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Launch({"start-dev", "--https-certificates-reload-period=wrong"})
|
||||
public void testHttpCertificateReloadPeriod(CLIResult result) {
|
||||
|
||||
@ -215,6 +215,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -285,6 +285,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -263,6 +263,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -286,6 +286,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -235,6 +235,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -258,6 +258,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -262,6 +262,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -285,6 +285,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -260,6 +260,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -283,6 +283,11 @@ Hostname v2:
|
||||
|
||||
HTTP(S):
|
||||
|
||||
--http-accept-non-normalized-paths <true|false>
|
||||
DEPRECATED. If the server should accept paths that are not normalized
|
||||
according to RFC3986 or that contain a double slash ('//'). While accepting
|
||||
those requests might be relevant for legacy applications, it is recommended
|
||||
to disable it to allow for more concise URL filtering. Default: false.
|
||||
--http-enabled <true|false>
|
||||
Enables the HTTP listener. Enabled by default in development mode. Typically
|
||||
not enabled in production unless the server is fronted by a TLS termination
|
||||
|
||||
@ -334,7 +334,11 @@ public final class RawKeycloakDistribution implements KeycloakDistribution {
|
||||
}
|
||||
|
||||
private void waitForReadiness(String scheme, int port) throws MalformedURLException {
|
||||
URL contextRoot = new URL(scheme + "://localhost:" + port + ("/" + relativePath + "/realms/master/").replace("//", "/"));
|
||||
var myRelativePath = relativePath;
|
||||
if (!myRelativePath.endsWith("/")) {
|
||||
myRelativePath += "/";
|
||||
}
|
||||
URL contextRoot = new URL(scheme + "://localhost:" + port + myRelativePath + "realms/master/");
|
||||
HttpURLConnection connection = null;
|
||||
long startTime = System.currentTimeMillis();
|
||||
Exception ex = null;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user