1 /***
2 *
3 * Copyright 2003-2005 Core Developers Network Ltd.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 package org.codehaus.wadi.web.impl;
37
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.OutputStream;
41 import java.net.URI;
42 import java.util.Enumeration;
43 import java.util.HashMap;
44 import java.util.Map;
45
46 import javax.servlet.http.HttpServletRequest;
47 import javax.servlet.http.HttpServletResponse;
48
49 import org.apache.commons.httpclient.ConnectMethod;
50 import org.apache.commons.httpclient.Cookie;
51 import org.apache.commons.httpclient.Header;
52 import org.apache.commons.httpclient.HostConfiguration;
53 import org.apache.commons.httpclient.HttpClient;
54 import org.apache.commons.httpclient.HttpMethod;
55 import org.apache.commons.httpclient.HttpState;
56 import org.apache.commons.httpclient.methods.DeleteMethod;
57 import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
58 import org.apache.commons.httpclient.methods.GetMethod;
59 import org.apache.commons.httpclient.methods.HeadMethod;
60 import org.apache.commons.httpclient.methods.MultipartPostMethod;
61 import org.apache.commons.httpclient.methods.OptionsMethod;
62 import org.apache.commons.httpclient.methods.PutMethod;
63 import org.apache.commons.httpclient.methods.TraceMethod;
64 import org.apache.commons.logging.Log;
65 import org.apache.commons.logging.LogFactory;
66
67
68
69
70
71
72
73 /***
74 * HttpProxy implementation based on commons-httpclient
75 *
76 * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
77 * @author <a href="mailto:gregw@mortbay.com">Greg Wilkins</a>
78 * @version $Revision: 1737 $
79 */
80 public class CommonsHttpProxy extends AbstractHttpProxy {
81
82 protected static final Log _log=LogFactory.getLog(CommonsHttpProxy.class);
83
84 protected static final Map _methods=new HashMap();
85 static {
86 _methods.put("CONNECT",ConnectMethod.class);
87 _methods.put("DELETE",DeleteMethod.class);
88 _methods.put("GET", GetMethod.class);
89 _methods.put("HEAD",HeadMethod.class);
90 _methods.put("OPTIONS",OptionsMethod.class);
91 _methods.put("TRACE",TraceMethod.class);
92 _methods.put("POST",MultipartPostMethod.class);
93 _methods.put("PUT",PutMethod.class);
94
95 }
96
97 public CommonsHttpProxy(String sessionPathParamKey) {
98 super(sessionPathParamKey);
99 }
100
101 protected void doProxy(URI uri, WebInvocation context) throws ProxyingException {
102 HttpServletRequest hreq = context.getHreq();
103 HttpServletResponse hres = context.getHres();
104
105 long startTime=System.currentTimeMillis();
106
107 String m=hreq.getMethod();
108 Class clazz=(Class)_methods.get(m);
109 if (clazz==null) {
110 throw new IrrecoverableException("unsupported http method: "+m);
111 }
112
113 HttpMethod hm=null;
114 try {
115 hm=(HttpMethod)clazz.newInstance();
116 } catch (Exception e) {
117 throw new IrrecoverableException("could not create HttpMethod instance", e);
118 }
119
120 String requestURI = getRequestURI(hreq);
121 hm.setPath(requestURI);
122
123 String queryString=hreq.getQueryString();
124 if (queryString!=null) {
125 hm.setQueryString(queryString);
126 requestURI += queryString;
127 }
128
129 hm.setFollowRedirects(false);
130
131 hm.setStrictMode(false);
132
133
134 String connectionHdr = hreq.getHeader("Connection");
135 if (connectionHdr != null) {
136 connectionHdr = connectionHdr.toLowerCase();
137 if (connectionHdr.equals("keep-alive")|| connectionHdr.equals("close"))
138 connectionHdr = null;
139 }
140
141
142 boolean xForwardedFor = false;
143 boolean hasContent = false;
144 int contentLength=0;
145 Enumeration enm = hreq.getHeaderNames();
146 while (enm.hasMoreElements()) {
147
148 String hdr = (String) enm.nextElement();
149 String lhdr = hdr.toLowerCase();
150
151 if (_DontProxyHeaders.contains(lhdr))
152 continue;
153 if (connectionHdr != null && connectionHdr.indexOf(lhdr) >= 0)
154 continue;
155
156 if ("content-length".equals(lhdr)) {
157 try {
158 contentLength=hreq.getIntHeader(hdr);
159 hasContent=contentLength>0;
160 } catch (NumberFormatException e) {
161 if (_log.isWarnEnabled()) _log.warn("bad Content-Length header value: "+hreq.getHeader(hdr), e);
162 }
163 }
164
165 if ("content-type".equals(lhdr)) {
166 hasContent=true;
167 }
168
169 Enumeration vals = hreq.getHeaders(hdr);
170 while (vals.hasMoreElements()) {
171 String val = (String) vals.nextElement();
172 if (val != null) {
173 hm.addRequestHeader(hdr, val);
174
175 xForwardedFor |= "X-Forwarded-For".equalsIgnoreCase(hdr);
176 }
177 }
178 }
179
180
181
182
183
184
185
186
187
188 HttpState state=new HttpState();
189 javax.servlet.http.Cookie[] cookies=hreq.getCookies();
190 if (cookies!=null)
191 {
192 for (int i=0;i<cookies.length;i++)
193 {
194 javax.servlet.http.Cookie c=cookies[i];
195 String domain=c.getDomain();
196 if (domain==null) {
197 domain=hreq.getServerName();
198
199 }
200
201 String cpath=c.getPath();
202 if (cpath==null) {
203 cpath=hreq.getContextPath();
204
205 }
206
207 Cookie cookie=new Cookie(domain, c.getName(), c.getValue(), cpath, c.getMaxAge(), c.getSecure());
208
209 state.addCookie(cookie);
210
211 }
212 }
213
214
215 hm.addRequestHeader("Via", "1.1 "+hreq.getLocalName()+":"+hreq.getLocalPort()+" \"WADI\"");
216 if (!xForwardedFor)
217 hm.addRequestHeader("X-Forwarded-For", hreq.getRemoteAddr());
218
219
220
221
222
223
224
225
226
227
228 int client2ServerTotal=0;
229 if (hasContent) {
230
231
232 try {
233 if (hm instanceof EntityEnclosingMethod)
234 ((EntityEnclosingMethod)hm).setRequestBody(hreq.getInputStream());
235
236 } catch (IOException e) {
237 throw new IrrecoverableException("could not pss request input across proxy", e);
238 }
239 }
240
241 try
242 {
243 HttpClient client=new HttpClient();
244 HostConfiguration hc=new HostConfiguration();
245
246
247 String host= uri.getHost();
248 hc.setHost(host, uri.getPort());
249 client.executeMethod(hc, hm, state);
250 }
251 catch (IOException e)
252 {
253 _log.warn("problem proxying connection:", e);
254 }
255
256 InputStream fromServer = null;
257
258
259 int code=502;
260
261
262 code=hm.getStatusCode();
263
264 hres.setStatus(code);
265
266
267 try {
268 fromServer=hm.getResponseBodyAsStream();
269 } catch (IOException e) {
270 _log.warn("problem acquiring http client output", e);
271 }
272
273
274
275 hres.setHeader("Date", null);
276 hres.setHeader("Server", null);
277
278
279
280
281 Header[] headers=hm.getResponseHeaders();
282 for (int i=0; i<headers.length; i++) {
283 String h=headers[i].toExternalForm();
284 int index=h.indexOf(':');
285 String key=h.substring(0, index).trim().toLowerCase();
286 String val=h.substring(index+1, h.length()).trim();
287 if (val!=null && !_DontProxyHeaders.contains(key)) {
288 hres.addHeader(key, val);
289
290 }
291 }
292
293 hres.addHeader("Via", "1.1 (WADI)");
294
295
296 int server2ClientTotal=0;
297 if (fromServer!=null) {
298 try {
299 OutputStream toClient=hres.getOutputStream();
300 server2ClientTotal+=copy(fromServer, toClient, 8192);
301 } catch (IOException e) {
302 _log.warn("problem proxying server response back to client", e);
303 } finally {
304 try {
305 fromServer.close();
306 } catch (IOException e) {
307
308 _log.warn("problem closing server response stream", e);
309 }
310 }
311 }
312
313 long endTime=System.currentTimeMillis();
314 long elapsed=endTime-startTime;
315 if (_log.isDebugEnabled()) {
316 _log.debug("in:" + client2ServerTotal + ", out:" + server2ClientTotal + ", status:" + code + ", time:"
317 + elapsed + ", uri:" + uri);
318 }
319 }
320 }