Skip to content
Closed
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, 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 @@ -71,6 +71,27 @@

public abstract class HttpExchange implements AutoCloseable, Request {

/*
* Symbolic values for the responseLength parameter of
* sendResponseHeaders(int,long)
*/

/**
* No response body is being sent with this response
*
* @see #sendResponseHeaders(int, long)
* @since 26
*/
public static final long RSPBODY_EMPTY = -1l;

/**
* The response body length is unspecified and will be chunk encoded
*
* @see #sendResponseHeaders(int, long)
* @since 26
*/
public static final long RSPBODY_CHUNKED = 0;

/**
* Constructor for subclasses to call.
*/
Expand Down Expand Up @@ -163,26 +184,36 @@ protected HttpExchange() {
*/
public abstract OutputStream getResponseBody();


/**
* Starts sending the response back to the client using the current set of
* response headers and the numeric response code as specified in this
* method. The response body length is also specified as follows. If the
* response length parameter is greater than {@code zero}, this specifies an
* exact number of bytes to send and the application must send that exact
* amount of data. If the response length parameter is {@code zero}, then
* chunked transfer encoding is used and an arbitrary amount of data may be
* amount of data. If the response length parameter has the value
* {@link #RSPBODY_CHUNKED} (zero) then the response body uses
* chunked transfer encoding and an arbitrary amount of data may be
* sent. The application terminates the response body by closing the
* {@link OutputStream}.
* If response length has the value {@code -1} then no response body is
* being sent.
* If response length has the value {@link #RSPBODY_EMPTY} then no
* response body is being sent.
*
* <p> If the content-length response header has not already been set then
* this is set to the appropriate value depending on the response length
* parameter.
*
* <p> This method must be called prior to calling {@link #getResponseBody()}.
*
* @apiNote If a response body is to be sent from a byte array and the
* length of the array is used as the responseLength parameter, then note
* the behavior in the case when the array is empty. In that case, the
* responseLength will be zero which is the value of {@link #RSPBODY_CHUNKED}
* resulting in a zero length, but chunked encoded response body. While this
* is not incorrect, it may be preferable to check for an empty array and set
* responseLength to {@link #RSPBODY_EMPTY} instead. Also, when sending a
* chunked encoded response body, whatever length, the output stream must
* be explicitly closed by the handler.
*
* @implNote This implementation allows the caller to instruct the
* server to force a connection close after the exchange terminates, by
* supplying a {@code Connection: close} header to the {@linkplain
Expand All @@ -193,10 +224,11 @@ protected HttpExchange() {
* @param responseLength if {@literal > 0}, specifies a fixed response body
* length and that exact number of bytes must be written
* to the stream acquired from {@link #getResponseCode()}
* If {@literal == 0}, then chunked encoding is used,
* If equal to {@link #RSPBODY_CHUNKED}, then chunked encoding is used,
* and an arbitrary number of bytes may be written.
* If {@literal <= -1}, then no response body length is
* specified and no response body may be written.
* If equal to {@link #RSPBODY_EMPTY}, then no response body length is
* specified and no response body may be written. Any value {@literal <= -1}
* is treated the same as {@link #RSPBODY_EMPTY}.
* @throws IOException if the response headers have already been sent or an I/O error occurs
* @see HttpExchange#getResponseBody()
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2025, 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 @@ -28,6 +28,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.function.Predicate;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;

/**
* Implementations of {@link com.sun.net.httpserver.HttpHandler HttpHandler}
Expand Down Expand Up @@ -155,10 +156,10 @@ public static HttpHandler of(int statusCode, Headers headers, String body) {
exchange.getResponseHeaders().putAll(headersCopy);
if (exchange.getRequestMethod().equals("HEAD")) {
exchange.getResponseHeaders().set("Content-Length", Integer.toString(bytes.length));
exchange.sendResponseHeaders(statusCode, -1);
exchange.sendResponseHeaders(statusCode, RSPBODY_EMPTY);
}
else if (bytes.length == 0) {
exchange.sendResponseHeaders(statusCode, -1);
exchange.sendResponseHeaders(statusCode, RSPBODY_EMPTY);
} else {
exchange.sendResponseHeaders(statusCode, bytes.length);
exchange.getResponseBody().write(bytes);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2025, 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 @@ -27,6 +27,7 @@

import com.sun.net.httpserver.*;
import java.io.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;

public class AuthFilter extends Filter {

Expand Down Expand Up @@ -66,11 +67,11 @@ public void doFilter (HttpExchange t, Filter.Chain chain) throws IOException
} else if (r instanceof Authenticator.Retry) {
Authenticator.Retry ry = (Authenticator.Retry)r;
consumeInput (t);
t.sendResponseHeaders (ry.getResponseCode(), -1);
t.sendResponseHeaders (ry.getResponseCode(), RSPBODY_EMPTY);
} else if (r instanceof Authenticator.Failure) {
Authenticator.Failure f = (Authenticator.Failure)r;
consumeInput (t);
t.sendResponseHeaders (f.getResponseCode(), -1);
t.sendResponseHeaders (f.getResponseCode(), RSPBODY_EMPTY);
}
} else {
chain.doFilter (t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import java.time.format.DateTimeFormatter;
import java.util.stream.Stream;
import com.sun.net.httpserver.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;

class ExchangeImpl {

Expand Down Expand Up @@ -227,12 +229,12 @@ public void sendResponseHeaders (int rCode, long contentLen)
||(rCode == 204) /* no content */
||(rCode == 304)) /* not modified */
{
if (contentLen != -1) {
if (contentLen != RSPBODY_EMPTY) {
String msg = "sendResponseHeaders: rCode = "+ rCode
+ ": forcing contentLen = -1";
+ ": forcing contentLen = RSPBODY_EMPTY";
logger.log (Level.WARNING, msg);
}
contentLen = -1;
contentLen = RSPBODY_EMPTY;
noContentLengthHeader = (rCode != 304);
}

Expand All @@ -249,7 +251,7 @@ public void sendResponseHeaders (int rCode, long contentLen)
contentLen = 0;
o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));
} else { /* not a HEAD request or 304 response */
if (contentLen == 0) {
if (contentLen == RSPBODY_CHUNKED) {
if (http10) {
o.setWrappedStream (new UndefLengthOutputStream (this, ros));
close = true;
Expand All @@ -258,7 +260,7 @@ public void sendResponseHeaders (int rCode, long contentLen)
o.setWrappedStream (new ChunkedOutputStream (this, ros));
}
} else {
if (contentLen == -1) {
if (contentLen == RSPBODY_EMPTY) {
noContentToSend = true;
contentLen = 0;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, 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 @@ -43,6 +43,7 @@
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpHandlers;
import static java.nio.charset.StandardCharsets.UTF_8;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;

/**
* A basic HTTP file server handler for static content.
Expand Down Expand Up @@ -122,11 +123,11 @@ private void handleSupportedMethod(HttpExchange exchange, Path path, boolean wri

private void handleMovedPermanently(HttpExchange exchange) throws IOException {
exchange.getResponseHeaders().set("Location", getRedirectURI(exchange.getRequestURI()));
exchange.sendResponseHeaders(301, -1);
exchange.sendResponseHeaders(301, RSPBODY_EMPTY);
}

private void handleForbidden(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(403, -1);
exchange.sendResponseHeaders(403, RSPBODY_EMPTY);
}

private void handleNotFound(HttpExchange exchange) throws IOException {
Expand All @@ -139,7 +140,7 @@ private void handleNotFound(HttpExchange exchange) throws IOException {

if (exchange.getRequestMethod().equals("HEAD")) {
exchange.getResponseHeaders().set("Content-Length", Integer.toString(bytes.length));
exchange.sendResponseHeaders(404, -1);
exchange.sendResponseHeaders(404, RSPBODY_EMPTY);
} else {
exchange.sendResponseHeaders(404, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
Expand Down Expand Up @@ -260,7 +261,7 @@ private void serveDefaultFavIcon(HttpExchange exchange, boolean writeBody)
}
} else {
respHdrs.set("Content-Length", Integer.toString(bytes.length));
exchange.sendResponseHeaders(200, -1);
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
}
}
}
Expand All @@ -280,7 +281,7 @@ private void serveFile(HttpExchange exchange, Path path, boolean writeBody)
}
} else {
respHdrs.set("Content-Length", Long.toString(Files.size(path)));
exchange.sendResponseHeaders(200, -1);
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
}
}

Expand All @@ -298,7 +299,7 @@ private void listFiles(HttpExchange exchange, Path path, boolean writeBody)
}
} else {
respHdrs.set("Content-Length", Integer.toString(bodyBytes.length));
exchange.sendResponseHeaders(200, -1);
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
}
}

Expand Down
7 changes: 4 additions & 3 deletions test/jdk/com/sun/net/httpserver/BasicAuthToken.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, 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 @@ -39,6 +39,7 @@
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.HttpServer;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;

public class BasicAuthToken {
private static final String CRLF = "\r\n";
Expand All @@ -59,11 +60,11 @@ static HttpServer server() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
server.createContext(someContext, exchange -> {
if (authenticator.authenticate(exchange) instanceof Authenticator.Failure) {
exchange.sendResponseHeaders(401, -1);
exchange.sendResponseHeaders(401, RSPBODY_EMPTY);
exchange.close();
return;
}
exchange.sendResponseHeaders(200, -1);
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
exchange.close();
}).setAuthenticator(authenticator);
server.start();
Expand Down
8 changes: 5 additions & 3 deletions test/jdk/com/sun/net/httpserver/EchoHandler.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2025, 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 @@ -29,6 +29,8 @@
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;

/**
* Implements a basic static EchoHandler for an HTTP server
Expand All @@ -50,9 +52,9 @@ public void handle (HttpExchange t)
in = Integer.toString(in.length).getBytes(StandardCharsets.UTF_8);
}
if (fixedrequest != null) {
t.sendResponseHeaders(200, in.length == 0 ? -1 : in.length);
t.sendResponseHeaders(200, in.length == 0 ? RSPBODY_EMPTY : in.length);
} else {
t.sendResponseHeaders(200, 0);
t.sendResponseHeaders(200, RSPBODY_CHUNKED);
}
os.write(in);
close(t, os);
Expand Down
5 changes: 3 additions & 2 deletions test/jdk/com/sun/net/httpserver/ExchangeAttributeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static java.net.http.HttpClient.Builder.NO_PROXY;
import static org.junit.jupiter.api.Assertions.*;

Expand Down Expand Up @@ -117,10 +118,10 @@ public void handle(HttpExchange exchange) throws IOException {
}
exchange.setAttribute("attr", null);
assertNull(exchange.getAttribute("attr"));
exchange.sendResponseHeaders(200, -1);
exchange.sendResponseHeaders(200, RSPBODY_EMPTY);
} catch (Throwable t) {
t.printStackTrace();
exchange.sendResponseHeaders(500, -1);
exchange.sendResponseHeaders(500, RSPBODY_EMPTY);
}
}
}
Expand Down
12 changes: 7 additions & 5 deletions test/jdk/com/sun/net/httpserver/FileServerHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.net.*;

import com.sun.net.httpserver.*;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_EMPTY;
import static com.sun.net.httpserver.HttpExchange.RSPBODY_CHUNKED;

/**
* Implements a basic static content HTTP file server handler
Expand Down Expand Up @@ -65,10 +67,10 @@ public void handle (HttpExchange t)
String method = t.getRequestMethod();
if (method.equals ("HEAD")) {
rmap.set ("Content-Length", Long.toString (f.length()));
t.sendResponseHeaders (200, -1);
t.sendResponseHeaders (200, RSPBODY_EMPTY);
t.close();
} else if (!method.equals("GET")) {
t.sendResponseHeaders (405, -1);
t.sendResponseHeaders (405, RSPBODY_EMPTY);
t.close();
return;
}
Expand All @@ -84,7 +86,7 @@ public void handle (HttpExchange t)
return;
}
rmap.set ("Content-Type", "text/html");
t.sendResponseHeaders (200, 0);
t.sendResponseHeaders (200, RSPBODY_CHUNKED);
String[] list = f.list();
try (final OutputStream os = t.getResponseBody();
final PrintStream p = new PrintStream (os)) {
Expand Down Expand Up @@ -127,13 +129,13 @@ void moved (HttpExchange t) throws IOException {
String location = "http://"+host+uri.getPath() + "/";
map.set ("Content-Type", "text/html");
map.set ("Location", location);
t.sendResponseHeaders (301, -1);
t.sendResponseHeaders (301, RSPBODY_EMPTY);
t.close();
}

void notfound (HttpExchange t, String p) throws IOException {
t.getResponseHeaders().set ("Content-Type", "text/html");
t.sendResponseHeaders (404, 0);
t.sendResponseHeaders (404, RSPBODY_CHUNKED);
OutputStream os = t.getResponseBody();
String s = "<h2>File not found</h2>";
s = s + p + "<p>";
Expand Down
Loading