Skip to content

Commit b783fe8

Browse files
committed
Added batch support to the local DNS helper.
1 parent beea588 commit b783fe8

File tree

3 files changed

+1528
-6
lines changed

3 files changed

+1528
-6
lines changed

gcloud-java-dns/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
</exclusion>
4141
</exclusions>
4242
</dependency>
43+
<dependency>
44+
<groupId>commons-fileupload</groupId>
45+
<artifactId>commons-fileupload</artifactId>
46+
<version>1.3.1</version>
47+
</dependency>
4348
<dependency>
4449
<groupId>${project.groupId}</groupId>
4550
<artifactId>gcloud-java-core</artifactId>

gcloud-java-dns/src/main/java/com/google/cloud/dns/testing/LocalDnsHelper.java

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
2121
import static java.net.HttpURLConnection.HTTP_OK;
2222

23+
import com.google.api.client.http.HttpMediaType;
2324
import com.google.api.client.json.JsonFactory;
2425
import com.google.api.client.json.jackson.JacksonFactory;
2526
import com.google.api.services.dns.model.Change;
@@ -43,13 +44,19 @@
4344
import com.sun.net.httpserver.HttpHandler;
4445
import com.sun.net.httpserver.HttpServer;
4546

47+
import org.apache.commons.fileupload.MultipartStream;
4648
import org.joda.time.format.ISODateTimeFormat;
4749

50+
import java.io.BufferedReader;
51+
import java.io.ByteArrayInputStream;
52+
import java.io.ByteArrayOutputStream;
4853
import java.io.IOException;
4954
import java.io.InputStream;
55+
import java.io.InputStreamReader;
5056
import java.io.OutputStream;
5157
import java.math.BigInteger;
5258
import java.net.InetSocketAddress;
59+
import java.net.Socket;
5360
import java.net.URI;
5461
import java.net.URISyntaxException;
5562
import java.nio.charset.StandardCharsets;
@@ -138,7 +145,8 @@ private enum CallRegex {
138145
ZONE_GET("GET", CONTEXT + "/[^/]+/managedZones/[^/]+"),
139146
ZONE_LIST("GET", CONTEXT + "/[^/]+/managedZones"),
140147
PROJECT_GET("GET", CONTEXT + "/[^/]+"),
141-
RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets");
148+
RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets"),
149+
BATCH("POST", "/batch");
142150

143151
private final String method;
144152
private final String pathRegex;
@@ -273,13 +281,18 @@ private String toJson(String message) throws IOException {
273281
private class RequestHandler implements HttpHandler {
274282

275283
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+
}
277290
String path = relative.getPath();
278291
String[] tokens = path.split("/");
279292
String projectId = tokens.length > 0 ? tokens[0] : null;
280293
String zoneName = tokens.length > 2 ? tokens[2] : null;
281294
String changeId = tokens.length > 4 ? tokens[4] : null;
282-
String query = relative.getQuery();
295+
String query = exchange.getRequestURI().getQuery();
283296
switch (regex) {
284297
case CHANGE_GET:
285298
return getChange(projectId, zoneName, changeId, query);
@@ -307,6 +320,13 @@ private Response pickHandler(HttpExchange exchange, CallRegex regex) {
307320
} catch (IOException ex) {
308321
return Error.BAD_REQUEST.response(ex.getMessage());
309322
}
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+
}
310330
default:
311331
return Error.INTERNAL_ERROR.response("Operation without a handler.");
312332
}
@@ -319,7 +339,11 @@ public void handle(HttpExchange exchange) throws IOException {
319339
for (CallRegex regex : CallRegex.values()) {
320340
if (requestMethod.equals(regex.method) && rawPath.matches(regex.pathRegex)) {
321341
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+
}
323347
return;
324348
}
325349
}
@@ -328,6 +352,71 @@ public void handle(HttpExchange exchange) throws IOException {
328352
requestMethod, exchange.getRequestURI())));
329353
}
330354

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+
331420
/**
332421
* @throws IOException if the request cannot be parsed.
333422
*/
@@ -368,7 +457,8 @@ private LocalDnsHelper(long delay) {
368457
try {
369458
server = HttpServer.create(new InetSocketAddress(0), 0);
370459
port = server.getAddress().getPort();
371-
server.createContext(CONTEXT, new RequestHandler());
460+
server.setExecutor(Executors.newCachedThreadPool());
461+
server.createContext("/", new RequestHandler());
372462
} catch (IOException e) {
373463
throw new RuntimeException("Could not bind the mock DNS server.", e);
374464
}
@@ -430,6 +520,21 @@ private static void writeResponse(HttpExchange exchange, Response response) {
430520
}
431521
}
432522

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+
433538
private static String decodeContent(Headers headers, InputStream inputStream) throws IOException {
434539
List<String> contentEncoding = headers.get("Content-encoding");
435540
InputStream input = inputStream;

0 commit comments

Comments
 (0)