55import java .io .PrintWriter ;
66import java .io .StringWriter ;
77import java .nio .charset .Charset ;
8+ import java .util .ArrayList ;
89import java .util .Arrays ;
910import java .util .LinkedHashMap ;
1011import java .util .List ;
@@ -149,13 +150,14 @@ private void doHandle(final String verb, final String requestURI,
149150 }
150151 });
151152
152- final Optional <RouteDescriptor > descriptor = routes (routes , path , contentType , accept );
153+ final List <RouteDescriptor > descriptors = routerChain (routes , verb , requestURI , contentType ,
154+ accept );
153155
154- final List <MediaType > produces = descriptor . map ( d -> d . produces ). orElse ( accept ) ;
156+ final List <MediaType > produces = descriptors . size () > 0 ? descriptors . get ( 0 ). produces : accept ;
155157 log .trace (" produces: {}" , produces );
156158
157159 final Request request = reqFactory .newRequest (injector ,
158- descriptor . map ( d -> d . matcher ). orElse ( noMatch (requestURI ) ),
160+ descriptors . size () > 0 ? descriptors . get ( 0 ). matcher : noMatch (requestURI ),
159161 selector ,
160162 charset ,
161163 contentType ,
@@ -174,13 +176,13 @@ private void doHandle(final String verb, final String requestURI,
174176 interceptor .before (request , response );
175177 }
176178
177- RouteDefinition routeDefinition = descriptor
178- .orElseThrow (() -> sendError (routes , verb , requestURI , contentType , accept ))
179- .definition ;
180- Route route = routeDefinition .route ();
179+ for (int idx = 0 ; idx < descriptors .size () && !response .committed (); idx ++) {
180+ RouteDefinition routeDefinition = descriptors .get (idx ).definition ;
181+ Route route = routeDefinition .route ();
181182
182- // invoke route
183- route .handle (request , response );
183+ // invoke route
184+ route .handle (request , response );
185+ }
184186
185187 // after callback
186188 for (RouteInterceptor interceptor : interceptors ) {
@@ -218,7 +220,7 @@ private void doHandle(final String verb, final String requestURI,
218220 }
219221 }
220222
221- private RouteMatcher noMatch (final String path ) {
223+ private static RouteMatcher noMatch (final String path ) {
222224 return new RouteMatcher () {
223225
224226 @ Override
@@ -233,20 +235,52 @@ public boolean matches() {
233235 };
234236 }
235237
236- private static Optional <RouteDescriptor > routes (final Set <RouteDefinition > routes ,
238+ private static List <RouteDescriptor > routerChain (final Set <RouteDefinition > routes ,
239+ final String verb ,
240+ final String requestURI ,
241+ final MediaType contentType ,
242+ final List <MediaType > accept ) {
243+ String path = verb + requestURI ;
244+ List <RouteDescriptor > routers = routers (routes , path , contentType , accept );
245+
246+ // 406 or 415
247+ routers .add (new RouteDescriptor (new RouteDefinitionImpl (verb , path , (req , resp ) -> {
248+ HttpException ex = throw406or415 (routes , verb , requestURI , contentType , accept );
249+ if (ex != null ) {
250+ throw ex ;
251+ }
252+ }), noMatch (path ), accept ));
253+
254+ // 405
255+ routers .add (new RouteDescriptor (new RouteDefinitionImpl (verb , path , (req , resp ) -> {
256+ HttpException ex = throw405 (routes , verb , requestURI , contentType , accept );
257+ if (ex != null ) {
258+ throw ex ;
259+ }
260+ }), noMatch (path ), accept ));
261+
262+ // 404
263+ routers .add (new RouteDescriptor (new RouteDefinitionImpl (verb , path , (req , resp ) -> {
264+ throw new HttpException (HttpStatus .NOT_FOUND , path );
265+ }), noMatch (path ), accept ));
266+ return routers ;
267+ }
268+
269+ private static List <RouteDescriptor > routers (final Set <RouteDefinition > routes ,
237270 final String path ,
238271 final MediaType contentType ,
239272 final List <MediaType > accept ) {
273+ List <RouteDescriptor > routers = new ArrayList <RouteDescriptor >();
240274 for (RouteDefinition route : routes ) {
241275 RouteMatcher matcher = route .matcher (path );
242276 if (matcher .matches ()) {
243277 List <MediaType > produces = MediaType .matcher (accept ).filter (route .produces ());
244278 if (route .canConsume (contentType ) && produces .size () > 0 ) {
245- return Optional . of (new RouteDescriptor (route , matcher , produces ));
279+ routers . add (new RouteDescriptor (route , matcher , produces ));
246280 }
247281 }
248282 }
249- return Optional . empty () ;
283+ return routers ;
250284 }
251285
252286 private void defaultErrorPage (final Request request , final Response response ,
@@ -363,8 +397,8 @@ private static HttpException throw405(final Set<RouteDefinition> routes,
363397 verbs .remove (verb );
364398 for (String candidate : verbs ) {
365399 String path = candidate + requestURI ;
366- Optional <RouteDescriptor > holder = routes (routes , path , contentType , accept );
367- if (holder . isPresent () ) {
400+ List <RouteDescriptor > routers = routers (routes , path , contentType , accept );
401+ if (routers . size () > 0 ) {
368402 return new HttpException (HttpStatus .METHOD_NOT_ALLOWED , verb + requestURI );
369403 }
370404 }
@@ -378,7 +412,7 @@ private static HttpException throw406or415(final Set<RouteDefinition> routes,
378412 String path = verb + requestURI ;
379413 for (RouteDefinition route : routes ) {
380414 RouteMatcher matcher = route .matcher (path );
381- if (matcher .matches ()) {
415+ if (matcher .matches () && ! route . path (). pattern (). endsWith ( "/**/*" ) ) {
382416 if (!route .canProduce (accept )) {
383417 return new HttpException (HttpStatus .NOT_ACCEPTABLE , accept .stream ()
384418 .map (MediaType ::name )
@@ -390,16 +424,6 @@ private static HttpException throw406or415(final Set<RouteDefinition> routes,
390424 return null ;
391425 }
392426
393- private HttpException sendError (final Set <RouteDefinition > routes ,
394- final String method , final String requestURI ,
395- final MediaType contentType ,
396- final List <MediaType > accept ) {
397- return Optional .ofNullable (
398- Optional .ofNullable (throw406or415 (routes , method , requestURI , contentType , accept ))
399- .orElseGet (() -> throw405 (routes , method , requestURI , contentType , accept ))
400- ).orElseGet (() -> new HttpException (HttpStatus .NOT_FOUND , method + requestURI ));
401- }
402-
403427 @ Override
404428 public String toString () {
405429 StringBuilder buffer = new StringBuilder ();
0 commit comments