ConnectMethod.java
  1  /*
  2   * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/ConnectMethod.java,v 1.29 2004/06/24 21:39:52 mbecke Exp $
  3   * $Revision: 483949 $
  4   * $Date: 2006-12-08 12:34:50 +0100 (Fri, 08 Dec 2006) $
  5   *
  6   * ====================================================================
  7   *
  8   *  Licensed to the Apache Software Foundation (ASF) under one or more
  9   *  contributor license agreements.  See the NOTICE file distributed with
 10   *  this work for additional information regarding copyright ownership.
 11   *  The ASF licenses this file to You under the Apache License, Version 2.0
 12   *  (the "License"); you may not use this file except in compliance with
 13   *  the License.  You may obtain a copy of the License at
 14   *
 15   *      http://www.apache.org/licenses/LICENSE-2.0
 16   *
 17   *  Unless required by applicable law or agreed to in writing, software
 18   *  distributed under the License is distributed on an "AS IS" BASIS,
 19   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 20   *  See the License for the specific language governing permissions and
 21   *  limitations under the License.
 22   * ====================================================================
 23   *
 24   * This software consists of voluntary contributions made by many
 25   * individuals on behalf of the Apache Software Foundation.  For more
 26   * information on the Apache Software Foundation, please see
 27   * <http://www.apache.org/>.
 28   *
 29   */
 30  
 31  package org.apache.commons.httpclient;
 32  
 33  import java.io.IOException;
 34  
 35  import org.apache.commons.logging.Log;
 36  import org.apache.commons.logging.LogFactory;
 37  
 38  /**
 39   * Establishes a tunneled HTTP connection via the CONNECT method.
 40   *
 41   * @author Ortwin Gl???ck
 42   * @author dIon Gillard
 43   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
 44   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
 45   * @since 2.0
 46   * @version $Revision: 483949 $ $Date: 2006-12-08 12:34:50 +0100 (Fri, 08 Dec 2006) $
 47   */
 48  public class ConnectMethod extends HttpMethodBase {
 49      
 50      /** the name of this method */
 51      public static final String NAME = "CONNECT";
 52  
 53      private final HostConfiguration targethost;
 54  
 55      /**
 56       * @deprecated use #ConnectMethod(HttpHost);
 57       * 
 58       * Create a connect method.
 59       * 
 60       * @since 3.0
 61       */
 62      public ConnectMethod() {
 63          super();
 64          this.targethost = null;
 65      }
 66  
 67      /**
 68       * @deprecated the wrapped method is no longer used
 69       * 
 70       * Create a connect method wrapping the existing method
 71       *
 72       * @param method the {@link HttpMethod method} to execute after connecting
 73       *      to the server
 74       */
 75      public ConnectMethod(HttpMethod method) {
 76          super();
 77          this.targethost = null;
 78      }
 79  
 80      /**
 81       * Create a connect method.
 82       * 
 83       * @since 3.0
 84       */
 85      public ConnectMethod(final HostConfiguration targethost) {
 86          super();
 87          if (targethost == null) {
 88              throw new IllegalArgumentException("Target host may not be null");
 89          }
 90          this.targethost = targethost;
 91      }
 92  
 93      /**
 94       * Provide the {@link #NAME name} of this method.
 95       *
 96       * @return the String "CONNECT"
 97       */
 98      public String getName() {
 99          return NAME;
100      }
101      
102      public String getPath() {
103          if (this.targethost != null) {
104              StringBuffer buffer = new StringBuffer();
105              buffer.append(this.targethost.getHost()); 
106              int port = this.targethost.getPort();
107              if (port == -1) {
108                  port = this.targethost.getProtocol().getDefaultPort();  
109              }
110              buffer.append(':'); 
111              buffer.append(port);
112              return buffer.toString();
113          } else {
114              return "/";
115          }
116      }
117  
118      public URI getURI() throws URIException {
119          String charset = getParams().getUriCharset();
120          return new URI(getPath(), true, charset);
121      }
122  
123      /**
124       * This method does nothing. <tt>CONNECT</tt> request is not supposed 
125       * to contain <tt>Cookie</tt> request header.
126       *
127       * @param state current state of http requests
128       * @param conn the connection to use for I/O
129       *
130       * @throws IOException when errors occur reading or writing to/from the
131       *         connection
132       * @throws HttpException when a recoverable error occurs
133       *  
134       * @see HttpMethodBase#addCookieRequestHeader(HttpState, HttpConnection)
135       */
136      protected void addCookieRequestHeader(HttpState state, HttpConnection conn)
137          throws IOException, HttpException {
138          // Do nothing. Not applicable to CONNECT method
139      }
140  
141  
142      /**
143       * Populates the request headers map to with additional {@link Header
144       * headers} to be submitted to the given {@link HttpConnection}.
145       *
146       * <p>
147       * This implementation adds <tt>User-Agent</tt>, <tt>Host</tt>,
148       * and <tt>Proxy-Authorization</tt> headers, when appropriate.
149       * </p>
150       *
151       * @param state the client state
152       * @param conn the {@link HttpConnection} the headers will eventually be
153       *        written to
154       * @throws IOException when an error occurs writing the request
155       * @throws HttpException when a HTTP protocol error occurs
156       *
157       * @see #writeRequestHeaders
158       */
159      protected void addRequestHeaders(HttpState state, HttpConnection conn)
160          throws IOException, HttpException {
161          LOG.trace("enter ConnectMethod.addRequestHeaders(HttpState, "
162              + "HttpConnection)");
163          addUserAgentRequestHeader(state, conn);
164          addHostRequestHeader(state, conn);
165          addProxyConnectionHeader(state, conn);
166      }
167  
168      /**
169       * Execute this method and create a tunneled HttpConnection.  If the method
170       * is successful (i.e. the status is a 2xx) tunnelCreated() will be called
171       * on the connection.
172       *
173       * @param state the current http state
174       * @param conn the connection to write to
175       * @return the http status code from execution
176       * @throws HttpException when an error occurs writing the headers
177       * @throws IOException when an error occurs writing the headers
178       * 
179       * @see HttpConnection#tunnelCreated()
180       */
181      public int execute(HttpState state, HttpConnection conn) 
182      throws IOException, HttpException {
183  
184          LOG.trace("enter ConnectMethod.execute(HttpState, HttpConnection)");
185          int code = super.execute(state, conn);
186          if (LOG.isDebugEnabled()) {
187              LOG.debug("CONNECT status code " + code);
188          }
189          return code;
190      }
191  
192      /**
193       * Special Connect request.
194       *
195       * @param state the current http state
196       * @param conn the connection to write to
197       * @throws IOException when an error occurs writing the request
198       * @throws HttpException when an error occurs writing the request
199       */
200      protected void writeRequestLine(HttpState state, HttpConnection conn)
201      throws IOException, HttpException {
202          StringBuffer buffer = new StringBuffer();
203          buffer.append(getName()); 
204          buffer.append(' '); 
205          if (this.targethost != null) {
206              buffer.append(getPath()); 
207          } else {
208              int port = conn.getPort();
209              if (port == -1) {
210                  port = conn.getProtocol().getDefaultPort();  
211              }
212              buffer.append(conn.getHost()); 
213              buffer.append(':'); 
214              buffer.append(port); 
215          }
216          buffer.append(" "); 
217          buffer.append(getEffectiveVersion()); 
218          String line = buffer.toString();
219          conn.printLine(line, getParams().getHttpElementCharset());
220          if (Wire.HEADER_WIRE.enabled()) {
221              Wire.HEADER_WIRE.output(line);
222          }
223      }
224  
225      /**
226       * Returns <code>true</code> if the status code is anything other than
227       * SC_OK, <code>false</code> otherwise.
228       * 
229       * @see HttpMethodBase#shouldCloseConnection(HttpConnection)
230       * @see HttpStatus#SC_OK
231       * 
232       * @return <code>true</code> if the connection should be closed
233       */
234      protected boolean shouldCloseConnection(HttpConnection conn) {
235          if (getStatusCode() == HttpStatus.SC_OK) {
236              Header connectionHeader = null;
237              if (!conn.isTransparent()) {
238                  connectionHeader = getResponseHeader("proxy-connection");
239              }
240              if (connectionHeader == null) {
241                  connectionHeader = getResponseHeader("connection");
242              }
243              if (connectionHeader != null) {
244                  if (connectionHeader.getValue().equalsIgnoreCase("close")) {
245                      if (LOG.isWarnEnabled()) {
246                          LOG.warn("Invalid header encountered '" + connectionHeader.toExternalForm() 
247                          + "' in response " + getStatusLine().toString());
248                      }
249                  }
250              }
251              return false;
252          } else {
253              return super.shouldCloseConnection(conn);
254          }
255      }
256      
257      /** Log object for this class. */
258      private static final Log LOG = LogFactory.getLog(ConnectMethod.class);
259  
260  }