20
20
import static java .net .HttpURLConnection .HTTP_NO_CONTENT ;
21
21
import static java .net .HttpURLConnection .HTTP_OK ;
22
22
23
+ import com .google .api .client .http .HttpMediaType ;
23
24
import com .google .api .client .json .JsonFactory ;
24
25
import com .google .api .client .json .jackson .JacksonFactory ;
25
26
import com .google .api .services .dns .model .Change ;
43
44
import com .sun .net .httpserver .HttpHandler ;
44
45
import com .sun .net .httpserver .HttpServer ;
45
46
47
+ import org .apache .commons .fileupload .MultipartStream ;
46
48
import org .joda .time .format .ISODateTimeFormat ;
47
49
50
+ import java .io .BufferedReader ;
51
+ import java .io .ByteArrayInputStream ;
52
+ import java .io .ByteArrayOutputStream ;
48
53
import java .io .IOException ;
49
54
import java .io .InputStream ;
55
+ import java .io .InputStreamReader ;
50
56
import java .io .OutputStream ;
51
57
import java .math .BigInteger ;
52
58
import java .net .InetSocketAddress ;
59
+ import java .net .Socket ;
53
60
import java .net .URI ;
54
61
import java .net .URISyntaxException ;
55
62
import java .nio .charset .StandardCharsets ;
@@ -138,7 +145,8 @@ private enum CallRegex {
138
145
ZONE_GET ("GET" , CONTEXT + "/[^/]+/managedZones/[^/]+" ),
139
146
ZONE_LIST ("GET" , CONTEXT + "/[^/]+/managedZones" ),
140
147
PROJECT_GET ("GET" , CONTEXT + "/[^/]+" ),
141
- RECORD_LIST ("GET" , CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets" );
148
+ RECORD_LIST ("GET" , CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets" ),
149
+ BATCH ("POST" , "/batch" );
142
150
143
151
private final String method ;
144
152
private final String pathRegex ;
@@ -273,13 +281,18 @@ private String toJson(String message) throws IOException {
273
281
private class RequestHandler implements HttpHandler {
274
282
275
283
private Response pickHandler (HttpExchange exchange , CallRegex regex ) {
276
- URI relative = BASE_CONTEXT .relativize (exchange .getRequestURI ());
284
+ URI relative = null ;
285
+ try {
286
+ relative = BASE_CONTEXT .relativize (new URI (exchange .getRequestURI ().getRawPath ()));
287
+ } catch (URISyntaxException e ) {
288
+ return Error .INTERNAL_ERROR .response ("Parsing URI failed." );
289
+ }
277
290
String path = relative .getPath ();
278
291
String [] tokens = path .split ("/" );
279
292
String projectId = tokens .length > 0 ? tokens [0 ] : null ;
280
293
String zoneName = tokens .length > 2 ? tokens [2 ] : null ;
281
294
String changeId = tokens .length > 4 ? tokens [4 ] : null ;
282
- String query = relative .getQuery ();
295
+ String query = exchange . getRequestURI () .getQuery ();
283
296
switch (regex ) {
284
297
case CHANGE_GET :
285
298
return getChange (projectId , zoneName , changeId , query );
@@ -307,6 +320,13 @@ private Response pickHandler(HttpExchange exchange, CallRegex regex) {
307
320
} catch (IOException ex ) {
308
321
return Error .BAD_REQUEST .response (ex .getMessage ());
309
322
}
323
+ case BATCH :
324
+ try {
325
+ return handleBatch (exchange );
326
+ } catch (IOException ex ) {
327
+ ex .printStackTrace ();
328
+ return Error .BAD_REQUEST .response (ex .getMessage ());
329
+ }
310
330
default :
311
331
return Error .INTERNAL_ERROR .response ("Operation without a handler." );
312
332
}
@@ -319,7 +339,11 @@ public void handle(HttpExchange exchange) throws IOException {
319
339
for (CallRegex regex : CallRegex .values ()) {
320
340
if (requestMethod .equals (regex .method ) && rawPath .matches (regex .pathRegex )) {
321
341
Response response = pickHandler (exchange , regex );
322
- writeResponse (exchange , response );
342
+ if (response != null ) {
343
+ /* null response is returned by batch request, because it handles writing
344
+ the response on its own */
345
+ writeResponse (exchange , response );
346
+ }
323
347
return ;
324
348
}
325
349
}
@@ -328,6 +352,71 @@ public void handle(HttpExchange exchange) throws IOException {
328
352
requestMethod , exchange .getRequestURI ())));
329
353
}
330
354
355
+ private Response handleBatch (final HttpExchange exchange ) throws IOException {
356
+ String contentType = exchange .getRequestHeaders ().getFirst ("Content-type" );
357
+ if (contentType != null ) {
358
+ int port = server .getAddress ().getPort ();
359
+ String responseBoundary = "____THIS_IS_HELPERS_BOUNDARY____" ;
360
+ String responseSeparator = new StringBuilder ("--" )
361
+ .append (responseBoundary )
362
+ .append ("\r \n " )
363
+ .toString ();
364
+ String responseEnd = new StringBuilder ("--" )
365
+ .append (responseBoundary )
366
+ .append ("--\r \n \r \n " )
367
+ .toString ();
368
+ HttpMediaType httpMediaType = new HttpMediaType (contentType );
369
+ String boundary = httpMediaType .getParameter ("boundary" );
370
+ MultipartStream multipartStream =
371
+ new MultipartStream (exchange .getRequestBody (), boundary .getBytes (), 1024 , null );
372
+ ByteArrayOutputStream out = new ByteArrayOutputStream ();
373
+ byte [] bytes = new byte [1024 ];
374
+ multipartStream .skipPreamble ();
375
+ while (multipartStream .readBoundary ()) {
376
+ Socket socket = new Socket ("localhost" , port );
377
+ OutputStream socketOutput = socket .getOutputStream ();
378
+ ByteArrayOutputStream section = new ByteArrayOutputStream ();
379
+ multipartStream .readBodyData (section );
380
+ BufferedReader reader = new BufferedReader (
381
+ new InputStreamReader (new ByteArrayInputStream (section .toByteArray ())));
382
+ String line ;
383
+ String contentId = null ;
384
+ while (!(line = reader .readLine ()).isEmpty ()) {
385
+ if (line .toLowerCase ().startsWith ("content-id" )) {
386
+ contentId = line .split (":" )[1 ].trim ();
387
+ }
388
+ }
389
+ String requestLine = reader .readLine ();
390
+ socketOutput .write ((requestLine + " \r \n " ).getBytes ());
391
+ socketOutput .write ("Connection: close \r \n " .getBytes ());
392
+ while ((line = reader .readLine ()) != null ) {
393
+ socketOutput .write (line .getBytes ());
394
+ if (!line .isEmpty ()) {
395
+ socketOutput .write (" \r \n " .getBytes ());
396
+ } else {
397
+ socketOutput .write ("\r \n " .getBytes ());
398
+ }
399
+ }
400
+ socketOutput .flush ();
401
+ InputStream in = socket .getInputStream ();
402
+ int length ;
403
+ out .write (responseSeparator .getBytes ());
404
+ out .write ("Content-Type: application/http \r \n " .getBytes ());
405
+ out .write (("Content-ID: " + contentId + " \r \n \r \n " ).getBytes ());
406
+ try {
407
+ while ((length = in .read (bytes )) != -1 ) {
408
+ out .write (bytes , 0 , length );
409
+ }
410
+ } catch (IOException ex ) {
411
+ // this handles connection reset error
412
+ }
413
+ }
414
+ out .write (responseEnd .getBytes ());
415
+ writeBatchResponse (exchange , out , responseBoundary );
416
+ }
417
+ return null ;
418
+ }
419
+
331
420
/**
332
421
* @throws IOException if the request cannot be parsed.
333
422
*/
@@ -368,7 +457,8 @@ private LocalDnsHelper(long delay) {
368
457
try {
369
458
server = HttpServer .create (new InetSocketAddress (0 ), 0 );
370
459
port = server .getAddress ().getPort ();
371
- server .createContext (CONTEXT , new RequestHandler ());
460
+ server .setExecutor (Executors .newCachedThreadPool ());
461
+ server .createContext ("/" , new RequestHandler ());
372
462
} catch (IOException e ) {
373
463
throw new RuntimeException ("Could not bind the mock DNS server." , e );
374
464
}
@@ -430,6 +520,21 @@ private static void writeResponse(HttpExchange exchange, Response response) {
430
520
}
431
521
}
432
522
523
+ private static void writeBatchResponse (HttpExchange exchange , ByteArrayOutputStream out ,
524
+ String boundary ) {
525
+ exchange .getResponseHeaders ().set ("Content-type" , "multipart/mixed; boundary=" + boundary );
526
+ try {
527
+ exchange .getResponseHeaders ().add ("Connection" , "close" );
528
+ exchange .getResponseHeaders ().add ("Connection" , "close" );
529
+ exchange .sendResponseHeaders (200 , out .toByteArray ().length );
530
+ OutputStream responseBody = exchange .getResponseBody ();
531
+ out .writeTo (responseBody );
532
+ responseBody .close ();
533
+ } catch (IOException e ) {
534
+ log .log (Level .WARNING , "IOException encountered when sending response." , e );
535
+ }
536
+ }
537
+
433
538
private static String decodeContent (Headers headers , InputStream inputStream ) throws IOException {
434
539
List <String > contentEncoding = headers .get ("Content-encoding" );
435
540
InputStream input = inputStream ;
0 commit comments