View Javadoc
1   /*
2    * Copyright (C) 2009-2010, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.http.server.glue;
12  
13  import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
14  import static org.eclipse.jgit.http.server.glue.MetaFilter.REGEX_GROUPS;
15  
16  import java.io.IOException;
17  import java.util.regex.Matcher;
18  import java.util.regex.Pattern;
19  
20  import javax.servlet.Filter;
21  import javax.servlet.ServletException;
22  import javax.servlet.http.HttpServlet;
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpServletResponse;
25  
26  /**
27   * Selects requests by matching the URI against a regular expression.
28   * <p>
29   * The pattern is bound and matched against the path info of the servlet
30   * request, as this class assumes it is invoked by {@link MetaServlet}.
31   * <p>
32   * If there are capture groups in the regular expression, the matched ranges of
33   * the capture groups are stored as an array of modified HttpServetRequests,
34   * into the request attribute {@link MetaFilter#REGEX_GROUPS}. Using a capture
35   * group that may not capture, e.g. {@code "(/foo)?"}, will cause an error at
36   * request handling time.
37   * <p>
38   * Each servlet request has been altered to have its {@code getServletPath()}
39   * method return the original path info up to the beginning of the corresponding
40   * capture group, and its {@code getPathInfo()} method return the matched text.
41   * A {@link RegexGroupFilter} can be applied in the pipeline to switch the
42   * current HttpServletRequest to reference a different capture group before
43   * running additional filters, or the final servlet.
44   * <p>
45   * Note that for {@code getPathInfo()} to start with a leading "/" as described
46   * in the servlet documentation, capture groups must actually capture the
47   * leading "/".
48   * <p>
49   * This class dispatches the remainder of the pipeline using the first capture
50   * group as the current request, making {@code RegexGroupFilter} required only
51   * to access capture groups beyond the first.
52   */
53  class RegexPipeline extends UrlPipeline {
54  	static class Binder extends ServletBinderImpl {
55  		private final Pattern pattern;
56  
57  		Binder(String p) {
58  			pattern = Pattern.compile(p);
59  		}
60  
61  		Binder(Pattern p) {
62  			pattern = p;
63  		}
64  
65  		@Override
66  		UrlPipeline create() {
67  			return new RegexPipeline(pattern, getFilters(), getServlet());
68  		}
69  	}
70  
71  	private final Pattern pattern;
72  
73  	RegexPipeline(final Pattern pattern, final Filter[] filters,
74  			final HttpServlet servlet) {
75  		super(filters, servlet);
76  		this.pattern = pattern;
77  	}
78  
79  	@Override
80  	boolean match(HttpServletRequest req) {
81  		final String pathInfo = req.getPathInfo();
82  		return pathInfo != null && pattern.matcher(pathInfo).matches();
83  	}
84  
85  	@Override
86  	void service(HttpServletRequest req, HttpServletResponse rsp)
87  			throws ServletException, IOException {
88  		final String reqInfo = req.getPathInfo();
89  		if (reqInfo == null) {
90  			rsp.sendError(SC_NOT_FOUND);
91  			return;
92  		}
93  
94  		final Matcher cur = pattern.matcher(reqInfo);
95  		if (!cur.matches()) {
96  			rsp.sendError(SC_NOT_FOUND);
97  			return;
98  		}
99  
100 		final String reqPath = req.getServletPath();
101 		final Object old = req.getAttribute(REGEX_GROUPS);
102 		try {
103 			if (1 <= cur.groupCount()) {
104 				// If there are groups extract every capture group and
105 				// build a request for them so RegexGroupFilter can pick
106 				// a different capture group later. Continue using the
107 				// first capture group as the path info.
108 				WrappedRequest[] groups = new WrappedRequest[cur.groupCount()];
109 				for (int groupId = 1; groupId <= cur.groupCount(); groupId++) {
110 					final int s = cur.start(groupId);
111 					final String path, info;
112 
113 					path = reqPath + reqInfo.substring(0, s);
114 					info = cur.group(groupId);
115 					groups[groupId - 1] = new WrappedRequest(req, path, info);
116 				}
117 				req.setAttribute(REGEX_GROUPS, groups);
118 				super.service(groups[0], rsp);
119 
120 			} else {
121 				// No capture groups were present, service the whole request.
122 				final String path = reqPath + reqInfo;
123 				final String info = null;
124 				super.service(new WrappedRequest(req, path, info), rsp);
125 			}
126 		} finally {
127 			if (old != null)
128 				req.setAttribute(REGEX_GROUPS, old);
129 			else
130 				req.removeAttribute(REGEX_GROUPS);
131 		}
132 	}
133 
134 	/** {@inheritDoc} */
135 	@Override
136 	public String toString() {
137 		return "Pipeline[regex: " + pattern + " ]";
138 	}
139 }