• 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

89.53
/src/main/java/com/meterware/httpunit/WebWindow.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.scripting.ScriptingHandler;
11

12
import java.io.IOException;
13
import java.net.HttpURLConnection;
14
import java.net.MalformedURLException;
15
import java.net.URL;
16
import java.util.Hashtable;
17
import java.util.List;
18
import java.util.Map;
19

20
import org.xml.sax.SAXException;
21

22
/**
23
 * A window managed by a {@link com.meterware.httpunit.WebClient WebClient}.
24
 **/
25
public class WebWindow {
26

27
    /** The client which created this window. **/
28
    private WebClient _client;
29

30
    /** A map of frame names to current contents. **/
31
    private FrameHolder _frameContents;
32

33
    /** The name of the window, set via JavaScript. **/
34
    private String _name = "";
1✔
35

36
    /** The web response containing the reference that opened this window *. */
37
    private WebResponse _opener;
38

39
    /** True if this window has been closed. **/
40
    private boolean _closed;
41

42
    /** The Constant NO_NAME. */
43
    static final String NO_NAME = "$$HttpUnit_Window$$_";
44

45
    /** The urls that have been encountered as redirect locations in the course of a single client-initiated request. */
46
    private final Map _redirects;
47

48
    /** True if seen initial request. */
49
    private boolean _isInitialRequest = true;
1✔
50

51
    /**
52
     * Cache the initial client request to ensure that the _redirects structure gets reset.
53
     */
54
    private WebRequest _initialRequest;
55

56
    /**
57
     * Returns the web client associated with this window.
58
     *
59
     * @return the client
60
     */
61
    public WebClient getClient() {
62
        return _client;
1✔
63
    }
64

65
    /**
66
     * Returns true if this window has been closed.
67
     *
68
     * @return true, if is closed
69
     */
70
    public boolean isClosed() {
71
        return _closed;
1✔
72
    }
73

74
    /**
75
     * Closes this window.
76
     */
77
    public void close() {
78
        if (!_closed) {
1!
79
            _client.close(this);
1✔
80
        }
81
        _closed = true;
1✔
82
    }
1✔
83

84
    /**
85
     * Returns the name of this window. Windows created through normal HTML or browser commands have empty names, but
86
     * JavaScript can set the name. A name may be used as a target for a request.
87
     *
88
     * @return the name
89
     */
90
    public String getName() {
91
        return _name;
1✔
92
    }
93

94
    /**
95
     * Returns the web response that contained the script which opened this window.
96
     *
97
     * @return the opener
98
     */
99
    public WebResponse getOpener() {
100
        return _opener;
1✔
101
    }
102

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

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

138
    /**
139
     * Submits a web request and returns a response, using all state developed so far as stored in cookies as requested
140
     * by the server. see patch [ 1155415 ] Handle redirect instructions which can lead to a loop
141
     *
142
     * @param request
143
     *            the request
144
     *
145
     * @return the WebResponse or null
146
     *
147
     * @throws IOException
148
     *             Signals that an I/O exception has occurred.
149
     *
150
     * @exception SAXException
151
     *                thrown if there is an error parsing the retrieved page
152
     */
153
    public WebResponse getResponse(WebRequest request) throws IOException, SAXException {
154
        // Need to have some sort of ExecuteAroundMethod to ensure that the
155
        // redirects data structure gets cleared down upon exit - not
156
        // straightforward, since this could be a recursive call
157
        if (_isInitialRequest) {
1✔
158
            _initialRequest = request;
1✔
159
            _isInitialRequest = false;
1✔
160
        }
161

162
        WebResponse result = null;
1✔
163

164
        try {
165
            final RequestContext requestContext = new RequestContext();
1✔
166
            final WebResponse response = getSubframeResponse(request, requestContext);
1✔
167
            requestContext.runScripts();
1✔
168
            // javascript might replace the response in its frame
169
            result = response == null ? null : response.getWindow().getFrameContents(response.getFrame());
1✔
170
        } finally {
171
            if (null != request && request.equals(_initialRequest)) {
1!
172
                _redirects.clear();
1✔
173
                _initialRequest = null;
1✔
174
                _isInitialRequest = true;
1✔
175
            }
176
        }
177
        return result;
1✔
178
    }
179

180
    /**
181
     * get a Response from a SubFrame.
182
     *
183
     * @param request
184
     *            the request
185
     * @param requestContext
186
     *            the request context
187
     *
188
     * @return the WebResponse or null
189
     *
190
     * @throws IOException
191
     *             Signals that an I/O exception has occurred.
192
     * @throws SAXException
193
     *             the SAX exception
194
     */
195
    WebResponse getSubframeResponse(WebRequest request, RequestContext requestContext)
196
            throws IOException, SAXException {
197
        WebResponse response = getResource(request);
1✔
198

199
        return response == null ? null : updateWindow(request.getTarget(), response, requestContext);
1✔
200
    }
201

202
    /**
203
     * Updates this web client based on a received response. This includes updating cookies and frames.
204
     *
205
     * @param requestTarget
206
     *            the request target
207
     * @param response
208
     *            the response
209
     * @param requestContext
210
     *            the request context
211
     *
212
     * @return the web response
213
     *
214
     * @throws IOException
215
     *             Signals that an I/O exception has occurred.
216
     * @throws SAXException
217
     *             the SAX exception
218
     */
219
    WebResponse updateWindow(String requestTarget, WebResponse response, RequestContext requestContext)
220
            throws IOException, SAXException {
221
        _client.updateClient(response);
1✔
222
        if (getClient().getClientProperties().isAutoRefresh() && response.getRefreshRequest() != null) {
1✔
223
            WebRequest request = response.getRefreshRequest();
1✔
224
            return getResponse(request);
1✔
225
        }
226
        if (shouldFollowRedirect(response)) {
1✔
227
            delay(HttpUnitOptions.getRedirectDelay());
1✔
228
            return getResponse(new RedirectWebRequest(response));
1✔
229
        }
230
        _client.updateFrameContents(this, requestTarget, response, requestContext);
1✔
231
        return response;
1✔
232
    }
233

234
    /**
235
     * Returns the resource specified by the request. Does not update the window or load included framesets. May return
236
     * null if the resource is a JavaScript URL which would normally leave the client unchanged.
237
     *
238
     * @param request
239
     *            the request
240
     *
241
     * @return the resource
242
     *
243
     * @throws IOException
244
     *             Signals that an I/O exception has occurred.
245
     */
246
    public WebResponse getResource(WebRequest request) throws IOException {
247
        _client.tellListeners(request);
1✔
248

249
        WebResponse response = null;
1✔
250
        String urlString = request.getURLString().trim();
1✔
251
        FrameSelector targetFrame = _frameContents.getTargetFrame(request);
1✔
252
        if (urlString.startsWith("about:")) {
1✔
253
            response = new DefaultWebResponse(_client, targetFrame, null, "");
1✔
254
        } else if (!HttpUnitUtils.isJavaScriptURL(urlString)) {
1✔
255
            response = _client.createResponse(request, targetFrame);
1✔
256
        } else {
257
            ScriptingHandler handler = request.getSourceScriptingHandler();
1✔
258
            if (handler == null) {
1!
259
                handler = getCurrentPage().getScriptingHandler();
1✔
260
            }
261
            Object result = handler.evaluateExpression(urlString);
1✔
262
            if (result != null) {
1✔
263
                response = new DefaultWebResponse(_client, targetFrame, request.getURL(), result.toString());
1✔
264
            }
265
        }
266

267
        if (response != null) {
1✔
268
            _client.tellListeners(response);
1✔
269
        }
270
        return response;
1✔
271
    }
272

273
    /**
274
     * Returns the name of the currently active frames.
275
     *
276
     * @return the frame names
277
     */
278
    public String[] getFrameNames() {
279
        final List<String> names = _frameContents.getActiveFrameNames();
1✔
280
        return names.toArray(new String[names.size()]);
1✔
281
    }
282

283
    /**
284
     * Returns true if the specified frame name is defined in this window.
285
     *
286
     * @param frameName
287
     *            the frame name
288
     *
289
     * @return true, if successful
290
     */
291
    public boolean hasFrame(String frameName) {
292
        return _frameContents.get(frameName) != null;
×
293
    }
294

295
    /**
296
     * Checks for frame.
297
     *
298
     * @param frame
299
     *            the frame
300
     *
301
     * @return true, if successful
302
     */
303
    boolean hasFrame(FrameSelector frame) {
304
        return _frameContents.get(frame) != null;
1✔
305
    }
306

307
    /**
308
     * Returns the response associated with the specified frame name. Throws a runtime exception if no matching frame is
309
     * defined.
310
     *
311
     * @param frameName
312
     *            the frame name
313
     *
314
     * @return the frame contents
315
     */
316
    public WebResponse getFrameContents(String frameName) {
317
        WebResponse response = _frameContents.get(frameName);
1✔
318
        if (response == null) {
1!
319
            throw new NoSuchFrameException(frameName);
×
320
        }
321
        return response;
1✔
322
    }
323

324
    /**
325
     * Returns the response associated with the specified frame target. Throws a runtime exception if no matching frame
326
     * is defined.
327
     *
328
     * @param targetFrame
329
     *            the target frame
330
     *
331
     * @return the frame contents
332
     */
333
    WebResponse getFrameContents(FrameSelector targetFrame) {
334
        return _frameContents.getFrameContents(targetFrame);
1✔
335
    }
336

337
    /**
338
     * Gets the subframe contents.
339
     *
340
     * @param frame
341
     *            the frame
342
     * @param subFrameName
343
     *            the sub frame name
344
     *
345
     * @return the subframe contents
346
     */
347
    WebResponse getSubframeContents(FrameSelector frame, String subFrameName) {
348
        return _frameContents.getSubframeContents(frame, subFrameName);
1✔
349
    }
350

351
    /**
352
     * Gets the parent frame contents.
353
     *
354
     * @param frame
355
     *            the frame
356
     *
357
     * @return the parent frame contents
358
     */
359
    WebResponse getParentFrameContents(FrameSelector frame) {
360
        return _frameContents.getParentFrameContents(frame);
1✔
361
    }
362

363
    /**
364
     * Returns the response representing the main page in this window.
365
     *
366
     * @return the current page
367
     */
368
    public WebResponse getCurrentPage() {
369
        return getFrameContents(WebRequest.TOP_FRAME);
1✔
370
    }
371

372
    /**
373
     * construct a WebWindow from a given client.
374
     *
375
     * @param client
376
     *            - the client to construct me from
377
     */
378
    WebWindow(WebClient client) {
1✔
379
        _client = client;
1✔
380
        _frameContents = new FrameHolder(this);
1✔
381
        _name = NO_NAME + _client.getOpenWindows().length;
1✔
382
        _redirects = new Hashtable<>();
1✔
383
    }
1✔
384

385
    /**
386
     * Instantiates a new web window.
387
     *
388
     * @param client
389
     *            the client
390
     * @param opener
391
     *            the opener
392
     */
393
    WebWindow(WebClient client, WebResponse opener) {
394
        this(client);
1✔
395
        _opener = opener;
1✔
396
    }
1✔
397

398
    /**
399
     * Update frame contents.
400
     *
401
     * @param response
402
     *            the response
403
     * @param requestContext
404
     *            the request context
405
     *
406
     * @throws IOException
407
     *             Signals that an I/O exception has occurred.
408
     * @throws SAXException
409
     *             the SAX exception
410
     */
411
    void updateFrameContents(WebResponse response, RequestContext requestContext) throws IOException, SAXException {
412
        response.setWindow(this);
1✔
413
        _frameContents.updateFrames(response, response.getFrame(), requestContext);
1✔
414
    }
1✔
415

416
    /**
417
     * Sets the name.
418
     *
419
     * @param name
420
     *            the new name
421
     */
422
    void setName(String name) {
423
        _name = name;
1✔
424
    }
1✔
425

426
    /**
427
     * Delays the specified amount of time.
428
     *
429
     * @param numMilliseconds
430
     *            the num milliseconds
431
     */
432
    private void delay(int numMilliseconds) {
433
        if (numMilliseconds == 0) {
1!
434
            return;
1✔
435
        }
436
        try {
437
            Thread.sleep(numMilliseconds);
×
438
        } catch (InterruptedException e) {
×
439
            // ignore the exception
440
            Thread.interrupted();
×
441
        }
×
442
    }
×
443

444
    /**
445
     * check whether redirect is configured.
446
     *
447
     * @param response
448
     *            the response
449
     *
450
     * @return true, if successful
451
     */
452
    private boolean redirectConfigured(WebResponse response) {
453
        boolean isAutoredirect = getClient().getClientProperties().isAutoRedirect();
1✔
454
        boolean hasLocation = response.getHeaderField("Location") != null;
1✔
455
        int responseCode = response.getResponseCode();
1✔
456
        return isAutoredirect && responseCode >= HttpURLConnection.HTTP_MOVED_PERM
1!
457
                && responseCode <= HttpURLConnection.HTTP_MOVED_TEMP && hasLocation;
458
    }
459

460
    /**
461
     * check wether we should follow the redirect given in the response make sure we don't run into a recursion.
462
     *
463
     * @param response
464
     *            the response
465
     *
466
     * @return true, if successful
467
     */
468
    private boolean shouldFollowRedirect(WebResponse response) {
469
        // first check whether redirect is configured for this response
470
        // this is the old pre [ 1155415 ] Handle redirect instructions which can lead to a loop
471
        // shouldFollowRedirect method - just renamed
472
        if (!redirectConfigured(response)) {
1✔
473
            return false;
1✔
474
        }
475
        // now do the recursion check
476
        String redirectLocation = response.getHeaderField("Location");
1✔
477

478
        URL url = null;
1✔
479

480
        try {
481
            if (redirectLocation != null) {
1!
482
                url = new URL(response.getURL(), redirectLocation);
1✔
483
            }
484
        } catch (MalformedURLException e) {
1✔
485
            // Fall through and allow existing exception handling code deal
486
            // with any exception - we don't know at this stage whether it is
487
            // a redirect instruction, although it is highly likely, given
488
            // there is a location header present in the response!
489
        }
1✔
490

491
        switch (response.getResponseCode()) {
1!
492
            case HttpURLConnection.HTTP_MOVED_PERM:
493
            case HttpURLConnection.HTTP_MOVED_TEMP: // Fall through
494
                int count = 0;
1✔
495
                if (null != url) {
1✔
496
                    Integer value = (Integer) _redirects.get(url);
1✔
497
                    if (null != value) {
1✔
498
                        // We have already been instructed to redirect to that
499
                        // location in the course of this attempt to resolve the
500
                        // resource
501

502
                        count = value.intValue();
1✔
503

504
                        int maxRedirects = getClient().getClientProperties().getMaxRedirects();
1✔
505

506
                        if (count == maxRedirects) {
1✔
507
                            throw new RecursiveRedirectionException(url, "Maximum number of redirects exceeded");
1✔
508
                        }
509
                    }
510

511
                    count++;
1✔
512
                    _redirects.put(url, Integer.valueOf(count));
1✔
513
                }
514
                break;
515
        }
516
        return redirectLocation != null;
1!
517
    }
518

519
    /**
520
     * Gets the top frame.
521
     *
522
     * @return the top frame
523
     */
524
    FrameSelector getTopFrame() {
525
        return _frameContents.getTopFrame();
1✔
526
    }
527

528
    /**
529
     * Gets the frame.
530
     *
531
     * @param target
532
     *            the target
533
     *
534
     * @return the frame
535
     */
536
    FrameSelector getFrame(String target) {
537
        return _frameContents.getFrame(target);
1✔
538
    }
539

540
}
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