1 /*
2  * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.net.httpserver;
27 
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.util.List;
32 import java.util.ListIterator;
33 import java.util.Objects;
34 import java.util.function.Consumer;
35 
36 /**
37  * A filter used to pre- and post-process incoming requests. Pre-processing occurs
38  * before the application's exchange handler is invoked, and post-processing
39  * occurs after the exchange handler returns. Filters are organised in chains,
40  * and are associated with {@link HttpContext} instances.
41  *
42  * <p> Each {@code Filter} in the chain, invokes the next filter within its own
43  * {@link #doFilter(HttpExchange, Chain)} implementation. The final {@code Filter}
44  * in the chain invokes the applications exchange handler.
45  *
46  * @since 1.6
47  */
48 public abstract class Filter {
49 
50     /**
51      * Constructor for subclasses to call.
52      */
Filter()53     protected Filter () {}
54 
55     /**
56      * A chain of filters associated with a {@link HttpServer}.
57      * Each filter in the chain is given one of these so it can invoke the
58      * next filter in the chain.
59      */
60     public static class Chain {
61 
62         /**
63          * The last element in the chain must invoke the user's
64          * handler.
65          */
66         private ListIterator<Filter> iter;
67         private HttpHandler handler;
68 
69         /**
70          * Creates a {@code Chain} instance with given filters and handler.
71          *
72          * @param filters the filters that make up the {@code Chain}
73          * @param handler the {@link HttpHandler} that will be invoked after
74          *                the final {@code Filter} has finished
75          */
Chain(List<Filter> filters, HttpHandler handler)76         public Chain (List<Filter> filters, HttpHandler handler) {
77             iter = filters.listIterator();
78             this.handler = handler;
79         }
80 
81         /**
82          * Calls the next filter in the chain, or else the users exchange
83          * handler, if this is the final filter in the chain. The {@code Filter}
84          * may decide to terminate the chain, by not calling this method.
85          * In this case, the filter <b>must</b> send the response to the
86          * request, because the application's {@linkplain HttpExchange exchange}
87          * handler will not be invoked.
88          *
89          * @param exchange the {@code HttpExchange}
90          * @throws IOException if an I/O error occurs
91          * @throws NullPointerException if exchange is {@code null}
92          */
doFilter(HttpExchange exchange)93         public void doFilter (HttpExchange exchange) throws IOException {
94             if (!iter.hasNext()) {
95                 handler.handle (exchange);
96             } else {
97                 Filter f = iter.next();
98                 f.doFilter (exchange, this);
99             }
100         }
101     }
102 
103     /**
104      * Asks this filter to pre/post-process the given exchange. The filter
105      * can:
106      *
107      * <ul>
108      *     <li> Examine or modify the request headers.
109      *     <li> Filter the request body or the response body, by creating suitable
110      *     filter streams and calling {@link HttpExchange#setStreams(InputStream, OutputStream)}.
111      *     <li> Set attribute objects in the exchange, which other filters or
112      *     the exchange handler can access.
113      *     <li> Decide to either:
114      *
115      *     <ol>
116      *         <li> Invoke the next filter in the chain, by calling
117      *         {@link Filter.Chain#doFilter(HttpExchange)}.
118      *         <li> Terminate the chain of invocation, by <b>not</b> calling
119      *         {@link Filter.Chain#doFilter(HttpExchange)}.
120      *     </ol>
121      *
122      *     <li> If option 1. above is taken, then when doFilter() returns all subsequent
123      *     filters in the Chain have been called, and the response headers can be
124      *     examined or modified.
125      *     <li> If option 2. above is taken, then this Filter must use the HttpExchange
126      *     to send back an appropriate response.
127      * </ul>
128      *
129      * @param exchange the {@code HttpExchange} to be filtered
130      * @param chain the {@code Chain} which allows the next filter to be invoked
131      * @throws IOException may be thrown by any filter module, and if caught,
132      * must be rethrown again
133      * @throws NullPointerException if either exchange or chain are {@code null}
134      */
doFilter(HttpExchange exchange, Chain chain)135     public abstract void doFilter (HttpExchange exchange, Chain chain)
136         throws IOException;
137 
138     /**
139      * Returns a short description of this {@code Filter}.
140      *
141      * @return a {@code String} describing the {@code Filter}
142      */
description()143     public abstract String description ();
144 
145     /**
146      * Returns a pre-processing {@code Filter} with the given description and
147      * operation.
148      *
149      * <p>The {@link Consumer operation} is the effective implementation of the
150      * filter. It is executed for each {@code HttpExchange} before invoking
151      * either the next filter in the chain or the exchange handler (if this is
152      * the final filter in the chain). Exceptions thrown by the
153      * {@code operation} are not handled by the filter.
154      *
155      * @apiNote
156      * A beforeHandler filter is typically used to examine or modify the
157      * exchange state before it is handled. The filter {@code operation} is
158      * executed before {@link Filter.Chain#doFilter(HttpExchange)} is invoked,
159      * so before any subsequent filters in the chain and the exchange handler
160      * are executed. The filter {@code operation} is not expected to handle the
161      * request or {@linkplain HttpExchange#sendResponseHeaders(int, long) send response headers},
162      * since this is commonly done by the exchange handler.
163      *
164      * <p> Example of adding the {@code "Foo"} response header to all responses:
165      * <pre>{@code
166      *     var filter = Filter.beforeHandler("Add response header Foo",
167      *                 e -> e.getResponseHeaders().set("Foo", "Bar"));
168      *     httpContext.getFilters().add(filter);
169      * }</pre>
170      *
171      * @param description the string to be returned from {@link #description()}
172      * @param operation the operation of the returned filter
173      * @return a filter whose operation is invoked before the exchange is handled
174      * @throws NullPointerException if any argument is null
175      * @since 17
176      */
beforeHandler(String description, Consumer<HttpExchange> operation)177     public static Filter beforeHandler(String description,
178                                        Consumer<HttpExchange> operation) {
179         Objects.requireNonNull(description);
180         Objects.requireNonNull(operation);
181         return new Filter() {
182             @Override
183             public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
184                 operation.accept(exchange);
185                 chain.doFilter(exchange);
186             }
187             @Override
188             public String description() {
189                 return description;
190             }
191         };
192     }
193 
194     /**
195      * Returns a post-processing {@code Filter} with the given description and
196      * operation.
197      *
198      * <p>The {@link Consumer operation} is the effective implementation of the
199      * filter. It is executed for each {@code HttpExchange} after invoking
200      * either the next filter in the chain or the exchange handler (if this
201      * filter is the final filter in the chain). Exceptions thrown by the
202      * {@code operation} are not handled by the filter.
203      *
204      * @apiNote
205      * An afterHandler filter is typically used to examine the exchange state
206      * rather than modifying it. The filter {@code operation} is executed after
207      * {@link Filter.Chain#doFilter(HttpExchange)} is invoked, this means any
208      * subsequent filters in the chain and the exchange handler have been
209      * executed. The filter {@code operation} is not expected to handle the
210      * exchange or {@linkplain HttpExchange#sendResponseHeaders(int, long) send the response headers}.
211      * Doing so is likely to fail, since the exchange has commonly been handled
212      * before the {@code operation} is invoked. More specifically, the response
213      * may be sent before the filter {@code operation} is executed.
214      *
215      * <p> Example of adding a filter that logs the response code of all exchanges:
216      * <pre>{@code
217      *     var filter = Filter.afterHandler("Log response code", e -> log(e.getResponseCode());
218      *     httpContext.getFilters().add(filter);
219      * }</pre>
220      *
221      * <p> Example of adding a sequence of afterHandler filters to a context:<br>
222      * The order in which the filter operations are invoked is reverse to the
223      * order in which the filters are added to the context's filter-list.
224      *
225      * <pre>{@code
226      *     var a1Set = Filter.afterHandler("Set a1", e -> e.setAttribute("a1", "some value"));
227      *     var a1Get = Filter.afterHandler("Get a1", e -> doSomething(e.getAttribute("a1")));
228      *     httpContext.getFilters().addAll(List.of(a1Get, a1Set));
229      * }</pre>
230      * <p>The operation of {@code a1Get} will be invoked after the operation of
231      * {@code a1Set} because {@code a1Get} was added before {@code a1Set}.
232      *
233      * @param description the string to be returned from {@link #description()}
234      * @param operation the operation of the returned filter
235      * @return a filter whose operation is invoked after the exchange is handled
236      * @throws NullPointerException if any argument is null
237      * @since 17
238      */
239     public static Filter afterHandler(String description,
240                                       Consumer<HttpExchange> operation) {
241         Objects.requireNonNull(description);
242         Objects.requireNonNull(operation);
243         return new Filter() {
244             @Override
245             public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
246                 chain.doFilter(exchange);
247                 operation.accept(exchange);
248             }
249             @Override
250             public String description() {
251                 return description;
252             }
253         };
254     }
255 }
256