Skip to content

Commit 6fc8d0b

Browse files
aozarovmziccard
authored andcommitted
Fix LocalDnsHelper Batch handling (#938)
1 parent e63bc53 commit 6fc8d0b

File tree

1 file changed

+58
-42
lines changed

1 file changed

+58
-42
lines changed

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

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.google.api.services.dns.model.Project;
2929
import com.google.api.services.dns.model.Quota;
3030
import com.google.api.services.dns.model.ResourceRecordSet;
31+
import com.google.cloud.AuthCredentials;
3132
import com.google.cloud.dns.DnsOptions;
3233
import com.google.common.annotations.VisibleForTesting;
3334
import com.google.common.base.Joiner;
@@ -38,7 +39,6 @@
3839
import com.google.common.collect.Lists;
3940
import com.google.common.collect.Sets;
4041
import com.google.common.io.ByteStreams;
41-
4242
import com.sun.net.httpserver.Headers;
4343
import com.sun.net.httpserver.HttpExchange;
4444
import com.sun.net.httpserver.HttpHandler;
@@ -282,18 +282,13 @@ private String toJson(String message) throws IOException {
282282
private class RequestHandler implements HttpHandler {
283283

284284
private Response pickHandler(HttpExchange exchange, CallRegex regex) {
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-
}
285+
URI relative = BASE_CONTEXT.relativize(exchange.getRequestURI());
291286
String path = relative.getPath();
292287
String[] tokens = path.split("/");
293288
String projectId = tokens.length > 0 ? tokens[0] : null;
294289
String zoneName = tokens.length > 2 ? tokens[2] : null;
295290
String changeId = tokens.length > 4 ? tokens[4] : null;
296-
String query = exchange.getRequestURI().getQuery();
291+
String query = relative.getQuery();
297292
switch (regex) {
298293
case CHANGE_GET:
299294
return getChange(projectId, zoneName, changeId, query);
@@ -324,7 +319,7 @@ private Response pickHandler(HttpExchange exchange, CallRegex regex) {
324319
case BATCH:
325320
try {
326321
return handleBatch(exchange);
327-
} catch (IOException ex) {
322+
} catch (IOException | URISyntaxException ex) {
328323
return Error.BAD_REQUEST.response(ex.getMessage());
329324
}
330325
default:
@@ -352,58 +347,75 @@ public void handle(HttpExchange exchange) throws IOException {
352347
requestMethod, exchange.getRequestURI())));
353348
}
354349

355-
private Response handleBatch(final HttpExchange exchange) throws IOException {
350+
private Response handleBatch(final HttpExchange exchange) throws IOException,
351+
URISyntaxException {
356352
String contentType = exchange.getRequestHeaders().getFirst("Content-type");
357353
if (contentType != null) {
358-
int port = server.getAddress().getPort();
359354
HttpMediaType httpMediaType = new HttpMediaType(contentType);
360355
String boundary = httpMediaType.getParameter("boundary");
361356
MultipartStream multipartStream =
362357
new MultipartStream(exchange.getRequestBody(), boundary.getBytes(), 1024, null);
363358
ByteArrayOutputStream out = new ByteArrayOutputStream();
364359
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);
360+
boolean nextPart = multipartStream.skipPreamble();
361+
while (nextPart) {
371362
String line;
372363
String contentId = null;
373-
Scanner scanner = new Scanner(new String(section.toByteArray()));
364+
String headers = multipartStream.readHeaders();
365+
Scanner scanner = new Scanner(headers);
374366
while (scanner.hasNextLine()) {
375367
line = scanner.nextLine();
376-
if(line.isEmpty()) {
377-
break;
378-
} else if (line.toLowerCase().startsWith("content-id")) {
368+
if (line.toLowerCase().startsWith("content-id")) {
379369
contentId = line.split(":")[1].trim();
380370
}
381371
}
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());
372+
// TODO: remove and write directly to socket once api client provides a complete
373+
// location line (e.g. GET /aaa/bbb HTTP/1.0)
374+
// and uses a request path for location instead of a complete URL.
375+
ByteArrayOutputStream bouts = new ByteArrayOutputStream();
376+
multipartStream.readBodyData(bouts);
377+
byte[] contentBytes = bouts.toByteArray();
378+
int indexOfCr = -1;
379+
for (int i = 0; i < contentBytes.length; i++) {
380+
if (contentBytes[i] == '\r') {
381+
indexOfCr = i;
382+
break;
392383
}
393384
}
385+
Socket socket = new Socket("127.0.0.1", server.getAddress().getPort());
386+
OutputStream socketOutput = socket.getOutputStream();
387+
InputStream socketInput = socket.getInputStream();
388+
//multipartStream.readBodyData(socketOutput);
389+
if (indexOfCr < 0) {
390+
socketOutput.write(contentBytes);
391+
} else {
392+
String[] requestLine =
393+
new String(contentBytes, 0, indexOfCr, StandardCharsets.UTF_8).split(" ");
394+
socketOutput.write(requestLine[0].getBytes());
395+
socketOutput.write(' ');
396+
URI uri = new URI(requestLine[1]);
397+
socketOutput.write(uri.getRawPath().getBytes());
398+
if (uri.getRawQuery() != null) {
399+
socketOutput.write('?');
400+
socketOutput.write(uri.getRawQuery().getBytes());
401+
}
402+
if (uri.getRawFragment() != null) {
403+
socketOutput.write('#');
404+
socketOutput.write(uri.getRawFragment().getBytes());
405+
}
406+
socketOutput.write(" HTTP/1.0".getBytes());
407+
socketOutput.write(contentBytes, indexOfCr, contentBytes.length - indexOfCr);
408+
}
394409
socketOutput.flush();
395-
InputStream in = socket.getInputStream();
396-
int length;
397410
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
411+
out.write("Content-Type: application/http\r\n".getBytes());
412+
out.write(("Content-ID: " + contentId + "\r\n\r\n").getBytes());
413+
int length;
414+
while ((length = socketInput.read(bytes)) != -1) {
415+
out.write(bytes, 0, length);
406416
}
417+
socket.close();
418+
nextPart = multipartStream.skipPreamble();
407419
}
408420
out.write(RESPONSE_END.getBytes());
409421
writeBatchResponse(exchange, out);
@@ -483,7 +495,11 @@ public static LocalDnsHelper create(Long delay) {
483495
* Returns a {@link DnsOptions} instance that sets the host to use the mock server.
484496
*/
485497
public DnsOptions options() {
486-
return DnsOptions.builder().projectId(PROJECT_ID).host("http://localhost:" + port).build();
498+
return DnsOptions.builder()
499+
.projectId(PROJECT_ID)
500+
.host("http://localhost:" + port)
501+
.authCredentials(AuthCredentials.noAuth())
502+
.build();
487503
}
488504

489505
/**

0 commit comments

Comments
 (0)