-
Notifications
You must be signed in to change notification settings - Fork 4
/
normalized_uri_helpers.clj
141 lines (130 loc) · 6.3 KB
/
normalized_uri_helpers.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
(ns puppetlabs.trapperkeeper.services.webserver.normalized-uri-helpers
(:require [puppetlabs.i18n.core :as i18n]
[ring.util.servlet :as servlet]
[schema.core :as schema])
(:import (com.puppetlabs.trapperkeeper.services.webserver.jetty10.utils
HttpServletRequestWithAlternateRequestUri)
(java.util EnumSet)
(javax.servlet DispatcherType Filter)
(javax.servlet.http HttpServletRequest HttpServletResponse)
(org.eclipse.jetty.server Request)
(org.eclipse.jetty.server.handler AbstractHandler HandlerWrapper)
(org.eclipse.jetty.servlet FilterHolder ServletContextHandler)
(org.eclipse.jetty.util URIUtil)))
(schema/defn ^:always-validate normalize-uri-path :- schema/Str
"Return a 'normalized' version of the uri path represented on the incoming
request. The 'normalization' consists of three steps:
1) URL (percent) decode the path, assuming any percent-encodings represent
UTF-8 characters.
An exception may be thrown if the request has malformed content, e.g.,
partially-formed percent-encoded characters like '%A%B'.
If a semicolon character, U+003B, is found during the decoding process, it
and any following characters will be removed from the decoded path.
2) Check the percent-decoded path for any relative path segments ('..' or
'.').
An IllegalArgumentException is thrown if one or more segments are found.
3) Compact any repeated forward slash characters in a path."
[request :- HttpServletRequest]
(let [percent-decoded-uri-path (-> request
(.getRequestURI)
(URIUtil/decodePath))
canonicalized-uri-path (URIUtil/canonicalPath percent-decoded-uri-path)]
(if (or (nil? canonicalized-uri-path)
(not= (.length percent-decoded-uri-path)
(.length canonicalized-uri-path)))
(throw (IllegalArgumentException.
^String (i18n/trs "Invalid relative path (.. or .) in: {0}"
percent-decoded-uri-path)))
(URIUtil/compactPath canonicalized-uri-path))))
(schema/defn ^:always-validate
normalize-uri-handler :- HandlerWrapper
"Create a `HandlerWrapper` which provides a normalized request URI on to
its downstream handler for an incoming request. The normalized URI would
be returned for a 'getRequestURI' call made by the downstream handler on
its incoming HttpServletRequest request parameter. Normalization is done
per the rules described in the `normalize-uri-path` function. If an error
is encountered during request URI normalization, an HTTP 400 (Bad Request)
response is returned rather than the request being passed on its downstream
handler."
[]
(proxy [HandlerWrapper] []
(handle [^String target ^Request base-request
^HttpServletRequest request ^HttpServletResponse response]
(when-let [handler (proxy-super getHandler)]
(if-let [normalized-uri
(try
(normalize-uri-path request)
(catch IllegalArgumentException ex
(do
(servlet/update-servlet-response
response
{:status 400
:body (.getMessage ex)})
(.setHandled base-request true))
nil))]
(.handle
handler
target
base-request
(HttpServletRequestWithAlternateRequestUri.
request
normalized-uri)
response))))))
(schema/defn ^:always-validate normalized-uri-filter :- Filter
"Create a servlet filter which provides a normalized request URI on to its
downstream consumers for an incoming request. The normalized URI would be
returned for a 'getRequestURI' call on the HttpServletRequest parameter.
Normalization is done per the rules described in the `normalize-uri-path`
function. If an error is encountered during request URI normalization, an
HTTP 400 (Bad Request) response is returned rather than the request being
passed on its downstream consumers."
[]
(reify Filter
(init [_ _])
(doFilter [_ request response chain]
;; The method signature for a servlet filter has a 'request' of the
;; more generic 'ServletRequest' and 'response' of the more generic
;; 'ServletResponse'. While we practically shouldn't see anything
;; but the more specific Http types for each, this code explicitly
;; checks to see that the requests are Http types as the URI
;; normalization would be irrelevant for other types.
(if (and (instance? HttpServletRequest request)
(instance? HttpServletResponse response))
(if-let [normalized-uri
(try
(normalize-uri-path request)
(catch IllegalArgumentException ex
(servlet/update-servlet-response
response
{:status 400
:body (.getMessage ex)})
nil))]
(.doFilter chain
(HttpServletRequestWithAlternateRequestUri.
request
normalized-uri)
response))
(.doFilter chain request response)))
(destroy [_])))
(schema/defn ^:always-validate
add-normalized-uri-filter-to-servlet-handler!
"Adds a servlet filter to the servlet handler which provides a normalized
request URI on to its downstream consumers for an incoming request."
[handler :- ServletContextHandler]
(let [filter-holder (FilterHolder. (normalized-uri-filter))]
(.addFilter handler
filter-holder
"/*"
(EnumSet/of DispatcherType/REQUEST))))
(schema/defn ^:always-validate
handler-maybe-wrapped-with-normalized-uri :- AbstractHandler
"If the supplied `normalize-request-uri?` parameter is 'true', return a
handler that normalizes a request uri before passing it on downstream to
the supplied handler for an incoming request. If the supplied
`normalize-request-uri?` is 'false', return the supplied handler."
[handler :- AbstractHandler
normalize-request-uri? :- schema/Bool]
(if normalize-request-uri?
(doto (normalize-uri-handler)
(.setHandler handler))
handler))