| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.wicket.request.http; |
| |
| import java.io.IOException; |
| import java.nio.charset.StandardCharsets; |
| import java.time.Duration; |
| import java.time.Instant; |
| import jakarta.servlet.http.Cookie; |
| import org.apache.wicket.request.Response; |
| import org.apache.wicket.util.encoding.UrlEncoder; |
| import org.apache.wicket.util.lang.Args; |
| import org.apache.wicket.util.string.Strings; |
| |
| /** |
| * Base class for web-related responses. |
| * |
| * @author Matej Knopp |
| */ |
| public abstract class WebResponse extends Response |
| { |
| /** Recommended value for cache duration */ |
| // one year, maximum recommended cache duration in RFC-2616 |
| public static final Duration MAX_CACHE_DURATION = Duration.ofDays(365); |
| |
| /** |
| * Add a cookie to the web response |
| * |
| * @param cookie |
| */ |
| public abstract void addCookie(final Cookie cookie); |
| |
| /** |
| * Convenience method for clearing a cookie. |
| * |
| * @param cookie |
| * The cookie to set |
| * @see WebResponse#addCookie(Cookie) |
| */ |
| public abstract void clearCookie(final Cookie cookie); |
| |
| /** |
| * Indicates if the response supports setting headers. When this method returns |
| * false, {@link #setHeader(String, String)} and its variations will thrown an |
| * {@code UnsupportedOperationException}. |
| * |
| * @return True when this {@code WebResponse} supports setting headers. |
| */ |
| public abstract boolean isHeaderSupported(); |
| |
| /** |
| * Set a header to the string value in the servlet response stream. |
| * |
| * @param name |
| * @param value |
| */ |
| public abstract void setHeader(String name, String value); |
| |
| /** |
| * Add a value to the servlet response stream. |
| * |
| * @param name |
| * @param value |
| */ |
| public abstract void addHeader(String name, String value); |
| |
| /** |
| * Set a header to the date value in the servlet response stream. |
| * |
| * @param name |
| * @param date |
| */ |
| public abstract void setDateHeader(String name, Instant date); |
| |
| /** |
| * Set the content length on the response, if appropriate in the subclass. This default |
| * implementation does nothing. |
| * |
| * @param length |
| * The length of the content |
| */ |
| public abstract void setContentLength(final long length); |
| |
| /** |
| * Set the content type on the response, if appropriate in the subclass. This default |
| * implementation does nothing. |
| * |
| * @param mimeType |
| * The mime type |
| */ |
| public abstract void setContentType(final String mimeType); |
| |
| /** |
| * Sets the content range of the response. If no content range is set the client assumes the |
| * whole content. Please note that if the content range is set, the content length, the status |
| * code and the accept range must be set right, too. |
| * |
| * @param contentRange |
| * the content range |
| */ |
| public void setContentRange(final String contentRange) |
| { |
| setHeader("Content-Range", contentRange); |
| } |
| |
| |
| /** |
| * Sets the accept range (e.g. bytes) |
| * |
| * @param acceptRange |
| * the accept range header information |
| */ |
| public void setAcceptRange(final String acceptRange) |
| { |
| setHeader("Accept-Range", acceptRange); |
| |
| } |
| |
| /** |
| * Set the contents last modified time, if appropriate in the subclass. |
| * |
| * @param time |
| * The last modified time |
| */ |
| public void setLastModifiedTime(final Instant time) |
| { |
| setDateHeader("Last-Modified", time); |
| } |
| |
| /** |
| * Convenience method for setting the content-disposition:attachment header. This header is used |
| * if the response should prompt the user to download it as a file instead of opening in a |
| * browser. |
| * <p> |
| * The file name will be <a href="http://greenbytes.de/tech/tc2231/">encoded</a> |
| * |
| * @param filename |
| * file name of the attachment |
| */ |
| public void setAttachmentHeader(final String filename) |
| { |
| setHeader("Content-Disposition", "attachment" + encodeDispositionHeaderValue(filename)); |
| } |
| |
| /** |
| * Convenience method for setting the content-disposition:inline header. This header is used if |
| * the response should be shown embedded in browser window while having custom file name when |
| * user saves the response. browser. |
| * <p> |
| * The file name will be <a href="http://greenbytes.de/tech/tc2231/">encoded</a> |
| * |
| * @param filename |
| * file name of the attachment |
| */ |
| public void setInlineHeader(final String filename) |
| { |
| setHeader("Content-Disposition", "inline" + encodeDispositionHeaderValue(filename)); |
| } |
| |
| /** |
| * <a href="http://greenbytes.de/tech/tc2231/">Encodes</a> the value of the filename used in |
| * "Content-Disposition" response header |
| * |
| * @param filename |
| * the non-encoded file name |
| * @return encoded filename |
| */ |
| private String encodeDispositionHeaderValue(final String filename) |
| { |
| return (Strings.isEmpty(filename) ? "" : String.format( |
| "; filename=\"%1$s\"; filename*=UTF-8''%1$s", |
| UrlEncoder.HEADER_INSTANCE.encode(filename, StandardCharsets.UTF_8))); |
| } |
| |
| /** |
| * Sets the status code for this response. |
| * |
| * @param sc |
| * status code |
| */ |
| public abstract void setStatus(int sc); |
| |
| /** |
| * Send error status code with optional message. |
| * |
| * @param sc |
| * @param msg |
| * @throws IOException |
| */ |
| public abstract void sendError(int sc, String msg); |
| |
| /** |
| * Encodes urls used to redirect. Sometimes rules for encoding URLs for redirecting differ from |
| * encoding URLs for links, so this method is broken out away form |
| * {@link #encodeURL(CharSequence)}. |
| * |
| * @param url |
| * @return encoded URL |
| */ |
| public abstract String encodeRedirectURL(CharSequence url); |
| |
| /** |
| * Redirects the response to specified URL. The implementation is responsible for properly |
| * encoding the URL. Implementations of this method should run passed in {@code url} parameters |
| * through the {@link #encodeRedirectURL(CharSequence)} method. |
| * |
| * @param url |
| */ |
| public abstract void sendRedirect(String url); |
| |
| /** |
| * @return <code>true</code> is {@link #sendRedirect(String)} was called, <code>false</code> |
| * otherwise. |
| */ |
| public abstract boolean isRedirect(); |
| |
| /** |
| * Flushes the response. |
| */ |
| public abstract void flush(); |
| |
| /** |
| * Make this response non-cacheable |
| */ |
| public void disableCaching() |
| { |
| setDateHeader("Date", Instant.now()); |
| setDateHeader("Expires", Instant.EPOCH); |
| setHeader("Pragma", "no-cache"); |
| setHeader("Cache-Control", "no-cache, no-store"); |
| } |
| |
| /** |
| * Make this response cacheable |
| * <p/> |
| * when trying to enable caching for web pages check this out: <a |
| * href="https://issues.apache.org/jira/browse/WICKET-4357">WICKET-4357</a> |
| * |
| * @param duration |
| * maximum duration before the response must be invalidated by any caches. It should |
| * not exceed one year, based on <a |
| * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>. |
| * @param scope |
| * controls which caches are allowed to cache the response |
| * |
| * @see WebResponse#MAX_CACHE_DURATION |
| */ |
| public void enableCaching(Duration duration, final WebResponse.CacheScope scope) |
| { |
| Args.notNull(duration, "duration"); |
| Args.notNull(scope, "scope"); |
| |
| // do not exceed the maximum recommended value from RFC-2616 |
| if (duration.compareTo(MAX_CACHE_DURATION) > 0) |
| { |
| duration = MAX_CACHE_DURATION; |
| } |
| |
| // Get current time |
| Instant now = Instant.now(); |
| |
| // Time of message generation |
| setDateHeader("Date", now); |
| |
| // Time for cache expiry = now + duration |
| setDateHeader("Expires", now.plus(duration)); |
| |
| // Set cache scope |
| setHeader("Cache-Control", scope.cacheControl); |
| |
| // Set maximum age for caching in seconds (rounded) |
| addHeader("Cache-Control", "max-age=" + Math.round(duration.getSeconds())); |
| |
| // Though 'cache' is not an official value it will eliminate an eventual 'no-cache' header |
| setHeader("Pragma", "cache"); |
| } |
| |
| /** |
| * caching scope for data |
| * <p/> |
| * Unless the data is confidential, session-specific or user-specific the general advice is to |
| * prefer value <code>PUBLIC</code> for best network performance. |
| * <p/> |
| * This value will basically affect the header [Cache-Control]. Details can be found <a |
| * href="http://palisade.plynt.com/issues/2008Jul/cache-control-attributes">here</a> or in <a |
| * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">RFC-2616</a>. |
| */ |
| public static enum CacheScope |
| { |
| /** |
| * use all caches (private + public) |
| * <p/> |
| * Use this value for caching if the data is not confidential or session-specific. It will |
| * allow public caches to cache the data. In some versions of Firefox this will enable |
| * caching of resources over SSL (details can be found <a |
| * href="http://blog.pluron.com/2008/07/why-you-should.html">here</a>). |
| */ |
| PUBLIC("public"), |
| /** |
| * only use non-public caches |
| * <p/> |
| * Use this setting if the response is session-specific or confidential and you don't want |
| * it to be cached on public caches or proxies. On some versions of Firefox this will |
| * disable caching of any resources in over SSL connections. |
| */ |
| PRIVATE("private"); |
| |
| // value for Cache-Control header |
| private final String cacheControl; |
| |
| CacheScope(final String cacheControl) |
| { |
| this.cacheControl = cacheControl; |
| } |
| } |
| } |