1 /* |
1 /* |
2 * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
5 * This code is free software; you can redistribute it and/or modify it |
6 * under the terms of the GNU General Public License version 2 only, as |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. |
7 * published by the Free Software Foundation. |
27 import java.io.UncheckedIOException; |
27 import java.io.UncheckedIOException; |
28 import java.net.InetAddress; |
28 import java.net.InetAddress; |
29 import java.net.InetSocketAddress; |
29 import java.net.InetSocketAddress; |
30 import java.net.ServerSocket; |
30 import java.net.ServerSocket; |
31 import java.net.Socket; |
31 import java.net.Socket; |
|
32 import java.net.SocketException; |
32 import java.net.URI; |
33 import java.net.URI; |
33 import java.net.http.HttpClient; |
34 import java.net.http.HttpClient; |
34 import java.net.http.HttpRequest; |
35 import java.net.http.HttpRequest; |
35 import java.net.http.HttpRequest.BodyPublishers; |
36 import java.net.http.HttpRequest.BodyPublishers; |
36 import java.net.http.HttpResponse; |
37 import java.net.http.HttpResponse; |
74 // between client and server easier. |
75 // between client and server easier. |
75 static final int[] BODY_LENGTHS = new int[] { STRING_BODY.length(), |
76 static final int[] BODY_LENGTHS = new int[] { STRING_BODY.length(), |
76 BYTE_ARRAY_BODY.length, |
77 BYTE_ARRAY_BODY.length, |
77 fileSize(FILE_BODY) }; |
78 fileSize(FILE_BODY) }; |
78 static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 }; |
79 static final int[] BODY_OFFSETS = new int[] { 0, +1, -1, +2, -2, +3, -3 }; |
|
80 static final String MARKER = "ShortRequestBody"; |
79 |
81 |
80 // A delegating Body Publisher. Subtypes will have a concrete body type. |
82 // A delegating Body Publisher. Subtypes will have a concrete body type. |
81 static abstract class AbstractDelegateRequestBody |
83 static abstract class AbstractDelegateRequestBody |
82 implements HttpRequest.BodyPublisher { |
84 implements HttpRequest.BodyPublisher { |
83 |
85 |
132 clientSuppliers.add(() -> sharedClient); |
134 clientSuppliers.add(() -> sharedClient); |
133 |
135 |
134 try (Server server = new Server()) { |
136 try (Server server = new Server()) { |
135 for (Supplier<HttpClient> cs : clientSuppliers) { |
137 for (Supplier<HttpClient> cs : clientSuppliers) { |
136 err.println("\n---- next supplier ----\n"); |
138 err.println("\n---- next supplier ----\n"); |
137 URI uri = new URI("http://localhost:" + server.getPort() + "/"); |
139 URI uri = new URI("http://localhost:" + server.getPort() + "/" + MARKER); |
138 |
140 |
139 // sanity ( 6 requests to keep client and server offsets easy to workout ) |
141 // sanity ( 6 requests to keep client and server offsets easy to workout ) |
140 success(cs, uri, new StringRequestBody(STRING_BODY, 0)); |
142 success(cs, uri, new StringRequestBody(STRING_BODY, 0)); |
141 success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0)); |
143 success(cs, uri, new ByteArrayRequestBody(BYTE_ARRAY_BODY, 0)); |
142 success(cs, uri, new FileRequestBody(FILE_BODY, 0)); |
144 success(cs, uri, new FileRequestBody(FILE_BODY, 0)); |
246 public void run() { |
248 public void run() { |
247 int count = 0; |
249 int count = 0; |
248 int offset = 0; |
250 int offset = 0; |
249 |
251 |
250 while (!closed) { |
252 while (!closed) { |
|
253 err.println("Server: waiting for connection"); |
251 try (Socket s = ss.accept()) { |
254 try (Socket s = ss.accept()) { |
252 err.println("Server: got connection"); |
255 err.println("Server: got connection"); |
253 InputStream is = s.getInputStream(); |
256 InputStream is = s.getInputStream(); |
254 readRequestHeaders(is); |
257 try { |
|
258 String headers = readRequestHeaders(is); |
|
259 if (headers == null) continue; |
|
260 } catch (SocketException ex) { |
|
261 err.println("Ignoring unexpected exception while reading headers: " + ex); |
|
262 ex.printStackTrace(err); |
|
263 // proceed in order to update count etc..., even though |
|
264 // we know that read() will fail; |
|
265 } |
255 byte[] ba = new byte[1024]; |
266 byte[] ba = new byte[1024]; |
256 |
267 |
257 int length = BODY_LENGTHS[count % 3]; |
268 int length = BODY_LENGTHS[count % 3]; |
258 length += BODY_OFFSETS[offset]; |
269 length += BODY_OFFSETS[offset]; |
259 err.println("Server: count=" + count + ", offset=" + offset); |
270 err.println("Server: count=" + count + ", offset=" + offset); |
260 err.println("Server: expecting " +length+ " bytes"); |
271 err.println("Server: expecting " +length+ " bytes"); |
261 int read = is.readNBytes(ba, 0, length); |
272 int read = 0; |
262 err.println("Server: actually read " + read + " bytes"); |
273 try { |
263 |
274 read = is.readNBytes(ba, 0, length); |
264 // Update the counts before replying, to prevent the |
275 err.println("Server: actually read " + read + " bytes"); |
265 // client-side racing reset with this thread. |
276 } finally { |
266 count++; |
277 // Update the counts before replying, to prevent the |
267 if (count % 6 == 0) // 6 is the number of failure requests per offset |
278 // client-side racing reset with this thread. |
268 offset++; |
279 count++; |
269 if (count % 42 == 0) { |
280 if (count % 6 == 0) // 6 is the number of failure requests per offset |
270 count = 0; // reset, for second iteration |
281 offset++; |
271 offset = 0; |
282 if (count % 42 == 0) { |
|
283 count = 0; // reset, for second iteration |
|
284 offset = 0; |
|
285 } |
272 } |
286 } |
273 |
|
274 if (read < length) { |
287 if (read < length) { |
275 // no need to reply, client has already closed |
288 // no need to reply, client has already closed |
276 // ensure closed |
289 // ensure closed |
277 if (is.read() != -1) |
290 if (is.read() != -1) |
278 new AssertionError("Unexpected read"); |
291 new AssertionError("Unexpected read: " + read); |
279 } else { |
292 } else { |
280 OutputStream os = s.getOutputStream(); |
293 OutputStream os = s.getOutputStream(); |
281 err.println("Server: writing " |
294 err.println("Server: writing " |
282 + RESPONSE.getBytes(US_ASCII).length + " bytes"); |
295 + RESPONSE.getBytes(US_ASCII).length + " bytes"); |
283 os.write(RESPONSE.getBytes(US_ASCII)); |
296 os.write(RESPONSE.getBytes(US_ASCII)); |
284 } |
297 } |
285 |
298 } catch (Throwable e) { |
286 } catch (IOException e) { |
299 if (!closed) { |
287 if (!closed) |
300 err.println("Unexpected: " + e); |
288 System.out.println("Unexpected" + e); |
301 e.printStackTrace(); |
|
302 } |
289 } |
303 } |
290 } |
304 } |
291 } |
305 } |
292 |
306 |
293 @Override |
307 @Override |
304 } |
318 } |
305 |
319 |
306 static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' }; |
320 static final byte[] requestEnd = new byte[] {'\r', '\n', '\r', '\n' }; |
307 |
321 |
308 // Read until the end of a HTTP request headers |
322 // Read until the end of a HTTP request headers |
309 static void readRequestHeaders(InputStream is) throws IOException { |
323 static String readRequestHeaders(InputStream is) throws IOException { |
310 int requestEndCount = 0, r; |
324 int requestEndCount = 0, r, eol = -1; |
|
325 StringBuilder headers = new StringBuilder(); |
311 while ((r = is.read()) != -1) { |
326 while ((r = is.read()) != -1) { |
|
327 if (r == '\r' && eol < 0) { |
|
328 eol = headers.length(); |
|
329 } |
|
330 headers.append((char) r); |
312 if (r == requestEnd[requestEndCount]) { |
331 if (r == requestEnd[requestEndCount]) { |
313 requestEndCount++; |
332 requestEndCount++; |
314 if (requestEndCount == 4) { |
333 if (requestEndCount == 4) { |
315 break; |
334 break; |
316 } |
335 } |
317 } else { |
336 } else { |
318 requestEndCount = 0; |
337 requestEndCount = 0; |
319 } |
338 } |
320 } |
339 } |
|
340 |
|
341 if (eol <= 0) return null; |
|
342 String requestLine = headers.toString().substring(0, eol); |
|
343 if (!requestLine.contains(MARKER)) return null; |
|
344 return headers.toString(); |
321 } |
345 } |
322 |
346 |
323 static int fileSize(Path p) { |
347 static int fileSize(Path p) { |
324 try { return (int) Files.size(p); } |
348 try { return (int) Files.size(p); } |
325 catch (IOException x) { throw new UncheckedIOException(x); } |
349 catch (IOException x) { throw new UncheckedIOException(x); } |