Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete shortcut methods for all HTTP methods for the WebClient #6358

Merged
merged 3 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -182,35 +182,34 @@ void testExpect100() {
.baseUri(baseURI)
.sendExpect100Continue(true)
.build();
Http1ClientRequest request = client.method(Http.Method.PUT)
.uri("/test");
Http1ClientRequest request = client.put("/test");

Http1ClientResponse response = getHttp1ClientResponseFromOutputStream(request, requestEntityParts);

validateChunkTransfer(response, true, NO_CONTENT_LENGTH, String.join("", requestEntityParts));
assertThat(response.headers(), hasHeader(REQ_EXPECT_100_HEADER_NAME));
}

@Test
// validates that HEAD is not allowed with entity payload
@Test
void testHeadMethod() {
String path = "/test";
assertThrows(IllegalArgumentException.class, () ->
injectedHttp1client.method(Http.Method.HEAD).uri(path).submit("Foo Bar"));
injectedHttp1client.head(path).submit("Foo Bar"));
assertThrows(IllegalArgumentException.class, () ->
injectedHttp1client.method(Http.Method.HEAD).uri(path).outputStream(it -> {
injectedHttp1client.head(path).outputStream(it -> {
it.write("Foo Bar".getBytes(StandardCharsets.UTF_8));
it.close();
}));
injectedHttp1client.method(Http.Method.HEAD).uri(path).request();
injectedHttp1client.head(path).request();
}

@Test
void testConnectionQueueDequeue() {
ClientConnection connectionNow = null;
ClientConnection connectionPrior = null;
for (int i = 0; i < 5; ++i) {
Http1ClientRequest request = injectedHttp1client.method(Http.Method.PUT).path("/test");
Http1ClientRequest request = injectedHttp1client.put("/test");
// connection will be dequeued if queue is not empty
connectionNow = ((ClientRequestImpl) request).getConnection(true);
request.connection(connectionNow);
Expand All @@ -232,7 +231,7 @@ void testConnectionQueueSizeLimit() {
List<Http1ClientResponse> responseList = new ArrayList<Http1ClientResponse>();
// create connections beyond the queue size limit
for (int i = 0; i < connectionQueueSize + 1; ++i) {
Http1ClientRequest request = injectedHttp1client.method(Http.Method.PUT).path("/test");
Http1ClientRequest request = injectedHttp1client.put("/test");
connectionList.add(((ClientRequestImpl) request).getConnection(true));
request.connection(connectionList.get(i));
responseList.add(request.request());
Expand All @@ -247,7 +246,7 @@ void testConnectionQueueSizeLimit() {
ClientConnection connection = null;
Http1ClientResponse response = null;
for (int i = 0; i < connectionQueueSize + 1; ++i) {
Http1ClientRequest request = injectedHttp1client.method(Http.Method.PUT).path("/test");
Http1ClientRequest request = injectedHttp1client.put("/test");
connection = ((ClientRequestImpl) request).getConnection(true);
request.connection(connection);
response = request.request();
Expand All @@ -262,7 +261,7 @@ void testConnectionQueueSizeLimit() {

// The queue is currently empty so check if we can add the last created connection into it.
response.close();
Http1ClientRequest request = injectedHttp1client.method(Http.Method.PUT).path("/test");
Http1ClientRequest request = injectedHttp1client.put("/test");
ClientConnection connectionNow = ((ClientRequestImpl) request).getConnection(true);
request.connection(connectionNow);
Http1ClientResponse responseNow = request.request();
Expand All @@ -272,7 +271,7 @@ void testConnectionQueueSizeLimit() {

private static void validateSuccessfulResponse(Http1Client client) {
String requestEntity = "Sending Something";
Http1ClientRequest request = client.method(Http.Method.PUT).path("/test");
Http1ClientRequest request = client.put("/test");
Http1ClientResponse response = request.submit(requestEntity);

assertThat(response.status(), is(Http.Status.OK_200));
Expand All @@ -281,7 +280,7 @@ private static void validateSuccessfulResponse(Http1Client client) {

private static void validateFailedResponse(Http1Client client, String errorMessage) {
String requestEntity = "Sending Something";
Http1ClientRequest request = client.method(Http.Method.PUT).path("/test");
Http1ClientRequest request = client.put("/test");
IllegalStateException ie = assertThrows(IllegalStateException.class, () -> request.submit(requestEntity));
assertThat(ie.getMessage(), containsString(errorMessage));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Oracle and/or its affiliates.
* Copyright (c) 2022, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,7 +35,7 @@ public interface HttpClient<REQ extends ClientRequest<REQ, RES>, RES extends Cli
REQ method(Http.Method method);

/**
* Shortcut for a get method with a path.
* Shortcut for get method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
Expand All @@ -52,4 +52,137 @@ default REQ get(String uri) {
default REQ get() {
return method(Http.Method.GET);
}

/**
* Shortcut for post method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ post(String uri) {
return method(Http.Method.POST).uri(uri);
}

/**
* Shortcut for post method with default path.
*
* @return a new request (not thread safe)
*/
default REQ post() {
return method(Http.Method.POST);
}

/**
* Shortcut for put method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ put(String uri) {
return method(Http.Method.PUT).uri(uri);
}

/**
* Shortcut for put method with default path.
*
* @return a new request (not thread safe)
*/
default REQ put() {
return method(Http.Method.PUT);
}

/**
* Shortcut for delete method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ delete(String uri) {
return method(Http.Method.DELETE).uri(uri);
}

/**
* Shortcut for delete method with default path.
*
* @return a new request (not thread safe)
*/
default REQ delete() {
return method(Http.Method.DELETE);
}

/**
* Shortcut for head method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ head(String uri) {
return method(Http.Method.HEAD).uri(uri);
}

/**
* Shortcut for head method with default path.
*
* @return a new request (not thread safe)
*/
default REQ head() {
return method(Http.Method.HEAD);
}

/**
* Shortcut for options method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ options(String uri) {
return method(Http.Method.OPTIONS).uri(uri);
}

/**
* Shortcut for options method with default path.
*
* @return a new request (not thread safe)
*/
default REQ options() {
return method(Http.Method.OPTIONS);
}

/**
* Shortcut for trace method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ trace(String uri) {
return method(Http.Method.TRACE).uri(uri);
}

/**
* Shortcut for trace method with default path.
*
* @return a new request (not thread safe)
*/
default REQ trace() {
return method(Http.Method.TRACE);
}

/**
* Shortcut for patch method with a path.
*
* @param uri path to resolve against base URI, or full URI
* @return a new request (not thread safe)
*/
default REQ patch(String uri) {
return method(Http.Method.PATCH).uri(uri);
}

/**
* Shortcut for patch method with default path.
*
* @return a new request (not thread safe)
*/
default REQ patch() {
return method(Http.Method.PATCH);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* 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 io.helidon.nima.webclient;

import java.net.URI;
import java.util.Map;
import java.util.function.Function;

import io.helidon.common.http.ClientRequestHeaders;
import io.helidon.common.http.Http;
import io.helidon.common.http.WritableHeaders;
import io.helidon.nima.common.tls.Tls;
import io.helidon.nima.webclient.http1.Http1ClientResponse;

import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

class HttpClientTest {

// Validate that the Http Shortcut methods are using their corresponding HTTP Method
@Test
void testHttpMethodShortcuts() {
Map<Http.Method, FakeHttpClientRequest> map = Map.of(Http.Method.GET, new FakeHttpClient().get(),
Http.Method.POST, new FakeHttpClient().post(),
Http.Method.PUT, new FakeHttpClient().put(),
Http.Method.DELETE, new FakeHttpClient().delete(),
Http.Method.HEAD, new FakeHttpClient().head(),
Http.Method.OPTIONS, new FakeHttpClient().options(),
Http.Method.TRACE, new FakeHttpClient().trace(),
Http.Method.PATCH, new FakeHttpClient().patch());

for (Map.Entry<Http.Method, FakeHttpClientRequest> entry : map.entrySet()) {
assertThat(entry.getValue().getMethod(), is(entry.getKey()));
}
}

// Validate that the correct HTTP Method is used and URI is passed correctly to the shortcut method
@Test
void testHttpMethodShortcutsWithUri() {
String baseURI = "http://localhost:1234";
Map<Http.Method, FakeHttpClientRequest> map = Map.of(
// use the method name as the path of the URL passed as argument to the http method shortcut
// ex. http://localhost:1234/GET
Http.Method.GET, new FakeHttpClient().get(baseURI + "/" + Http.Method.GET.text()),
Http.Method.POST, new FakeHttpClient().post(baseURI + "/" + Http.Method.POST.text()),
Http.Method.PUT, new FakeHttpClient().put(baseURI + "/" + Http.Method.PUT.text()),
Http.Method.DELETE, new FakeHttpClient().delete(baseURI + "/" + Http.Method.DELETE.text()),
Http.Method.HEAD, new FakeHttpClient().head(baseURI + "/" + Http.Method.HEAD.text()),
Http.Method.OPTIONS, new FakeHttpClient().options(baseURI + "/" + Http.Method.OPTIONS.text()),
Http.Method.TRACE, new FakeHttpClient().trace(baseURI + "/" + Http.Method.TRACE.text()),
Http.Method.PATCH, new FakeHttpClient().patch(baseURI + "/" + Http.Method.PATCH.text())
);

for (Map.Entry<Http.Method, FakeHttpClientRequest> entry : map.entrySet()) {
assertThat(entry.getValue().getMethod(), is(entry.getKey()));
// validate that the URL path is the method name as passed on to the shortcut method during map init above
assertThat(entry.getValue().getUri().getPath(), is("/" + entry.getKey().text()));
}
}

static class FakeHttpClient implements HttpClient<FakeHttpClientRequest, Http1ClientResponse> {
@Override
public FakeHttpClientRequest method(Http.Method method) {
return new FakeHttpClientRequest(method);
}
}

static class FakeHttpClientRequest implements ClientRequest<FakeHttpClientRequest, Http1ClientResponse> {
private Http.Method method;
private URI uri;

FakeHttpClientRequest(Http.Method method) {
this.method = method;
}

public Http.Method getMethod() {
return this.method;
}

public URI getUri() {
return this.uri;
}

@Override
public FakeHttpClientRequest tls(Tls tls) {
return null;
}

@Override
public FakeHttpClientRequest uri(URI uri) {
this.uri = uri;
return this;
}

@Override
public FakeHttpClientRequest header(Http.HeaderValue header) {
return null;
}

@Override
public FakeHttpClientRequest headers(Function<ClientRequestHeaders, WritableHeaders<?>> headersConsumer) {
return null;
}

@Override
public FakeHttpClientRequest pathParam(String name, String value) {
return null;
}

@Override
public FakeHttpClientRequest queryParam(String name, String... values) {
return null;
}

@Override
public Http1ClientResponse request() {
return null;
}

@Override
public Http1ClientResponse submit(Object entity) {
return null;
}

@Override
public Http1ClientResponse outputStream(OutputStreamHandler outputStreamConsumer) {
return null;
}

@Override
public URI resolvedUri() {
return null;
}

@Override
public FakeHttpClientRequest connection(ClientConnection connection) {
return null;
}
}
}
Loading