Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 11 additions & 2 deletions src/main/java/org/perlonjava/runtime/perlmodule/HttpTiny.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,18 @@ public static RuntimeList request(RuntimeArray args, int ctx) throws Exception {

// Set headers
requestBuilder.header("User-Agent", instanceHash.get("agent").toString());
// requestBuilder.header("Connection", "keep-alive");

// Add default and custom headers
// Replicate HTTP::Tiny _prepare_headers_and_cb: for a non-empty scalar body,
// send Content-Type: application/octet-stream unless the caller already supplied one.
if (!content.isEmpty()) {
boolean callerHasContentType = headers.elements.keySet().stream()
.anyMatch(k -> k.equalsIgnoreCase("content-type"));
if (!callerHasContentType) {
requestBuilder.header("Content-Type", "application/octet-stream");
}
}

// Add caller-supplied headers (override any defaults set above)
headers.elements.forEach((key, value) ->
requestBuilder.header(key, value.toString())
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.net.InetSocketAddress;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

class HttpTinyBinaryResponseTest {
Expand Down Expand Up @@ -65,4 +66,107 @@ void requestPreservesBinaryResponseOctets() throws Exception {
server.stop(0);
}
}

@Tag("unit")
@Test
void requestAddsDefaultContentTypeForNonEmptyBody() throws Exception {
String[] capturedContentType = {null};
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/post", exchange -> {
capturedContentType[0] = exchange.getRequestHeaders().getFirst("Content-Type");
exchange.sendResponseHeaders(200, 0);
exchange.getResponseBody().close();
});
server.start();
try {
RuntimeHash instance = buildInstance();

RuntimeHash optHeaders = new RuntimeHash();
RuntimeHash options = new RuntimeHash();
options.put("content", new RuntimeScalar("hello"));
options.put("headers", optHeaders.createReference());

RuntimeList result = HttpTiny.request(buildArgs(instance, "POST",
"http://127.0.0.1:" + server.getAddress().getPort() + "/post", options), 0);
assertNotNull(result);
assertEquals("application/octet-stream", capturedContentType[0],
"non-empty scalar body without caller content-type gets default Content-Type");
} finally {
server.stop(0);
}
}

@Tag("unit")
@Test
void requestPreservesCallerSuppliedContentType() throws Exception {
String[] capturedContentType = {null};
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/post", exchange -> {
capturedContentType[0] = exchange.getRequestHeaders().getFirst("Content-Type");
exchange.sendResponseHeaders(200, 0);
exchange.getResponseBody().close();
});
server.start();
try {
RuntimeHash instance = buildInstance();

RuntimeHash optHeaders = new RuntimeHash();
optHeaders.put("Content-Type", new RuntimeScalar("application/json"));
RuntimeHash options = new RuntimeHash();
options.put("content", new RuntimeScalar("{\"k\":1}"));
options.put("headers", optHeaders.createReference());

HttpTiny.request(buildArgs(instance, "POST",
"http://127.0.0.1:" + server.getAddress().getPort() + "/post", options), 0);
assertEquals("application/json", capturedContentType[0],
"caller-supplied content-type is not overridden by the default");
} finally {
server.stop(0);
}
}

@Tag("unit")
@Test
void requestDoesNotAddContentTypeForEmptyBody() throws Exception {
String[] capturedContentType = {"(sentinel)"};
HttpServer server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
server.createContext("/post", exchange -> {
String ct = exchange.getRequestHeaders().getFirst("Content-Type");
capturedContentType[0] = ct;
exchange.sendResponseHeaders(200, 0);
exchange.getResponseBody().close();
});
server.start();
try {
RuntimeHash instance = buildInstance();

RuntimeHash options = new RuntimeHash();
options.put("content", new RuntimeScalar(""));

HttpTiny.request(buildArgs(instance, "POST",
"http://127.0.0.1:" + server.getAddress().getPort() + "/post", options), 0);
assertNull(capturedContentType[0],
"empty body does not get a default Content-Type header");
} finally {
server.stop(0);
}
}

private static RuntimeHash buildInstance() {
RuntimeHash instance = new RuntimeHash();
instance.put("agent", new RuntimeScalar("PerlOnJava-test"));
instance.put("timeout", new RuntimeScalar(10));
instance.put("verify_SSL", new RuntimeScalar(false));
return instance;
}

private static RuntimeArray buildArgs(RuntimeHash instance, String method, String url,
RuntimeHash options) {
RuntimeArray args = new RuntimeArray();
args.elements.add(instance.createReference());
args.elements.add(new RuntimeScalar(method));
args.elements.add(new RuntimeScalar(url));
args.elements.add(options.createReference());
return args;
}
}
Loading