Coverage Report - org.apache.commons.latka.http.RequestImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
RequestImpl
0%
0/163
0%
0/40
2
 
 1  
 /*
 2  
  * Copyright 1999-2002,2004 The Apache Software Foundation.
 3  
  * 
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 
 17  
 package org.apache.commons.latka.http;
 18  
 // java imports
 19  
 import java.net.URL;
 20  
 import java.io.ByteArrayInputStream;
 21  
 import java.io.InputStream;
 22  
 import java.io.IOException;
 23  
 import java.util.List;
 24  
 import java.util.LinkedList;
 25  
 // latka imports
 26  
 import org.apache.commons.httpclient.HostConfiguration;
 27  
 import org.apache.commons.httpclient.UsernamePasswordCredentials;
 28  
 import org.apache.commons.httpclient.HttpClient;
 29  
 import org.apache.commons.httpclient.HttpException;
 30  
 import org.apache.commons.httpclient.HttpMethod;
 31  
 import org.apache.commons.httpclient.HttpState;
 32  
 import org.apache.commons.httpclient.HttpMethodBase;
 33  
 import org.apache.commons.httpclient.methods.PostMethod;
 34  
 // log4j imports
 35  
 import org.apache.log4j.Category;
 36  
 
 37  
 /**
 38  
  * An implementation of a Latka Request interface based on the Apache Commons
 39  
  * HttpClient package.
 40  
  *
 41  
  * @todo pass proxy host and port to httpclient
 42  
  * @author <a href="mailto:dsale@us.britannica.com">Doug Sale</a>
 43  
  * @author <a href="mailto:mdelagra@us.britannica.com">Morgan Delagrange</a>
 44  
  * @author dIon Gillard
 45  
  * @version $Id: RequestImpl.java 561366 2007-07-31 15:58:29Z rahul $
 46  
  * @see Request
 47  
  */
 48  
 public class RequestImpl implements Request {
 49  
     
 50  
     /** Standard HTTP Port */
 51  
     public static final int HTTP_PORT = 80;
 52  
     
 53  
     /** Standard HTTPS Port */
 54  
     public static final int HTTPS_PORT = 443;
 55  
     /** host the request is being made on */
 56  0
     protected String  _host   = null;
 57  
     /** port the request is being made on */
 58  0
     protected int     _port   = -1;
 59  
     /** http method being used to make the request */
 60  0
     protected int     _method = -1;
 61  
     /** http session the request is part of */
 62  0
     protected SessionImpl _session    = null;
 63  
     /** credentials for the request */
 64  0
     protected Credentials _credentials = null;
 65  
     /** http method implementation */
 66  0
     protected HttpMethod  _httpMethod = null;
 67  
     /** URL being requested */
 68  0
     protected URL         _targetURL  = null;
 69  
     /** query string portion of the URL */
 70  0
     protected String      _query      = null;
 71  
     /** time taken for the request in milliseconds */
 72  0
     protected long        _requestTiming = -1;
 73  
     /** name given to the request */
 74  0
     protected String      _label      = null;
 75  
     /** headers sent with this request */
 76  0
     protected RequestHeaders _requestHeaders = new RequestHeadersImpl();
 77  
     /** parameters sent with this request */
 78  0
     protected Parameters _parameters = new ParametersImpl();
 79  
     /** manually constructed post body */
 80  0
     protected String _requestBody = null;
 81  
     /** whether or not redirect responses should be followed as part of the
 82  
         request processing*/
 83  0
     protected boolean _followRedirects = true;
 84  
     /** object used to perform the http requests */
 85  0
     protected HttpClient _httpClient = new HttpClient();
 86  
     /** proxy used to perform the requests */
 87  0
     private Proxy _proxy = null;
 88  
     /** log4j category used when logging messages */
 89  0
     protected static final Category _log = 
 90  0
         Category.getInstance(RequestImpl.class);
 91  
 
 92  
     /**
 93  
      * HTTP Version 1.0
 94  
      */
 95  
     private static final String HTTP_10 = "1.0";
 96  
     /**
 97  
      * HTTP Version 1.1
 98  
      */    
 99  
     private static final String HTTP_11 = "1.1";
 100  
 
 101  0
     protected String _httpVersion = HTTP_11;
 102  
 
 103  0
     protected List _visitedURLs = new LinkedList();
 104  
 
 105  
     /**
 106  
      * Create a request for the specified URL, process using the supplied method
 107  
      * and use the supplied state and session for persistent data
 108  
      * @param url the URL to send the request to
 109  
      * @param httpMethod the http method to use in sending the request, as 
 110  
      *        defined in {@link Request}
 111  
      * @param state shared information across requests
 112  
      * @param session state shared across servers and requests
 113  
      */
 114  
     protected RequestImpl(URL url, int httpMethod, HttpState state,
 115  
         SessionImpl session) {
 116  0
         this(null, url, httpMethod, state, session, true);
 117  0
     }
 118  
     
 119  
     /**
 120  
      * Create a RequestImpl
 121  
      *
 122  
      * @param label       a name for the request
 123  
      * @param url         the url that this request embodies
 124  
      * @param httpMethod  the method by which this request should be executed
 125  
      * @param state       shared information across requests
 126  
      * @param session     the session that the request should be executed in
 127  
      * @param followRedirects whether http redirect responses should be honoured
 128  
      */
 129  
     protected RequestImpl(String label, URL url, int httpMethod, 
 130  0
         HttpState state, SessionImpl session, boolean followRedirects) {
 131  
             
 132  0
         _followRedirects = followRedirects;
 133  0
         _method = httpMethod;
 134  0
         _httpClient.setState(state);
 135  
         
 136  0
         _label = label;
 137  0
         _query = url.getQuery();
 138  
         
 139  0
         _session = session;
 140  0
         _targetURL = url;
 141  0
         _httpMethod = MethodFactory.getInstance(httpMethod, url);
 142  0
         if (_query != null) {
 143  0
             _httpMethod.setQueryString(_query);
 144  
         }
 145  0
         _httpMethod.setFollowRedirects(followRedirects);
 146  0
     }
 147  
     
 148  
     /**
 149  
      * Returns the object implementing the HttpMethod
 150  
      * (get, post, etc.) for this request.
 151  
      *
 152  
      * @return the underlying HttpMethod object representing the request/
 153  
      *      response pair.
 154  
      */
 155  
     protected HttpMethod getHttpMethod() {
 156  0
         return _httpMethod;
 157  
     }
 158  
     
 159  
     /**
 160  
      * Defined in the implemented interface
 161  
      * @return the headers used for this request
 162  
      * @see Request#getHeaders()
 163  
      */
 164  
     public RequestHeaders getHeaders() {
 165  0
         return _requestHeaders;
 166  
     }
 167  
     
 168  
     /**
 169  
      * Defined in the implemented interface
 170  
      * @param requestHeaders new value for the headers property
 171  
      * @see Request#setHeaders(RequestHeaders)
 172  
      */
 173  
     public void setHeaders(RequestHeaders requestHeaders) {
 174  0
         _requestHeaders = requestHeaders;
 175  0
     }
 176  
     
 177  
     /**
 178  
      * Defined in the implemented interface
 179  
      * @return the parameters property
 180  
      * @see Request#getParameters()
 181  
      */
 182  
     public Parameters getParameters() {
 183  0
         return _parameters;
 184  
     }
 185  
 
 186  
     /**
 187  
      * Defined in the implemented interface
 188  
      * @see Request#setRequestBody(String)
 189  
      */
 190  
     public void setRequestBody(String body) {
 191  0
         _requestBody = body;
 192  0
     }
 193  
     
 194  
     /**
 195  
      * Defined in the implemented interface
 196  
      * @param parameters new value for parameters property
 197  
      * @see Request#setParameters(Parameters)
 198  
      */
 199  
     public void setParameters(Parameters parameters) {
 200  0
         _parameters = parameters;
 201  0
     }
 202  
     
 203  
     /**
 204  
      * Defined in the implemented interface
 205  
      * @param credentials username and password to use
 206  
      * @see Request#setCredentials(Credentials)
 207  
      */
 208  
     public void setCredentials(Credentials credentials) {
 209  
         // null implies that this credential is the default (vs. specifying a 
 210  
         // realm)
 211  0
         UsernamePasswordCredentials creds = new UsernamePasswordCredentials(
 212  
             credentials.getUserName(), credentials.getPassword());
 213  0
         _session._state.setCredentials(null, creds);
 214  0
         _credentials = credentials;
 215  0
     }
 216  
 
 217  
     public Credentials getCredentials() {
 218  0
         return _credentials;
 219  
     }
 220  
     
 221  
     /**
 222  
      * Execute the request - perform the http interaction.
 223  
      * Since HttpClient doesn't follow off-server
 224  
      * redirects, execute() may have to construct further 
 225  
      * requests that call this method.
 226  
      * 
 227  
      * @return a {@link Response} detailing the html etc
 228  
      * @exception IOException
 229  
      *                   when there are problems reading and writing
 230  
      * @see Request#execute()
 231  
      */
 232  
     protected Response executeRequestPerHost() throws IOException {
 233  
         
 234  
         // set the request headers in HTTPClient
 235  0
         List headers = _requestHeaders.getHeaders();
 236  0
         for (int i = 0; i < headers.size(); ++i) {
 237  0
             String[] header = (String[]) headers.get(i);
 238  0
             _httpMethod.addRequestHeader(header[0], header[1]);
 239  
         }
 240  
         
 241  0
         if (_requestBody != null) {
 242  0
             InputStream  is = new ByteArrayInputStream(
 243  
                 _requestBody.getBytes("ISO-8859-1"));
 244  0
             ((PostMethod) _httpMethod).setRequestBody(is);
 245  0
         } else {
 246  0
             List parameters = _parameters.getParameters();
 247  0
             for (int i = 0; i < parameters.size(); ++i) {
 248  0
                 String[] parameter = (String[]) parameters.get(i);
 249  0
                 addHttpClientParameter(parameter[0], parameter[1]);
 250  
             }
 251  
         }
 252  
         
 253  
         // for timing
 254  0
         long startDate = System.currentTimeMillis();
 255  
         
 256  0
         Response response = null;
 257  
         try {
 258  
             // open the connection
 259  0
             openConnection();
 260  
             
 261  0
             _log.debug("executing request");
 262  
             
 263  0
             _httpClient.executeMethod(_httpMethod);
 264  
             
 265  0
             _log.debug("request executed");
 266  
             
 267  0
             response = new ResponseImpl(this);
 268  
 
 269  
             // Cache the response body to allow it to be accessed
 270  
             // after the http connection is closed.
 271  0
             String sideEffectOnly = response.getResource();
 272  
             
 273  
             // set the referer
 274  
             // note: If followRedirects
 275  
             // is enabled, HTTPClient may return a path
 276  
             // that is different from the initial request.
 277  
             // HTTPClient will not follow redirects to another
 278  
             // host, port, or protocol; in that event, it will always
 279  
             // return a 301 or 302.
 280  0
             _session.setReferer(new URL(_targetURL.getProtocol(), _host, _port,
 281  
                 _httpMethod.getPath()));
 282  
             
 283  0
         } catch (HttpException e) {
 284  0
             throw new IOException(e.toString());
 285  0
         } catch (IOException e) {
 286  
             // rethrow it after closing the connection
 287  0
             throw e;
 288  
         } finally {
 289  0
             try {
 290  0
                 closeConnection(); 
 291  
                 // FIXME: Shouldn't use Exception here.
 292  0
             } catch (Exception e) {
 293  0
                 e.printStackTrace();
 294  0
             }
 295  0
         }
 296  
         
 297  
         
 298  0
         _requestTiming = System.currentTimeMillis() - startDate;
 299  
         
 300  0
         if (_log.isInfoEnabled()) {
 301  0
             _log.info("response obtained (response logging disabled because "
 302  
                 + "some responses are binary)");
 303  
         }
 304  
         
 305  0
         return response;
 306  
     }
 307  
     
 308  
     /**
 309  
      * Executes the request.  In the event of a 301 or 302,
 310  
      * this method may need to create a new Request, 
 311  
      * due to an idiosyncracy of
 312  
      * HttpClient.  In that case, the Response.getRequest()
 313  
      * method will return the actual Request that was executed.
 314  
      */
 315  
     public Response execute() throws IOException {
 316  0
         Response response = executeRequestPerHost();
 317  
 
 318  0
         if (followRedirects() == false) {
 319  0
             return response;
 320  
         }
 321  
 
 322  0
         Request lastRequest = this;
 323  
         // execute the request until either we get a non-redirect response, or
 324  
         // we visit a URL we have already visited
 325  0
         while (response.getStatusCode() == 301 || response.getStatusCode() == 302) {
 326  
             // follow the redirect
 327  0
             URL url = new URL(response.getHeader("location"));
 328  
 
 329  0
             if (_visitedURLs.contains(url.toString())) {
 330  0
                 return response;
 331  
             }
 332  
 
 333  0
             Request request = _session.createRequest(lastRequest.getLabel(), url,
 334  
                 lastRequest.getMethod(), lastRequest.getVersion(), true, getProxy());
 335  0
             request.setParameters(lastRequest.getParameters());
 336  0
             request.setHeaders(lastRequest.getHeaders());
 337  0
             Credentials credentials = lastRequest.getCredentials();
 338  0
             if (credentials != null) {
 339  0
                 request.setCredentials(credentials);
 340  
             }
 341  0
             response = request.execute();
 342  0
             _visitedURLs.add(url.toString());
 343  0
             lastRequest = request;
 344  0
         }
 345  
 
 346  0
         return response;
 347  
     }
 348  
 
 349  
     /**
 350  
      * Get the URL used for the request
 351  
      * @return the {@link URL} of the request
 352  
      * @see Request#getURL
 353  
      */
 354  
     public URL getURL() {
 355  0
         return _targetURL;
 356  
     }
 357  
     
 358  
     /**
 359  
      * Get the label used for the request
 360  
      * @return the label used to create the request
 361  
      * @see Request#getLabel
 362  
      */
 363  
     public String getLabel() {
 364  0
         return _label;
 365  
     }
 366  
     
 367  
     /**
 368  
      * Add a parameter (name and value) to the request
 369  
      * @param name the name of the parameter to be added
 370  
      * @param value the value of the parameter to be added
 371  
      * @see Request#addParameter(String,String)
 372  
      */
 373  
     public void addParameter(String name, String value) {
 374  0
         _parameters.addParameter(name, value);
 375  0
     }
 376  
     
 377  
     /**
 378  
      * Associate a parameter with this request.
 379  
      *
 380  
      * @param name  the lvalue of the parameter - must not be null
 381  
      * @param value  the rvalue of the parameter - must not be null
 382  
      */
 383  
     protected void addHttpClientParameter(String name, String value) {
 384  0
         if (name == null) {
 385  0
             throw new NullPointerException("name parameter is null");
 386  
         }
 387  
         
 388  0
         if (value == null) {
 389  0
             throw new NullPointerException("value parameter is null");
 390  
         }
 391  0
         _log.info("adding parameter, name: " + name + ", value: " + value);
 392  
         
 393  0
         if (_httpMethod instanceof PostMethod) {
 394  
             // addParameter adds to POST Entity, not URL
 395  0
             ((PostMethod) _httpMethod).addParameter(name, value);
 396  
         } else {
 397  0
             StringBuffer query = new StringBuffer();
 398  
 
 399  
             // setParameter adds to URL as query string
 400  0
             if (_query == null || _query.equals("")) {
 401  0
                 query.append(name);
 402  0
                 query.append("=");
 403  0
                 query.append(value);
 404  
             } else {
 405  0
                 query.append(_query);
 406  0
                 query.append("&");
 407  0
                 query.append(name);
 408  0
                 query.append("=");
 409  0
                 query.append(value);
 410  
             }
 411  0
             _query = query.toString();
 412  0
             ((HttpMethod) _httpMethod).setQueryString(_query);
 413  
         }
 414  0
     }
 415  
     
 416  
     /**
 417  
      * Set a header in the request
 418  
      *
 419  
      * @param headerName name of any HTTP request header
 420  
      * @param headerValue value of that header
 421  
      */
 422  
     public void addHeader(String headerName, String headerValue) {
 423  0
         _requestHeaders.addHeader(headerName, headerValue);
 424  0
     }
 425  
     
 426  
     /**
 427  
      * Retrieve the session associated with this request.
 428  
      *
 429  
      * @return a <code>Session</code> object
 430  
      */
 431  
     public Session getSession() {
 432  0
         return _session;
 433  
     }
 434  
     
 435  
     /**
 436  
      * @return the time it took to execute this request in milliseconds
 437  
      * or -1 if the request has not yet been executed
 438  
      */
 439  
     public int getRequestTiming() {
 440  0
         return (int) _requestTiming;
 441  
     }
 442  
     
 443  
     /**
 444  
      * opens an HTTP connection.  This method is called
 445  
      * before executing the method.
 446  
      *
 447  
      * @exception IOException
 448  
      *                   if the server could not be contacted
 449  
      */
 450  
     protected void openConnection() throws IOException {
 451  0
         _log.debug("Opening connection");
 452  
         
 453  0
         URL url = getURL();
 454  0
         String protocol = url.getProtocol();
 455  0
         String host = url.getHost();
 456  0
         int port = url.getPort();
 457  
         
 458  
         // explicitly set port if not in url,
 459  
         // just for storing away and comparison
 460  0
         if (port == -1) {
 461  0
             if (protocol.equals("http")) {
 462  0
                 port = HTTP_PORT;
 463  0
             } else if (protocol.equals("https")) {
 464  0
                 port = HTTPS_PORT;
 465  
             } else {
 466  0
                 throw new IllegalArgumentException("Unsupported Protocol");
 467  
             }
 468  
         }
 469  
         
 470  
         // save session values
 471  0
         _host = host;
 472  0
         _port = port;
 473  
         
 474  0
         HostConfiguration hostConfiguration = _httpClient.getHostConfiguration();
 475  0
         hostConfiguration.setHost(host, port, protocol);
 476  0
         if (getProxy() != null) {
 477  0
             hostConfiguration.setProxy(getProxy().getHost(), getProxy().getPort());
 478  
         }
 479  
         
 480  0
         _log.debug("connection open");
 481  0
     }
 482  
     
 483  
     /**
 484  
      * Closes the http connection associated with the request
 485  
      * @throws IOException if there are problems closing the connection
 486  
      */
 487  
     protected void closeConnection() throws IOException {
 488  0
         _log.debug("closing connection");
 489  0
         _httpMethod.releaseConnection();
 490  0
         _log.debug("connection closed");
 491  0
     }
 492  
     
 493  
     /**
 494  
      * Defined in the interface
 495  
      * @return whether the request will honour http redirect status codes
 496  
      * @see Request#followRedirects()
 497  
      */
 498  
     public boolean followRedirects() {
 499  0
         return _followRedirects;
 500  
     }
 501  
     
 502  
     /**
 503  
      * Defined in the interface
 504  
      * @return the http method being used for the request, as defined in the
 505  
      *      interface
 506  
      * @see Request#getMethod
 507  
      */
 508  
     public int getMethod() {
 509  0
         return _method;
 510  
     }
 511  
     
 512  
     /** Getter for property proxy.
 513  
      * @return Value of property proxy.
 514  
      */
 515  
     public Proxy getProxy() {
 516  0
         return _proxy;
 517  
     }
 518  
     
 519  
     /** Setter for property proxy.
 520  
      * @param proxy New value of property proxy.
 521  
      */
 522  
     public void setProxy(Proxy proxy) {
 523  0
         _proxy = proxy;
 524  0
     }
 525  
     
 526  
     /**
 527  
      * Sets the HTTP version for this request.  If
 528  
      * the version provided is not 1.0 or 1.1, then
 529  
      * 1.1 will be used.
 530  
      *
 531  
      * @param version  HTTP version
 532  
      */
 533  
     public void setVersion(String version) {
 534  0
         ((HttpMethodBase) _httpMethod).setHttp11(!HTTP_10.equals(version));
 535  0
         _httpVersion = version;
 536  0
     }
 537  
 
 538  
     /**
 539  
      * Get the HTTP version to use in this request.
 540  
      * 
 541  
      * @return HTTP version for the request
 542  
      */
 543  
     public String getVersion() {
 544  0
         return _httpVersion;
 545  
     }
 546  
 }