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 package org.codehaus.wadi.web.impl;
36
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.OutputStream;
40 import java.net.HttpURLConnection;
41 import java.net.MalformedURLException;
42 import java.net.ProtocolException;
43 import java.net.URI;
44 import java.net.URL;
45 import java.util.Enumeration;
46
47 import javax.servlet.http.HttpServletRequest;
48 import javax.servlet.http.HttpServletResponse;
49
50 import org.apache.commons.logging.Log;
51 import org.apache.commons.logging.LogFactory;
52
53
54
55
56
57
58
59
60 /***
61 * HttpProxy implementation based on java.net.HttpURLConnection
62 *
63 * @author <a href="mailto:jules@coredevelopers.net">Jules Gosnell</a>
64 * @author <a href="mailto:gregw@mortbay.com">Greg Wilkins</a>
65 * @version $Revision: 1737 $
66 */
67 public class StandardHttpProxy extends AbstractHttpProxy {
68
69 protected static final Log _log=LogFactory.getLog(StandardHttpProxy.class);
70
71 public StandardHttpProxy(String sessionPathParamKey) {
72 super(sessionPathParamKey);
73 }
74
75 protected void doProxy(URI uri, WebInvocation context) throws ProxyingException {
76 HttpServletRequest req = context.getHreq();
77 HttpServletResponse res = context.getHres();
78
79 String requestURI=getRequestURI(req);
80 String qs=req.getQueryString();
81 if (qs!=null) {
82 requestURI=new StringBuffer(requestURI).append("?").append(qs).toString();
83 }
84
85 URL url=null;
86 try {
87 url=new URL("http", uri.getHost(), uri.getPort(), requestURI);
88 if (_log.isTraceEnabled()) _log.trace("proxying to: "+url);
89 } catch (MalformedURLException e) {
90 if (_log.isWarnEnabled()) _log.warn("bad proxy url: "+url, e);
91 throw new IrrecoverableException("bad proxy url", e);
92 }
93
94 long startTime=System.currentTimeMillis();
95
96 HttpURLConnection huc=null;
97 String m=req.getMethod();
98 try {
99 huc=(HttpURLConnection)url.openConnection();
100 huc.setRequestMethod(m);
101 } catch (ProtocolException e) {
102 if (_log.isWarnEnabled()) _log.warn("unsupported http method: "+m, e);
103 throw new IrrecoverableException("unsupported HTTP method: "+m, e);
104 } catch (IOException e) {
105 if (_log.isWarnEnabled()) _log.warn("proxy IO problem", e);
106 throw new RecoverableException("could not open proxy connection", e);
107 }
108
109 huc.setAllowUserInteraction(false);
110 huc.setInstanceFollowRedirects(false);
111
112
113
114 String connectionHdr = req.getHeader("Connection");
115 if (connectionHdr != null) {
116 connectionHdr = connectionHdr.toLowerCase();
117 if (connectionHdr.equals("keep-alive")|| connectionHdr.equals("close"))
118 connectionHdr = null;
119 }
120
121
122 {
123 for (Enumeration e=req.getHeaderNames(); e.hasMoreElements();) {
124 String hdr = (String) e.nextElement();
125 String lhdr = hdr.toLowerCase();
126
127 if (_DontProxyHeaders.contains(lhdr))
128 continue;
129 if (connectionHdr != null && connectionHdr.indexOf(lhdr) >= 0)
130 continue;
131
132 if (_WADI_IsSecure.equals(hdr))
133 continue;
134
135 for (Enumeration f=req.getHeaders(hdr); f.hasMoreElements();) {
136 String val=(String)f.nextElement();
137 if (val!=null) {
138 huc.addRequestProperty(hdr, val);
139 }
140 }
141 }
142 }
143
144
145 boolean hasContent=false;
146 {
147 int contentLength=0;
148 String tmp=huc.getRequestProperty("Content-Length");
149 if (tmp!=null) {
150 try {
151 contentLength=Integer.parseInt(tmp);
152 } catch (NumberFormatException ignore) {
153
154 }
155 }
156
157 if (contentLength>0)
158 hasContent=true;
159 else
160 hasContent=(huc.getRequestProperty("Content-Type")!=null);
161 }
162
163
164 {
165 huc.addRequestProperty("Via", "1.1 "+req.getLocalName()+":"+req.getLocalPort()+" \"WADI\"");
166 huc.addRequestProperty("X-Forwarded-For", req.getRemoteAddr());
167
168 }
169
170
171 {
172 String cacheControl=huc.getRequestProperty("Cache-Control");
173 if (cacheControl!=null && (cacheControl.indexOf("no-cache")>=0 || cacheControl.indexOf("no-store")>=0))
174 huc.setUseCaches(false);
175 }
176
177
178 {
179 if (req.isSecure()) {
180 huc.addRequestProperty(_WADI_IsSecure, req.getLocalAddr().toString());
181 }
182
183
184
185
186
187
188
189
190
191 }
192
193 huc.setDoInput(true);
194
195
196 int client2ServerTotal=0;
197 {
198 if (hasContent) {
199 huc.setDoOutput(true);
200
201 OutputStream toServer=null;
202 try {
203 InputStream fromClient=req.getInputStream();
204 toServer=huc.getOutputStream();
205 client2ServerTotal=copy(fromClient, toServer, 8192);
206 } catch (IOException e) {
207 new IrrecoverableException("problem proxying client request to server", e);
208 } finally {
209 if (toServer!=null) {
210 try {
211 toServer.close();
212 } catch (IOException e) {
213 _log.warn("problem closing server request stream", e);
214 }
215 }
216 }
217 }
218 }
219
220
221 try {
222 huc.connect();
223 } catch (IOException e) {
224 if (_log.isWarnEnabled()) _log.warn("proxy connection problem: "+url, e);
225 throw new RecoverableException("could not connect to proxy target", e);
226 }
227
228 InputStream fromServer=null;
229
230
231 int code=0;
232 if (huc==null) {
233 try {
234 fromServer = huc.getInputStream();
235 } catch (IOException e) {
236 if (_log.isWarnEnabled()) _log.warn("proxying problem", e);
237 throw new IrrecoverableException("problem acquiring client output", e);
238 }
239 } else {
240 code=502;
241
242 try {
243 code=huc.getResponseCode();
244
245 } catch (IOException e) {
246 if (_log.isWarnEnabled()) _log.warn("proxying problem", e);
247 throw new IrrecoverableException("problem acquiring http server response code/message", e);
248 } finally {
249
250 res.setStatus(code);
251 }
252
253 if (code<400) {
254
255 try {
256 fromServer=huc.getInputStream();
257 } catch (IOException e) {
258 if (_log.isWarnEnabled()) _log.warn("proxying problem", e);
259 throw new IrrecoverableException("problem acquiring http client output", e);
260 }
261 } else {
262
263 fromServer = huc.getErrorStream();
264
265 }
266 }
267
268
269 res.setHeader("Date", null);
270 res.setHeader("Server", null);
271
272
273 if (false) {
274 int h = 0;
275 String hdr = huc.getHeaderFieldKey(h);
276 String val = huc.getHeaderField(h);
277 while (hdr != null || val != null) {
278 String lhdr = (hdr != null) ? hdr.toLowerCase() : null;
279 if (hdr != null && val != null && !_DontProxyHeaders.contains(lhdr))
280 res.addHeader(hdr, val);
281
282
283
284 h++;
285 hdr = huc.getHeaderFieldKey(h);
286 val = huc.getHeaderField(h);
287 }
288 } else {
289
290
291 String key;
292 for (int i=1; (key=huc.getHeaderFieldKey(i))!=null; i++) {
293 key=key.toLowerCase();
294 String val=huc.getHeaderField(i);
295 if (val!=null && !_DontProxyHeaders.contains(key)) {
296 res.addHeader(key, val);
297 }
298 }
299 }
300
301
302
303
304 int server2ClientTotal=0;
305 {
306 if (fromServer!=null) {
307 try {
308 OutputStream toClient=res.getOutputStream();
309 server2ClientTotal+=copy(fromServer, toClient, 8192);
310 } catch (IOException e) {
311 if (_log.isWarnEnabled()) _log.warn("proxying problem", e);
312 throw new IrrecoverableException("problem proxying server response back to client", e);
313 } finally {
314 try {
315 fromServer.close();
316 } catch (IOException e) {
317
318 _log.warn("problem closing server response stream", e);
319 }
320 }
321 }
322 }
323
324 huc.disconnect();
325
326 long endTime=System.currentTimeMillis();
327 long elapsed=endTime-startTime;
328 if (_log.isDebugEnabled()) _log.debug("in:"+client2ServerTotal+", out:"+server2ClientTotal+", status:"+code+", time:"+elapsed+", url:"+url);
329 }
330
331 }