Javadocs for Response and Context. (#9388)

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
Co-authored-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
Simone Bordet 2023-02-17 00:47:16 +01:00 committed by GitHub
parent d21f38798e
commit a8737cd170
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 230 additions and 42 deletions

View File

@ -18,21 +18,22 @@ import java.util.List;
import java.util.concurrent.Executor;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.Decorator;
import org.eclipse.jetty.util.resource.Resource;
/**
* A Context for handling a request.
* Every request will always have a non-null context, which may initially be the Server context, or
* a context provided by a ContextHandler.
* <p>
* A Context is also an {@link Executor}, which allows tasks to be run by a thread pool, but scoped
* to the classloader and any other aspects of the context. Method {@link #run(Runnable)}
* is also provided to allow the current Thread to be scoped to the context.
* <p>
* A Context is also a {@link Decorator}, allowing objects to be decorated in a context scope.
*
* <p>The context for handling an HTTP request.</p>
* <p>Every request has a non-{@code null} context, which may initially
* be the {@link Server#getContext() server context}, or
* a context provided by a {@link ContextHandler}.</p>
* <p>A context is also an {@link Executor}, which allows tasks to be run by a
* thread pool, but scoped to the ClassLoader and any other context property.</p>
* <p>Method {@link #run(Runnable)} is also provided to allow the current thread
* to be scoped to the context for the execution of the task.</p>
* <p>A Context is also a {@link Decorator}, allowing objects to be decorated
* in a context scope.</p>
*/
public interface Context extends Attributes, Decorator, Executor
{
@ -41,39 +42,70 @@ public interface Context extends Attributes, Decorator, Executor
*/
String getContextPath();
/**
* @return the {@link ClassLoader} associated with this Context
*/
ClassLoader getClassLoader();
/**
* @return the base resource used to lookup other resources
* specified by the request URI path
*/
Resource getBaseResource();
/**
* @return the error {@link Request.Handler} associated with this Context
*/
Request.Handler getErrorHandler();
/**
* @return a list of virtual host names associated with this Context
*/
List<String> getVirtualHosts();
/**
* @return the mime types associated with this Context
*/
MimeTypes getMimeTypes();
/** execute runnable in container thread scoped to context */
/**
* <p>Executes the given task in a thread scoped to this Context.</p>
*
* @param task the task to run
* @see #run(Runnable)
*/
@Override
void execute(Runnable runnable);
void execute(Runnable task);
/** scope the calling thread to the context and run the runnable. */
void run(Runnable runnable);
/**
* <p>Runs the given task in the current thread scoped to this Context.</p>
*
* @param task the task to run
* @see #run(Runnable, Request)
*/
void run(Runnable task);
/** scope the calling thread to the context and request and run the runnable. */
void run(Runnable runnable, Request request);
/**
* <p>Runs the given task in the current thread scoped to this Context and the given Request.</p>
*
* @param task the task to run
* @param request the HTTP request to use in the scope
*/
void run(Runnable task, Request request);
/**
* <p>Returns a URI path scoped to this Context.</p>
* <p>For example, if the context path is {@code /ctx} then a
* full path of {@code /ctx/foo/bar} will return {@code /foo/bar}.</p>
*
* @param fullPath A full URI path
* @return The URI path scoped to this Context, or {@code null} if the full path does not match this Context.
* The empty string is returned if the full path is exactly the context path.
* @param fullPath a full URI path
* @return the URI path scoped to this Context, or {@code null} if the full path does not match this Context.
* The empty string is returned if the full path is exactly the context path.
*/
String getPathInContext(String fullPath);
/**
* @return A temporary directory, configured either for the context, the server or the JVM. Never null.
* @return a non-{@code null} temporary directory, configured either for the context, the server or the JVM
*/
File getTempDirectory();
}

View File

@ -504,7 +504,7 @@ public interface Request extends Attributes, Content.Source
* completing the passed callback. The handling may be asynchronous, i.e. this method may return true and
* complete the given callback later, possibly from a different thread. If this method returns false,
* then the callback must not be invoked and any mutation on the response reversed.</p>
* <p>Exceptions thrown by this method may be subsequently handled by an {@link ErrorHandler},
* <p>Exceptions thrown by this method may be subsequently handled by an error {@link Request.Handler},
* if present, otherwise a default HTTP 500 error is generated and the
* callback completed while writing the error response.</p>
* <p>The simplest implementation is:</p>

View File

@ -38,44 +38,98 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An asynchronous HTTP response.
* TODO Javadoc
* <p>The representation of an HTTP response, for any protocol version (HTTP/1.1, HTTP/2, HTTP/3).</p>
*/
public interface Response extends Content.Sink
{
// This is needed so that response methods can access the wrapped Request#getContext method
/**
* @return the {@link Request} associated with this {@code Response}
*/
Request getRequest();
/**
* @return the response HTTP status code
*/
int getStatus();
/**
* @param code the response HTTP status code
*/
void setStatus(int code);
/**
* @return the response HTTP headers
*/
HttpFields.Mutable getHeaders();
/**
* @return a supplier for the HTTP trailers
*/
Supplier<HttpFields> getTrailersSupplier();
/**
* @param trailers a supplier for the HTTP trailers
*/
void setTrailersSupplier(Supplier<HttpFields> trailers);
/**
* <p>Returns whether this response has already been committed.</p>
* <p>Committing a response means that the HTTP status code and HTTP headers
* cannot be modified anymore, typically because they have already been
* serialized and sent over the network.</p>
*
* @return whether this response has already been committed
*/
boolean isCommitted();
/**
* <p>Returns whether the response completed successfully.</p>
* <p>The response HTTP status code, HTTP headers and content
* have been successfully serialized and sent over the network
* without errors.</p>
*
* @return whether the response completed successfully
*/
boolean isCompletedSuccessfully();
/**
* <p>Resets this response, clearing the HTTP status code, HTTP headers
* and HTTP trailers.</p>
*
* @throws IllegalStateException if the response is already
* {@link #isCommitted() committed}
*/
void reset();
/**
* <p>Writes an {@link HttpStatus#isInterim(int) HTTP interim response},
* with the given HTTP status code and HTTP headers.</p>
* <p>It is possible to write more than one interim response, for example
* in case of {@link HttpStatus#EARLY_HINT_103}.</p>
* <p>The returned {@link CompletableFuture} is notified of the result
* of this write, whether it succeeded or failed.</p>
*
* @param status the interim HTTP status code
* @param headers the HTTP headers
* @return a {@link CompletableFuture} with the result of the write
*/
CompletableFuture<Void> writeInterim(int status, HttpFields headers);
/**
* {@inheritDoc}
* <p>Invocations of the passed {@code Callback} are serialized and a callback for a completed {@code write} call is
* not invoked until any previous {@code write} callback has returned.
* Thus the {@code Callback} should not block waiting for a callback of a future write call.</p>
* <p>The invocation of the passed {@code Callback} is serialized
* with previous calls of this method, so that it is not invoked until
* any invocation of the callback of a previous call to this method
* has returned.</p>
* <p>Thus a {@code Callback} should not block waiting for a callback
* of a future call to this method.</p>
* <p>Furthermore, the invocation of the passed callback is serialized
* with invocations of the {@link Runnable} demand callback passed to
* {@link Request#demand(Runnable)}.</p>
*
* @param last whether the ByteBuffer is the last to write
* @param byteBuffer the ByteBuffer to write
* @param callback the callback to notify when the write operation is complete
* In addition to the invocation guarantees of {@link Content.Sink#write(boolean, ByteBuffer, Callback)},
* this implementation serializes the invocation of the {@code Callback} with
* invocations of any {@link Request#demand(Runnable)} {@code Runnable} invocations.
* @see Content.Sink#write(boolean, ByteBuffer, Callback)
*/
@Override
void write(boolean last, ByteBuffer byteBuffer, Callback callback);
@ -125,6 +179,16 @@ public interface Response extends Content.Sink
};
}
/**
* <p>Unwraps the given response, recursively, until the wrapped instance
* is an instance of the given type, otherwise returns {@code null}.</p>
*
* @param response the response to unwrap
* @param type the response type to find
* @return the response as the given type, or {@code null}
* @param <T> the response type
* @see Wrapper
*/
@SuppressWarnings("unchecked")
static <T extends Response.Wrapper> T as(Response response, Class<T> type)
{
@ -137,11 +201,34 @@ public interface Response extends Content.Sink
return null;
}
/**
* <p>Sends a {@code 302} HTTP redirect status code to the given location,
* without consuming the available request content.</p>
*
* @param request the HTTP request
* @param response the HTTP response
* @param callback the callback to complete
* @param location the redirect location
* @see #sendRedirect(Request, Response, Callback, int, String, boolean)
*/
static void sendRedirect(Request request, Response response, Callback callback, String location)
{
sendRedirect(request, response, callback, HttpStatus.MOVED_TEMPORARILY_302, location, false);
}
/**
* <p>Sends a {@code 302} HTTP redirect status code to the given location.</p>
*
* @param request the HTTP request
* @param response the HTTP response
* @param callback the callback to complete
* @param code the redirect HTTP status code
* @param location the redirect location
* @param consumeAvailable whether to consumer the available request content
* @see Request#toRedirectURI(Request, String)
* @throws IllegalArgumentException if the status code is not a redirect, or the location is {@code null}
* @throws IllegalStateException if the response is already {@link #isCommitted() committed}
*/
static void sendRedirect(Request request, Response response, Callback callback, int code, String location, boolean consumeAvailable)
{
if (!HttpStatus.isRedirection(code))
@ -174,6 +261,12 @@ public interface Response extends Content.Sink
response.write(true, null, callback);
}
/**
* <p>Adds an HTTP cookie to the response.</p>
*
* @param response the HTTP response
* @param cookie the HTTP cookie to add
*/
static void addCookie(Response response, HttpCookie cookie)
{
if (StringUtil.isBlank(cookie.getName()))
@ -187,6 +280,12 @@ public interface Response extends Content.Sink
response.getHeaders().put(HttpFields.EXPIRES_01JAN1970);
}
/**
* <p>Replaces (if already exists, otherwise adds) an HTTP cookie to the response.</p>
*
* @param response the HTTP response
* @param cookie the HTTP cookie to replace or add
*/
static void replaceCookie(Response response, HttpCookie cookie)
{
if (StringUtil.isBlank(cookie.getName()))
@ -222,6 +321,16 @@ public interface Response extends Content.Sink
addCookie(response, cookie);
}
/**
* <p>Writes an error response with HTTP status code {@code 500}.</p>
* <p>The error {@link Request.Handler} returned by {@link Context#getErrorHandler()},
* if any, is invoked.</p>
*
* @param request the HTTP request
* @param response the HTTP response
* @param callback the callback to complete
* @param cause the cause of the error
*/
static void writeError(Request request, Response response, Callback callback, Throwable cause)
{
if (cause == null)
@ -236,16 +345,51 @@ public interface Response extends Content.Sink
writeError(request, response, callback, status, message, cause);
}
/**
* <p>Writes an error response with the given HTTP status code.</p>
* <p>The error {@link Request.Handler} returned by {@link Context#getErrorHandler()},
* if any, is invoked.</p>
*
* @param request the HTTP request
* @param response the HTTP response
* @param callback the callback to complete
* @param status the error HTTP status code
*/
static void writeError(Request request, Response response, Callback callback, int status)
{
writeError(request, response, callback, status, null, null);
}
/**
* <p>Writes an error response with the given HTTP status code,
* and the given message in the response content.</p>
* <p>The error {@link Request.Handler} returned by {@link Context#getErrorHandler()},
* if any, is invoked.</p>
*
* @param request the HTTP request
* @param response the HTTP response
* @param callback the callback to complete
* @param status the error HTTP status code
* @param message the error message to write in the response content
*/
static void writeError(Request request, Response response, Callback callback, int status, String message)
{
writeError(request, response, callback, status, message, null);
}
/**
* <p>Writes an error response with the given HTTP status code,
* and the given message in the response content.</p>
* <p>The error {@link Request.Handler} returned by {@link Context#getErrorHandler()},
* if any, is invoked.</p>
*
* @param request the HTTP request
* @param response the HTTP response
* @param callback the callback to complete
* @param status the error HTTP status code
* @param message the error message to write in the response content
* @param cause the cause of the error
*/
static void writeError(Request request, Response response, Callback callback, int status, String message, Throwable cause)
{
// Retrieve the Logger instance here, rather than having a
@ -298,6 +442,13 @@ public interface Response extends Content.Sink
response.write(true, null, callback);
}
/**
* <p>Unwraps the given response until the innermost wrapped response instance.</p>
*
* @param response the response to unwrap
* @return the innermost wrapped response instance
* @see Wrapper
*/
static Response getOriginalResponse(Response response)
{
while (response instanceof Response.Wrapper wrapped)
@ -307,6 +458,11 @@ public interface Response extends Content.Sink
return response;
}
/**
* @param response the HTTP response
* @return the number of response content bytes written so far,
* or {@code -1} if the number is unknown
*/
static long getContentBytesWritten(Response response)
{
Response originalResponse = getOriginalResponse(response);
@ -326,17 +482,17 @@ public interface Response extends Content.Sink
_wrapped = wrapped;
}
public Response getWrapped()
{
return _wrapped;
}
@Override
public Request getRequest()
{
return _request;
}
public Response getWrapped()
{
return _wrapped;
}
@Override
public int getStatus()
{
@ -367,12 +523,6 @@ public interface Response extends Content.Sink
getWrapped().setTrailersSupplier(trailers);
}
@Override
public void write(boolean last, ByteBuffer byteBuffer, Callback callback)
{
getWrapped().write(last, byteBuffer, callback);
}
@Override
public boolean isCommitted()
{
@ -396,5 +546,11 @@ public interface Response extends Content.Sink
{
return getWrapped().writeInterim(status, headers);
}
@Override
public void write(boolean last, ByteBuffer byteBuffer, Callback callback)
{
getWrapped().write(last, byteBuffer, callback);
}
}
}