001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http.server; 018 019import java.util.List; 020 021import org.springframework.util.MultiValueMap; 022 023/** 024 * Structured representation of a URI path parsed via {@link #parsePath(String)} 025 * into a sequence of {@link Separator} and {@link PathSegment} elements. 026 * 027 * <p>Each {@link PathSegment} exposes its content in decoded form and with path 028 * parameters removed. This makes it safe to match one path segment at a time 029 * without the risk of decoded reserved characters altering the structure of 030 * the path. 031 * 032 * @author Rossen Stoyanchev 033 * @since 5.0 034 */ 035public interface PathContainer { 036 037 /** 038 * The original path from which this instance was parsed. 039 */ 040 String value(); 041 042 /** 043 * The contained path elements, either {@link Separator} or {@link PathSegment}. 044 */ 045 List<Element> elements(); 046 047 /** 048 * Extract a sub-path from the given offset into the elements list. 049 * @param index the start element index (inclusive) 050 * @return the sub-path 051 */ 052 default PathContainer subPath(int index) { 053 return subPath(index, elements().size()); 054 } 055 056 /** 057 * Extract a sub-path from the given start offset into the element list 058 * (inclusive) and to the end offset (exclusive). 059 * @param startIndex the start element index (inclusive) 060 * @param endIndex the end element index (exclusive) 061 * @return the sub-path 062 */ 063 default PathContainer subPath(int startIndex, int endIndex) { 064 return DefaultPathContainer.subPath(this, startIndex, endIndex); 065 } 066 067 068 /** 069 * Parse the path value into a sequence of {@code "/"} {@link Separator Separator} 070 * and {@link PathSegment PathSegment} elements. 071 * @param path the encoded, raw path value to parse 072 * @return the parsed path 073 */ 074 static PathContainer parsePath(String path) { 075 return DefaultPathContainer.createFromUrlPath(path, Options.HTTP_PATH); 076 } 077 078 /** 079 * Parse the path value into a sequence of {@link Separator Separator} and 080 * {@link PathSegment PathSegment} elements. 081 * @param path the encoded, raw path value to parse 082 * @param options to customize parsing 083 * @return the parsed path 084 * @since 5.2 085 */ 086 static PathContainer parsePath(String path, Options options) { 087 return DefaultPathContainer.createFromUrlPath(path, options); 088 } 089 090 091 /** 092 * A path element, either separator or path segment. 093 */ 094 interface Element { 095 096 /** 097 * The unmodified, original value of this element. 098 */ 099 String value(); 100 } 101 102 103 /** 104 * Path separator element. 105 */ 106 interface Separator extends Element { 107 } 108 109 110 /** 111 * Path segment element. 112 */ 113 interface PathSegment extends Element { 114 115 /** 116 * Return the path segment value, decoded and sanitized, for path matching. 117 */ 118 String valueToMatch(); 119 120 /** 121 * Expose {@link #valueToMatch()} as a character array. 122 */ 123 char[] valueToMatchAsChars(); 124 125 /** 126 * Path parameters associated with this path segment. 127 */ 128 MultiValueMap<String, String> parameters(); 129 } 130 131 132 /** 133 * Options to customize parsing based on the type of input path. 134 * @since 5.2 135 */ 136 class Options { 137 138 /** 139 * Options for HTTP URL paths: 140 * <p>Separator '/' with URL decoding and parsing of path params. 141 */ 142 public final static Options HTTP_PATH = Options.create('/', true); 143 144 /** 145 * Options for a message route: 146 * <p>Separator '.' without URL decoding nor parsing of params. Escape 147 * sequences for the separator char in segment values are still decoded. 148 */ 149 public final static Options MESSAGE_ROUTE = Options.create('.', false); 150 151 private final char separator; 152 153 private final boolean decodeAndParseSegments; 154 155 private Options(char separator, boolean decodeAndParseSegments) { 156 this.separator = separator; 157 this.decodeAndParseSegments = decodeAndParseSegments; 158 } 159 160 public char separator() { 161 return this.separator; 162 } 163 164 public boolean shouldDecodeAndParseSegments() { 165 return this.decodeAndParseSegments; 166 } 167 168 /** 169 * Create an {@link Options} instance with the given settings. 170 * @param separator the separator for parsing the path into segments; 171 * currently this must be slash or dot. 172 * @param decodeAndParseSegments whether to URL decode path segment 173 * values and parse path parameters. If set to false, only escape 174 * sequences for the separator char are decoded. 175 */ 176 public static Options create(char separator, boolean decodeAndParseSegments) { 177 return new Options(separator, decodeAndParseSegments); 178 } 179 } 180 181}