• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10124 relevant lines covered (81.44%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.3
/src/main/java/com/meterware/httpunit/WebClient.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.httpunit;
9

10
import com.meterware.httpunit.cookies.Cookie;
11
import com.meterware.httpunit.cookies.CookieJar;
12

13
import java.io.IOException;
14
import java.io.OutputStream;
15
import java.net.HttpURLConnection;
16
import java.net.PasswordAuthentication;
17
import java.net.URL;
18
import java.nio.charset.StandardCharsets;
19
import java.util.ArrayList;
20
import java.util.Base64;
21
import java.util.Dictionary;
22
import java.util.Enumeration;
23
import java.util.Hashtable;
24
import java.util.Iterator;
25
import java.util.LinkedList;
26
import java.util.List;
27

28
import org.xml.sax.SAXException;
29

30
/**
31
 * The context for a series of web requests. This class manages cookies used to maintain session context, computes
32
 * relative URLs, and generally emulates the browser behavior needed to build an automated test of a web site.
33
 **/
34
public abstract class WebClient {
35

36
    /** The open windows. */
37
    private ArrayList _openWindows = new ArrayList<>();
1✔
38

39
    /** The current main window. **/
40
    private WebWindow _mainWindow = new WebWindow(this);
1✔
41

42
    /** An authorization string to be sent with every request, whether challenged or not. May be null. **/
43
    private String _fixedAuthorizationString;
44

45
    /** An authorization string to be sent with the next request only. May be null. **/
46
    private String _authorizationString;
47

48
    /** The proxy authorization string. */
49
    private String _proxyAuthorizationString;
50

51
    /** The credentials. */
52
    private Hashtable _credentials = new Hashtable<>();
1✔
53

54
    /**
55
     * Gets the main window.
56
     *
57
     * @return the main window
58
     */
59
    public WebWindow getMainWindow() {
60
        return _mainWindow;
1✔
61
    }
62

63
    /**
64
     * Sets the main window.
65
     *
66
     * @param mainWindow
67
     *            the new main window
68
     */
69
    public void setMainWindow(WebWindow mainWindow) {
70
        if (!_openWindows.contains(mainWindow)) {
×
71
            throw new IllegalArgumentException("May only select an open window owned by this client");
×
72
        }
73
        _mainWindow = mainWindow;
×
74
    }
×
75

76
    /**
77
     * Gets the open windows.
78
     *
79
     * @return the open windows
80
     */
81
    public WebWindow[] getOpenWindows() {
82
        return (WebWindow[]) _openWindows.toArray(new WebWindow[_openWindows.size()]);
1✔
83
    }
84

85
    /**
86
     * Gets the open window.
87
     *
88
     * @param name
89
     *            the name
90
     *
91
     * @return the open window
92
     */
93
    public WebWindow getOpenWindow(String name) {
94
        if (name == null || name.isEmpty()) {
1!
95
            return null;
×
96
        }
97
        for (Iterator i = _openWindows.iterator(); i.hasNext();) {
1!
98
            WebWindow window = (WebWindow) i.next();
1✔
99
            if (name.equals(window.getName())) {
1✔
100
                return window;
1✔
101
            }
102
        }
1✔
103
        return null;
×
104
    }
105

106
    /**
107
     * Submits a GET method request and returns a response.
108
     *
109
     * @param urlString
110
     *            the url string
111
     *
112
     * @return the response
113
     *
114
     * @throws IOException
115
     *             Signals that an I/O exception has occurred.
116
     *
117
     * @exception SAXException
118
     *                thrown if there is an error parsing the retrieved page
119
     */
120
    public WebResponse getResponse(String urlString) throws IOException, SAXException {
121
        return _mainWindow.getResponse(urlString);
1✔
122
    }
123

124
    /**
125
     * Submits a web request and returns a response. This is an alternate name for the getResponse method.
126
     *
127
     * @param request
128
     *            the request
129
     *
130
     * @return the web response
131
     *
132
     * @throws IOException
133
     *             Signals that an I/O exception has occurred.
134
     * @throws SAXException
135
     *             the SAX exception
136
     */
137
    public WebResponse sendRequest(WebRequest request) throws IOException, SAXException {
138
        return _mainWindow.sendRequest(request);
1✔
139
    }
140

141
    /**
142
     * Returns the response representing the current top page in the main window.
143
     *
144
     * @return the current page
145
     */
146
    public WebResponse getCurrentPage() {
147
        return _mainWindow.getCurrentPage();
1✔
148
    }
149

150
    /**
151
     * Submits a web request and returns a response, using all state developed so far as stored in cookies as requested
152
     * by the server.
153
     *
154
     * @param request
155
     *            the request
156
     *
157
     * @return the response
158
     *
159
     * @throws IOException
160
     *             Signals that an I/O exception has occurred.
161
     *
162
     * @exception SAXException
163
     *                thrown if there is an error parsing the retrieved page
164
     */
165
    public WebResponse getResponse(WebRequest request) throws IOException, SAXException {
166
        return _mainWindow.getResponse(request);
1✔
167
    }
168

169
    /**
170
     * Returns the name of the currently active frames in the main window.
171
     *
172
     * @return the frame names
173
     */
174
    public String[] getFrameNames() {
175
        return _mainWindow.getFrameNames();
1✔
176
    }
177

178
    /**
179
     * Returns the response associated with the specified frame name in the main window. Throws a runtime exception if
180
     * no matching frame is defined.
181
     *
182
     * @param frameName
183
     *            the frame name
184
     *
185
     * @return the frame contents
186
     */
187
    public WebResponse getFrameContents(String frameName) {
188
        return _mainWindow.getFrameContents(frameName);
1✔
189
    }
190

191
    /**
192
     * Returns the response associated with the specified frame name in the main window. Throws a runtime exception if
193
     * no matching frame is defined.
194
     *
195
     * @param targetFrame
196
     *            the target frame
197
     *
198
     * @return the frame contents
199
     */
200
    public WebResponse getFrameContents(FrameSelector targetFrame) {
201
        return _mainWindow.getFrameContents(targetFrame);
1✔
202
    }
203

204
    /**
205
     * Returns the resource specified by the request. Does not update the client or load included framesets or scripts.
206
     * May return null if the resource is a JavaScript URL which would normally leave the client unchanged.
207
     *
208
     * @param request
209
     *            the request
210
     *
211
     * @return the resource
212
     *
213
     * @throws IOException
214
     *             Signals that an I/O exception has occurred.
215
     */
216
    public WebResponse getResource(WebRequest request) throws IOException {
217
        return _mainWindow.getResource(request);
×
218
    }
219

220
    /**
221
     * Resets the state of this client, removing all cookies, frames, and per-client headers. This does not affect any
222
     * listeners or preferences which may have been set.
223
     **/
224
    public void clearContents() {
225
        _mainWindow = new WebWindow(this);
×
226
        _cookieJar.clear();
×
227
        _headers = new HeaderDictionary();
×
228
    }
×
229

230
    /**
231
     * Defines a cookie to be sent to the server on every request.
232
     *
233
     * @param name
234
     *            the name
235
     * @param value
236
     *            the value
237
     *
238
     * @deprecated as of 1.6, use #putCookie instead.
239
     */
240
    @Deprecated
241
    public void addCookie(String name, String value) {
242
        _cookieJar.addCookie(name, value);
×
243
    }
×
244

245
    /**
246
     * Defines a cookie to be sent to the server on every request. This overrides any previous setting for this cookie
247
     * name.
248
     *
249
     * @param name
250
     *            the name
251
     * @param value
252
     *            the value
253
     */
254
    public void putCookie(String name, String value) {
255
        _cookieJar.putCookie(name, value);
1✔
256
    }
1✔
257

258
    /**
259
     * Returns the name of all the active cookies which will be sent to the server.
260
     *
261
     * @return the cookie names
262
     */
263
    public String[] getCookieNames() {
264
        return _cookieJar.getCookieNames();
1✔
265
    }
266

267
    /**
268
     * Returns an object containing the details of the named cookie.
269
     *
270
     * @param name
271
     *            the name
272
     *
273
     * @return the cookie details
274
     */
275
    public Cookie getCookieDetails(String name) {
276
        return _cookieJar.getCookie(name);
1✔
277
    }
278

279
    /**
280
     * Returns the value of the specified cookie.
281
     *
282
     * @param name
283
     *            the name
284
     *
285
     * @return the cookie value
286
     */
287
    public String getCookieValue(String name) {
288
        return _cookieJar.getCookieValue(name);
1✔
289
    }
290

291
    /**
292
     * Returns the properties associated with this client.
293
     *
294
     * @return the client properties
295
     */
296
    public ClientProperties getClientProperties() {
297
        if (_clientProperties == null) {
1✔
298
            _clientProperties = ClientProperties.getDefaultProperties().cloneProperties();
1✔
299
        }
300
        return _clientProperties;
1✔
301
    }
302

303
    /**
304
     * Specifies the user agent identification. Used to trigger browser-specific server behavior.
305
     *
306
     * @param userAgent
307
     *            the new user agent
308
     *
309
     * @deprecated as of 1.4.6. Use ClientProperties#setUserAgent instead.
310
     */
311
    @Deprecated
312
    public void setUserAgent(String userAgent) {
313
        getClientProperties().setUserAgent(userAgent);
×
314
    }
×
315

316
    /**
317
     * Returns the current user agent setting.
318
     *
319
     * @return the user agent
320
     *
321
     * @deprecated as of 1.4.6. Use ClientProperties#getUserAgent instead.
322
     */
323
    @Deprecated
324
    public String getUserAgent() {
325
        return getClientProperties().getUserAgent();
×
326
    }
327

328
    /**
329
     * Sets a username and password for a basic authentication scheme. Use #setAuthentication for more accurate
330
     * emulation of browser behavior.
331
     *
332
     * @param userName
333
     *            the user name
334
     * @param password
335
     *            the password
336
     */
337
    public void setAuthorization(String userName, String password) {
338
        _fixedAuthorizationString = "Basic "
1✔
339
                + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
1✔
340
    }
1✔
341

342
    /**
343
     * Specifies a username and password for on-demand authentication. Will only send the authorization header when
344
     * challenged for the specified realm.
345
     *
346
     * @param realm
347
     *            the realm for which the credentials apply.
348
     * @param username
349
     *            the user to authenticate
350
     * @param password
351
     *            the credentials for the user
352
     */
353
    public void setAuthentication(String realm, String username, String password) {
354
        _credentials.put(realm, new PasswordAuthentication(username, password.toCharArray()));
1✔
355
    }
1✔
356

357
    /**
358
     * get the credentials for the given realm.
359
     *
360
     * @param realm
361
     *            the realm
362
     *
363
     * @return the credentials for realm
364
     */
365
    PasswordAuthentication getCredentialsForRealm(String realm) {
366
        if (_credentials == null) {
1!
367
            throw new Error("null _credentials while calling getCredentialsForRealm");
×
368
        }
369
        if (realm == null) {
1!
370
            throw new Error("null realm while calling getCredentialsForRealm");
×
371
        }
372
        return (PasswordAuthentication) _credentials.get(realm);
1✔
373
    }
374

375
    /**
376
     * Specifies a proxy server to use for requests from this client.
377
     *
378
     * @param proxyHost
379
     *            the proxy host
380
     * @param proxyPort
381
     *            the proxy port
382
     */
383
    public abstract void setProxyServer(String proxyHost, int proxyPort);
384

385
    /**
386
     * Specifies a proxy server to use, along with a user and password for authentication.
387
     *
388
     * @param proxyHost
389
     *            the proxy host
390
     * @param proxyPort
391
     *            the proxy port
392
     * @param userName
393
     *            the user name
394
     * @param password
395
     *            the password
396
     */
397
    public void setProxyServer(String proxyHost, int proxyPort, String userName, String password) {
398
        setProxyServer(proxyHost, proxyPort);
×
399
        _proxyAuthorizationString = "Basic "
×
400
                + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
×
401
    }
×
402

403
    /**
404
     * Clears the proxy server settings.
405
     */
406
    public void clearProxyServer() {
407
    }
×
408

409
    /**
410
     * Returns the name of the active proxy server.
411
     *
412
     * @return the proxy host
413
     */
414
    public String getProxyHost() {
415
        return System.getProperty("proxyHost");
×
416
    }
417

418
    /**
419
     * Returns the number of the active proxy port, or 0 is none is specified.
420
     *
421
     * @return the proxy port
422
     */
423
    public int getProxyPort() {
424
        try {
425
            return Integer.getInteger("proxyPort");
×
426
        } catch (NumberFormatException e) {
×
427
            return 0;
×
428
        }
429
    }
430

431
    /**
432
     * Sets the value for a header field to be sent with all requests. If the value set is null, removes the header from
433
     * those to be sent.
434
     *
435
     * @param fieldName
436
     *            the field name
437
     * @param fieldValue
438
     *            the field value
439
     */
440
    public void setHeaderField(String fieldName, String fieldValue) {
441
        _headers.put(fieldName, fieldValue);
1✔
442
    }
1✔
443

444
    /**
445
     * Returns the value for the header field with the specified name. This method will ignore the case of the field
446
     * name.
447
     *
448
     * @param fieldName
449
     *            the field name
450
     *
451
     * @return the header field
452
     */
453
    public String getHeaderField(String fieldName) {
454
        return (String) _headers.get(fieldName);
×
455
    }
456

457
    /**
458
     * Specifies whether an exception will be thrown when an error status (4xx or 5xx) is detected on a response.
459
     * Defaults to the value returned by HttpUnitOptions.getExceptionsThrownOnErrorStatus.
460
     *
461
     * @param throwExceptions
462
     *            the new exceptions thrown on error status
463
     */
464
    public void setExceptionsThrownOnErrorStatus(boolean throwExceptions) {
465
        _exceptionsThrownOnErrorStatus = throwExceptions;
1✔
466
    }
1✔
467

468
    /**
469
     * Returns true if an exception will be thrown when an error status (4xx or 5xx) is detected on a response.
470
     *
471
     * @return the exceptions thrown on error status
472
     */
473
    public boolean getExceptionsThrownOnErrorStatus() {
474
        return _exceptionsThrownOnErrorStatus;
1✔
475
    }
476

477
    /**
478
     * Adds a listener to watch for requests and responses.
479
     *
480
     * @param listener
481
     *            the listener
482
     */
483
    public void addClientListener(WebClientListener listener) {
484
        synchronized (_clientListeners) {
1✔
485
            if (listener != null && !_clientListeners.contains(listener)) {
1!
486
                _clientListeners.add(listener);
1✔
487
            }
488
        }
1✔
489
    }
1✔
490

491
    /**
492
     * Removes a listener to watch for requests and responses.
493
     *
494
     * @param listener
495
     *            the listener
496
     */
497
    public void removeClientListener(WebClientListener listener) {
498
        synchronized (_clientListeners) {
×
499
            _clientListeners.remove(listener);
×
500
        }
×
501
    }
×
502

503
    /**
504
     * Adds a listener to watch for window openings and closings.
505
     *
506
     * @param listener
507
     *            the listener
508
     */
509
    public void addWindowListener(WebWindowListener listener) {
510
        synchronized (_windowListeners) {
1✔
511
            if (listener != null && !_windowListeners.contains(listener)) {
1!
512
                _windowListeners.add(listener);
1✔
513
            }
514
        }
1✔
515
    }
1✔
516

517
    /**
518
     * Removes a listener to watch for window openings and closings.
519
     *
520
     * @param listener
521
     *            the listener
522
     */
523
    public void removeWindowListener(WebWindowListener listener) {
524
        synchronized (_windowListeners) {
×
525
            _windowListeners.remove(listener);
×
526
        }
×
527
    }
×
528

529
    /**
530
     * Returns the next javascript alert without removing it from the queue.
531
     *
532
     * @return the next alert
533
     */
534
    public String getNextAlert() {
535
        return _alerts.isEmpty() ? null : (String) _alerts.getFirst();
1✔
536
    }
537

538
    /**
539
     * Returns the next javascript alert and removes it from the queue. If the queue is empty, will return an empty
540
     * string.
541
     *
542
     * @return the string
543
     */
544
    public String popNextAlert() {
545
        if (_alerts.isEmpty()) {
1✔
546
            return "";
1✔
547
        }
548
        return (String) _alerts.removeFirst();
1✔
549
    }
550

551
    /**
552
     * Specifies the object which will respond to all dialogs.
553
     *
554
     * @param responder
555
     *            the new dialog responder
556
     */
557
    public void setDialogResponder(DialogResponder responder) {
558
        _dialogResponder = responder;
1✔
559
    }
1✔
560

561
    // ------------------------------------------ protected members -----------------------------------
562

563
    /**
564
     * Instantiates a new web client.
565
     */
566
    protected WebClient() {
1✔
567
        _openWindows.add(_mainWindow);
1✔
568
    }
1✔
569

570
    /**
571
     * Creates a web response object which represents the response to the specified web request.
572
     *
573
     * @param request
574
     *            the request to which the response should be generated
575
     * @param targetFrame
576
     *            the frame in which the response should be stored
577
     *
578
     * @return the web response
579
     *
580
     * @throws IOException
581
     *             Signals that an I/O exception has occurred.
582
     */
583
    abstract protected WebResponse newResponse(WebRequest request, FrameSelector targetFrame) throws IOException;
584

585
    /**
586
     * Writes the message body for the request.
587
     *
588
     * @param request
589
     *            the request
590
     * @param stream
591
     *            the stream
592
     *
593
     * @throws IOException
594
     *             Signals that an I/O exception has occurred.
595
     */
596
    protected final void writeMessageBody(WebRequest request, OutputStream stream) throws IOException {
597
        request.writeMessageBody(stream);
1✔
598
    }
1✔
599

600
    /**
601
     * Returns the value of all current header fields.
602
     *
603
     * @param targetURL
604
     *            the target URL
605
     *
606
     * @return the header fields
607
     */
608
    protected Dictionary getHeaderFields(URL targetURL) {
609
        Hashtable result = (Hashtable) _headers.clone();
1✔
610
        result.put("User-Agent", getClientProperties().getUserAgent());
1✔
611
        if (getClientProperties().isAcceptGzip()) {
1✔
612
            result.put("Accept-Encoding", "gzip");
1✔
613
        }
614
        AddHeaderIfNotNull(result, "Cookie", _cookieJar.getCookieHeaderField(targetURL));
1✔
615
        if (_authorizationString == null) {
1✔
616
            _authorizationString = _fixedAuthorizationString;
1✔
617
        }
618
        AddHeaderIfNotNull(result, "Authorization", _authorizationString);
1✔
619
        AddHeaderIfNotNull(result, "Proxy-Authorization", _proxyAuthorizationString);
1✔
620
        _authorizationString = null;
1✔
621
        return result;
1✔
622
    }
623

624
    /**
625
     * Adds the header if not null.
626
     *
627
     * @param result
628
     *            the result
629
     * @param headerName
630
     *            the header name
631
     * @param headerValue
632
     *            the header value
633
     */
634
    private void AddHeaderIfNotNull(Hashtable result, final String headerName, final String headerValue) {
635
        if (headerValue != null) {
1✔
636
            result.put(headerName, headerValue);
1✔
637
        }
638
    }
1✔
639

640
    /**
641
     * Updates this web client based on a received response. This includes updating cookies and frames. This method is
642
     * required by ServletUnit, which cannot call the updateWindow method directly.
643
     *
644
     * @param frame
645
     *            the frame
646
     * @param response
647
     *            the response
648
     *
649
     * @throws IOException
650
     *             Signals that an I/O exception has occurred.
651
     * @throws SAXException
652
     *             the SAX exception
653
     */
654
    protected final void updateMainWindow(FrameSelector frame, WebResponse response) throws IOException, SAXException {
655
        getMainWindow().updateWindow(frame.getName(), response, new RequestContext());
1✔
656
    }
1✔
657

658
    // ------------------------------------------------- package members
659
    // ----------------------------------------------------
660

661
    /**
662
     * Tell listeners.
663
     *
664
     * @param request
665
     *            the request
666
     */
667
    void tellListeners(WebRequest request) {
668
        List listeners;
669

670
        synchronized (_clientListeners) {
1✔
671
            listeners = new ArrayList(_clientListeners);
1✔
672
        }
1✔
673

674
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
675
            ((WebClientListener) i.next()).requestSent(this, request);
1✔
676
        }
677
    }
1✔
678

679
    /**
680
     * Tell listeners.
681
     *
682
     * @param response
683
     *            the response
684
     */
685
    void tellListeners(WebResponse response) {
686
        List listeners;
687

688
        synchronized (_clientListeners) {
1✔
689
            listeners = new ArrayList(_clientListeners);
1✔
690
        }
1✔
691

692
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
693
            ((WebClientListener) i.next()).responseReceived(this, response);
1✔
694
        }
695
    }
1✔
696

697
    /**
698
     * Update client.
699
     *
700
     * @param response
701
     *            the response
702
     *
703
     * @throws IOException
704
     *             Signals that an I/O exception has occurred.
705
     */
706
    void updateClient(WebResponse response) throws IOException {
707
        if (getClientProperties().isAcceptCookies()) {
1✔
708
            _cookieJar.updateCookies(response.getCookieJar());
1✔
709
        }
710
        validateHeaders(response);
1✔
711
    }
1✔
712

713
    /**
714
     * Support Request [ 1288796 ] getCookieJar() in WebClient.
715
     *
716
     * @return the cookie jar
717
     *
718
     * @deprecated - use with care - was not public in the past
719
     */
720
    @Deprecated
721
    public CookieJar getCookieJar() {
722
        return _cookieJar;
1✔
723
    }
724

725
    /**
726
     * Update frame contents.
727
     *
728
     * @param requestWindow
729
     *            the request window
730
     * @param requestTarget
731
     *            the request target
732
     * @param response
733
     *            the response
734
     * @param requestContext
735
     *            the request context
736
     *
737
     * @throws IOException
738
     *             Signals that an I/O exception has occurred.
739
     * @throws SAXException
740
     *             the SAX exception
741
     */
742
    void updateFrameContents(WebWindow requestWindow, String requestTarget, WebResponse response,
743
            RequestContext requestContext) throws IOException, SAXException {
744
        if (response.getFrame() == FrameSelector.NEW_FRAME) {
1✔
745
            WebWindow window = new WebWindow(this, requestWindow.getCurrentPage());
1✔
746
            if (!WebRequest.NEW_WINDOW.equalsIgnoreCase(requestTarget)) {
1✔
747
                window.setName(requestTarget);
1✔
748
            }
749
            response.setFrame(window.getTopFrame());
1✔
750
            window.updateFrameContents(response, requestContext);
1✔
751
            _openWindows.add(window);
1✔
752
            reportWindowOpened(window);
1✔
753
        } else if (response.getFrame().getWindow() != null && response.getFrame().getWindow() != requestWindow) {
1✔
754
            response.getFrame().getWindow().updateFrameContents(response, requestContext);
1✔
755
        } else {
756
            if (response.getFrame() == FrameSelector.TOP_FRAME) {
1✔
757
                response.setFrame(requestWindow.getTopFrame());
1✔
758
            }
759
            requestWindow.updateFrameContents(response, requestContext);
1✔
760
        }
761
    }
1✔
762

763
    /**
764
     * Close.
765
     *
766
     * @param window
767
     *            the window
768
     */
769
    void close(WebWindow window) {
770
        if (!_openWindows.contains(window)) {
1!
771
            throw new IllegalStateException("Window is already closed");
×
772
        }
773
        _openWindows.remove(window);
1✔
774
        if (_openWindows.isEmpty()) {
1✔
775
            _openWindows.add(new WebWindow(this));
1✔
776
        }
777
        if (window.equals(_mainWindow)) {
1✔
778
            _mainWindow = (WebWindow) _openWindows.get(0);
1✔
779
        }
780
        reportWindowClosed(window);
1✔
781
    }
1✔
782

783
    /**
784
     * Report window opened.
785
     *
786
     * @param window
787
     *            the window
788
     */
789
    private void reportWindowOpened(WebWindow window) {
790
        List listeners;
791

792
        synchronized (_windowListeners) {
1✔
793
            listeners = new ArrayList(_windowListeners);
1✔
794
        }
1✔
795

796
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
797
            ((WebWindowListener) i.next()).windowOpened(this, window);
1✔
798
        }
799
    }
1✔
800

801
    /**
802
     * Report window closed.
803
     *
804
     * @param window
805
     *            the window
806
     */
807
    private void reportWindowClosed(WebWindow window) {
808
        List listeners;
809

810
        synchronized (_windowListeners) {
1✔
811
            listeners = new ArrayList(_windowListeners);
1✔
812
        }
1✔
813

814
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
815
            ((WebWindowListener) i.next()).windowClosed(this, window);
1✔
816
        }
817
    }
1✔
818

819
    // ------------------------------------------ package members ------------------------------------
820

821
    /**
822
     * Gets the confirmation response.
823
     *
824
     * @param message
825
     *            the message
826
     *
827
     * @return the confirmation response
828
     */
829
    boolean getConfirmationResponse(String message) {
830
        return _dialogResponder.getConfirmation(message);
1✔
831
    }
832

833
    /**
834
     * Gets the user response.
835
     *
836
     * @param message
837
     *            the message
838
     * @param defaultResponse
839
     *            the default response
840
     *
841
     * @return the user response
842
     */
843
    String getUserResponse(String message, String defaultResponse) {
844
        return _dialogResponder.getUserResponse(message, defaultResponse);
1✔
845
    }
846

847
    /**
848
     * simulate an alert by remembering the alert message on a Stack.
849
     *
850
     * @param message
851
     *            - the alert message to post
852
     */
853
    void postAlert(String message) {
854
        _alerts.addLast(message);
1✔
855
    }
1✔
856

857
    // ------------------------------------------ private members -------------------------------------
858

859
    /** The list of alerts generated by JavaScript. **/
860
    private LinkedList _alerts = new LinkedList();
1✔
861

862
    /** The currently defined cookies. **/
863
    private CookieJar _cookieJar = new CookieJar();
1✔
864

865
    /** A map of header names to values. **/
866
    private HeaderDictionary _headers = new HeaderDictionary();
1✔
867

868
    /** The exceptions thrown on error status. */
869
    private boolean _exceptionsThrownOnErrorStatus = HttpUnitOptions.getExceptionsThrownOnErrorStatus();
1✔
870

871
    /** The client listeners. */
872
    private final List _clientListeners = new ArrayList<>();
1✔
873

874
    /** The window listeners. */
875
    private final List _windowListeners = new ArrayList<>();
1✔
876

877
    /** The dialog responder. */
878
    private DialogResponder _dialogResponder = new DialogAdapter();
1✔
879

880
    /** The client properties. */
881
    private ClientProperties _clientProperties;
882

883
    /**
884
     * Examines the headers in the response and throws an exception if appropriate.
885
     *
886
     * @param response
887
     *            the response
888
     *
889
     * @throws HttpException
890
     *             the http exception
891
     *
892
     * @parm response - the response to validate
893
     */
894
    private void validateHeaders(WebResponse response) throws HttpException {
895
        HttpException exception = null;
1✔
896
        if (response.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) {
1!
897
            exception = new HttpInternalErrorException(response.getURL());
×
898
        } else if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
1✔
899
            exception = new HttpNotFoundException(response.getResponseMessage(), response.getURL());
1✔
900
        } else if (response.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
1✔
901
            exception = new HttpException(response.getResponseCode(), response.getResponseMessage(), response.getURL());
1✔
902
        }
903
        // is there an exception?
904
        if (exception != null) {
1✔
905
            // see feature request [ 914314 ] Add HttpException.getResponse for better reporting
906
            exception.setResponse(response);
1✔
907
            // shall we ignore errors?
908
            if (!getExceptionsThrownOnErrorStatus()) {
1✔
909
                return;
1✔
910
            }
911
            throw exception;
1✔
912
        }
913
    }
1✔
914

915
    /**
916
     * Find frame.
917
     *
918
     * @param target
919
     *            the target
920
     *
921
     * @return the frame selector
922
     */
923
    FrameSelector findFrame(String target) {
924
        for (Object _openWindow : _openWindows) {
1✔
925
            WebWindow webWindow = (WebWindow) _openWindow;
1✔
926
            FrameSelector frame = webWindow.getFrame(target);
1✔
927
            if (frame != null) {
1✔
928
                return frame;
1✔
929
            }
930
        }
1✔
931
        return null;
1✔
932
    }
933

934
    /**
935
     * Sends a request and returns a response after dealing with any authentication challenge. If challenged and able to
936
     * respond, resends the request after setting the authentication header (which will apply only for the that
937
     * request).
938
     *
939
     * @param request
940
     *            the original request
941
     * @param targetFrame
942
     *            the frame into which the result will be stored
943
     *
944
     * @return a response from the server
945
     *
946
     * @throws IOException
947
     *             if an exception (including authorization failure) occurs
948
     */
949
    WebResponse createResponse(WebRequest request, FrameSelector targetFrame) throws IOException {
950
        WebResponse response = newResponse(request, targetFrame);
1✔
951
        AuthenticationChallenge challenge = new AuthenticationChallenge(this, request,
1✔
952
                response.getHeaderField("WWW-Authenticate"));
1✔
953
        if (!challenge.needToAuthenticate()) {
1✔
954
            return response;
1✔
955
        }
956
        setOnetimeAuthenticationHeader(challenge.createAuthenticationHeader());
1✔
957
        WebResponse response2 = newResponse(request, targetFrame);
1✔
958
        if (response2.getHeaderField("WWW-Authenticate") != null && getExceptionsThrownOnErrorStatus()) {
1!
959
            throw AuthenticationChallenge.createException(response2.getHeaderField("WWW-Authenticate"));
×
960
        }
961
        return response2;
1✔
962
    }
963

964
    /**
965
     * Sets the onetime authentication header.
966
     *
967
     * @param authorizationHeader
968
     *            the new onetime authentication header
969
     */
970
    private void setOnetimeAuthenticationHeader(String authorizationHeader) {
971
        _authorizationString = authorizationHeader;
1✔
972
    }
1✔
973

974
    // ==================================================================================================
975

976
    /**
977
     * The Class HeaderDictionary.
978
     */
979
    static public class HeaderDictionary extends Hashtable {
1✔
980

981
        /** The Constant serialVersionUID. */
982
        private static final long serialVersionUID = 1L;
983

984
        /**
985
         * Adds the entries.
986
         *
987
         * @param source
988
         *            the source
989
         */
990
        public void addEntries(Dictionary source) {
991
            for (Enumeration e = source.keys(); e.hasMoreElements();) {
1✔
992
                Object key = e.nextElement();
1✔
993
                put(key, source.get(key));
1✔
994
            }
1✔
995
        }
1✔
996

997
        @Override
998
        public boolean containsKey(Object key) {
999
            return super.containsKey(matchPreviousFieldName(key.toString()));
1✔
1000
        }
1001

1002
        @Override
1003
        public Object get(Object fieldName) {
1004
            return super.get(matchPreviousFieldName(fieldName.toString()));
1✔
1005
        }
1006

1007
        @Override
1008
        public Object put(Object fieldName, Object fieldValue) {
1009
            fieldName = matchPreviousFieldName(fieldName.toString());
1✔
1010
            Object oldValue = super.get(fieldName);
1✔
1011
            if (fieldValue == null) {
1!
1012
                remove(fieldName);
×
1013
            } else {
1014
                super.put(fieldName, fieldValue);
1✔
1015
            }
1016
            return oldValue;
1✔
1017
        }
1018

1019
        /**
1020
         * If a matching field name with different case is already known, returns the older name. Otherwise, returns the
1021
         * specified name.
1022
         *
1023
         * @param fieldName
1024
         *            the field name
1025
         *
1026
         * @return the string
1027
         */
1028
        private String matchPreviousFieldName(String fieldName) {
1029
            for (Enumeration e = keys(); e.hasMoreElements();) {
1✔
1030
                String key = (String) e.nextElement();
1✔
1031
                if (key.equalsIgnoreCase(fieldName)) {
1✔
1032
                    return key;
1✔
1033
                }
1034
            }
1✔
1035
            return fieldName;
1✔
1036
        }
1037

1038
    }
1039

1040
}
1041

1042
// ==================================================================================================
1043

1044
class RedirectWebRequest extends WebRequest {
1045

1046
    RedirectWebRequest(WebResponse response) {
1047
        super(response.getURL(), response.getHeaderField("Location"), response.getFrame(), response.getFrameName());
1✔
1048
        if (response.getReferer() != null) {
1✔
1049
            setHeaderField("Referer", response.getReferer());
1✔
1050
        }
1051
    }
1✔
1052

1053
    /**
1054
     * Returns the HTTP method defined for this request.
1055
     **/
1056
    @Override
1057
    public String getMethod() {
1058
        return "GET";
1✔
1059
    }
1060
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc