Skip to content

Commit

Permalink
8278353: Provide Duke as default favicon in Simple Web Server
Browse files Browse the repository at this point in the history
Reviewed-by: dfuchs
  • Loading branch information
sormuras committed May 8, 2024
1 parent 466a21d commit 7b79426
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 4 deletions.
4 changes: 3 additions & 1 deletion make/modules/jdk.httpserver/Java.gmk
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
Expand All @@ -24,3 +24,5 @@
#

DISABLED_WARNINGS_java += missing-explicit-ctor this-escape

COPY += .ico
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -57,6 +57,9 @@ public final class FileServerHandler implements HttpHandler {
private static final List<String> SUPPORTED_METHODS = List.of("HEAD", "GET");
private static final List<String> UNSUPPORTED_METHODS =
List.of("CONNECT", "DELETE", "OPTIONS", "PATCH", "POST", "PUT", "TRACE");
private static final String FAVICON_RESOURCE_PATH =
"/sun/net/httpserver/simpleserver/resources/favicon.ico";
private static final String FAVICON_LAST_MODIFIED = "Mon, 23 May 1995 11:11:11 GMT";

private final Path root;
private final UnaryOperator<String> mimeTable;
Expand Down Expand Up @@ -250,6 +253,31 @@ private static Path indexFile(Path path) {
return Files.exists(html) ? html : Files.exists(htm) ? htm : null;
}

private static boolean isFavIconRequest(HttpExchange exchange) {
return "/favicon.ico".equals(exchange.getRequestURI().getPath());
}

private void serveDefaultFavIcon(HttpExchange exchange, boolean writeBody)
throws IOException
{
var respHdrs = exchange.getResponseHeaders();
try (var stream = getClass().getModule().getResourceAsStream(FAVICON_RESOURCE_PATH)) {
var bytes = stream.readAllBytes();
respHdrs.set("Content-Type", "image/x-icon");
respHdrs.set("Last-Modified", FAVICON_LAST_MODIFIED);
if (writeBody) {
exchange.sendResponseHeaders(200, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);
}
} else {
respHdrs.set("Content-Length", Integer.toString(bytes.length));
exchange.sendResponseHeaders(200, -1);
}
}
}


private void serveFile(HttpExchange exchange, Path path, boolean writeBody)
throws IOException
{
Expand Down Expand Up @@ -371,17 +399,26 @@ public void handle(HttpExchange exchange) throws IOException {
assert List.of("GET", "HEAD").contains(exchange.getRequestMethod());
try (exchange) {
discardRequestBody(exchange);
boolean isHeadRequest = exchange.getRequestMethod().equals("HEAD");
Path path = mapToPath(exchange, root);
if (path != null) {
exchange.setAttribute("request-path", path.toString()); // store for OutputFilter
if (!Files.exists(path) || !Files.isReadable(path) || isHiddenOrSymLink(path)) {
handleNotFound(exchange);
} else if (exchange.getRequestMethod().equals("HEAD")) {
} else if (isHeadRequest) {
handleHEAD(exchange, path);
} else {
handleGET(exchange, path);
}
} else {
if (isFavIconRequest(exchange)) {
try {
serveDefaultFavIcon(exchange, !isHeadRequest);
return;
} catch (IOException ignore) {
// fall through to send the not-found response
}
}
exchange.setAttribute("request-path", "could not resolve request URI path");
handleNotFound(exchange);
}
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -76,6 +76,8 @@ public class SimpleFileServerTest {
static final boolean ENABLE_LOGGING = true;
static final Logger LOGGER = Logger.getLogger("com.sun.net.httpserver");

static final String EXPECTED_LAST_MODIFIED_OF_FAVICON = "Mon, 23 May 1995 11:11:11 GMT";

@BeforeTest
public void setup() throws IOException {
if (ENABLE_LOGGING) {
Expand Down Expand Up @@ -142,6 +144,65 @@ public void testDirectoryGET() throws Exception {
}
}

@Test
public void testFavIconGET() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFavIconGET"));

var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
// expect built-in icon
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "favicon.ico")).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(response.statusCode(), 200);
assertEquals(response.headers().firstValue("content-type").get(), "image/x-icon");
assertEquals(response.headers().firstValue("last-modified").get(), EXPECTED_LAST_MODIFIED_OF_FAVICON);

// expect custom (and broken) icon
var file = Files.writeString(root.resolve("favicon.ico"), "broken icon", CREATE);
try {
var lastModified = getLastModified(file);
var expectedLength = Long.toString(Files.size(file));
response = client.send(request, BodyHandlers.ofString());
assertEquals(response.statusCode(), 200);
assertEquals(response.headers().firstValue("content-type").get(), "application/octet-stream");
assertEquals(response.headers().firstValue("content-length").get(), expectedLength);
assertEquals(response.headers().firstValue("last-modified").get(), lastModified);
} finally {
Files.delete(file);
}

// expect built-in icon
response = client.send(request, BodyHandlers.ofString());
assertEquals(response.statusCode(), 200);
assertEquals(response.headers().firstValue("content-type").get(), "image/x-icon");
assertEquals(response.headers().firstValue("last-modified").get(), EXPECTED_LAST_MODIFIED_OF_FAVICON);
} finally {
server.stop(0);
}
}

@Test
public void testFavIconHEAD() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFavIconHEAD"));

var server = SimpleFileServer.createFileServer(LOOPBACK_ADDR, root, OutputLevel.VERBOSE);
server.start();
try {
var client = HttpClient.newBuilder().proxy(NO_PROXY).build();
var request = HttpRequest.newBuilder(uri(server, "favicon.ico"))
.method("HEAD", BodyPublishers.noBody()).build();
var response = client.send(request, BodyHandlers.ofString());
assertEquals(response.statusCode(), 200);
assertEquals(response.headers().firstValue("content-type").get(), "image/x-icon");
assertEquals(response.headers().firstValue("last-modified").get(), EXPECTED_LAST_MODIFIED_OF_FAVICON);
assertEquals(response.body(), "");
} finally {
server.stop(0);
}
}

@Test
public void testFileHEAD() throws Exception {
var root = Files.createDirectory(TEST_DIR.resolve("testFileHEAD"));
Expand Down

1 comment on commit 7b79426

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.