Skip to content

Commit e63bc53

Browse files
mderkaaozarov
authored andcommitted
Added batch support to the local DNS helper. (#925)
Added batch support to the local DNS helper
1 parent 8ec2027 commit e63bc53

File tree

3 files changed

+1184
-6
lines changed

3 files changed

+1184
-6
lines changed

gcloud-java-dns/pom.xml

+5
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

+105-5
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,16 @@
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.ByteArrayOutputStream;
4851
import java.io.IOException;
4952
import java.io.InputStream;
5053
import java.io.OutputStream;
5154
import java.math.BigInteger;
5255
import java.net.InetSocketAddress;
56+
import java.net.Socket;
5357
import java.net.URI;
5458
import java.net.URISyntaxException;
5559
import java.nio.charset.StandardCharsets;
@@ -62,6 +66,7 @@
6266
import java.util.NavigableMap;
6367
import java.util.NavigableSet;
6468
import java.util.Random;
69+
import java.util.Scanner;
6570
import java.util.Set;
6671
import java.util.SortedMap;
6772
import java.util.TreeMap;
@@ -112,6 +117,9 @@ public class LocalDnsHelper {
112117
private static final ScheduledExecutorService EXECUTORS =
113118
Executors.newScheduledThreadPool(2, Executors.defaultThreadFactory());
114119
private static final String PROJECT_ID = "dummyprojectid";
120+
private static final String RESPONSE_BOUNDARY = "____THIS_IS_HELPERS_BOUNDARY____";
121+
private static final String RESPONSE_SEPARATOR = "--" + RESPONSE_BOUNDARY + "\r\n";
122+
private static final String RESPONSE_END = "--" + RESPONSE_BOUNDARY + "--\r\n\r\n";
115123

116124
static {
117125
try {
@@ -138,7 +146,8 @@ private enum CallRegex {
138146
ZONE_GET("GET", CONTEXT + "/[^/]+/managedZones/[^/]+"),
139147
ZONE_LIST("GET", CONTEXT + "/[^/]+/managedZones"),
140148
PROJECT_GET("GET", CONTEXT + "/[^/]+"),
141-
RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets");
149+
RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets"),
150+
BATCH("POST", "/batch");
142151

143152
private final String method;
144153
private final String pathRegex;
@@ -273,13 +282,18 @@ private String toJson(String message) throws IOException {
273282
private class RequestHandler implements HttpHandler {
274283

275284
private Response pickHandler(HttpExchange exchange, CallRegex regex) {
276-
URI relative = BASE_CONTEXT.relativize(exchange.getRequestURI());
285+
URI relative = null;
286+
try {
287+
relative = BASE_CONTEXT.relativize(new URI(exchange.getRequestURI().getRawPath()));
288+
} catch (URISyntaxException e) {
289+
return Error.INTERNAL_ERROR.response("Parsing URI failed.");
290+
}
277291
String path = relative.getPath();
278292
String[] tokens = path.split("/");
279293
String projectId = tokens.length > 0 ? tokens[0] : null;
280294
String zoneName = tokens.length > 2 ? tokens[2] : null;
281295
String changeId = tokens.length > 4 ? tokens[4] : null;
282-
String query = relative.getQuery();
296+
String query = exchange.getRequestURI().getQuery();
283297
switch (regex) {
284298
case CHANGE_GET:
285299
return getChange(projectId, zoneName, changeId, query);
@@ -307,6 +321,12 @@ private Response pickHandler(HttpExchange exchange, CallRegex regex) {
307321
} catch (IOException ex) {
308322
return Error.BAD_REQUEST.response(ex.getMessage());
309323
}
324+
case BATCH:
325+
try {
326+
return handleBatch(exchange);
327+
} catch (IOException ex) {
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,67 @@ 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+
HttpMediaType httpMediaType = new HttpMediaType(contentType);
360+
String boundary = httpMediaType.getParameter("boundary");
361+
MultipartStream multipartStream =
362+
new MultipartStream(exchange.getRequestBody(), boundary.getBytes(), 1024, null);
363+
ByteArrayOutputStream out = new ByteArrayOutputStream();
364+
byte[] bytes = new byte[1024];
365+
multipartStream.skipPreamble();
366+
while (multipartStream.readBoundary()) {
367+
Socket socket = new Socket("localhost", port);
368+
OutputStream socketOutput = socket.getOutputStream();
369+
ByteArrayOutputStream section = new ByteArrayOutputStream();
370+
multipartStream.readBodyData(section);
371+
String line;
372+
String contentId = null;
373+
Scanner scanner = new Scanner(new String(section.toByteArray()));
374+
while (scanner.hasNextLine()) {
375+
line = scanner.nextLine();
376+
if(line.isEmpty()) {
377+
break;
378+
} else if (line.toLowerCase().startsWith("content-id")) {
379+
contentId = line.split(":")[1].trim();
380+
}
381+
}
382+
String requestLine = scanner.nextLine();
383+
socketOutput.write((requestLine + " \r\n").getBytes());
384+
socketOutput.write("Connection: close \r\n".getBytes());
385+
while(scanner.hasNextLine()) {
386+
line = scanner.nextLine();
387+
socketOutput.write(line.getBytes());
388+
if (!line.isEmpty()) {
389+
socketOutput.write(" \r\n".getBytes());
390+
} else {
391+
socketOutput.write("\r\n".getBytes());
392+
}
393+
}
394+
socketOutput.flush();
395+
InputStream in = socket.getInputStream();
396+
int length;
397+
out.write(RESPONSE_SEPARATOR.getBytes());
398+
out.write("Content-Type: application/http \r\n".getBytes());
399+
out.write(("Content-ID: " + contentId + " \r\n\r\n").getBytes());
400+
try {
401+
while ((length = in.read(bytes)) != -1) {
402+
out.write(bytes, 0, length);
403+
}
404+
} catch (IOException ex) {
405+
// this handles connection reset error
406+
}
407+
}
408+
out.write(RESPONSE_END.getBytes());
409+
writeBatchResponse(exchange, out);
410+
} else {
411+
return Error.BAD_REQUEST.response("Content-type header was not provided for batch.");
412+
}
413+
return null;
414+
}
415+
331416
/**
332417
* @throws IOException if the request cannot be parsed.
333418
*/
@@ -368,7 +453,8 @@ private LocalDnsHelper(long delay) {
368453
try {
369454
server = HttpServer.create(new InetSocketAddress(0), 0);
370455
port = server.getAddress().getPort();
371-
server.createContext(CONTEXT, new RequestHandler());
456+
server.setExecutor(Executors.newCachedThreadPool());
457+
server.createContext("/", new RequestHandler());
372458
} catch (IOException e) {
373459
throw new RuntimeException("Could not bind the mock DNS server.", e);
374460
}
@@ -430,6 +516,20 @@ private static void writeResponse(HttpExchange exchange, Response response) {
430516
}
431517
}
432518

519+
private static void writeBatchResponse(HttpExchange exchange, ByteArrayOutputStream output) {
520+
exchange.getResponseHeaders().set(
521+
"Content-type", "multipart/mixed; boundary=" + RESPONSE_BOUNDARY);
522+
try {
523+
exchange.getResponseHeaders().add("Connection", "close");
524+
exchange.sendResponseHeaders(200, output.toByteArray().length);
525+
OutputStream responseBody = exchange.getResponseBody();
526+
output.writeTo(responseBody);
527+
responseBody.close();
528+
} catch (IOException e) {
529+
log.log(Level.WARNING, "IOException encountered when sending response.", e);
530+
}
531+
}
532+
433533
private static String decodeContent(Headers headers, InputStream inputStream) throws IOException {
434534
List<String> contentEncoding = headers.get("Content-encoding");
435535
InputStream input = inputStream;

0 commit comments

Comments
 (0)