Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added batch support to the local DNS helper. #925

Merged
merged 5 commits into from
Apr 15, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions gcloud-java-dns/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>gcloud-java-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
import static java.net.HttpURLConnection.HTTP_OK;

import com.google.api.client.http.HttpMediaType;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.dns.model.Change;
Expand All @@ -43,13 +44,19 @@
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import org.apache.commons.fileupload.MultipartStream;
import org.joda.time.format.ISODateTimeFormat;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -138,7 +145,8 @@ private enum CallRegex {
ZONE_GET("GET", CONTEXT + "/[^/]+/managedZones/[^/]+"),
ZONE_LIST("GET", CONTEXT + "/[^/]+/managedZones"),
PROJECT_GET("GET", CONTEXT + "/[^/]+"),
RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets");
RECORD_LIST("GET", CONTEXT + "/[^/]+/managedZones/[^/]+/rrsets"),
BATCH("POST", "/batch");

This comment was marked as spam.

This comment was marked as spam.


private final String method;
private final String pathRegex;
Expand Down Expand Up @@ -273,13 +281,18 @@ private String toJson(String message) throws IOException {
private class RequestHandler implements HttpHandler {

private Response pickHandler(HttpExchange exchange, CallRegex regex) {
URI relative = BASE_CONTEXT.relativize(exchange.getRequestURI());
URI relative = null;
try {
relative = BASE_CONTEXT.relativize(new URI(exchange.getRequestURI().getRawPath()));

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

} catch (URISyntaxException e) {
return Error.INTERNAL_ERROR.response("Parsing URI failed.");
}
String path = relative.getPath();
String[] tokens = path.split("/");
String projectId = tokens.length > 0 ? tokens[0] : null;
String zoneName = tokens.length > 2 ? tokens[2] : null;
String changeId = tokens.length > 4 ? tokens[4] : null;
String query = relative.getQuery();
String query = exchange.getRequestURI().getQuery();
switch (regex) {
case CHANGE_GET:
return getChange(projectId, zoneName, changeId, query);
Expand Down Expand Up @@ -307,6 +320,13 @@ private Response pickHandler(HttpExchange exchange, CallRegex regex) {
} catch (IOException ex) {
return Error.BAD_REQUEST.response(ex.getMessage());
}
case BATCH:
try {
return handleBatch(exchange);
} catch (IOException ex) {
ex.printStackTrace();

This comment was marked as spam.

This comment was marked as spam.

return Error.BAD_REQUEST.response(ex.getMessage());
}
default:
return Error.INTERNAL_ERROR.response("Operation without a handler.");
}
Expand All @@ -319,7 +339,11 @@ public void handle(HttpExchange exchange) throws IOException {
for (CallRegex regex : CallRegex.values()) {
if (requestMethod.equals(regex.method) && rawPath.matches(regex.pathRegex)) {
Response response = pickHandler(exchange, regex);
writeResponse(exchange, response);
if (response != null) {
/* null response is returned by batch request, because it handles writing
the response on its own */
writeResponse(exchange, response);
}
return;
}
}
Expand All @@ -328,6 +352,71 @@ public void handle(HttpExchange exchange) throws IOException {
requestMethod, exchange.getRequestURI())));
}

private Response handleBatch(final HttpExchange exchange) throws IOException {
String contentType = exchange.getRequestHeaders().getFirst("Content-type");
if (contentType != null) {

This comment was marked as spam.

This comment was marked as spam.

int port = server.getAddress().getPort();
String responseBoundary = "____THIS_IS_HELPERS_BOUNDARY____";

This comment was marked as spam.

This comment was marked as spam.

String responseSeparator = new StringBuilder("--")
.append(responseBoundary)
.append("\r\n")
.toString();
String responseEnd = new StringBuilder("--")
.append(responseBoundary)
.append("--\r\n\r\n")
.toString();
HttpMediaType httpMediaType = new HttpMediaType(contentType);
String boundary = httpMediaType.getParameter("boundary");
MultipartStream multipartStream =
new MultipartStream(exchange.getRequestBody(), boundary.getBytes(), 1024, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
multipartStream.skipPreamble();
while (multipartStream.readBoundary()) {
Socket socket = new Socket("localhost", port);
OutputStream socketOutput = socket.getOutputStream();
ByteArrayOutputStream section = new ByteArrayOutputStream();
multipartStream.readBodyData(section);
BufferedReader reader = new BufferedReader(

This comment was marked as spam.

This comment was marked as spam.

new InputStreamReader(new ByteArrayInputStream(section.toByteArray())));
String line;
String contentId = null;
while (!(line = reader.readLine()).isEmpty()) {
if (line.toLowerCase().startsWith("content-id")) {
contentId = line.split(":")[1].trim();
}
}
String requestLine = reader.readLine();
socketOutput.write((requestLine + " \r\n").getBytes());

This comment was marked as spam.

This comment was marked as spam.

socketOutput.write("Connection: close \r\n".getBytes());
while ((line = reader.readLine()) != null) {
socketOutput.write(line.getBytes());
if (!line.isEmpty()) {
socketOutput.write(" \r\n".getBytes());
} else {
socketOutput.write("\r\n".getBytes());
}
}
socketOutput.flush();
InputStream in = socket.getInputStream();
int length;
out.write(responseSeparator.getBytes());
out.write("Content-Type: application/http \r\n".getBytes());
out.write(("Content-ID: " + contentId + " \r\n\r\n").getBytes());
try {
while ((length = in.read(bytes)) != -1) {
out.write(bytes, 0, length);
}
} catch (IOException ex) {
// this handles connection reset error

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

}
}
out.write(responseEnd.getBytes());
writeBatchResponse(exchange, out, responseBoundary);
}
return null;
}

/**
* @throws IOException if the request cannot be parsed.
*/
Expand Down Expand Up @@ -368,7 +457,8 @@ private LocalDnsHelper(long delay) {
try {
server = HttpServer.create(new InetSocketAddress(0), 0);
port = server.getAddress().getPort();
server.createContext(CONTEXT, new RequestHandler());
server.setExecutor(Executors.newCachedThreadPool());
server.createContext("/", new RequestHandler());
} catch (IOException e) {
throw new RuntimeException("Could not bind the mock DNS server.", e);
}
Expand Down Expand Up @@ -430,6 +520,21 @@ private static void writeResponse(HttpExchange exchange, Response response) {
}
}

private static void writeBatchResponse(HttpExchange exchange, ByteArrayOutputStream out,
String boundary) {
exchange.getResponseHeaders().set("Content-type", "multipart/mixed; boundary=" + boundary);
try {
exchange.getResponseHeaders().add("Connection", "close");
exchange.getResponseHeaders().add("Connection", "close");

This comment was marked as spam.

This comment was marked as spam.

exchange.sendResponseHeaders(200, out.toByteArray().length);
OutputStream responseBody = exchange.getResponseBody();
out.writeTo(responseBody);
responseBody.close();
} catch (IOException e) {
log.log(Level.WARNING, "IOException encountered when sending response.", e);
}
}

private static String decodeContent(Headers headers, InputStream inputStream) throws IOException {
List<String> contentEncoding = headers.get("Content-encoding");
InputStream input = inputStream;
Expand Down
Loading