This repository was archived by the owner on Mar 3, 2026. It is now read-only.
forked from jooby-project/jooby
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClient.java
More file actions
481 lines (397 loc) · 13.5 KB
/
Client.java
File metadata and controls
481 lines (397 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
package org.jooby.test;
import com.google.common.base.Throwables;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.fluent.Executor;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import org.junit.rules.ExternalResource;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
/**
* Utility test class integration test. Internal use only.
*
* @author edgar
*/
public class Client extends ExternalResource {
public interface Callback {
void execute(String value) throws Exception;
}
public interface ArrayCallback {
void execute(String[] values) throws Exception;
}
public interface ServerCallback {
void execute(Client request) throws Exception;
}
public static class Request {
private Executor executor;
private org.apache.http.client.fluent.Request req;
private org.apache.http.HttpResponse rsp;
private Client server;
public Request(final Client server, final Executor executor,
final org.apache.http.client.fluent.Request req) {
this.server = server;
this.executor = executor;
this.req = req;
}
public Response execute() throws Exception {
this.rsp = executor.execute(req).returnResponse();
return new Response(server, rsp);
}
public Response expect(final String content) throws Exception {
return execute().expect(content);
}
public Response expect(final Callback callback) throws Exception {
return execute().expect(callback);
}
public Response expect(final int status) throws Exception {
return execute().expect(status);
}
public Response expect(final byte[] content) throws Exception {
return execute().expect(content);
}
public Request header(final String name, final Object value) {
req.addHeader(name, value.toString());
return this;
}
public Body multipart() {
return new Body(MultipartEntityBuilder.create(), this);
}
public Body form() {
return new Body(this);
}
public void close() {
EntityUtils.consumeQuietly(rsp.getEntity());
}
public Request body(final String body, final String type) {
if (type == null) {
byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
HttpEntity entity = new InputStreamEntity(new ByteArrayInputStream(bytes), bytes.length);
req.body(entity);
} else {
req.bodyString(body, ContentType.parse(type));
}
return this;
}
}
public static class Body {
private Request req;
private MultipartEntityBuilder parts;
private List<BasicNameValuePair> fields;
public Body(final MultipartEntityBuilder parts, final Request req) {
this.parts = parts;
this.req = req;
}
public Body(final Request req) {
this.fields = new ArrayList<>();
this.req = req;
}
public Response expect(final String content) throws Exception {
if (parts != null) {
req.req.body(parts.build());
} else {
req.req.bodyForm(fields);
}
return req.expect(content);
}
public Response expect(final int status) throws Exception {
if (parts != null) {
req.req.body(parts.build());
} else {
req.req.bodyForm(fields);
}
return req.expect(status);
}
public Body add(final String name, final Object value, final String type) {
if (parts != null) {
parts.addTextBody(name, value.toString(), ContentType.parse(type));
} else {
fields.add(new BasicNameValuePair(name, value.toString()));
}
return this;
}
public Body add(final String name, final Object value) {
return add(name, value, "text/plain");
}
public Body file(final String name, final byte[] bytes, final String type,
final String filename) {
if (parts != null) {
parts.addBinaryBody(name, bytes, ContentType.parse(type), filename);
} else {
throw new IllegalStateException("Not a multipart");
}
return this;
}
}
public static class Response {
private Client server;
private HttpResponse rsp;
public Response(final Client server, final org.apache.http.HttpResponse rsp) {
this.server = server;
this.rsp = rsp;
}
public Response expect(final String content) throws Exception {
assertEquals(content, EntityUtils.toString(this.rsp.getEntity()));
return this;
}
public Response expect(final int status) throws Exception {
assertEquals(status, rsp.getStatusLine().getStatusCode());
return this;
}
public Response expect(final byte[] content) throws Exception {
assertArrayEquals(content, EntityUtils.toByteArray(this.rsp.getEntity()));
return this;
}
public Response expect(final Callback callback) throws Exception {
callback.execute(EntityUtils.toString(this.rsp.getEntity()));
return this;
}
public Response header(final String headerName, final String headerValue)
throws Exception {
if (headerValue == null) {
assertNull(rsp.getFirstHeader(headerName));
} else {
Header header = rsp.getFirstHeader(headerName);
if (header == null) {
// friendly junit err
assertEquals(headerValue, header);
} else {
assertEquals(headerValue.toLowerCase(), header.getValue().toLowerCase());
}
}
return this;
}
public Response headers(final BiConsumer<String, String> headers)
throws Exception {
for (Header header : rsp.getAllHeaders()) {
headers.accept(header.getName(), header.getValue());
}
return this;
}
public Response header(final String headerName, final Object headerValue)
throws Exception {
if (headerValue == null) {
return header(headerName, (String) null);
} else {
return header(headerName, headerValue.toString());
}
}
public Response header(final String headerName, final Optional<Object> headerValue)
throws Exception {
Header header = rsp.getFirstHeader(headerName);
if (header != null) {
assertEquals(headerValue.get(), header.getValue());
}
return this;
}
public Response header(final String headerName, final Callback callback) throws Exception {
callback.execute(
Optional.ofNullable(rsp.getFirstHeader(headerName))
.map(Header::getValue)
.orElse(null));
return this;
}
public Response headers(final String headerName, final ArrayCallback callback)
throws Exception {
Header[] headers = rsp.getHeaders(headerName);
String[] values = new String[headers.length];
for (int i = 0; i < values.length; i++) {
values[i] = headers[i].getValue();
}
callback.execute(values);
return this;
}
public Response empty() throws Exception {
HttpEntity entity = this.rsp.getEntity();
if (entity != null) {
assertEquals("", EntityUtils.toString(entity));
}
return this;
}
public void request(final ServerCallback request) throws Exception {
request.execute(server);
}
public void startsWith(final String value) throws IOException {
String rsp = EntityUtils.toString(this.rsp.getEntity());
if (!rsp.startsWith(value)) {
assertEquals(value, rsp);
}
}
}
private Executor executor;
private CloseableHttpClient client;
private BasicCookieStore cookieStore;
private String host;
private Request req;
private HttpClientBuilder builder;
private UsernamePasswordCredentials creds;
public Client(final String host) {
this.host = host;
}
public Client() {
this("http://localhost:8080");
}
public void start() {
this.cookieStore = new BasicCookieStore();
this.builder = HttpClientBuilder.create()
.setMaxConnTotal(1)
.setRetryHandler(new StandardHttpRequestRetryHandler(0, false))
.setMaxConnPerRoute(1)
.setDefaultCookieStore(cookieStore);
}
public Client resetCookies() {
cookieStore.clear();
return this;
}
public Client dontFollowRedirect() {
builder.setRedirectStrategy(new RedirectStrategy() {
@Override
public boolean isRedirected(final HttpRequest request, final HttpResponse response,
final HttpContext context) throws ProtocolException {
return false;
}
@Override
public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response,
final HttpContext context) throws ProtocolException {
return null;
}
});
return this;
}
public Request get(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Get(host
+ path));
return req;
}
public Request trace(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Trace(host
+ path));
return req;
}
public Request options(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Options(host
+ path));
return req;
}
public Request head(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Head(host
+ path));
return req;
}
private Executor executor() {
if (executor == null) {
if (this.host.startsWith("https://")) {
try {
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(null, (chain, authType) -> true)
.build();
builder.setSSLContext(sslContext);
builder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
} catch (Exception ex) {
Throwables.propagate(ex);
}
}
client = builder.build();
executor = Executor.newInstance(client);
if (creds != null) {
executor.auth(creds);
}
}
return executor;
}
public Request post(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Post(host
+ path));
return req;
}
public Request put(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Put(host
+ path));
return req;
}
public Request delete(final String path) {
this.req = new Request(this, executor(), org.apache.http.client.fluent.Request.Delete(host
+ path));
return req;
}
public Request patch(final String path) {
this.req = new Request(this, executor(), pathHack(host + path));
return req;
}
@SuppressWarnings({"unchecked", "rawtypes"})
private org.apache.http.client.fluent.Request pathHack(final String string) {
try {
// Patch is available since 4.4, but we are in 4.3 because of AWS-SDK
Class ireqclass = getClass().getClassLoader().loadClass(
"org.apache.http.client.fluent.InternalHttpRequest");
Constructor<org.apache.http.client.fluent.Request> constructor = org.apache.http.client.fluent.Request.class
.getDeclaredConstructor(ireqclass);
constructor.setAccessible(true);
Constructor ireqcons = ireqclass.getDeclaredConstructor(String.class, URI.class);
ireqcons.setAccessible(true);
Object ireq = ireqcons.newInstance("PATCH", URI.create(string));
return constructor.newInstance(ireq);
} catch (NoSuchMethodException | SecurityException | ClassNotFoundException
| InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
throw new UnsupportedOperationException(ex);
}
}
public void stop() throws IOException {
if (this.req != null) {
try {
this.req.close();
} catch (NullPointerException ex) {
}
}
if (client != null) {
client.close();
}
this.builder = null;
this.executor = null;
}
public Client basic(final String username, final String password) {
creds = new UsernamePasswordCredentials(username, password);
return this;
}
@Override
protected void before() throws Throwable {
start();
}
@Override
protected void after() {
try {
stop();
} catch (IOException ex) {
throw new IllegalStateException("Unable to stop client", ex);
}
}
}