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 package org.apache.commons.httpclient;
33
34 import java.io.IOException;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38
39 /***
40 * <p>Wraps another method to tunnel through a proxy.</p>
41 *
42 * @author Ortwin Gl�ck
43 * @author dIon Gillard
44 * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
45 * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
46 * @since 2.0
47 * @version $Revision: 1.18.2.3 $ $Date: 2004/06/25 03:27:40 $
48 */
49 public class ConnectMethod extends HttpMethodBase {
50 /*** the name of this method */
51 public static final String NAME = "CONNECT";
52
53 /***
54 * Create a connect method wrapping the existing method
55 *
56 * @param method the {@link HttpMethod method} to execute after connecting
57 * to the server
58 */
59 public ConnectMethod(HttpMethod method) {
60 LOG.trace("enter ConnectMethod(HttpMethod)");
61 this.method = method;
62 }
63
64 /***
65 * Provide the {@link #NAME name} of this method.
66 *
67 * @return the String "CONNECT"
68 */
69 public String getName() {
70 return NAME;
71 }
72
73
74 /***
75 * This method does nothing. <tt>CONNECT</tt> request is not supposed
76 * to contain <tt>Authorization</tt> request header.
77 *
78 * @param state current state of http requests
79 * @param conn the connection to use for I/O
80 *
81 * @throws IOException when errors occur reading or writing to/from the
82 * connection
83 * @throws HttpException when a recoverable error occurs
84 *
85 * @see HttpMethodBase#addAuthorizationRequestHeader(HttpState, HttpConnection)
86 */
87 protected void addAuthorizationRequestHeader(HttpState state, HttpConnection conn)
88 throws IOException, HttpException {
89
90 }
91
92 /***
93 * This method does nothing. <tt>CONNECT</tt> request is not supposed
94 * to contain <tt>Content-Length</tt> request header.
95 *
96 * @param state current state of http requests
97 * @param conn the connection to use for I/O
98 *
99 * @throws IOException when errors occur reading or writing to/from the
100 * connection
101 * @throws HttpException when a recoverable error occurs
102 *
103 * @see HttpMethodBase#addContentLengthRequestHeader(HttpState, HttpConnection)
104 */
105 protected void addContentLengthRequestHeader(HttpState state, HttpConnection conn)
106 throws IOException, HttpException {
107
108 }
109
110 /***
111 * This method does nothing. <tt>CONNECT</tt> request is not supposed
112 * to contain <tt>Cookie</tt> request header.
113 *
114 * @param state current state of http requests
115 * @param conn the connection to use for I/O
116 *
117 * @throws IOException when errors occur reading or writing to/from the
118 * connection
119 * @throws HttpException when a recoverable error occurs
120 *
121 * @see HttpMethodBase#addCookieRequestHeader(HttpState, HttpConnection)
122 */
123 protected void addCookieRequestHeader(HttpState state, HttpConnection conn)
124 throws IOException, HttpException {
125
126 }
127
128
129 /***
130 * Populates the request headers map to with additional {@link Header
131 * headers} to be submitted to the given {@link HttpConnection}.
132 *
133 * <p>
134 * This implementation adds <tt>User-Agent</tt>, <tt>Host</tt>,
135 * and <tt>Proxy-Authorization</tt> headers, when appropriate.
136 * </p>
137 *
138 * @param state the client state
139 * @param conn the {@link HttpConnection} the headers will eventually be
140 * written to
141 * @throws IOException when an error occurs writing the request
142 * @throws HttpException when a HTTP protocol error occurs
143 *
144 * @see #writeRequestHeaders
145 */
146 protected void addRequestHeaders(HttpState state, HttpConnection conn)
147 throws IOException, HttpException {
148 LOG.trace("enter ConnectMethod.addRequestHeaders(HttpState, "
149 + "HttpConnection)");
150 addUserAgentRequestHeader(state, conn);
151 addHostRequestHeader(state, conn);
152 addProxyAuthorizationRequestHeader(state, conn);
153 addProxyConnectionHeader(state, conn);
154 }
155
156 /***
157 * Execute this method by tunnelling and then executing the wrapped method.
158 *
159 * @param state the current http state
160 * @param conn the connection to write to
161 * @return the http status code from execution
162 * @throws HttpException when an error occurs writing the headers
163 * @throws IOException when an error occurs writing the headers
164 */
165 public int execute(HttpState state, HttpConnection conn)
166 throws IOException, HttpException {
167
168 LOG.trace("enter ConnectMethod.execute(HttpState, HttpConnection)");
169 int code = super.execute(state, conn);
170 LOG.debug("CONNECT status code " + code);
171 if ((code >= 200) && (code < 300)) {
172 conn.tunnelCreated();
173 code = method.execute(state, conn);
174 } else {
175
176
177
178
179
180
181
182
183
184
185
186
187 LOG.debug("CONNECT failed, fake the response for the original method");
188 if (method instanceof HttpMethodBase) {
189
190
191
192
193
194
195
196 ((HttpMethodBase) method).fakeResponse(
197 getStatusLine(),
198 getResponseHeaderGroup(),
199 getResponseStream()
200 );
201 } else {
202 releaseConnection();
203 }
204 }
205 return code;
206 }
207
208 /***
209 * Special Connect request.
210 *
211 * @param state the current http state
212 * @param conn the connection to write to
213 * @throws IOException when an error occurs writing the request
214 * @throws HttpException when an error occurs writing the request
215 */
216 protected void writeRequestLine(HttpState state, HttpConnection conn)
217 throws IOException, HttpException {
218 int port = conn.getPort();
219 if (port == -1) {
220 port = conn.getProtocol().getDefaultPort();
221 }
222 StringBuffer buffer = new StringBuffer();
223 buffer.append(getName());
224 buffer.append(' ');
225 buffer.append(conn.getHost());
226 if (port > -1) {
227 buffer.append(':');
228 buffer.append(port);
229 }
230 buffer.append(" HTTP/1.1");
231 String line = buffer.toString();
232 conn.printLine(line);
233 if (Wire.HEADER_WIRE.enabled()) {
234 Wire.HEADER_WIRE.output(line);
235 }
236 }
237
238 /***
239 * Returns <code>true</code> if the status code is anything other than
240 * SC_OK, <code>false</code> otherwise.
241 *
242 * @param conn the connection to test
243 *
244 * @return <code>true</code> if the connection should be closed
245 *
246 * @see HttpMethodBase#shouldCloseConnection(HttpConnection)
247 * @see HttpStatus#SC_OK
248 */
249 protected boolean shouldCloseConnection(HttpConnection conn) {
250 if (getStatusCode() == HttpStatus.SC_OK) {
251 Header connectionHeader = null;
252 if (!conn.isTransparent()) {
253 connectionHeader = getResponseHeader("proxy-connection");
254 }
255 if (connectionHeader == null) {
256 connectionHeader = getResponseHeader("connection");
257 }
258 if (connectionHeader != null) {
259 if (connectionHeader.getValue().equalsIgnoreCase("close")) {
260 if (LOG.isWarnEnabled()) {
261 LOG.warn("Invalid header encountered '" + connectionHeader.toExternalForm()
262 + "' in response " + getStatusLine().toString());
263 }
264 }
265 }
266 return false;
267 } else {
268 return super.shouldCloseConnection(conn);
269 }
270 }
271
272 /*** Log object for this class. */
273 private static final Log LOG = LogFactory.getLog(ConnectMethod.class);
274
275 /*** The wrapped method */
276 private HttpMethod method;
277
278 }