21
21
* questions.
22
22
*/
23
23
24
+ import java .io .Closeable ;
24
25
import java .io .IOException ;
25
26
import java .io .InputStream ;
26
- import java .net .InetAddress ;
27
- import java .net .InetSocketAddress ;
28
- import java .util .concurrent .Executor ;
27
+ import java .net .URI ;
28
+ import java .net .http .HttpClient .Version ;
29
+ import java .net .http .HttpRequest ;
30
+ import java .net .http .HttpResponse ;
31
+ import java .net .http .HttpResponse .BodyHandlers ;
32
+ import java .nio .ByteBuffer ;
33
+ import java .nio .charset .Charset ;
34
+ import java .nio .charset .StandardCharsets ;
29
35
import java .util .concurrent .ExecutorService ;
30
36
import java .util .concurrent .Executors ;
31
- import com .sun .net .httpserver .HttpExchange ;
32
- import com .sun .net .httpserver .HttpHandler ;
33
- import com .sun .net .httpserver .HttpServer ;
34
- import com .sun .net .httpserver .HttpsConfigurator ;
35
- import com .sun .net .httpserver .HttpsServer ;
36
37
import java .net .http .HttpClient ;
38
+ import java .util .concurrent .atomic .AtomicLong ;
37
39
import javax .net .ssl .SSLContext ;
38
- import jdk .httpclient .test .lib .http2 .Http2TestServer ;
39
- import jdk .httpclient .test .lib .http2 .Http2TestExchange ;
40
- import jdk .httpclient .test .lib .http2 .Http2Handler ;
40
+
41
+ import jdk .httpclient .test .lib .common .HttpServerAdapters ;
41
42
import jdk .test .lib .net .SimpleSSLContext ;
42
43
import org .testng .annotations .AfterTest ;
43
44
import org .testng .annotations .BeforeTest ;
44
45
import org .testng .annotations .DataProvider ;
45
46
46
- public abstract class AbstractNoBody {
47
+ import static java .lang .System .err ;
48
+ import static java .lang .System .out ;
49
+ import static java .net .http .HttpClient .Builder .NO_PROXY ;
50
+ import static java .net .http .HttpClient .Version .HTTP_1_1 ;
51
+ import static java .net .http .HttpClient .Version .HTTP_2 ;
52
+ import static org .testng .Assert .assertEquals ;
53
+
54
+ public abstract class AbstractNoBody implements HttpServerAdapters {
47
55
48
56
SSLContext sslContext ;
49
- HttpServer httpTestServer ; // HTTP/1.1 [ 4 servers ]
50
- HttpsServer httpsTestServer ; // HTTPS/1.1
51
- Http2TestServer http2TestServer ; // HTTP/2 ( h2c )
52
- Http2TestServer https2TestServer ; // HTTP/2 ( h2 )
57
+ HttpTestServer httpTestServer ; // HTTP/1.1 [ 4 servers ]
58
+ HttpTestServer httpsTestServer ; // HTTPS/1.1
59
+ HttpTestServer http2TestServer ; // HTTP/2 ( h2c )
60
+ HttpTestServer https2TestServer ; // HTTP/2 ( h2 )
53
61
String httpURI_fixed ;
54
62
String httpURI_chunk ;
55
63
String httpsURI_fixed ;
@@ -62,8 +70,17 @@ public abstract class AbstractNoBody {
62
70
static final String SIMPLE_STRING = "Hello world. Goodbye world" ;
63
71
static final int ITERATION_COUNT = 3 ;
64
72
// a shared executor helps reduce the amount of threads created by the test
65
- static final Executor executor = Executors .newFixedThreadPool (ITERATION_COUNT * 2 );
73
+ static final ExecutorService executor = Executors .newFixedThreadPool (ITERATION_COUNT * 2 );
66
74
static final ExecutorService serverExecutor = Executors .newFixedThreadPool (ITERATION_COUNT * 4 );
75
+ static final AtomicLong clientCount = new AtomicLong ();
76
+ static final long start = System .nanoTime ();
77
+ public static String now () {
78
+ long now = System .nanoTime () - start ;
79
+ long secs = now / 1000_000_000 ;
80
+ long mill = (now % 1000_000_000 ) / 1000_000 ;
81
+ long nan = now % 1000_000 ;
82
+ return String .format ("[%d s, %d ms, %d ns] " , secs , mill , nan );
83
+ }
67
84
68
85
@ DataProvider (name = "variants" )
69
86
public Object [][] variants () {
@@ -88,55 +105,86 @@ public Object[][] variants() {
88
105
};
89
106
}
90
107
91
- HttpClient newHttpClient () {
108
+ private volatile HttpClient sharedClient ;
109
+
110
+ static Version version (String uri ) {
111
+ if (uri .contains ("/http1/" ) || uri .contains ("/https1/" ))
112
+ return HTTP_1_1 ;
113
+ if (uri .contains ("/http2/" ) || uri .contains ("/https2/" ))
114
+ return HTTP_2 ;
115
+ return null ;
116
+ }
117
+
118
+ HttpRequest .Builder newRequestBuilder (String uri ) {
119
+ var builder = HttpRequest .newBuilder (URI .create (uri ));
120
+ return builder ;
121
+ }
122
+
123
+ private HttpClient makeNewClient () {
124
+ clientCount .incrementAndGet ();
92
125
return HttpClient .newBuilder ()
93
126
.executor (executor )
127
+ .proxy (NO_PROXY )
94
128
.sslContext (sslContext )
95
129
.build ();
96
130
}
97
131
98
- static String serverAuthority (HttpServer server ) {
99
- return InetAddress .getLoopbackAddress ().getHostName () + ":"
100
- + server .getAddress ().getPort ();
132
+ HttpClient newHttpClient (boolean share ) {
133
+ if (!share ) return makeNewClient ();
134
+ HttpClient shared = sharedClient ;
135
+ if (shared != null ) return shared ;
136
+ synchronized (this ) {
137
+ shared = sharedClient ;
138
+ if (shared == null ) {
139
+ shared = sharedClient = makeNewClient ();
140
+ }
141
+ return shared ;
142
+ }
143
+ }
144
+
145
+ record CloseableClient (HttpClient client , boolean shared )
146
+ implements Closeable {
147
+ public void close () {
148
+ if (shared ) return ;
149
+ client .close ();
150
+ }
101
151
}
102
152
103
153
@ BeforeTest
104
154
public void setup () throws Exception {
105
155
printStamp (START , "setup" );
156
+ HttpServerAdapters .enableServerLogging ();
106
157
sslContext = new SimpleSSLContext ().get ();
107
158
if (sslContext == null )
108
159
throw new AssertionError ("Unexpected null sslContext" );
109
160
110
161
// HTTP/1.1
111
- HttpHandler h1_fixedLengthNoBodyHandler = new HTTP1_FixedLengthNoBodyHandler ();
112
- HttpHandler h1_chunkNoBodyHandler = new HTTP1_ChunkedNoBodyHandler ();
113
- InetSocketAddress sa = new InetSocketAddress (InetAddress .getLoopbackAddress (), 0 );
114
- httpTestServer = HttpServer .create (sa , 0 );
115
- httpTestServer .setExecutor (serverExecutor );
116
- httpTestServer .createContext ("/http1/noBodyFixed" , h1_fixedLengthNoBodyHandler );
117
- httpTestServer .createContext ("/http1/noBodyChunk" , h1_chunkNoBodyHandler );
118
- httpURI_fixed = "http://" + serverAuthority (httpTestServer ) + "/http1/noBodyFixed" ;
119
- httpURI_chunk = "http://" + serverAuthority (httpTestServer ) + "/http1/noBodyChunk" ;
120
-
121
- httpsTestServer = HttpsServer .create (sa , 0 );
122
- httpsTestServer .setExecutor (serverExecutor );
123
- httpsTestServer .setHttpsConfigurator (new HttpsConfigurator (sslContext ));
124
- httpsTestServer .createContext ("/https1/noBodyFixed" , h1_fixedLengthNoBodyHandler );
125
- httpsTestServer .createContext ("/https1/noBodyChunk" , h1_chunkNoBodyHandler );
126
- httpsURI_fixed = "https://" + serverAuthority (httpsTestServer ) + "/https1/noBodyFixed" ;
127
- httpsURI_chunk = "https://" + serverAuthority (httpsTestServer ) + "/https1/noBodyChunk" ;
162
+ HttpTestHandler h1_fixedLengthNoBodyHandler = new FixedLengthNoBodyHandler ();
163
+ HttpTestHandler h1_chunkNoBodyHandler = new ChunkedNoBodyHandler ();
164
+
165
+ httpTestServer = HttpTestServer .create (HTTP_1_1 , null , serverExecutor );
166
+ httpTestServer .addHandler (h1_fixedLengthNoBodyHandler ,"/http1/noBodyFixed" );
167
+ httpTestServer .addHandler (h1_chunkNoBodyHandler , "/http1/noBodyChunk" );
168
+ httpURI_fixed = "http://" + httpTestServer .serverAuthority () + "/http1/noBodyFixed" ;
169
+ httpURI_chunk = "http://" + httpTestServer .serverAuthority () + "/http1/noBodyChunk" ;
170
+
171
+ httpsTestServer = HttpTestServer .create (HTTP_1_1 , sslContext , serverExecutor );
172
+ httpsTestServer .addHandler (h1_fixedLengthNoBodyHandler ,"/https1/noBodyFixed" );
173
+ httpsTestServer .addHandler (h1_chunkNoBodyHandler , "/https1/noBodyChunk" );
174
+ httpsURI_fixed = "https://" + httpsTestServer .serverAuthority () + "/https1/noBodyFixed" ;
175
+ httpsURI_chunk = "https://" + httpsTestServer .serverAuthority () + "/https1/noBodyChunk" ;
128
176
129
177
// HTTP/2
130
- Http2Handler h2_fixedLengthNoBodyHandler = new HTTP2_FixedLengthNoBodyHandler ();
131
- Http2Handler h2_chunkedNoBodyHandler = new HTTP2_ChunkedNoBodyHandler ();
178
+ HttpTestHandler h2_fixedLengthNoBodyHandler = new FixedLengthNoBodyHandler ();
179
+ HttpTestHandler h2_chunkedNoBodyHandler = new ChunkedNoBodyHandler ();
132
180
133
- http2TestServer = new Http2TestServer ( "localhost" , false , 0 , serverExecutor , null );
181
+ http2TestServer = HttpTestServer . create ( HTTP_2 , null , serverExecutor );
134
182
http2TestServer .addHandler (h2_fixedLengthNoBodyHandler , "/http2/noBodyFixed" );
135
183
http2TestServer .addHandler (h2_chunkedNoBodyHandler , "/http2/noBodyChunk" );
136
184
http2URI_fixed = "http://" + http2TestServer .serverAuthority () + "/http2/noBodyFixed" ;
137
185
http2URI_chunk = "http://" + http2TestServer .serverAuthority () + "/http2/noBodyChunk" ;
138
186
139
- https2TestServer = new Http2TestServer ( "localhost" , true , 0 , serverExecutor , sslContext );
187
+ https2TestServer = HttpTestServer . create ( HTTP_2 , sslContext , serverExecutor );
140
188
https2TestServer .addHandler (h2_fixedLengthNoBodyHandler , "/https2/noBodyFixed" );
141
189
https2TestServer .addHandler (h2_chunkedNoBodyHandler , "/https2/noBodyChunk" );
142
190
https2URI_fixed = "https://" + https2TestServer .serverAuthority () + "/https2/noBodyFixed" ;
@@ -146,77 +194,102 @@ public void setup() throws Exception {
146
194
httpsTestServer .start ();
147
195
http2TestServer .start ();
148
196
https2TestServer .start ();
197
+
198
+ var shared = newHttpClient (true );
199
+
200
+ out .println ("HTTP/1.1 server (http) listening at: " + httpTestServer .serverAuthority ());
201
+ out .println ("HTTP/1.1 server (TLS) listening at: " + httpsTestServer .serverAuthority ());
202
+ out .println ("HTTP/2 server (h2c) listening at: " + http2TestServer .serverAuthority ());
203
+ out .println ("HTTP/2 server (h2) listening at: " + https2TestServer .serverAuthority ());
204
+
205
+ out .println ("Shared client is: " + shared );
206
+
149
207
printStamp (END ,"setup" );
150
208
}
151
209
152
210
@ AfterTest
153
211
public void teardown () throws Exception {
154
212
printStamp (START , "teardown" );
155
- httpTestServer .stop (0 );
156
- httpsTestServer .stop (0 );
213
+ sharedClient .close ();
214
+ httpTestServer .stop ();
215
+ httpsTestServer .stop ();
157
216
http2TestServer .stop ();
158
217
https2TestServer .stop ();
218
+ executor .close ();
219
+ serverExecutor .close ();
159
220
printStamp (END , "teardown" );
160
221
}
161
222
162
- static final long start = System .nanoTime ();
163
223
static final String START = "start" ;
164
224
static final String END = "end " ;
165
- static long elapsed () { return (System .nanoTime () - start )/1000_000 ;}
166
225
void printStamp (String what , String fmt , Object ... args ) {
167
- long elapsed = elapsed ();
168
- long sec = elapsed /1000 ;
169
- long ms = elapsed % 1000 ;
170
- String time = sec > 0 ? sec + "sec " : "" ;
171
- time = time + ms + "ms" ;
172
226
System .out .printf ("%s: %s \t [%s]\t %s%n" ,
173
- getClass ().getSimpleName (), what , time , String .format (fmt ,args ));
227
+ getClass ().getSimpleName (), what , now () , String .format (fmt ,args ));
174
228
}
175
229
176
230
177
- static class HTTP1_FixedLengthNoBodyHandler implements HttpHandler {
231
+ static class FixedLengthNoBodyHandler implements HttpTestHandler {
178
232
@ Override
179
- public void handle (HttpExchange t ) throws IOException {
233
+ public void handle (HttpTestExchange t ) throws IOException {
180
234
//out.println("NoBodyHandler received request to " + t.getRequestURI());
235
+ boolean echo = "echo" .equals (t .getRequestURI ().getRawQuery ());
236
+ byte [] reqbytes ;
181
237
try (InputStream is = t .getRequestBody ()) {
182
- is .readAllBytes ();
238
+ reqbytes = is .readAllBytes ();
239
+ }
240
+ if (echo ) {
241
+ t .sendResponseHeaders (200 , reqbytes .length );
242
+ if (reqbytes .length > 0 ) {
243
+ try (var os = t .getResponseBody ()) {
244
+ os .write (reqbytes );
245
+ }
246
+ }
247
+ } else {
248
+ t .sendResponseHeaders (200 , 0 ); // no body
183
249
}
184
- t .sendResponseHeaders (200 , -1 ); // no body
185
250
}
186
251
}
187
252
188
- static class HTTP1_ChunkedNoBodyHandler implements HttpHandler {
253
+ static class ChunkedNoBodyHandler implements HttpTestHandler {
189
254
@ Override
190
- public void handle (HttpExchange t ) throws IOException {
255
+ public void handle (HttpTestExchange t ) throws IOException {
191
256
//out.println("NoBodyHandler received request to " + t.getRequestURI());
257
+ boolean echo = "echo" .equals (t .getRequestURI ().getRawQuery ());
258
+ byte [] reqbytes ;
192
259
try (InputStream is = t .getRequestBody ()) {
193
- is .readAllBytes ();
260
+ reqbytes = is .readAllBytes ();
261
+ }
262
+ if (echo ) {
263
+ t .sendResponseHeaders (200 , -1 );
264
+ try (var os = t .getResponseBody ()) {
265
+ os .write (reqbytes );
266
+ }
267
+ } else {
268
+ t .sendResponseHeaders (200 , -1 ); // chunked
269
+ t .getResponseBody ().close (); // write nothing
194
270
}
195
- t .sendResponseHeaders (200 , 0 ); // chunked
196
- t .getResponseBody ().close (); // write nothing
197
271
}
198
272
}
199
273
200
- static class HTTP2_FixedLengthNoBodyHandler implements Http2Handler {
201
- @ Override
202
- public void handle (Http2TestExchange t ) throws IOException {
203
- //out.println("NoBodyHandler received request to " + t.getRequestURI());
204
- try (InputStream is = t .getRequestBody ()) {
205
- is .readAllBytes ();
206
- }
207
- t .sendResponseHeaders (200 , 0 );
208
- }
274
+ /*
275
+ * Converts a ByteBuffer containing bytes encoded using
276
+ * the given charset into a string.
277
+ * This method does not throw but will replace
278
+ * unrecognized sequences with the replacement character.
279
+ */
280
+ public static String asString (ByteBuffer buffer , Charset charset ) {
281
+ var decoded = charset .decode (buffer );
282
+ char [] chars = new char [decoded .length ()];
283
+ decoded .get (chars );
284
+ return new String (chars );
209
285
}
210
286
211
- static class HTTP2_ChunkedNoBodyHandler implements Http2Handler {
212
- @ Override
213
- public void handle (Http2TestExchange t ) throws IOException {
214
- //out.println("NoBodyHandler received request to " + t.getRequestURI());
215
- try (InputStream is = t .getRequestBody ()) {
216
- is .readAllBytes ();
217
- }
218
- t .sendResponseHeaders (200 , -1 );
219
- t .getResponseBody ().close (); // write nothing
220
- }
287
+ /*
288
+ * Converts a ByteBuffer containing UTF-8 bytes into a
289
+ * string. This method does not throw but will replace
290
+ * unrecognized sequences with the replacement character.
291
+ */
292
+ public static String asString (ByteBuffer buffer ) {
293
+ return asString (buffer , StandardCharsets .UTF_8 );
221
294
}
222
295
}
0 commit comments