Skip to content

Commit fec0486

Browse files
committed
#19 - ENH: Add Basic Authentication support - BasicAuthIntercept
1 parent 7eeb3ae commit fec0486

File tree

7 files changed

+135
-8
lines changed

7 files changed

+135
-8
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package io.avaje.http.client;
2+
3+
import java.net.http.HttpResponse;
4+
import java.util.Base64;
5+
6+
import static java.nio.charset.StandardCharsets.UTF_8;
7+
8+
/**
9+
* Adds Basic Authorization header to requests.
10+
*/
11+
public class BasicAuthIntercept implements RequestIntercept {
12+
13+
private final String headerValue;
14+
15+
/**
16+
* Construct with the username and password.
17+
*/
18+
public BasicAuthIntercept(String username, String password) {
19+
this.headerValue = "Basic "+encode(username, password);
20+
}
21+
22+
/**
23+
* Return Base64 encoding of {@literal username:password}
24+
*/
25+
public static String encode(String username, String password) {
26+
return Base64.getEncoder().encodeToString((username + ":" + password).getBytes(UTF_8));
27+
}
28+
29+
@Override
30+
public void beforeRequest(HttpClientRequest request) {
31+
request.header("Authorization", headerValue);
32+
}
33+
34+
@Override
35+
public void afterResponse(HttpResponse<?> response, HttpClientRequest request) {
36+
// do nothing
37+
}
38+
}

client/src/main/java/io/avaje/http/client/DHttpClientRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ public HttpClientRequest header(String name, Object value) {
9191
return value != null ? header(name, value.toString()) : this;
9292
}
9393

94+
@Override
95+
public List<String> header(String name) {
96+
final List<String> values = headers.get(name);
97+
return values == null ? Collections.emptyList() : values;
98+
}
99+
94100
@Override
95101
public HttpClientRequest gzip(boolean gzip) {
96102
this.gzip = gzip;

client/src/main/java/io/avaje/http/client/HttpClientRequest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.net.http.HttpRequest;
66
import java.nio.file.Path;
77
import java.time.Duration;
8+
import java.util.List;
89
import java.util.function.Supplier;
910

1011
/**
@@ -69,6 +70,13 @@ public interface HttpClientRequest {
6970
*/
7071
HttpClientRequest header(String name, Object value);
7172

73+
/**
74+
* Return the header values that have been set for the given header name.
75+
*
76+
* @return The headers values or an empty collection if the header has not been specified yet.
77+
*/
78+
List<String> header(String name);
79+
7280
/**
7381
* Set if body content should be gzip encoded.
7482
*
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package io.avaje.http.client;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.util.List;
6+
7+
import static org.assertj.core.api.Assertions.assertThat;
8+
9+
class BasicAuthInterceptTest {
10+
11+
@Test
12+
void encode() {
13+
final String encode = BasicAuthIntercept.encode("Aladdin", "open sesame");
14+
assertThat(encode).isEqualTo("QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
15+
}
16+
17+
@Test
18+
void beforeRequest() {
19+
// setup
20+
final BasicAuthIntercept intercept = new BasicAuthIntercept("Aladdin", "open sesame");
21+
final HttpClientContext ctx = HttpClientContext.newBuilder().withBaseUrl("junk").build();
22+
23+
// act
24+
final HttpClientRequest request = ctx.request();
25+
intercept.beforeRequest(request);
26+
27+
final List<String> values = request.header("Authorization");
28+
assertThat(values).containsExactly("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
29+
}
30+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.avaje.http.client;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.junit.jupiter.api.Test;
5+
6+
import java.net.http.HttpResponse;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
class HelloBasicAuthTest extends BaseWebTest {
11+
12+
final HttpClientContext clientContext = client();
13+
14+
public static HttpClientContext client() {
15+
return HttpClientContext.newBuilder()
16+
.withBaseUrl(baseUrl)
17+
.withRequestListener(new RequestLogger())
18+
.withBodyAdapter(new JacksonBodyAdapter(new ObjectMapper()))
19+
.withRequestIntercept(new BasicAuthIntercept("rob", "bot"))
20+
.build();
21+
}
22+
23+
@Test
24+
void basicAuth() {
25+
26+
final HttpResponse<String> hres = clientContext.request()
27+
.path("hello/basicAuth")
28+
.GET()
29+
.asString();
30+
31+
assertThat(hres.statusCode()).isEqualTo(200);
32+
assertThat(hres.body()).isEqualTo("decoded: rob:bot");
33+
}
34+
35+
}

client/src/test/java/org/example/webserver/HelloController$Route.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public void registerRoutes() {
3636
ctx.contentType("text/plain").result(controller.retry());
3737
});
3838

39+
ApiBuilder.get("/hello/basicAuth", ctx -> {
40+
ctx.status(200);
41+
final String authorization = ctx.header("Authorization");
42+
ctx.result(controller.basicAuth(authorization));
43+
});
44+
3945
ApiBuilder.get("/hello/stream", ctx -> {
4046
ctx.status(200);
4147
controller.stream(ctx);

client/src/test/java/org/example/webserver/HelloController.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
package org.example.webserver;
22

3-
import io.avaje.http.api.Controller;
4-
import io.avaje.http.api.Delete;
5-
import io.avaje.http.api.Form;
6-
import io.avaje.http.api.Get;
7-
import io.avaje.http.api.MediaType;
8-
import io.avaje.http.api.Path;
9-
import io.avaje.http.api.Post;
10-
import io.avaje.http.api.Produces;
3+
import io.avaje.http.api.*;
114
import io.javalin.http.Context;
125

136
import javax.validation.Valid;
147
import java.time.LocalDate;
158
import java.util.ArrayList;
9+
import java.util.Base64;
1610
import java.util.List;
1711

12+
import static java.nio.charset.StandardCharsets.UTF_8;
1813
import static java.util.Objects.requireNonNull;
1914

2015
/**
@@ -47,6 +42,15 @@ String retry() {
4742
}
4843
}
4944

45+
@Get
46+
String basicAuth(@Header String authorization) {
47+
final String[] split = authorization.split(" ");
48+
if (split[0].equals("Basic")) {
49+
return "decoded: " + new String(Base64.getDecoder().decode(split[1]), UTF_8);
50+
}
51+
return "NotExpected: " + authorization;
52+
}
53+
5054
@Get("stream")
5155
void stream(Context context) {
5256
// simulate x-json-stream response

0 commit comments

Comments
 (0)