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

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

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
 * MIT License
3
 *
4
 * Copyright 2011-2025 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.httpunit;
21

22
import com.meterware.httpunit.cookies.Cookie;
23
import com.meterware.httpunit.cookies.CookieJar;
24

25
import java.io.IOException;
26
import java.io.OutputStream;
27
import java.net.HttpURLConnection;
28
import java.net.PasswordAuthentication;
29
import java.net.URL;
30
import java.nio.charset.StandardCharsets;
31
import java.util.ArrayList;
32
import java.util.Base64;
33
import java.util.Dictionary;
34
import java.util.Enumeration;
35
import java.util.Hashtable;
36
import java.util.Iterator;
37
import java.util.LinkedList;
38
import java.util.List;
39

40
import org.xml.sax.SAXException;
41

42
/**
43
 * The context for a series of web requests. This class manages cookies used to maintain session context, computes
44
 * relative URLs, and generally emulates the browser behavior needed to build an automated test of a web site.
45
 **/
46
public abstract class WebClient {
47

48
    /** The open windows. */
49
    private ArrayList _openWindows = new ArrayList<>();
1✔
50

51
    /** The current main window. **/
52
    private WebWindow _mainWindow = new WebWindow(this);
1✔
53

54
    /** An authorization string to be sent with every request, whether challenged or not. May be null. **/
55
    private String _fixedAuthorizationString;
56

57
    /** An authorization string to be sent with the next request only. May be null. **/
58
    private String _authorizationString;
59

60
    /** The proxy authorization string. */
61
    private String _proxyAuthorizationString;
62

63
    /** The credentials. */
64
    private Hashtable _credentials = new Hashtable<>();
1✔
65

66
    /**
67
     * Gets the main window.
68
     *
69
     * @return the main window
70
     */
71
    public WebWindow getMainWindow() {
72
        return _mainWindow;
1✔
73
    }
74

75
    /**
76
     * Sets the main window.
77
     *
78
     * @param mainWindow
79
     *            the new main window
80
     */
81
    public void setMainWindow(WebWindow mainWindow) {
82
        if (!_openWindows.contains(mainWindow)) {
×
83
            throw new IllegalArgumentException("May only select an open window owned by this client");
×
84
        }
85
        _mainWindow = mainWindow;
×
86
    }
×
87

88
    /**
89
     * Gets the open windows.
90
     *
91
     * @return the open windows
92
     */
93
    public WebWindow[] getOpenWindows() {
94
        return (WebWindow[]) _openWindows.toArray(new WebWindow[_openWindows.size()]);
1✔
95
    }
96

97
    /**
98
     * Gets the open window.
99
     *
100
     * @param name
101
     *            the name
102
     *
103
     * @return the open window
104
     */
105
    public WebWindow getOpenWindow(String name) {
106
        if (name == null || name.isEmpty()) {
1!
107
            return null;
×
108
        }
109
        for (Iterator i = _openWindows.iterator(); i.hasNext();) {
1!
110
            WebWindow window = (WebWindow) i.next();
1✔
111
            if (name.equals(window.getName())) {
1✔
112
                return window;
1✔
113
            }
114
        }
1✔
115
        return null;
×
116
    }
117

118
    /**
119
     * Submits a GET method request and returns a response.
120
     *
121
     * @param urlString
122
     *            the url string
123
     *
124
     * @return the response
125
     *
126
     * @throws IOException
127
     *             Signals that an I/O exception has occurred.
128
     *
129
     * @exception SAXException
130
     *                thrown if there is an error parsing the retrieved page
131
     */
132
    public WebResponse getResponse(String urlString) throws IOException, SAXException {
133
        return _mainWindow.getResponse(urlString);
1✔
134
    }
135

136
    /**
137
     * Submits a web request and returns a response. This is an alternate name for the getResponse method.
138
     *
139
     * @param request
140
     *            the request
141
     *
142
     * @return the web response
143
     *
144
     * @throws IOException
145
     *             Signals that an I/O exception has occurred.
146
     * @throws SAXException
147
     *             the SAX exception
148
     */
149
    public WebResponse sendRequest(WebRequest request) throws IOException, SAXException {
150
        return _mainWindow.sendRequest(request);
1✔
151
    }
152

153
    /**
154
     * Returns the response representing the current top page in the main window.
155
     *
156
     * @return the current page
157
     */
158
    public WebResponse getCurrentPage() {
159
        return _mainWindow.getCurrentPage();
1✔
160
    }
161

162
    /**
163
     * Submits a web request and returns a response, using all state developed so far as stored in cookies as requested
164
     * by the server.
165
     *
166
     * @param request
167
     *            the request
168
     *
169
     * @return the response
170
     *
171
     * @throws IOException
172
     *             Signals that an I/O exception has occurred.
173
     *
174
     * @exception SAXException
175
     *                thrown if there is an error parsing the retrieved page
176
     */
177
    public WebResponse getResponse(WebRequest request) throws IOException, SAXException {
178
        return _mainWindow.getResponse(request);
1✔
179
    }
180

181
    /**
182
     * Returns the name of the currently active frames in the main window.
183
     *
184
     * @return the frame names
185
     */
186
    public String[] getFrameNames() {
187
        return _mainWindow.getFrameNames();
1✔
188
    }
189

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

203
    /**
204
     * Returns the response associated with the specified frame name in the main window. Throws a runtime exception if
205
     * no matching frame is defined.
206
     *
207
     * @param targetFrame
208
     *            the target frame
209
     *
210
     * @return the frame contents
211
     */
212
    public WebResponse getFrameContents(FrameSelector targetFrame) {
213
        return _mainWindow.getFrameContents(targetFrame);
1✔
214
    }
215

216
    /**
217
     * Returns the resource specified by the request. Does not update the client or load included framesets or scripts.
218
     * May return null if the resource is a JavaScript URL which would normally leave the client unchanged.
219
     *
220
     * @param request
221
     *            the request
222
     *
223
     * @return the resource
224
     *
225
     * @throws IOException
226
     *             Signals that an I/O exception has occurred.
227
     */
228
    public WebResponse getResource(WebRequest request) throws IOException {
229
        return _mainWindow.getResource(request);
×
230
    }
231

232
    /**
233
     * Resets the state of this client, removing all cookies, frames, and per-client headers. This does not affect any
234
     * listeners or preferences which may have been set.
235
     **/
236
    public void clearContents() {
237
        _mainWindow = new WebWindow(this);
×
238
        _cookieJar.clear();
×
239
        _headers = new HeaderDictionary();
×
240
    }
×
241

242
    /**
243
     * Defines a cookie to be sent to the server on every request.
244
     *
245
     * @param name
246
     *            the name
247
     * @param value
248
     *            the value
249
     *
250
     * @deprecated as of 1.6, use #putCookie instead.
251
     */
252
    @Deprecated
253
    public void addCookie(String name, String value) {
254
        _cookieJar.addCookie(name, value);
×
255
    }
×
256

257
    /**
258
     * Defines a cookie to be sent to the server on every request. This overrides any previous setting for this cookie
259
     * name.
260
     *
261
     * @param name
262
     *            the name
263
     * @param value
264
     *            the value
265
     */
266
    public void putCookie(String name, String value) {
267
        _cookieJar.putCookie(name, value);
1✔
268
    }
1✔
269

270
    /**
271
     * Returns the name of all the active cookies which will be sent to the server.
272
     *
273
     * @return the cookie names
274
     */
275
    public String[] getCookieNames() {
276
        return _cookieJar.getCookieNames();
1✔
277
    }
278

279
    /**
280
     * Returns an object containing the details of the named cookie.
281
     *
282
     * @param name
283
     *            the name
284
     *
285
     * @return the cookie details
286
     */
287
    public Cookie getCookieDetails(String name) {
288
        return _cookieJar.getCookie(name);
1✔
289
    }
290

291
    /**
292
     * Returns the value of the specified cookie.
293
     *
294
     * @param name
295
     *            the name
296
     *
297
     * @return the cookie value
298
     */
299
    public String getCookieValue(String name) {
300
        return _cookieJar.getCookieValue(name);
1✔
301
    }
302

303
    /**
304
     * Returns the properties associated with this client.
305
     *
306
     * @return the client properties
307
     */
308
    public ClientProperties getClientProperties() {
309
        if (_clientProperties == null) {
1✔
310
            _clientProperties = ClientProperties.getDefaultProperties().cloneProperties();
1✔
311
        }
312
        return _clientProperties;
1✔
313
    }
314

315
    /**
316
     * Specifies the user agent identification. Used to trigger browser-specific server behavior.
317
     *
318
     * @param userAgent
319
     *            the new user agent
320
     *
321
     * @deprecated as of 1.4.6. Use ClientProperties#setUserAgent instead.
322
     */
323
    @Deprecated
324
    public void setUserAgent(String userAgent) {
325
        getClientProperties().setUserAgent(userAgent);
×
326
    }
×
327

328
    /**
329
     * Returns the current user agent setting.
330
     *
331
     * @return the user agent
332
     *
333
     * @deprecated as of 1.4.6. Use ClientProperties#getUserAgent instead.
334
     */
335
    @Deprecated
336
    public String getUserAgent() {
337
        return getClientProperties().getUserAgent();
×
338
    }
339

340
    /**
341
     * Sets a username and password for a basic authentication scheme. Use #setAuthentication for more accurate
342
     * emulation of browser behavior.
343
     *
344
     * @param userName
345
     *            the user name
346
     * @param password
347
     *            the password
348
     */
349
    public void setAuthorization(String userName, String password) {
350
        _fixedAuthorizationString = "Basic "
1✔
351
                + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
1✔
352
    }
1✔
353

354
    /**
355
     * Specifies a username and password for on-demand authentication. Will only send the authorization header when
356
     * challenged for the specified realm.
357
     *
358
     * @param realm
359
     *            the realm for which the credentials apply.
360
     * @param username
361
     *            the user to authenticate
362
     * @param password
363
     *            the credentials for the user
364
     */
365
    public void setAuthentication(String realm, String username, String password) {
366
        _credentials.put(realm, new PasswordAuthentication(username, password.toCharArray()));
1✔
367
    }
1✔
368

369
    /**
370
     * get the credentials for the given realm.
371
     *
372
     * @param realm
373
     *            the realm
374
     *
375
     * @return the credentials for realm
376
     */
377
    PasswordAuthentication getCredentialsForRealm(String realm) {
378
        if (_credentials == null) {
1!
379
            throw new Error("null _credentials while calling getCredentialsForRealm");
×
380
        }
381
        if (realm == null) {
1!
382
            throw new Error("null realm while calling getCredentialsForRealm");
×
383
        }
384
        return (PasswordAuthentication) _credentials.get(realm);
1✔
385
    }
386

387
    /**
388
     * Specifies a proxy server to use for requests from this client.
389
     *
390
     * @param proxyHost
391
     *            the proxy host
392
     * @param proxyPort
393
     *            the proxy port
394
     */
395
    public abstract void setProxyServer(String proxyHost, int proxyPort);
396

397
    /**
398
     * Specifies a proxy server to use, along with a user and password for authentication.
399
     *
400
     * @param proxyHost
401
     *            the proxy host
402
     * @param proxyPort
403
     *            the proxy port
404
     * @param userName
405
     *            the user name
406
     * @param password
407
     *            the password
408
     */
409
    public void setProxyServer(String proxyHost, int proxyPort, String userName, String password) {
410
        setProxyServer(proxyHost, proxyPort);
×
411
        _proxyAuthorizationString = "Basic "
×
412
                + Base64.getEncoder().encodeToString((userName + ':' + password).getBytes(StandardCharsets.UTF_8));
×
413
    }
×
414

415
    /**
416
     * Clears the proxy server settings.
417
     */
418
    public void clearProxyServer() {
419
    }
×
420

421
    /**
422
     * Returns the name of the active proxy server.
423
     *
424
     * @return the proxy host
425
     */
426
    public String getProxyHost() {
427
        return System.getProperty("proxyHost");
×
428
    }
429

430
    /**
431
     * Returns the number of the active proxy port, or 0 is none is specified.
432
     *
433
     * @return the proxy port
434
     */
435
    public int getProxyPort() {
436
        try {
437
            return Integer.getInteger("proxyPort");
×
438
        } catch (NumberFormatException e) {
×
439
            return 0;
×
440
        }
441
    }
442

443
    /**
444
     * Sets the value for a header field to be sent with all requests. If the value set is null, removes the header from
445
     * those to be sent.
446
     *
447
     * @param fieldName
448
     *            the field name
449
     * @param fieldValue
450
     *            the field value
451
     */
452
    public void setHeaderField(String fieldName, String fieldValue) {
453
        _headers.put(fieldName, fieldValue);
1✔
454
    }
1✔
455

456
    /**
457
     * Returns the value for the header field with the specified name. This method will ignore the case of the field
458
     * name.
459
     *
460
     * @param fieldName
461
     *            the field name
462
     *
463
     * @return the header field
464
     */
465
    public String getHeaderField(String fieldName) {
466
        return (String) _headers.get(fieldName);
×
467
    }
468

469
    /**
470
     * Specifies whether an exception will be thrown when an error status (4xx or 5xx) is detected on a response.
471
     * Defaults to the value returned by HttpUnitOptions.getExceptionsThrownOnErrorStatus.
472
     *
473
     * @param throwExceptions
474
     *            the new exceptions thrown on error status
475
     */
476
    public void setExceptionsThrownOnErrorStatus(boolean throwExceptions) {
477
        _exceptionsThrownOnErrorStatus = throwExceptions;
1✔
478
    }
1✔
479

480
    /**
481
     * Returns true if an exception will be thrown when an error status (4xx or 5xx) is detected on a response.
482
     *
483
     * @return the exceptions thrown on error status
484
     */
485
    public boolean getExceptionsThrownOnErrorStatus() {
486
        return _exceptionsThrownOnErrorStatus;
1✔
487
    }
488

489
    /**
490
     * Adds a listener to watch for requests and responses.
491
     *
492
     * @param listener
493
     *            the listener
494
     */
495
    public void addClientListener(WebClientListener listener) {
496
        synchronized (_clientListeners) {
1✔
497
            if (listener != null && !_clientListeners.contains(listener)) {
1!
498
                _clientListeners.add(listener);
1✔
499
            }
500
        }
1✔
501
    }
1✔
502

503
    /**
504
     * Removes a listener to watch for requests and responses.
505
     *
506
     * @param listener
507
     *            the listener
508
     */
509
    public void removeClientListener(WebClientListener listener) {
510
        synchronized (_clientListeners) {
×
511
            _clientListeners.remove(listener);
×
512
        }
×
513
    }
×
514

515
    /**
516
     * Adds a listener to watch for window openings and closings.
517
     *
518
     * @param listener
519
     *            the listener
520
     */
521
    public void addWindowListener(WebWindowListener listener) {
522
        synchronized (_windowListeners) {
1✔
523
            if (listener != null && !_windowListeners.contains(listener)) {
1!
524
                _windowListeners.add(listener);
1✔
525
            }
526
        }
1✔
527
    }
1✔
528

529
    /**
530
     * Removes a listener to watch for window openings and closings.
531
     *
532
     * @param listener
533
     *            the listener
534
     */
535
    public void removeWindowListener(WebWindowListener listener) {
536
        synchronized (_windowListeners) {
×
537
            _windowListeners.remove(listener);
×
538
        }
×
539
    }
×
540

541
    /**
542
     * Returns the next javascript alert without removing it from the queue.
543
     *
544
     * @return the next alert
545
     */
546
    public String getNextAlert() {
547
        return _alerts.isEmpty() ? null : (String) _alerts.getFirst();
1✔
548
    }
549

550
    /**
551
     * Returns the next javascript alert and removes it from the queue. If the queue is empty, will return an empty
552
     * string.
553
     *
554
     * @return the string
555
     */
556
    public String popNextAlert() {
557
        if (_alerts.isEmpty()) {
1✔
558
            return "";
1✔
559
        }
560
        return (String) _alerts.removeFirst();
1✔
561
    }
562

563
    /**
564
     * Specifies the object which will respond to all dialogs.
565
     *
566
     * @param responder
567
     *            the new dialog responder
568
     */
569
    public void setDialogResponder(DialogResponder responder) {
570
        _dialogResponder = responder;
1✔
571
    }
1✔
572

573
    // ------------------------------------------ protected members -----------------------------------
574

575
    /**
576
     * Instantiates a new web client.
577
     */
578
    protected WebClient() {
1✔
579
        _openWindows.add(_mainWindow);
1✔
580
    }
1✔
581

582
    /**
583
     * Creates a web response object which represents the response to the specified web request.
584
     *
585
     * @param request
586
     *            the request to which the response should be generated
587
     * @param targetFrame
588
     *            the frame in which the response should be stored
589
     *
590
     * @return the web response
591
     *
592
     * @throws IOException
593
     *             Signals that an I/O exception has occurred.
594
     */
595
    abstract protected WebResponse newResponse(WebRequest request, FrameSelector targetFrame) throws IOException;
596

597
    /**
598
     * Writes the message body for the request.
599
     *
600
     * @param request
601
     *            the request
602
     * @param stream
603
     *            the stream
604
     *
605
     * @throws IOException
606
     *             Signals that an I/O exception has occurred.
607
     */
608
    protected final void writeMessageBody(WebRequest request, OutputStream stream) throws IOException {
609
        request.writeMessageBody(stream);
1✔
610
    }
1✔
611

612
    /**
613
     * Returns the value of all current header fields.
614
     *
615
     * @param targetURL
616
     *            the target URL
617
     *
618
     * @return the header fields
619
     */
620
    protected Dictionary getHeaderFields(URL targetURL) {
621
        Hashtable result = (Hashtable) _headers.clone();
1✔
622
        result.put("User-Agent", getClientProperties().getUserAgent());
1✔
623
        if (getClientProperties().isAcceptGzip()) {
1✔
624
            result.put("Accept-Encoding", "gzip");
1✔
625
        }
626
        AddHeaderIfNotNull(result, "Cookie", _cookieJar.getCookieHeaderField(targetURL));
1✔
627
        if (_authorizationString == null) {
1✔
628
            _authorizationString = _fixedAuthorizationString;
1✔
629
        }
630
        AddHeaderIfNotNull(result, "Authorization", _authorizationString);
1✔
631
        AddHeaderIfNotNull(result, "Proxy-Authorization", _proxyAuthorizationString);
1✔
632
        _authorizationString = null;
1✔
633
        return result;
1✔
634
    }
635

636
    /**
637
     * Adds the header if not null.
638
     *
639
     * @param result
640
     *            the result
641
     * @param headerName
642
     *            the header name
643
     * @param headerValue
644
     *            the header value
645
     */
646
    private void AddHeaderIfNotNull(Hashtable result, final String headerName, final String headerValue) {
647
        if (headerValue != null) {
1✔
648
            result.put(headerName, headerValue);
1✔
649
        }
650
    }
1✔
651

652
    /**
653
     * Updates this web client based on a received response. This includes updating cookies and frames. This method is
654
     * required by ServletUnit, which cannot call the updateWindow method directly.
655
     *
656
     * @param frame
657
     *            the frame
658
     * @param response
659
     *            the response
660
     *
661
     * @throws IOException
662
     *             Signals that an I/O exception has occurred.
663
     * @throws SAXException
664
     *             the SAX exception
665
     */
666
    protected final void updateMainWindow(FrameSelector frame, WebResponse response) throws IOException, SAXException {
667
        getMainWindow().updateWindow(frame.getName(), response, new RequestContext());
1✔
668
    }
1✔
669

670
    // ------------------------------------------------- package members
671
    // ----------------------------------------------------
672

673
    /**
674
     * Tell listeners.
675
     *
676
     * @param request
677
     *            the request
678
     */
679
    void tellListeners(WebRequest request) {
680
        List listeners;
681

682
        synchronized (_clientListeners) {
1✔
683
            listeners = new ArrayList(_clientListeners);
1✔
684
        }
1✔
685

686
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
687
            ((WebClientListener) i.next()).requestSent(this, request);
1✔
688
        }
689
    }
1✔
690

691
    /**
692
     * Tell listeners.
693
     *
694
     * @param response
695
     *            the response
696
     */
697
    void tellListeners(WebResponse response) {
698
        List listeners;
699

700
        synchronized (_clientListeners) {
1✔
701
            listeners = new ArrayList(_clientListeners);
1✔
702
        }
1✔
703

704
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
705
            ((WebClientListener) i.next()).responseReceived(this, response);
1✔
706
        }
707
    }
1✔
708

709
    /**
710
     * Update client.
711
     *
712
     * @param response
713
     *            the response
714
     *
715
     * @throws IOException
716
     *             Signals that an I/O exception has occurred.
717
     */
718
    void updateClient(WebResponse response) throws IOException {
719
        if (getClientProperties().isAcceptCookies()) {
1✔
720
            _cookieJar.updateCookies(response.getCookieJar());
1✔
721
        }
722
        validateHeaders(response);
1✔
723
    }
1✔
724

725
    /**
726
     * Support Request [ 1288796 ] getCookieJar() in WebClient.
727
     *
728
     * @return the cookie jar
729
     *
730
     * @deprecated - use with care - was not public in the past
731
     */
732
    @Deprecated
733
    public CookieJar getCookieJar() {
734
        return _cookieJar;
1✔
735
    }
736

737
    /**
738
     * Update frame contents.
739
     *
740
     * @param requestWindow
741
     *            the request window
742
     * @param requestTarget
743
     *            the request target
744
     * @param response
745
     *            the response
746
     * @param requestContext
747
     *            the request context
748
     *
749
     * @throws IOException
750
     *             Signals that an I/O exception has occurred.
751
     * @throws SAXException
752
     *             the SAX exception
753
     */
754
    void updateFrameContents(WebWindow requestWindow, String requestTarget, WebResponse response,
755
            RequestContext requestContext) throws IOException, SAXException {
756
        if (response.getFrame() == FrameSelector.NEW_FRAME) {
1✔
757
            WebWindow window = new WebWindow(this, requestWindow.getCurrentPage());
1✔
758
            if (!WebRequest.NEW_WINDOW.equalsIgnoreCase(requestTarget)) {
1✔
759
                window.setName(requestTarget);
1✔
760
            }
761
            response.setFrame(window.getTopFrame());
1✔
762
            window.updateFrameContents(response, requestContext);
1✔
763
            _openWindows.add(window);
1✔
764
            reportWindowOpened(window);
1✔
765
        } else if (response.getFrame().getWindow() != null && response.getFrame().getWindow() != requestWindow) {
1✔
766
            response.getFrame().getWindow().updateFrameContents(response, requestContext);
1✔
767
        } else {
768
            if (response.getFrame() == FrameSelector.TOP_FRAME) {
1✔
769
                response.setFrame(requestWindow.getTopFrame());
1✔
770
            }
771
            requestWindow.updateFrameContents(response, requestContext);
1✔
772
        }
773
    }
1✔
774

775
    /**
776
     * Close.
777
     *
778
     * @param window
779
     *            the window
780
     */
781
    void close(WebWindow window) {
782
        if (!_openWindows.contains(window)) {
1!
783
            throw new IllegalStateException("Window is already closed");
×
784
        }
785
        _openWindows.remove(window);
1✔
786
        if (_openWindows.isEmpty()) {
1✔
787
            _openWindows.add(new WebWindow(this));
1✔
788
        }
789
        if (window.equals(_mainWindow)) {
1✔
790
            _mainWindow = (WebWindow) _openWindows.get(0);
1✔
791
        }
792
        reportWindowClosed(window);
1✔
793
    }
1✔
794

795
    /**
796
     * Report window opened.
797
     *
798
     * @param window
799
     *            the window
800
     */
801
    private void reportWindowOpened(WebWindow window) {
802
        List listeners;
803

804
        synchronized (_windowListeners) {
1✔
805
            listeners = new ArrayList(_windowListeners);
1✔
806
        }
1✔
807

808
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
809
            ((WebWindowListener) i.next()).windowOpened(this, window);
1✔
810
        }
811
    }
1✔
812

813
    /**
814
     * Report window closed.
815
     *
816
     * @param window
817
     *            the window
818
     */
819
    private void reportWindowClosed(WebWindow window) {
820
        List listeners;
821

822
        synchronized (_windowListeners) {
1✔
823
            listeners = new ArrayList(_windowListeners);
1✔
824
        }
1✔
825

826
        for (Iterator i = listeners.iterator(); i.hasNext();) {
1✔
827
            ((WebWindowListener) i.next()).windowClosed(this, window);
1✔
828
        }
829
    }
1✔
830

831
    // ------------------------------------------ package members ------------------------------------
832

833
    /**
834
     * Gets the confirmation response.
835
     *
836
     * @param message
837
     *            the message
838
     *
839
     * @return the confirmation response
840
     */
841
    boolean getConfirmationResponse(String message) {
842
        return _dialogResponder.getConfirmation(message);
1✔
843
    }
844

845
    /**
846
     * Gets the user response.
847
     *
848
     * @param message
849
     *            the message
850
     * @param defaultResponse
851
     *            the default response
852
     *
853
     * @return the user response
854
     */
855
    String getUserResponse(String message, String defaultResponse) {
856
        return _dialogResponder.getUserResponse(message, defaultResponse);
1✔
857
    }
858

859
    /**
860
     * simulate an alert by remembering the alert message on a Stack.
861
     *
862
     * @param message
863
     *            - the alert message to post
864
     */
865
    void postAlert(String message) {
866
        _alerts.addLast(message);
1✔
867
    }
1✔
868

869
    // ------------------------------------------ private members -------------------------------------
870

871
    /** The list of alerts generated by JavaScript. **/
872
    private LinkedList _alerts = new LinkedList();
1✔
873

874
    /** The currently defined cookies. **/
875
    private CookieJar _cookieJar = new CookieJar();
1✔
876

877
    /** A map of header names to values. **/
878
    private HeaderDictionary _headers = new HeaderDictionary();
1✔
879

880
    /** The exceptions thrown on error status. */
881
    private boolean _exceptionsThrownOnErrorStatus = HttpUnitOptions.getExceptionsThrownOnErrorStatus();
1✔
882

883
    /** The client listeners. */
884
    private final List _clientListeners = new ArrayList<>();
1✔
885

886
    /** The window listeners. */
887
    private final List _windowListeners = new ArrayList<>();
1✔
888

889
    /** The dialog responder. */
890
    private DialogResponder _dialogResponder = new DialogAdapter();
1✔
891

892
    /** The client properties. */
893
    private ClientProperties _clientProperties;
894

895
    /**
896
     * Examines the headers in the response and throws an exception if appropriate.
897
     *
898
     * @param response
899
     *            the response
900
     *
901
     * @throws HttpException
902
     *             the http exception
903
     *
904
     * @parm response - the response to validate
905
     */
906
    private void validateHeaders(WebResponse response) throws HttpException {
907
        HttpException exception = null;
1✔
908
        if (response.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR) {
1!
909
            exception = new HttpInternalErrorException(response.getURL());
×
910
        } else if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
1✔
911
            exception = new HttpNotFoundException(response.getResponseMessage(), response.getURL());
1✔
912
        } else if (response.getResponseCode() >= HttpURLConnection.HTTP_BAD_REQUEST) {
1✔
913
            exception = new HttpException(response.getResponseCode(), response.getResponseMessage(), response.getURL());
1✔
914
        }
915
        // is there an exception?
916
        if (exception != null) {
1✔
917
            // see feature request [ 914314 ] Add HttpException.getResponse for better reporting
918
            exception.setResponse(response);
1✔
919
            // shall we ignore errors?
920
            if (!getExceptionsThrownOnErrorStatus()) {
1✔
921
                return;
1✔
922
            }
923
            throw exception;
1✔
924
        }
925
    }
1✔
926

927
    /**
928
     * Find frame.
929
     *
930
     * @param target
931
     *            the target
932
     *
933
     * @return the frame selector
934
     */
935
    FrameSelector findFrame(String target) {
936
        for (Object _openWindow : _openWindows) {
1✔
937
            WebWindow webWindow = (WebWindow) _openWindow;
1✔
938
            FrameSelector frame = webWindow.getFrame(target);
1✔
939
            if (frame != null) {
1✔
940
                return frame;
1✔
941
            }
942
        }
1✔
943
        return null;
1✔
944
    }
945

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

976
    /**
977
     * Sets the onetime authentication header.
978
     *
979
     * @param authorizationHeader
980
     *            the new onetime authentication header
981
     */
982
    private void setOnetimeAuthenticationHeader(String authorizationHeader) {
983
        _authorizationString = authorizationHeader;
1✔
984
    }
1✔
985

986
    // ==================================================================================================
987

988
    /**
989
     * The Class HeaderDictionary.
990
     */
991
    static public class HeaderDictionary extends Hashtable {
1✔
992

993
        /** The Constant serialVersionUID. */
994
        private static final long serialVersionUID = 1L;
995

996
        /**
997
         * Adds the entries.
998
         *
999
         * @param source
1000
         *            the source
1001
         */
1002
        public void addEntries(Dictionary source) {
1003
            for (Enumeration e = source.keys(); e.hasMoreElements();) {
1✔
1004
                Object key = e.nextElement();
1✔
1005
                put(key, source.get(key));
1✔
1006
            }
1✔
1007
        }
1✔
1008

1009
        @Override
1010
        public boolean containsKey(Object key) {
1011
            return super.containsKey(matchPreviousFieldName(key.toString()));
1✔
1012
        }
1013

1014
        @Override
1015
        public Object get(Object fieldName) {
1016
            return super.get(matchPreviousFieldName(fieldName.toString()));
1✔
1017
        }
1018

1019
        @Override
1020
        public Object put(Object fieldName, Object fieldValue) {
1021
            fieldName = matchPreviousFieldName(fieldName.toString());
1✔
1022
            Object oldValue = super.get(fieldName);
1✔
1023
            if (fieldValue == null) {
1!
1024
                remove(fieldName);
×
1025
            } else {
1026
                super.put(fieldName, fieldValue);
1✔
1027
            }
1028
            return oldValue;
1✔
1029
        }
1030

1031
        /**
1032
         * If a matching field name with different case is already known, returns the older name. Otherwise, returns the
1033
         * specified name.
1034
         *
1035
         * @param fieldName
1036
         *            the field name
1037
         *
1038
         * @return the string
1039
         */
1040
        private String matchPreviousFieldName(String fieldName) {
1041
            for (Enumeration e = keys(); e.hasMoreElements();) {
1✔
1042
                String key = (String) e.nextElement();
1✔
1043
                if (key.equalsIgnoreCase(fieldName)) {
1✔
1044
                    return key;
1✔
1045
                }
1046
            }
1✔
1047
            return fieldName;
1✔
1048
        }
1049

1050
    }
1051

1052
}
1053

1054
// ==================================================================================================
1055

1056
class RedirectWebRequest extends WebRequest {
1057

1058
    RedirectWebRequest(WebResponse response) {
1059
        super(response.getURL(), response.getHeaderField("Location"), response.getFrame(), response.getFrameName());
1✔
1060
        if (response.getReferer() != null) {
1✔
1061
            setHeaderField("Referer", response.getReferer());
1✔
1062
        }
1063
    }
1✔
1064

1065
    /**
1066
     * Returns the HTTP method defined for this request.
1067
     **/
1068
    @Override
1069
    public String getMethod() {
1070
        return "GET";
1✔
1071
    }
1072
}
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