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