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}