Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/meterware/httpunit/WebClient.java


1   package com.meterware.httpunit;
2   /********************************************************************************************************************
3   * $Id: WebClient.java,v 1.62 2004/09/29 17:15:24 russgold Exp $
4   *
5   * Copyright (c) 2000-2004, Russell Gold
6   *
7   * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
8   * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
9   * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
10  * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all copies or substantial portions
13  * of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
16  * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
18  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19  * DEALINGS IN THE SOFTWARE.
20  *
21  *******************************************************************************************************************/
22  import java.io.IOException;
23  import java.io.OutputStream;
24  
25  import java.net.HttpURLConnection;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  
29  import java.util.*;
30  
31  import org.xml.sax.SAXException;
32  import com.meterware.httpunit.cookies.CookieJar;
33  
34  
35  /**
36   * The context for a series of web requests. This class manages cookies used to maintain
37   * session context, computes relative URLs, and generally emulates the browser behavior
38   * needed to build an automated test of a web site.
39   *
40   * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
41   * @author Jan Ohrstrom
42   * @author Seth Ladd
43   * @author Oliver Imbusch
44   **/
45  abstract
46  public class WebClient {
47  
48      private ArrayList _openWindows = new ArrayList();
49  
50      /** The current main window. **/
51      private WebWindow _mainWindow = new WebWindow( this );
52      private String _authorizationString;
53      private String _proxyAuthorizationString;
54  
55  
56      public WebWindow getMainWindow() {
57          return _mainWindow;
58      }
59  
60  
61      public void setMainWindow( WebWindow mainWindow ) {
62          if (!_openWindows.contains( mainWindow )) throw new IllegalArgumentException( "May only select an open window owned by this client" );
63          _mainWindow = mainWindow;
64      }
65  
66  
67      public WebWindow[] getOpenWindows() {
68          return (WebWindow[]) _openWindows.toArray( new WebWindow[ _openWindows.size() ] );
69      }
70  
71  
72      public WebWindow getOpenWindow( String name ) {
73          if (name == null || name.length() == 0) return null;
74          for (Iterator i = _openWindows.iterator(); i.hasNext();) {
75              WebWindow window = (WebWindow) i.next();
76              if (name.equals( window.getName() )) return window;
77          }
78          return null;
79      }
80  
81  
82      /**
83       * Submits a GET method request and returns a response.
84       * @exception SAXException thrown if there is an error parsing the retrieved page
85       **/
86      public WebResponse getResponse( String urlString ) throws MalformedURLException, IOException, SAXException {
87          return _mainWindow.getResponse( urlString );
88      }
89  
90  
91      /**
92       * Submits a web request and returns a response. This is an alternate name for the getResponse method.
93       */
94      public WebResponse sendRequest( WebRequest request ) throws MalformedURLException, IOException, SAXException {
95          return _mainWindow.sendRequest( request );
96      }
97  
98  
99      /**
100      * Returns the response representing the current top page in the main window.
101      */
102     public WebResponse getCurrentPage() {
103         return _mainWindow.getCurrentPage();
104     }
105 
106 
107     /**
108      * Submits a web request and returns a response, using all state developed so far as stored in
109      * cookies as requested by the server.
110      * @exception SAXException thrown if there is an error parsing the retrieved page
111      **/
112     public WebResponse getResponse( WebRequest request ) throws MalformedURLException, IOException, SAXException {
113         return _mainWindow.getResponse( request );
114     }
115 
116 
117     /**
118      * Returns the name of the currently active frames in the main window.
119      **/
120     public String[] getFrameNames() {
121         return _mainWindow.getFrameNames();
122     }
123 
124 
125     /**
126      * Returns the response associated with the specified frame name in the main window.
127      * Throws a runtime exception if no matching frame is defined.
128      **/
129     public WebResponse getFrameContents( String frameName ) {
130         return _mainWindow.getFrameContents( frameName );
131     }
132 
133 
134     /**
135      * Returns the response associated with the specified frame name in the main window.
136      * Throws a runtime exception if no matching frame is defined.
137      *
138      * @since 1.6
139      **/
140     public WebResponse getFrameContents( FrameSelector targetFrame ) {
141         return _mainWindow.getFrameContents( targetFrame );
142     }
143 
144 
145     /**
146      * Returns the resource specified by the request. Does not update the client or load included framesets or scripts.
147      * May return null if the resource is a JavaScript URL which would normally leave the client unchanged.
148      */
149     public WebResponse getResource( WebRequest request ) throws IOException {
150         return _mainWindow.getResource( request );
151     }
152 
153 
154     /**
155      * Resets the state of this client, removing all cookies, frames, and per-client headers. This does not affect
156      * any listeners or preferences which may have been set.
157      **/
158     public void clearContents() {
159         _mainWindow = new WebWindow( this );
160         _cookieJar.clear();
161         _headers = new HeaderDictionary();
162     }
163 
164 
165     /**
166      * Defines a cookie to be sent to the server on every request.
167      * @deprecated as of 1.6, use #putCookie instead.
168      **/
169     public void addCookie( String name, String value ) {
170         _cookieJar.addCookie( name, value );
171     }
172 
173 
174     /**
175      * Defines a cookie to be sent to the server on every request. This overrides any previous setting for this cookie name.
176      **/
177     public void putCookie( String name, String value ) {
178         _cookieJar.putCookie( name, value );
179     }
180 
181 
182     /**
183      * Returns the name of all the active cookies which will be sent to the server.
184      **/
185     public String[] getCookieNames() {
186         return _cookieJar.getCookieNames();
187     }
188 
189 
190     /**
191      * Returns the value of the specified cookie.
192      **/
193     public String getCookieValue( String name ) {
194         return _cookieJar.getCookieValue( name );
195     }
196 
197 
198     /**
199      * Returns the properties associated with this client.
200      */
201     public ClientProperties getClientProperties() {
202         return _clientProperties;
203     }
204 
205 
206     /**
207      * Specifies the user agent identification. Used to trigger browser-specific server behavior.
208      * @deprecated as of 1.4.6. Use ClientProperties#setUserAgent instead.
209      **/
210     public void setUserAgent( String userAgent ) {
211       getClientProperties().setUserAgent( userAgent );
212     }
213 
214 
215     /**
216      * Returns the current user agent setting.
217      * @deprecated as of 1.4.6. Use ClientProperties#getUserAgent instead.
218      **/
219     public String getUserAgent() {
220       return getClientProperties().getUserAgent();
221     }
222 
223 
224     /**
225      * Sets a username and password for a basic authentication scheme.
226      **/
227     public void setAuthorization( String userName, String password ) {
228         _authorizationString = "Basic " + Base64.encode( userName + ':' + password );
229     }
230 
231 
232     /**
233      * Specifies a proxy server to use for requests from this client.
234      */
235     public void setProxyServer( String proxyHost, int proxyPort ) {
236     }
237 
238 
239     /**
240      * Specifies a proxy server to use, along with a user and password for authentication.
241      *
242      * @since 1.6
243      */
244     public void setProxyServer( String proxyHost, int proxyPort, String userName, String password ) {
245         setProxyServer( proxyHost, proxyPort );
246         _proxyAuthorizationString = "Basic " + Base64.encode( userName + ':' + password );
247     }
248 
249 
250     /**
251      * Clears the proxy server settings.
252      */
253     public void clearProxyServer() {
254     }
255 
256 
257     /**
258      * Returns the name of the active proxy server.
259      */
260     public String getProxyHost() {
261         return System.getProperty( "proxyHost" );
262     }
263 
264 
265     /**
266      * Returns the number of the active proxy port, or 0 is none is specified.
267      */
268     public int getProxyPort() {
269         try {
270             return Integer.parseInt( System.getProperty( "proxyPort" ) );
271         } catch (NumberFormatException e) {
272             return 0;
273         }
274     }
275 
276 
277 
278 
279     /**
280      * Sets the value for a header field to be sent with all requests. If the value set is null,
281      * removes the header from those to be sent.
282      **/
283     public void setHeaderField( String fieldName, String fieldValue ) {
284         _headers.put( fieldName, fieldValue );
285     }
286 
287 
288     /**
289      * Returns the value for the header field with the specified name. This method will ignore the case of the field name.
290      */
291     public String getHeaderField( String fieldName ) {
292         return (String) _headers.get( fieldName );
293     }
294 
295 
296     /**
297      * Specifies whether an exception will be thrown when an error status (4xx or 5xx) is detected on a response.
298      * Defaults to the value returned by HttpUnitOptions.getExceptionsThrownOnErrorStatus.
299      **/
300     public void setExceptionsThrownOnErrorStatus( boolean throwExceptions ) {
301         _exceptionsThrownOnErrorStatus = throwExceptions;
302     }
303 
304 
305     /**
306      * Returns true if an exception will be thrown when an error status (4xx or 5xx) is detected on a response.
307      **/
308     public boolean getExceptionsThrownOnErrorStatus() {
309         return _exceptionsThrownOnErrorStatus;
310     }
311 
312 
313     /**
314      * Adds a listener to watch for requests and responses.
315      */
316     public void addClientListener( WebClientListener listener ) {
317         synchronized (_clientListeners) {
318             if (listener != null && !_clientListeners.contains( listener )) _clientListeners.add( listener );
319         }
320     }
321 
322 
323     /**
324      * Removes a listener to watch for requests and responses.
325      */
326     public void removeClientListener( WebClientListener listener ) {
327         synchronized (_clientListeners) {
328             _clientListeners.remove( listener );
329         }
330     }
331 
332 
333     /**
334      * Adds a listener to watch for window openings and closings.
335      */
336     public void addWindowListener( WebWindowListener listener ) {
337         synchronized (_windowListeners) {
338             if (listener != null && !_windowListeners.contains( listener )) _windowListeners.add( listener );
339         }
340     }
341 
342 
343     /**
344      * Removes a listener to watch for window openings and closings.
345      */
346     public void removeWindowListener( WebWindowListener listener ) {
347         synchronized (_windowListeners) {
348             _windowListeners.remove( listener );
349         }
350     }
351 
352 
353     /**
354      * Returns the next javascript alert without removing it from the queue.
355      */
356     public String getNextAlert() {
357         return _alerts.isEmpty() ? null : (String) _alerts.getFirst();
358     }
359 
360 
361     /**
362      * Returns the next javascript alert and removes it from the queue. If the queue is empty,
363      * will return an empty string.
364      */
365     public String popNextAlert() {
366         if (_alerts.isEmpty()) return "";
367         return (String) _alerts.removeFirst();
368     }
369 
370 
371     /**
372      * Specifies the object which will respond to all dialogs.
373      **/
374     public void setDialogResponder( DialogResponder responder ) {
375         _dialogResponder = responder;
376     }
377 
378 
379 //------------------------------------------ protected members -----------------------------------
380 
381 
382     protected WebClient() {
383         _openWindows.add( _mainWindow );
384     }
385 
386 
387     /**
388      * Creates a web response object which represents the response to the specified web request.
389      * @param request the request to which the response should be generated
390      * @param targetFrame the frame in which the response should be stored
391      **/
392     abstract
393     protected WebResponse newResponse( WebRequest request, FrameSelector targetFrame ) throws MalformedURLException, IOException;
394 
395 
396     /**
397      * Writes the message body for the request.
398      **/
399     final protected void writeMessageBody( WebRequest request, OutputStream stream ) throws IOException {
400         request.writeMessageBody( stream );
401     }
402 
403 
404     /**
405      * Returns the value of all current header fields.
406      **/
407     protected Dictionary getHeaderFields( URL targetURL ) {
408         Hashtable result = (Hashtable) _headers.clone();
409         result.put( "User-Agent", getClientProperties().getUserAgent() );
410         if (getClientProperties().isAcceptGzip()) result.put( "Accept-Encoding", "gzip" );
411         AddHeaderIfNotNull( result, "Cookie", _cookieJar.getCookieHeaderField( targetURL ) );
412         AddHeaderIfNotNull( result, "Authorization", _authorizationString );
413         AddHeaderIfNotNull( result, "Proxy-Authorization", _proxyAuthorizationString );
414         return result;
415     }
416 
417 
418     private void AddHeaderIfNotNull( Hashtable result, final String headerName, final String headerValue ) {
419         if (headerValue != null) result.put( headerName, headerValue );
420     }
421 
422 
423     /**
424      * Updates this web client based on a received response. This includes updating
425      * cookies and frames.  This method is required by ServletUnit, which cannot call the updateWindow method directly.
426      **/
427     final
428     protected void updateMainWindow( FrameSelector frame, WebResponse response ) throws MalformedURLException, IOException, SAXException {
429         getMainWindow().updateWindow( frame.getName(), response, new RequestContext() );
430     }
431 
432 
433 //------------------------------------------------- package members ----------------------------------------------------
434 
435 
436     void tellListeners( WebRequest request ) {
437         List listeners;
438 
439         synchronized (_clientListeners) {
440             listeners = new ArrayList( _clientListeners );
441         }
442 
443         for (Iterator i = listeners.iterator(); i.hasNext();) {
444             ((WebClientListener) i.next()).requestSent( this, request );
445         }
446     }
447 
448 
449     void tellListeners( WebResponse response ) {
450         List listeners;
451 
452         synchronized (_clientListeners) {
453             listeners = new ArrayList( _clientListeners );
454         }
455 
456         for (Iterator i = listeners.iterator(); i.hasNext();) {
457             ((WebClientListener) i.next()).responseReceived( this, response );
458         }
459     }
460 
461 
462     void updateClient( WebResponse response ) throws IOException {
463         if (getClientProperties().isAcceptCookies()) _cookieJar.updateCookies( response.getCookieJar() );
464         validateHeaders( response );
465     }
466 
467 
468     CookieJar getCookieJar() {
469         return _cookieJar;
470     }
471 
472 
473     void updateFrameContents( WebWindow requestWindow, String requestTarget, WebResponse response, RequestContext requestContext ) throws IOException, SAXException {
474         if (response.getFrame() == FrameSelector.NEW_FRAME) {
475             WebWindow window = new WebWindow( this, requestWindow.getCurrentPage() );
476             if (!WebRequest.NEW_WINDOW.equalsIgnoreCase( requestTarget )) window.setName( requestTarget );
477             response.setFrame( window.getTopFrame() );
478             window.updateFrameContents( response, requestContext );
479             _openWindows.add( window );
480             reportWindowOpened( window );
481         } else if (response.getFrame().getWindow() != null && response.getFrame().getWindow() != requestWindow) {
482             response.getFrame().getWindow().updateFrameContents( response, requestContext );
483         } else {
484             if (response.getFrame() == FrameSelector.TOP_FRAME) response.setFrame( requestWindow.getTopFrame() );
485             requestWindow.updateFrameContents( response, requestContext );
486         }
487     }
488 
489 
490     void close( WebWindow window ) {
491         if (!_openWindows.contains( window )) throw new IllegalStateException( "Window is already closed" );
492         _openWindows.remove( window );
493         if (_openWindows.isEmpty()) _openWindows.add( new WebWindow( this ) );
494         if (window.equals( _mainWindow )) _mainWindow = (WebWindow) _openWindows.get(0);
495         reportWindowClosed( window );
496     }
497 
498 
499     private void reportWindowOpened( WebWindow window ) {
500         List listeners;
501 
502         synchronized (_windowListeners) {
503             listeners = new ArrayList( _windowListeners );
504         }
505 
506         for (Iterator i = listeners.iterator(); i.hasNext();) {
507             ((WebWindowListener) i.next()).windowOpened( this, window );
508         }
509     }
510 
511 
512     private void reportWindowClosed( WebWindow window ) {
513         List listeners;
514 
515         synchronized (_windowListeners) {
516             listeners = new ArrayList( _windowListeners );
517         }
518 
519         for (Iterator i = listeners.iterator(); i.hasNext();) {
520             ((WebWindowListener) i.next()).windowClosed( this, window );
521         }
522     }
523 
524 //------------------------------------------ package members ------------------------------------
525 
526 
527     boolean getConfirmationResponse( String message ) {
528         return _dialogResponder.getConfirmation( message );
529     }
530 
531 
532     String getUserResponse( String message, String defaultResponse ) {
533         return _dialogResponder.getUserResponse( message, defaultResponse );
534     }
535 
536 
537     void postAlert( String message ) {
538         _alerts.addLast( message );
539     }
540 
541 //------------------------------------------ private members -------------------------------------
542 
543     /** The list of alerts generated by JavaScript. **/
544     private LinkedList _alerts = new LinkedList();
545 
546     /** The currently defined cookies. **/
547     private CookieJar _cookieJar = new CookieJar();
548 
549 
550     /** A map of header names to values. **/
551     private HeaderDictionary _headers = new HeaderDictionary();
552 
553     private boolean _exceptionsThrownOnErrorStatus = HttpUnitOptions.getExceptionsThrownOnErrorStatus();
554 
555     private List _clientListeners = new ArrayList();
556 
557     private List _windowListeners = new ArrayList();
558 
559     private DialogResponder _dialogResponder = new DialogAdapter();
560 
561     private ClientProperties _clientProperties = ClientProperties.getDefaultProperties().cloneProperties();
562 
563 
564     /**
565      * Examines the headers in the response and throws an exception if appropriate.
566      **/
567     private void validateHeaders( WebResponse response ) throws HttpException {
568         if (!getExceptionsThrownOnErrorStatus()) return;
569 
570         if (response.getHeaderField( "WWW-Authenticate" ) != null) {
571             throw new AuthorizationRequiredException( response.getHeaderField( "WWW-Authenticate" ) );
572         } else if (response.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) {
573             throw new HttpInternalErrorException( response.getURL() );
574         } else if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
575             throw new HttpNotFoundException( response.getResponseMessage(), response.getURL() );
576         } else if (response.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
577             throw new HttpException( response.getResponseCode(), response.getResponseMessage(), response.getURL() );
578         }
579     }
580 
581 
582     FrameSelector findFrame( String target ) {
583         for (int i = 0; i < _openWindows.size(); i++) {
584             WebWindow webWindow = (WebWindow) _openWindows.get( i );
585             FrameSelector frame = webWindow.getFrame( target );
586             if (frame != null) return frame;
587         }
588         return null;
589     }
590 
591 
592 //==================================================================================================
593 
594 
595     static public class HeaderDictionary extends Hashtable {
596 
597         public void addEntries( Dictionary source ) {
598             for (Enumeration e = source.keys(); e.hasMoreElements(); ) {
599                 Object key = e.nextElement();
600                 put( key, source.get( key ) );
601             }
602         }
603 
604 
605         public boolean containsKey( Object key ) {
606             return super.containsKey( matchPreviousFieldName( key.toString() ) );
607         }
608 
609 
610         public Object get( Object fieldName ) {
611             return (String) super.get( matchPreviousFieldName( fieldName.toString() ) );
612         }
613 
614 
615         public Object put( Object fieldName, Object fieldValue ) {
616             fieldName = matchPreviousFieldName( fieldName.toString() );
617             Object oldValue = super.get( fieldName );
618             if (fieldValue == null) {
619                 remove( fieldName );
620             } else {
621                 super.put( fieldName, fieldValue );
622             }
623             return oldValue;
624         }
625 
626 
627         /**
628          * If a matching field name with different case is already known, returns the older name.
629          * Otherwise, returns the specified name.
630          **/
631         private String matchPreviousFieldName( String fieldName ) {
632             for (Enumeration e = keys(); e.hasMoreElements(); ) {
633                 String key = (String) e.nextElement();
634                 if (key.equalsIgnoreCase( fieldName )) return key;
635             }
636             return fieldName;
637         }
638 
639 
640     }
641 
642 }
643 
644 
645 //==================================================================================================
646 
647 
648 class RedirectWebRequest extends WebRequest {
649 
650 
651     RedirectWebRequest( WebResponse response ) throws MalformedURLException {
652         super( response.getURL(), response.getHeaderField( "Location" ), response.getFrame(), response.getFrameName() );
653         if (response.getReferer() != null) setHeaderField( "Referer", response.getReferer() );
654     }
655 
656 
657     /**
658      * Returns the HTTP method defined for this request.
659      **/
660     public String getMethod() {
661         return "GET";
662     }
663 }
664 
665 
666 
667 
668 
669