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

hazendaz / httpunit / 389

12 Aug 2025 11:17PM UTC coverage: 80.48% (-0.02%) from 80.503%
389

push

github

hazendaz
Merge branch 'master' into javax

3216 of 4105 branches covered (78.34%)

Branch coverage included in aggregate %.

238 of 258 new or added lines in 68 files covered. (92.25%)

2 existing lines in 2 files now uncovered.

8254 of 10147 relevant lines covered (81.34%)

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
 * 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.scripting.ScriptingHandler;
23

24
import java.io.IOException;
25
import java.net.HttpURLConnection;
26
import java.net.MalformedURLException;
27
import java.net.URL;
28
import java.util.Hashtable;
29
import java.util.List;
30
import java.util.Map;
31

32
import org.xml.sax.SAXException;
33

34
/**
35
 * A window managed by a {@link com.meterware.httpunit.WebClient WebClient}.
36
 *
37
 * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
38
 **/
39
public class WebWindow {
40

41
    /** The client which created this window. **/
42
    private WebClient _client;
43

44
    /** A map of frame names to current contents. **/
45
    private FrameHolder _frameContents;
46

47
    /** The name of the window, set via JavaScript. **/
48
    private String _name = "";
1✔
49

50
    /** The web response containing the reference that opened this window **/
51
    private WebResponse _opener;
52

53
    /** True if this window has been closed. **/
54
    private boolean _closed;
55

56
    static final String NO_NAME = "$$HttpUnit_Window$$_";
57

58
    /**
59
     * The urls that have been encountered as redirect locations in the course of a single client-initiated request
60
     *
61
     * @since patch [ 1155415 ] Handle redirect instructions which can lead to a loop
62
     */
63
    private final Map _redirects;
64

65
    /**
66
     * True if seen initial request
67
     *
68
     * @since patch [ 1155415 ] Handle redirect instructions which can lead to a loop
69
     */
70
    private boolean _isInitialRequest = true;
1✔
71

72
    /**
73
     * Cache the initial client request to ensure that the _redirects structure gets reset.
74
     *
75
     * @since patch [ 1155415 ] Handle redirect instructions which can lead to a loop
76
     */
77
    private WebRequest _initialRequest;
78

79
    /**
80
     * Returns the web client associated with this window.
81
     */
82
    public WebClient getClient() {
83
        return _client;
1✔
84
    }
85

86
    /**
87
     * Returns true if this window has been closed.
88
     */
89
    public boolean isClosed() {
90
        return _closed;
1✔
91
    }
92

93
    /**
94
     * Closes this window.
95
     */
96
    public void close() {
97
        if (!_closed) {
1!
98
            _client.close(this);
1✔
99
        }
100
        _closed = true;
1✔
101
    }
1✔
102

103
    /**
104
     * Returns the name of this window. Windows created through normal HTML or browser commands have empty names, but
105
     * JavaScript can set the name. A name may be used as a target for a request.
106
     */
107
    public String getName() {
108
        return _name;
1✔
109
    }
110

111
    /**
112
     * Returns the web response that contained the script which opened this window.
113
     */
114
    public WebResponse getOpener() {
115
        return _opener;
1✔
116
    }
117

118
    /**
119
     * Submits a GET method request and returns a response.
120
     *
121
     * @exception SAXException
122
     *                thrown if there is an error parsing the retrieved page
123
     **/
124
    public WebResponse getResponse(String urlString) throws IOException, SAXException {
125
        return getResponse(new GetMethodWebRequest(urlString));
1✔
126
    }
127

128
    /**
129
     * Submits a web request and returns a response. This is an alternate name for the getResponse method.
130
     *
131
     * @return the WebResponse or null
132
     **/
133
    public WebResponse sendRequest(WebRequest request) throws IOException, SAXException {
134
        return getResponse(request);
1✔
135
    }
136

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

155
        WebResponse result = null;
1✔
156

157
        try {
158
            final RequestContext requestContext = new RequestContext();
1✔
159
            final WebResponse response = getSubframeResponse(request, requestContext);
1✔
160
            requestContext.runScripts();
1✔
161
            // javascript might replace the response in its frame
162
            result = response == null ? null : response.getWindow().getFrameContents(response.getFrame());
1✔
163
        } finally {
164
            if (null != request && request.equals(_initialRequest)) {
1!
165
                _redirects.clear();
1✔
166
                _initialRequest = null;
1✔
167
                _isInitialRequest = true;
1✔
168
            }
169
        }
170
        return result;
1✔
171
    }
172

173
    /**
174
     * get a Response from a SubFrame
175
     *
176
     * @param request
177
     * @param requestContext
178
     *
179
     * @return the WebResponse or null
180
     *
181
     * @throws IOException
182
     * @throws SAXException
183
     */
184
    WebResponse getSubframeResponse(WebRequest request, RequestContext requestContext)
185
            throws IOException, SAXException {
186
        WebResponse response = getResource(request);
1✔
187

188
        return response == null ? null : updateWindow(request.getTarget(), response, requestContext);
1✔
189
    }
190

191
    /**
192
     * Updates this web client based on a received response. This includes updating cookies and frames.
193
     **/
194
    WebResponse updateWindow(String requestTarget, WebResponse response, RequestContext requestContext)
195
            throws IOException, SAXException {
196
        _client.updateClient(response);
1✔
197
        if (getClient().getClientProperties().isAutoRefresh() && response.getRefreshRequest() != null) {
1✔
198
            WebRequest request = response.getRefreshRequest();
1✔
199
            return getResponse(request);
1✔
200
        }
201
        if (shouldFollowRedirect(response)) {
1✔
202
            delay(HttpUnitOptions.getRedirectDelay());
1✔
203
            return getResponse(new RedirectWebRequest(response));
1✔
204
        }
205
        _client.updateFrameContents(this, requestTarget, response, requestContext);
1✔
206
        return response;
1✔
207
    }
208

209
    /**
210
     * Returns the resource specified by the request. Does not update the window or load included framesets. May return
211
     * null if the resource is a JavaScript URL which would normally leave the client unchanged.
212
     */
213
    public WebResponse getResource(WebRequest request) throws IOException {
214
        _client.tellListeners(request);
1✔
215

216
        WebResponse response = null;
1✔
217
        String urlString = request.getURLString().trim();
1✔
218
        FrameSelector targetFrame = _frameContents.getTargetFrame(request);
1✔
219
        if (urlString.startsWith("about:")) {
1✔
220
            response = new DefaultWebResponse(_client, targetFrame, null, "");
1✔
221
        } else if (!HttpUnitUtils.isJavaScriptURL(urlString)) {
1✔
222
            response = _client.createResponse(request, targetFrame);
1✔
223
        } else {
224
            ScriptingHandler handler = request.getSourceScriptingHandler();
1✔
225
            if (handler == null) {
1!
226
                handler = getCurrentPage().getScriptingHandler();
1✔
227
            }
228
            Object result = handler.evaluateExpression(urlString);
1✔
229
            if (result != null) {
1✔
230
                response = new DefaultWebResponse(_client, targetFrame, request.getURL(), result.toString());
1✔
231
            }
232
        }
233

234
        if (response != null) {
1✔
235
            _client.tellListeners(response);
1✔
236
        }
237
        return response;
1✔
238
    }
239

240
    /**
241
     * Returns the name of the currently active frames.
242
     **/
243
    public String[] getFrameNames() {
244
        final List<String> names = _frameContents.getActiveFrameNames();
1✔
245
        return names.toArray(new String[names.size()]);
1✔
246
    }
247

248
    /**
249
     * Returns true if the specified frame name is defined in this window.
250
     */
251
    public boolean hasFrame(String frameName) {
252
        return _frameContents.get(frameName) != null;
×
253
    }
254

255
    boolean hasFrame(FrameSelector frame) {
256
        return _frameContents.get(frame) != null;
1✔
257
    }
258

259
    /**
260
     * Returns the response associated with the specified frame name. Throws a runtime exception if no matching frame is
261
     * defined.
262
     **/
263
    public WebResponse getFrameContents(String frameName) {
264
        WebResponse response = _frameContents.get(frameName);
1✔
265
        if (response == null) {
1!
266
            throw new NoSuchFrameException(frameName);
×
267
        }
268
        return response;
1✔
269
    }
270

271
    /**
272
     * Returns the response associated with the specified frame target. Throws a runtime exception if no matching frame
273
     * is defined.
274
     **/
275
    WebResponse getFrameContents(FrameSelector targetFrame) {
276
        return _frameContents.getFrameContents(targetFrame);
1✔
277
    }
278

279
    WebResponse getSubframeContents(FrameSelector frame, String subFrameName) {
280
        return _frameContents.getSubframeContents(frame, subFrameName);
1✔
281
    }
282

283
    WebResponse getParentFrameContents(FrameSelector frame) {
284
        return _frameContents.getParentFrameContents(frame);
1✔
285
    }
286

287
    /**
288
     * Returns the response representing the main page in this window.
289
     */
290
    public WebResponse getCurrentPage() {
291
        return getFrameContents(WebRequest.TOP_FRAME);
1✔
292
    }
293

294
    /**
295
     * construct a WebWindow from a given client
296
     *
297
     * @param client
298
     *            - the client to construct me from
299
     */
300
    WebWindow(WebClient client) {
1✔
301
        _client = client;
1✔
302
        _frameContents = new FrameHolder(this);
1✔
303
        _name = NO_NAME + _client.getOpenWindows().length;
1✔
304
        _redirects = new Hashtable<>();
1✔
305
    }
1✔
306

307
    WebWindow(WebClient client, WebResponse opener) {
308
        this(client);
1✔
309
        _opener = opener;
1✔
310
    }
1✔
311

312
    void updateFrameContents(WebResponse response, RequestContext requestContext) throws IOException, SAXException {
313
        response.setWindow(this);
1✔
314
        _frameContents.updateFrames(response, response.getFrame(), requestContext);
1✔
315
    }
1✔
316

317
    void setName(String name) {
318
        _name = name;
1✔
319
    }
1✔
320

321
    /**
322
     * Delays the specified amount of time.
323
     **/
324
    private void delay(int numMilliseconds) {
325
        if (numMilliseconds == 0) {
1!
326
            return;
1✔
327
        }
328
        try {
329
            Thread.sleep(numMilliseconds);
×
330
        } catch (InterruptedException e) {
×
331
            // ignore the exception
NEW
332
            Thread.interrupted();
×
333
        }
×
334
    }
×
335

336
    /**
337
     * check whether redirect is configured
338
     *
339
     * @param response
340
     *
341
     * @return
342
     */
343
    private boolean redirectConfigured(WebResponse response) {
344
        boolean isAutoredirect = getClient().getClientProperties().isAutoRedirect();
1✔
345
        boolean hasLocation = response.getHeaderField("Location") != null;
1✔
346
        int responseCode = response.getResponseCode();
1✔
347
        return isAutoredirect && responseCode >= HttpURLConnection.HTTP_MOVED_PERM
1!
348
                && responseCode <= HttpURLConnection.HTTP_MOVED_TEMP && hasLocation;
349
    }
350

351
    /**
352
     * check wether we should follow the redirect given in the response make sure we don't run into a recursion
353
     *
354
     * @param response
355
     *
356
     * @return
357
     */
358
    private boolean shouldFollowRedirect(WebResponse response) {
359
        // first check whether redirect is configured for this response
360
        // this is the old pre [ 1155415 ] Handle redirect instructions which can lead to a loop
361
        // shouldFollowRedirect method - just renamed
362
        if (!redirectConfigured(response)) {
1✔
363
            return false;
1✔
364
        }
365
        // now do the recursion check
366
        String redirectLocation = response.getHeaderField("Location");
1✔
367

368
        URL url = null;
1✔
369

370
        try {
371
            if (redirectLocation != null) {
1!
372
                url = new URL(response.getURL(), redirectLocation);
1✔
373
            }
374
        } catch (MalformedURLException e) {
1✔
375
            // Fall through and allow existing exception handling code deal
376
            // with any exception - we don't know at this stage whether it is
377
            // a redirect instruction, although it is highly likely, given
378
            // there is a location header present in the response!
379
        }
1✔
380

381
        switch (response.getResponseCode()) {
1!
382
            case HttpURLConnection.HTTP_MOVED_PERM:
383
            case HttpURLConnection.HTTP_MOVED_TEMP: // Fall through
384
                int count = 0;
1✔
385
                if (null != url) {
1✔
386
                    Integer value = (Integer) _redirects.get(url);
1✔
387
                    if (null != value) {
1✔
388
                        // We have already been instructed to redirect to that
389
                        // location in the course of this attempt to resolve the
390
                        // resource
391

392
                        count = value.intValue();
1✔
393

394
                        int maxRedirects = getClient().getClientProperties().getMaxRedirects();
1✔
395

396
                        if (count == maxRedirects) {
1✔
397
                            throw new RecursiveRedirectionException(url, "Maximum number of redirects exceeded");
1✔
398
                        }
399
                    }
400

401
                    count++;
1✔
402
                    _redirects.put(url, Integer.valueOf(count));
1✔
403
                }
404
                break;
405
        }
406
        return redirectLocation != null;
1!
407
    }
408

409
    FrameSelector getTopFrame() {
410
        return _frameContents.getTopFrame();
1✔
411
    }
412

413
    FrameSelector getFrame(String target) {
414
        return _frameContents.getFrame(target);
1✔
415
    }
416

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