Skip to content

Commit

Permalink
[GEOT-7557] Provide the ability for downstream applications (e.g. Geo…
Browse files Browse the repository at this point in the history
…Server) to append additional querystring parameters e.g. AuthKey to every request made by the HTTPClient (#4727)

* To support HTTP Headers see github.com/geoserver/geoserver/pull/7552

* Add AuthKey by appending to URL in HTTPClient

* Undo test version in pom files

* Fix SimpleHTTPClient

* Add test cases

* Fix review issues with URL

* URL Host not Authority

* Change AuthKey -> ExtraParams and from String to Map

* URL encode keys and values and build a test that verifies this

* Update modules/extension/wms/src/main/java/org/geotools/ows/wms/WebMapServer.java

Improve grammar

Co-authored-by: Jody Garnett <jody.garnett@gmail.com>

* Update modules/library/http/src/main/java/org/geotools/http/HTTPClient.java

Improve docs

Co-authored-by: Jody Garnett <jody.garnett@gmail.com>

* Petersmythe map string object (#2)

Replace Map<String, Object>

* Change null to emptyMap, add duplicate key check

* Oops, when applying patch

* Fix integer test

* Fix spelling

* To support HTTP Headers see github.com/geoserver/geoserver/pull/7552

* Add AuthKey by appending to URL in HTTPClient

* Undo test version in pom files

* Fix SimpleHTTPClient

* Add test cases

* Fix review issues with URL

* URL Host not Authority

* Change AuthKey -> ExtraParams and from String to Map

* URL encode keys and values and build a test that verifies this

* Update modules/extension/wms/src/main/java/org/geotools/ows/wms/WebMapServer.java

Improve grammar

Co-authored-by: Jody Garnett <jody.garnett@gmail.com>

* Update modules/library/http/src/main/java/org/geotools/http/HTTPClient.java

Improve docs

Co-authored-by: Jody Garnett <jody.garnett@gmail.com>

* Petersmythe map string object (#2)

Replace Map<String, Object>

* Change null to emptyMap, add duplicate key check

* Oops, when applying patch

* Fix integer test

* Fix spelling

---------

Co-authored-by: Peter Smythe <peter@afrigis.co.za>
Co-authored-by: Jody Garnett <jody.garnett@gmail.com>
  • Loading branch information
3 people committed May 2, 2024
1 parent c272512 commit 902bb18
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,27 @@ public WebMapServer(final URL serverURL, final HTTPClient httpClient, Map<String
super(serverURL, httpClient, null, hints);
}

/**
* Creates a new WebMapServer instance and retrieve the Capabilities document specified by
* serverURL.
*
* @param serverURL a URL that points to the capabilities document of a server
* @param httpClient The client to be used when performing HTTP requests
* @param hints A map of hints. Can be used to control some aspects of the XML parsing, see
* {@link XMLHandlerHints} for a reference
* @param headers A map of headers. These will be added when making the HTTP request
* @throws IOException if there is an error communicating with the server
* @throws ServiceException if the server responds with an error
*/
public WebMapServer(
final URL serverURL,
final HTTPClient httpClient,
Map<String, Object> hints,
Map<String, String> headers)
throws IOException, ServiceException {
super(serverURL, httpClient, null, hints, headers);
}

/**
* Creates a new WebMapServer instance and attempts to retrieve the Capabilities document
* specified by serverURL.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,15 @@

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Map;
import java.util.StringJoiner;

/**
* A base class for HTTPClient, that implements everything except the get and post methods.
Expand All @@ -32,6 +39,8 @@ public abstract class AbstractHttpClient implements HTTPClient {

protected String password;

protected Map<String, String> extraParams = Collections.emptyMap();

protected int connectTimeout;

protected int readTimeout;
Expand All @@ -53,6 +62,16 @@ public String getPassword() {
return this.password;
}

@Override
public void setExtraParams(Map<String, String> extraParams) {
this.extraParams = extraParams;
}

@Override
public Map<String, String> getExtraParams() {
return this.extraParams;
}

@Override
public void setPassword(String password) {
this.password = password;
Expand Down Expand Up @@ -90,6 +109,41 @@ public boolean isTryGzip() {
return tryGzip;
}

/**
* Appends query parameters to an existing URL.
*
* @param oldUrl The original URL to which parameters will be appended.
* @param appendQuery A map containing key-value pairs to be appended as query parameters.
* @return A new URL with the appended query parameters.
* @throws MalformedURLException If the resulting URL is malformed.
*/
protected static URL appendURL(URL oldUrl, Map<String, String> appendQuery)
throws MalformedURLException {
String oldQuery = oldUrl.getQuery();

StringJoiner stringJoiner = new StringJoiner("&");
appendQuery.forEach(
(key, value) -> {
try {
stringJoiner.add(
URLEncoder.encode(key, "UTF-8")
+ "="
+ URLEncoder.encode(value, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new UncheckedIOException(e);
}
});
String query = stringJoiner.toString();

String newQuery = oldQuery != null ? oldQuery + "&" + query : query;

return new URL(
oldUrl.getProtocol(),
oldUrl.getHost(),
oldUrl.getPort(),
oldUrl.getPath() + "?" + newQuery);
}

protected boolean isFile(URL url) {
return "file".equalsIgnoreCase(url.getProtocol());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ public void setPassword(String password) {
delegate.setPassword(password);
}

@Override
public Map<String, String> getExtraParams() {
return delegate.getExtraParams();
}

@Override
public void setExtraParams(Map<String, String> extraParams) {
delegate.setExtraParams(extraParams);
}

@Override
public int getConnectTimeout() {
return delegate.getConnectTimeout();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,16 @@ default HTTPResponse get(URL url, Map<String, String> headers) throws IOExceptio
/** @param password the HTTP BASIC Authentication password */
public void setPassword(String password);

/**
* Optional query parameters.
*
* @return querystring ExtraParams, or {@code emptyMap()} if not set
*/
public Map<String, String> getExtraParams();

/** @param extraParams the optional querystring ExtraParams to be appended to finalURL */
public void setExtraParams(Map<String, String> extraParams);

/** @return the tcp/ip connect timeout in seconds */
public int getConnectTimeout();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,19 @@ public HTTPResponse post(

private URLConnection openConnection(URL finalURL, Map<String, String> headers)
throws IOException {
Map<String, String> extraParams = getExtraParams();
if (!extraParams.isEmpty()) {
finalURL = appendURL(finalURL, extraParams);
}

URLConnection connection = finalURL.openConnection();
final boolean http = connection instanceof HttpURLConnection;
if (headers == null) {
headers = new HashMap<>();
} else {
headers = new HashMap<>(headers); // avoid parameter modification
}

if (http && tryGzip) {
headers.put("Accept-Encoding", "gzip");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static java.util.Collections.singletonMap;

Expand All @@ -35,6 +36,7 @@
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import org.junit.Rule;
import org.junit.Test;

Expand Down Expand Up @@ -103,4 +105,54 @@ public void testRequestsWithAdditionalHeaders() throws IOException {
postRequestedFor(urlEqualTo("/test"))
.withHeader("Authorization", equalTo(headerValue)));
}

/**
* Tests if extraParams are added to requests as expected
*
* @throws IOException
*/
@Test
public void testRequestsWithExtraParams() throws IOException {
SimpleHttpClient client = new SimpleHttpClient();

Map<String, String> testExtraParams =
Map.of("key1", "123", "key2", "value2", "key%3", "value/3");

URL urlWithoutExtraParams =
new URL("http://localhost:" + wireMockRule.port() + "/test?key2=duplicate");

// Mock the expected request and response
UrlPattern urlPattern = urlMatching("/test[\\w?&=%]*"); // \w or any of ?&=%
ResponseDefinitionBuilder response =
aResponse()
.withStatus(200)
.withHeader("Content-Type", "text/xml")
.withBody("<response>Some content</response>");
stubFor(get(urlPattern).willReturn(response));
stubFor(post(urlPattern).willReturn(response));

client.setExtraParams(testExtraParams);

// GET
client.get(urlWithoutExtraParams);
verify(
getRequestedFor(urlMatching("/test[\\w?&=%]*"))
.withQueryParam("key1", equalTo("123"))
.withQueryParam("key2", equalTo("value2"))
.withQueryParam("key2", equalTo("duplicate"))
.withQueryParam(
"key%3",
equalTo("value/3"))); // % and / are URL-encoded and then decoded
// again

// POST
ByteArrayInputStream postBody = new ByteArrayInputStream("GeoTools".getBytes());
client.post(urlWithoutExtraParams, postBody, "text/plain");
verify(
postRequestedFor(urlMatching("/test[\\w?&=%]*"))
.withQueryParam("key1", equalTo("123"))
.withQueryParam("key2", equalTo("value2"))
.withQueryParam("key2", equalTo("duplicate"))
.withQueryParam("key%3", equalTo("value/3")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,12 @@ public HTTPResponse get(URL url, Map<String, String> headers) throws IOException
} else {
headers = new HashMap<>(headers); // avoid parameter modification
}

Map<String, String> extraParams = getExtraParams();
if (!extraParams.isEmpty()) {
url = appendURL(url, extraParams);
}

HttpGet getMethod = new HttpGet(url.toExternalForm());
getMethod.setConfig(connectionConfig);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5475,7 +5475,7 @@ directory1, new RegexFileFilter("global_mosaic_[^0].*"), null)) {
failed = true;
}
}
assertFalse("Terminating test due to previus failures", failed);
assertFalse("Terminating test due to previous failures", failed);

// check that all the files are there
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ public String getPassword() {
@Override
public void setPassword(String password) {}

@Override
public Map<String, String> getExtraParams() {
return null;
}

@Override
public void setExtraParams(Map<String, String> extraParams) {}

@Override
public int getConnectTimeout() {
return 0;
Expand Down

0 comments on commit 902bb18

Please sign in to comment.