/** * Get HttpResourceModel which matches the HttpMethod of the request. * * @param routableDestinations List of ResourceModels. * @param targetHttpMethod HttpMethod. * @param requestUri request URI. * @return RoutableDestination that matches httpMethod that needs to be handled. null if there are * no matches. */ private List<PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel>> getMatchedDestination( List<PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel>> routableDestinations, HttpMethod targetHttpMethod, String requestUri) { Iterable<String> requestUriParts = Splitter.on('/').omitEmptyStrings().split(requestUri); List<PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel>> matchedDestinations = Lists.newArrayListWithExpectedSize(routableDestinations.size()); int maxExactMatch = 0; int maxGroupMatch = 0; int maxPatternLength = 0; for (PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel> destination : routableDestinations) { HttpResourceModel resourceModel = destination.getDestination(); int groupMatch = destination.getGroupNameValues().size(); for (HttpMethod httpMethod : resourceModel.getHttpMethod()) { if (targetHttpMethod.equals(httpMethod)) { int exactMatch = getExactPrefixMatchCount( requestUriParts, Splitter.on('/').omitEmptyStrings().split(resourceModel.getPath())); // When there are multiple matches present, the following precedence order is used - // 1. template path that has highest exact prefix match with the url is chosen. // 2. template path has the maximum groups is chosen. // 3. finally, template path that has the longest length is chosen. if (exactMatch > maxExactMatch) { maxExactMatch = exactMatch; maxGroupMatch = groupMatch; maxPatternLength = resourceModel.getPath().length(); matchedDestinations.clear(); matchedDestinations.add(destination); } else if (exactMatch == maxExactMatch && groupMatch >= maxGroupMatch) { if (groupMatch > maxGroupMatch || resourceModel.getPath().length() > maxPatternLength) { maxGroupMatch = groupMatch; maxPatternLength = resourceModel.getPath().length(); matchedDestinations.clear(); } matchedDestinations.add(destination); } } } } return matchedDestinations; }
/** * Call the appropriate handler for handling the httprequest. 404 if path is not found. 405 if * path is found but httpMethod does not match what's configured. * * @param request instance of {@code HttpRequest} * @param responder instance of {@code HttpResponder} to handle the request. * @return HttpMethodInfo object, null if urlRewriter rewrite returns false, also when method * cannot be invoked. * @throws HandlerException If URL rewriting fails */ public HttpMethodInfoBuilder getDestinationMethod(HttpRequest request, HttpResponder responder) throws HandlerException { if (urlRewriter != null) { try { request.setUri(URI.create(request.getUri()).normalize().toString()); if (!urlRewriter.rewrite(request, responder)) { return null; } } catch (Throwable t) { log.error("Exception thrown during rewriting of uri {}", request.getUri(), t); throw new HandlerException( HttpResponseStatus.INTERNAL_SERVER_ERROR, String.format("Caught exception processing request. Reason: %s", t.getMessage())); } } String acceptHeaderStr = request.headers().get(HttpHeaders.Names.ACCEPT); List<String> acceptHeader = (acceptHeaderStr != null) ? Arrays.asList(acceptHeaderStr.split("\\s*,\\s*")) .stream() .map(mediaType -> mediaType.split("\\s*;\\s*")[0]) .collect(Collectors.toList()) : null; String contentTypeHeaderStr = request.headers().get(HttpHeaders.Names.CONTENT_TYPE); // Trim specified charset since UTF-8 is assumed String contentTypeHeader = (contentTypeHeaderStr != null) ? contentTypeHeaderStr.split("\\s*;\\s*")[0] : null; try { String path = URI.create(request.getUri()).normalize().getPath(); List<PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel>> routableDestinations = patternRouter.getDestinations(path); List<PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel>> matchedDestinations = getMatchedDestination(routableDestinations, request.getMethod(), path); if (!matchedDestinations.isEmpty()) { PatternPathRouterWithGroups.RoutableDestination<HttpResourceModel> matchedDestination = matchedDestinations .stream() .filter( matchedDestination1 -> { return matchedDestination1 .getDestination() .matchConsumeMediaType(contentTypeHeader) && matchedDestination1 .getDestination() .matchProduceMediaType(acceptHeader); }) .findFirst() .get(); HttpResourceModel httpResourceModel = matchedDestination.getDestination(); // Call preCall method of handler interceptors. boolean terminated = false; ServiceMethodInfo serviceMethodInfo = new ServiceMethodInfo( httpResourceModel.getMethod().getDeclaringClass().getName(), httpResourceModel.getMethod()); for (Interceptor interceptor : interceptors) { if (!interceptor.preCall(request, responder, serviceMethodInfo)) { // Terminate further request processing if preCall returns false. terminated = true; break; } } // Call httpresource handle method, return the HttpMethodInfo Object. if (!terminated) { // Wrap responder to make post hook calls. responder = new WrappedHttpResponder(responder, interceptors, request, serviceMethodInfo); return HttpMethodInfoBuilder.getInstance() .httpResourceModel(httpResourceModel) .httpRequest(request) .httpResponder(responder) .requestInfo( matchedDestination.getGroupNameValues(), contentTypeHeader, acceptHeader); } } else if (!routableDestinations.isEmpty()) { // Found a matching resource but could not find the right HttpMethod so return 405 throw new HandlerException(HttpResponseStatus.METHOD_NOT_ALLOWED, request.getUri()); } else { throw new HandlerException( HttpResponseStatus.NOT_FOUND, String.format("Problem accessing: %s. Reason: Not Found", request.getUri())); } } catch (NoSuchElementException ex) { throw new HandlerException( HttpResponseStatus.UNSUPPORTED_MEDIA_TYPE, String.format("Problem accessing: %s. Reason: Unsupported Media Type", request.getUri()), ex); } return null; }