• 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

87.45
/src/main/java/com/meterware/httpunit/WebResponse.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.CookieJar;
23
import com.meterware.httpunit.cookies.CookieSource;
24
import com.meterware.httpunit.dom.DomWindow;
25
import com.meterware.httpunit.dom.DomWindowProxy;
26
import com.meterware.httpunit.dom.HTMLDocumentImpl;
27
import com.meterware.httpunit.dom.HTMLElementImpl;
28
import com.meterware.httpunit.protocol.MessageBody;
29
import com.meterware.httpunit.scripting.NamedDelegate;
30
import com.meterware.httpunit.scripting.ScriptableDelegate;
31
import com.meterware.httpunit.scripting.ScriptingHandler;
32

33
import java.io.ByteArrayInputStream;
34
import java.io.ByteArrayOutputStream;
35
import java.io.EOFException;
36
import java.io.IOException;
37
import java.io.InputStream;
38
import java.io.StringReader;
39
import java.net.HttpURLConnection;
40
import java.net.MalformedURLException;
41
import java.net.URL;
42
import java.net.URLConnection;
43
import java.nio.charset.Charset;
44
import java.nio.charset.StandardCharsets;
45
import java.nio.charset.UnsupportedCharsetException;
46
import java.util.Hashtable;
47
import java.util.Vector;
48
import java.util.zip.GZIPInputStream;
49

50
import org.w3c.dom.Document;
51
import org.w3c.dom.Node;
52
import org.xml.sax.InputSource;
53
import org.xml.sax.SAXException;
54

55
/**
56
 * A response to a web request from a web server.
57
 *
58
 * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
59
 * @author <a href="mailto:DREW.VARNER@oracle.com">Drew Varner</a>
60
 * @author <a href="mailto:dglo@ssec.wisc.edu">Dave Glowacki</a>
61
 * @author <a href="mailto:bx@bigfoot.com">Benoit Xhenseval</a>
62
 * @author Wolfgang Fahl
63
 **/
64
public abstract class WebResponse implements HTMLSegment, CookieSource, DomWindowProxy {
65

66
    private static final String HTML_CONTENT = "text/html";
67
    private static final String XHTML_CONTENT = "application/xhtml+xml";
68
    private static final String FAUX_XHTML_CONTENT = "text/xhtml";
69
    // [ 1281655 ] [patch] allow text/xml to be parsed as html
70
    // testTraversal test changed after positive reply by Russell
71
    private static final String XML_CONTENT = "text/xml";
72
    // the list of valid content Types
73
    private static String[] validContentTypes = { HTML_CONTENT, XHTML_CONTENT, FAUX_XHTML_CONTENT, XML_CONTENT };
1✔
74

75
    private static final int UNINITIALIZED_INT = -2;
76
    private static final int UNKNOWN_LENGTH_TIMEOUT = 500;
77
    private static final int UNKNOWN_LENGTH_RETRY_INTERVAL = 10;
78

79
    private FrameSelector _frame;
80
    // allow to switch off parsing e.g. for method="HEAD"
81
    private boolean _withParse = true;
1✔
82
    private String _baseTarget;
83
    private String _refreshHeader;
84
    private URL _baseURL;
85
    private boolean _parsingPage;
86

87
    /**
88
     * is parsing on?
89
     *
90
     * @return true if parsing is enabled
91
     */
92
    public boolean isWithParse() {
93
        return _withParse;
1✔
94
    }
95

96
    /**
97
     * set the parsing switch
98
     *
99
     * @param doParse
100
     */
101
    public void setWithParse(boolean doParse) {
102
        _withParse = doParse;
1✔
103
    }
1✔
104

105
    /**
106
     * Returns a web response built from a URL connection. Provided to allow access to WebResponse parsing without using
107
     * a WebClient.
108
     **/
109
    public static WebResponse newResponse(URLConnection connection) throws IOException {
110
        return new HttpWebResponse(null, FrameSelector.TOP_FRAME, connection.getURL(), connection,
×
111
                HttpUnitOptions.getExceptionsThrownOnErrorStatus());
×
112
    }
113

114
    /**
115
     * Returns true if the response is HTML.
116
     *
117
     * @return true if the contenType fits
118
     **/
119
    public boolean isHTML() {
120
        boolean result = false;
1✔
121
        // check the different content types
122
        for (String validContentType : validContentTypes) {
1✔
123
            result = getContentType().equalsIgnoreCase(validContentType);
1✔
124
            if (result) {
1✔
125
                break;
1✔
126
            }
127
        } // for
128
        return result;
1✔
129
    }
130

131
    /**
132
     * Returns the URL which invoked this response.
133
     **/
134
    @Override
135
    public URL getURL() {
136
        return _pageURL;
1✔
137
    }
138

139
    /**
140
     * Returns the title of the page.
141
     *
142
     * @exception SAXException
143
     *                thrown if there is an error parsing this response
144
     **/
145
    public String getTitle() throws SAXException {
146
        return getReceivedPage().getTitle();
1✔
147
    }
148

149
    /**
150
     * Returns the stylesheet linked in the head of the page. &lt;code&gt; &lt;link type="text/css" rel="stylesheet"
151
     * href="/mystyle.css" /&gt; &lt;/code&gt; will return "/mystyle.css".
152
     *
153
     * @exception SAXException
154
     *                thrown if there is an error parsing this response
155
     **/
156
    public String getExternalStyleSheet() throws SAXException {
157
        return getReceivedPage().getExternalStyleSheet();
1✔
158
    }
159

160
    /**
161
     * Retrieves the "content" of the meta tags for a key pair attribute-attributeValue. &lt;code&gt; &lt;meta
162
     * name="robots" content="index" /&gt; &lt;meta name="robots" content="follow" /&gt; &lt;meta http-equiv="Expires"
163
     * content="now" /&gt; &lt;/code&gt; this can be used like this &lt;code&gt; getMetaTagContent("name","robots") will
164
     * return { "index","follow" } getMetaTagContent("http-equiv","Expires") will return { "now" } &lt;/code&gt;
165
     *
166
     * @exception SAXException
167
     *                thrown if there is an error parsing this response
168
     **/
169
    public String[] getMetaTagContent(String attribute, String attributeValue) throws SAXException {
170
        return getReceivedPage().getMetaTagContent(attribute, attributeValue);
1✔
171
    }
172

173
    /**
174
     * Returns the name of the frame containing this page.
175
     **/
176
    public String getFrameName() {
177
        return _frame.getName();
1✔
178
    }
179

180
    void setFrame(FrameSelector frame) {
181
        if (!_frame.getName().equals(frame.getName())) {
1!
182
            throw new IllegalArgumentException("May not modify the frame name");
×
183
        }
184
        _frame = frame;
1✔
185
    }
1✔
186

187
    /**
188
     * Returns the frame containing this page.
189
     */
190
    FrameSelector getFrame() {
191
        return _frame;
1✔
192
    }
193

194
    /**
195
     * Returns a request to refresh this page, if any. This request will be defined by a meta tag in the header. If no
196
     * tag exists, will return null.
197
     **/
198
    public WebRequest getRefreshRequest() {
199
        readRefreshRequest();
1✔
200
        return _refreshRequest;
1✔
201
    }
202

203
    /**
204
     * Returns the delay before normally following the request to refresh this page, if any. This request will be
205
     * defined by a meta tag in the header. If no tag exists, will return zero.
206
     **/
207
    public int getRefreshDelay() {
208
        readRefreshRequest();
1✔
209
        return _refreshDelay;
1✔
210
    }
211

212
    /**
213
     * Returns the response code associated with this response.
214
     **/
215
    public abstract int getResponseCode();
216

217
    /**
218
     * Returns the response message associated with this response.
219
     **/
220
    public abstract String getResponseMessage();
221

222
    /**
223
     * Returns the content length of this response.
224
     *
225
     * @return the content length, if known, or -1.
226
     */
227
    public int getContentLength() {
228
        if (_contentLength == UNINITIALIZED_INT) {
1✔
229
            String length = getHeaderField("Content-Length");
1✔
230
            _contentLength = length == null ? -1 : Integer.parseInt(length);
1✔
231
        }
232
        return _contentLength;
1✔
233
    }
234

235
    /**
236
     * Returns the content type of this response.
237
     **/
238
    public String getContentType() {
239
        if (_contentType == null) {
1✔
240
            readContentTypeHeader();
1✔
241
        }
242
        return _contentType;
1✔
243
    }
244

245
    /**
246
     * Returns the character set used in this response.
247
     **/
248
    public String getCharacterSet() {
249
        if (_characterSet == null) {
1✔
250
            readContentTypeHeader();
1✔
251
            if (_characterSet == null) {
1✔
252
                setCharacterSet(getHeaderField("Charset"));
1✔
253
            }
254
            if (_characterSet == null) {
1✔
255
                setCharacterSet(HttpUnitOptions.getDefaultCharacterSet());
1✔
256
            }
257
        }
258
        return _characterSet;
1✔
259
    }
260

261
    /**
262
     * Returns a list of new cookie names defined as part of this response.
263
     **/
264
    public String[] getNewCookieNames() {
265
        return getCookieJar().getCookieNames();
1✔
266
    }
267

268
    /**
269
     * Returns the new cookie value defined as part of this response.
270
     **/
271
    public String getNewCookieValue(String name) {
272
        return getCookieJar().getCookieValue(name);
×
273
    }
274

275
    /**
276
     * Returns the names of the header fields found in the response.
277
     **/
278
    public abstract String[] getHeaderFieldNames();
279

280
    /**
281
     * Returns the value for the specified header field. If no such field is defined, will return null. If more than one
282
     * header is defined for the specified name, returns only the first found.
283
     **/
284
    public abstract String getHeaderField(String fieldName);
285

286
    /**
287
     * Returns the actual byte stream of the response e.g. for download results
288
     *
289
     * @return the byte array read for this response
290
     *
291
     * @throws IOException
292
     */
293
    public byte[] getBytes() throws IOException {
294
        if (_responseText == null) {
1✔
295
            loadResponseText();
1✔
296
        }
297
        return _bytes;
1✔
298
    }
299

300
    /**
301
     * Returns the text of the response (excluding headers) as a string. Use this method in preference to 'toString'
302
     * which may be used to represent internal state of this object.
303
     *
304
     * @return the response text
305
     **/
306
    public String getText() throws IOException {
307
        if (_responseText == null) {
1✔
308
            loadResponseText();
1✔
309
        }
310
        return _responseText;
1✔
311
    }
312

313
    /**
314
     * Returns a buffered input stream for reading the contents of this reply.
315
     **/
316
    public InputStream getInputStream() throws IOException {
317
        if (_inputStream == null) {
1✔
318
            _inputStream = new ByteArrayInputStream(getText().getBytes(StandardCharsets.UTF_8));
1✔
319
        }
320
        return _inputStream;
1✔
321
    }
322

323
    /**
324
     * Returns the names of the frames found in the page in the order in which they appear.
325
     *
326
     * @exception SAXException
327
     *                thrown if there is an error parsing this response
328
     **/
329
    public String[] getFrameNames() throws SAXException {
330
        WebFrame[] frames = getFrames();
1✔
331
        String[] result = new String[frames.length];
1✔
332
        for (int i = 0; i < result.length; i++) {
1✔
333
            result[i] = frames[i].getFrameName();
1✔
334
        }
335

336
        return result;
1✔
337
    }
338

339
    /**
340
     * Returns the frames found in the page in the order in which they appear.
341
     *
342
     * @exception SAXException
343
     *                thrown if there is an error parsing this response
344
     **/
345
    FrameSelector[] getFrameSelectors() throws SAXException {
346
        WebFrame[] frames = getFrames();
1✔
347
        FrameSelector[] result = new FrameSelector[frames.length];
1✔
348
        for (int i = 0; i < result.length; i++) {
1✔
349
            result[i] = frames[i].getSelector();
1✔
350
        }
351

352
        return result;
1✔
353
    }
354

355
    /**
356
     * Returns the contents of the specified subframe of this frameset response.
357
     *
358
     * @param subFrameName
359
     *            the name of the desired frame as defined in the frameset.
360
     **/
361
    public WebResponse getSubframeContents(String subFrameName) {
362
        if (_window == null) {
1✔
363
            throw new NoSuchFrameException(subFrameName);
1✔
364
        }
365
        return _window.getSubframeContents(_frame, subFrameName);
1✔
366
    }
367

368
    // ---------------------- HTMLSegment methods -----------------------------
369

370
    /**
371
     * Returns the HTMLElement with the specified ID.
372
     *
373
     * @throws SAXException
374
     *             thrown if there is an error parsing the response.
375
     */
376
    @Override
377
    public HTMLElement getElementWithID(String id) throws SAXException {
378
        return getReceivedPage().getElementWithID(id);
1✔
379
    }
380

381
    /**
382
     * return the HTMLElements with the specified tag name
383
     *
384
     * @param tagName
385
     *            e.g. "div" or "table"
386
     *
387
     * @return a list of all HTMLElements with that tag name
388
     *
389
     * @throws SAXException
390
     *
391
     * @since 1.7
392
     */
393
    public HTMLElement[] getElementsByTagName(String tagName) throws SAXException {
394
        return getReceivedPage().getElementsByTagName(getDOM(), tagName);
1✔
395
    }
396

397
    /**
398
     * Returns a list of HTML element names contained in this HTML section.
399
     */
400
    @Override
401
    public String[] getElementNames() throws SAXException {
402
        return getReceivedPage().getElementNames();
1✔
403
    }
404

405
    /**
406
     * Returns the HTMLElements found in this segment with the specified name.
407
     */
408
    @Override
409
    public HTMLElement[] getElementsWithName(String name) throws SAXException {
410
        return getReceivedPage().getElementsWithName(name);
1✔
411
    }
412

413
    /**
414
     * Returns the HTMLElements found in this segment with the specified class.
415
     */
416
    public HTMLElement[] getElementsWithClassName(String className) throws SAXException {
417
        return getReceivedPage().getElementsWithClassName(className);
1✔
418
    }
419

420
    /**
421
     * Returns the HTMLElements found with the specified attribute value.
422
     *
423
     * @since 1.6
424
     */
425
    @Override
426
    public HTMLElement[] getElementsWithAttribute(String name, String value) throws SAXException {
427
        return getReceivedPage().getElementsWithAttribute(name, value);
1✔
428
    }
429

430
    /**
431
     * Returns the forms found in the page in the order in which they appear.
432
     *
433
     * @exception SAXException
434
     *                thrown if there is an error parsing the response.
435
     **/
436
    @Override
437
    public WebForm[] getForms() throws SAXException {
438
        return getReceivedPage().getForms();
1✔
439
    }
440

441
    /**
442
     * Returns the form found in the page with the specified name.
443
     *
444
     * @exception SAXException
445
     *                thrown if there is an error parsing the response.
446
     **/
447
    @Override
448
    public WebForm getFormWithName(String name) throws SAXException {
449
        return getReceivedPage().getFormWithName(name);
1✔
450
    }
451

452
    /**
453
     * Returns the form found in the page with the specified ID.
454
     *
455
     * @exception SAXException
456
     *                thrown if there is an error parsing the response.
457
     **/
458
    @Override
459
    public WebForm getFormWithID(String ID) throws SAXException {
460
        return getReceivedPage().getFormWithID(ID);
1✔
461
    }
462

463
    /**
464
     * Returns the first form found in the page matching the specified criteria.
465
     *
466
     * @exception SAXException
467
     *                thrown if there is an error parsing the response.
468
     **/
469
    @Override
470
    public WebForm getFirstMatchingForm(HTMLElementPredicate predicate, Object criteria) throws SAXException {
471
        return getReceivedPage().getFirstMatchingForm(predicate, criteria);
1✔
472
    }
473

474
    /**
475
     * Returns all forms found in the page matching the specified criteria.
476
     *
477
     * @exception SAXException
478
     *                thrown if there is an error parsing the response.
479
     **/
480
    @Override
481
    public WebForm[] getMatchingForms(HTMLElementPredicate predicate, Object criteria) throws SAXException {
482
        return getReceivedPage().getMatchingForms(predicate, criteria);
×
483
    }
484

485
    /**
486
     * Returns the links found in the page in the order in which they appear.
487
     *
488
     * @exception SAXException
489
     *                thrown if there is an error parsing the response.
490
     **/
491
    @Override
492
    public WebLink[] getLinks() throws SAXException {
493
        return getReceivedPage().getLinks();
1✔
494
    }
495

496
    /**
497
     * Returns the first link which contains the specified text.
498
     *
499
     * @exception SAXException
500
     *                thrown if there is an error parsing the response.
501
     **/
502
    @Override
503
    public WebLink getLinkWith(String text) throws SAXException {
504
        return getReceivedPage().getLinkWith(text);
1✔
505
    }
506

507
    /**
508
     * Returns the first link which contains an image with the specified text as its 'alt' attribute.
509
     *
510
     * @exception SAXException
511
     *                thrown if there is an error parsing the response.
512
     **/
513
    @Override
514
    public WebLink getLinkWithImageText(String text) throws SAXException {
515
        return getReceivedPage().getLinkWithImageText(text);
1✔
516
    }
517

518
    /**
519
     * Returns the link found in the page with the specified name.
520
     *
521
     * @exception SAXException
522
     *                thrown if there is an error parsing the response.
523
     **/
524
    public WebLink getLinkWithName(String name) throws SAXException {
525
        return getReceivedPage().getLinkWithName(name);
1✔
526
    }
527

528
    /**
529
     * Returns the link found in the page with the specified ID.
530
     *
531
     * @exception SAXException
532
     *                thrown if there is an error parsing the response.
533
     **/
534
    public WebLink getLinkWithID(String ID) throws SAXException {
535
        return getReceivedPage().getLinkWithID(ID);
1✔
536
    }
537

538
    /**
539
     * Returns the first link found in the page matching the specified criteria.
540
     *
541
     * @exception SAXException
542
     *                thrown if there is an error parsing the response.
543
     **/
544
    @Override
545
    public WebLink getFirstMatchingLink(HTMLElementPredicate predicate, Object criteria) throws SAXException {
546
        return getReceivedPage().getFirstMatchingLink(predicate, criteria);
1✔
547
    }
548

549
    /**
550
     * Returns all links found in the page matching the specified criteria.
551
     *
552
     * @exception SAXException
553
     *                thrown if there is an error parsing the response.
554
     **/
555
    @Override
556
    public WebLink[] getMatchingLinks(HTMLElementPredicate predicate, Object criteria) throws SAXException {
557
        return getReceivedPage().getMatchingLinks(predicate, criteria);
1✔
558
    }
559

560
    /**
561
     * Returns the images found in the page in the order in which they appear.
562
     *
563
     * @exception SAXException
564
     *                thrown if there is an error parsing the response.
565
     **/
566
    @Override
567
    public WebImage[] getImages() throws SAXException {
568
        return getReceivedPage().getImages();
1✔
569
    }
570

571
    /**
572
     * Returns the image found in the page with the specified name attribute.
573
     *
574
     * @exception SAXException
575
     *                thrown if there is an error parsing the response.
576
     **/
577
    @Override
578
    public WebImage getImageWithName(String source) throws SAXException {
579
        return getReceivedPage().getImageWithName(source);
1✔
580
    }
581

582
    /**
583
     * Returns the first image found in the page with the specified src attribute.
584
     *
585
     * @exception SAXException
586
     *                thrown if there is an error parsing the response.
587
     **/
588
    @Override
589
    public WebImage getImageWithSource(String source) throws SAXException {
590
        return getReceivedPage().getImageWithSource(source);
1✔
591
    }
592

593
    /**
594
     * Returns the first image found in the page with the specified alt attribute.
595
     **/
596
    @Override
597
    public WebImage getImageWithAltText(String altText) throws SAXException {
598
        return getReceivedPage().getImageWithAltText(altText);
1✔
599
    }
600

601
    @Override
602
    public WebApplet[] getApplets() throws SAXException {
603
        return getReceivedPage().getApplets();
1✔
604
    }
605

606
    /**
607
     * Returns an array of text blocks found in the page.
608
     *
609
     * @since 1.6
610
     */
611
    @Override
612
    public TextBlock[] getTextBlocks() throws SAXException {
613
        return getReceivedPage().getTextBlocks();
1✔
614
    }
615

616
    /**
617
     * Returns the text block after the specified block, if any.
618
     *
619
     * @since 1.6
620
     */
621
    public TextBlock getNextTextBlock(TextBlock block) throws SAXException {
622
        return getReceivedPage().getNextTextBlock(block);
1✔
623
    }
624

625
    /**
626
     * Returns the first link found in the page matching the specified criteria.
627
     *
628
     * @since 1.6
629
     *
630
     * @exception SAXException
631
     *                thrown if there is an error parsing the response.
632
     **/
633
    public TextBlock getFirstMatchingTextBlock(HTMLElementPredicate predicate, Object criteria) throws SAXException {
634
        return getReceivedPage().getFirstMatchingTextBlock(predicate, criteria);
1✔
635
    }
636

637
    /**
638
     * Returns a copy of the domain object model tree associated with this response. If the response is HTML, it will
639
     * use a special parser which can transform HTML into an XML DOM.
640
     *
641
     * @exception SAXException
642
     *                thrown if there is an error parsing the response.
643
     **/
644
    public Document getDOM() throws SAXException {
645
        if (isHTML()) {
1!
646
            return (Document) getReceivedPage().getDOM();
1✔
647
        }
648
        try {
649
            return HttpUnitUtils.parse(new InputSource(new StringReader(getText())));
×
650
        } catch (IOException e) {
×
651
            throw new SAXException(e);
×
652
        }
653
    }
654

655
    /**
656
     * Returns the top-level tables found in this page in the order in which they appear.
657
     *
658
     * @exception SAXException
659
     *                thrown if there is an error parsing the response.
660
     **/
661
    @Override
662
    public WebTable[] getTables() throws SAXException {
663
        return getReceivedPage().getTables();
1✔
664
    }
665

666
    /**
667
     * Returns the first table in the response which matches the specified predicate and value. Will recurse into any
668
     * nested tables, as needed.
669
     *
670
     * @return the selected table, or null if none is found
671
     **/
672
    @Override
673
    public WebTable getFirstMatchingTable(HTMLElementPredicate predicate, Object criteria) throws SAXException {
674
        return getReceivedPage().getFirstMatchingTable(predicate, criteria);
×
675
    }
676

677
    /**
678
     * Returns all tables found in the page matching the specified criteria.
679
     *
680
     * @exception SAXException
681
     *                thrown if there is an error parsing the response.
682
     **/
683
    @Override
684
    public WebTable[] getMatchingTables(HTMLElementPredicate predicate, Object criteria) throws SAXException {
685
        return getReceivedPage().getMatchingTables(predicate, criteria);
×
686
    }
687

688
    /**
689
     * Returns the first table in the response which has the specified text as the full text of its first non-blank row
690
     * and non-blank column. Will recurse into any nested tables, as needed. Case is ignored.
691
     *
692
     * @exception SAXException
693
     *                thrown if there is an error parsing the response.
694
     *
695
     * @return the selected table, or null if none is found
696
     **/
697
    @Override
698
    public WebTable getTableStartingWith(String text) throws SAXException {
699
        return getReceivedPage().getTableStartingWith(text);
1✔
700
    }
701

702
    /**
703
     * Returns the first table in the response which has the specified text as a prefix of the text of its first
704
     * non-blank row and non-blank column. Will recurse into any nested tables, as needed. Case is ignored.
705
     *
706
     * @exception SAXException
707
     *                thrown if there is an error parsing the response.
708
     *
709
     * @return the selected table, or null if none is found
710
     **/
711
    @Override
712
    public WebTable getTableStartingWithPrefix(String text) throws SAXException {
713
        return getReceivedPage().getTableStartingWithPrefix(text);
1✔
714
    }
715

716
    /**
717
     * Returns the first table in the response which has the specified text as its summary attribute. Will recurse into
718
     * any nested tables, as needed. Case is ignored.
719
     *
720
     * @exception SAXException
721
     *                thrown if there is an error parsing the response.
722
     *
723
     * @return the selected table, or null if none is found
724
     **/
725
    @Override
726
    public WebTable getTableWithSummary(String text) throws SAXException {
727
        return getReceivedPage().getTableWithSummary(text);
1✔
728
    }
729

730
    /**
731
     * Returns the first table in the response which has the specified text as its ID attribute. Will recurse into any
732
     * nested tables, as needed. Case is ignored.
733
     *
734
     * @exception SAXException
735
     *                thrown if there is an error parsing the response.
736
     *
737
     * @return the selected table, or null if none is found
738
     **/
739
    @Override
740
    public WebTable getTableWithID(String text) throws SAXException {
741
        return getReceivedPage().getTableWithID(text);
1✔
742
    }
743

744
    // ---------------------------------------- JavaScript methods ----------------------------------------
745

746
    /**
747
     * get the scriptable object for this WebResponse
748
     */
749
    public Scriptable getScriptableObject() {
750
        ScriptingHandler result = this.getScriptingHandler();
1✔
751
        if (!(result instanceof Scriptable)) {
1!
752
            throw new RuntimeException(
×
753
                    "getScriptableObject failed for " + result.getClass().getName() + " - not a Scriptable");
×
754
        }
755
        return (Scriptable) result;
1✔
756
    }
757

758
    public void setScriptingHandler(ScriptingHandler scriptingHandler) {
759
        _scriptingHandler = scriptingHandler;
×
760
    }
×
761

762
    @Override
763
    public ScriptingHandler getScriptingHandler() {
764
        if (_scriptingHandler == null) {
1✔
765
            _scriptingHandler = HttpUnitOptions.getScriptingEngine().createHandler(this);
1✔
766
        }
767
        return _scriptingHandler;
1✔
768
    }
769

770
    public ScriptingHandler createJavascriptScriptingHandler() {
771
        return new Scriptable();
1✔
772
    }
773

774
    /**
775
     * create a DOMScriptingHandler
776
     *
777
     * @return the DOM scripting handler (the window)
778
     */
779
    public ScriptingHandler createDomScriptingHandler() {
780
        if (!isHTML()) {
×
781
            return new DomWindow(this);
×
782
        }
783
        try {
784
            HTMLPage page = this.getReceivedPage();
×
785
            Node rootNode = page.getRootNode();
×
786
            HTMLDocumentImpl document = (HTMLDocumentImpl) rootNode;
×
787
            DomWindow result = document.getWindow();
×
788
            result.setProxy(this);
×
789
            return result;
×
790
        } catch (SAXException e) {
×
791
            return new DomWindow(this);
×
792
        }
793
    }
794

795
    public static ScriptableDelegate newDelegate(String delegateClassName) {
796
        if (delegateClassName.equalsIgnoreCase("Option")) {
1!
797
            return FormControl.newSelectionOption();
1✔
798
        }
799
        throw new IllegalArgumentException("No such scripting class supported: " + delegateClassName);
×
800
    }
801

802
    HTMLPage.Scriptable getDocumentScriptable() {
803
        return getScriptableObject().getDocument();
1✔
804
    }
805

806
    /**
807
     * open a a new Window with the given name and relative URL
808
     *
809
     * @param name
810
     *            - the name of the window
811
     * @param relativeUrl
812
     *            - the relative URL to be used
813
     *
814
     * @return the WebResponse as a DomWindowProxy
815
     */
816
    @Override
817
    public DomWindowProxy openNewWindow(String name, String relativeUrl) throws IOException, SAXException {
818
        if (relativeUrl == null || relativeUrl.trim().isEmpty()) {
1✔
819
            relativeUrl = "about:";
1✔
820
        }
821
        GetMethodWebRequest request = new GetMethodWebRequest(getURL(), relativeUrl, _frame, name);
1✔
822
        return _window.getResponse(request);
1✔
823
    }
824

825
    @Override
826
    public DomWindowProxy submitRequest(HTMLElementImpl sourceElement, String method, String location, String target,
827
            MessageBody requestBody) throws IOException, SAXException {
828
        if (method.equalsIgnoreCase("get")) {
1!
829
            return getWindow().sendRequest(new GetMethodWebRequest(this, sourceElement, getURL(), location, target));
1✔
830
        }
831
        return null;
×
832
    }
833

834
    @Override
835
    public void close() {
836
        if (getFrameName().equals(WebRequest.TOP_FRAME)) {
1!
837
            _window.close();
1✔
838
        }
839
    }
1✔
840

841
    @Override
842
    public void alert(String message) {
843
        _client.postAlert(message);
1✔
844
    }
1✔
845

846
    @Override
847
    public boolean confirm(String message) {
848
        return _client.getConfirmationResponse(message);
1✔
849
    }
850

851
    @Override
852
    public String prompt(String prompt, String defaultResponse) {
853
        return _client.getUserResponse(prompt, defaultResponse);
1✔
854
    }
855

856
    String getBaseTarget() {
857
        return _baseTarget;
1✔
858
    }
859

860
    public class Scriptable extends ScriptableDelegate implements NamedDelegate {
1✔
861

862
        public void alertUser(String message) {
863
            alert(message);
1✔
864
        }
1✔
865

866
        public boolean getConfirmationResponse(String message) {
867
            return confirm(message);
1✔
868
        }
869

870
        public String getUserResponse(String prompt, String defaultResponse) {
871
            return prompt(prompt, defaultResponse);
1✔
872
        }
873

874
        public ClientProperties getClientProperties() {
875
            return _client == null ? ClientProperties.getDefaultProperties() : _client.getClientProperties();
1✔
876
        }
877

878
        public HTMLPage.Scriptable getDocument() {
879
            try {
880
                if (!isHTML()) {
1✔
881
                    replaceText(BLANK_HTML, HTML_CONTENT);
1✔
882
                }
883
                return getReceivedPage().getScriptableObject();
1✔
884
            } catch (SAXException e) {
×
885
                throw new RuntimeException(e.toString());
×
886
            }
887
        }
888

889
        public Scriptable[] getFrames() throws SAXException {
890
            String[] names = getFrameNames();
1✔
891
            Scriptable[] frames = new Scriptable[names.length];
1✔
892
            for (int i = 0; i < frames.length; i++) {
1✔
893
                frames[i] = getSubframeContents(names[i]).getScriptableObject();
1✔
894
            }
895
            return frames;
1✔
896
        }
897

898
        public void load() throws SAXException {
899
            if (isHTML() && isWithParse()) {
1!
900
                getReceivedPage().getForms(); // TODO be more explicit here - don't care about forms, after all
1✔
901
                doEventScript(getReceivedPage().getOnLoadEvent());
1✔
902
            }
903
        }
1✔
904

905
        public Scriptable open(String urlString, String name, String features, boolean replace)
906
                throws IOException, SAXException {
907
            WebResponse response = (WebResponse) openNewWindow(name, urlString);
1✔
908
            return response == null ? null : response.getScriptableObject();
1✔
909
        }
910

911
        public void closeWindow() {
912
            close();
1✔
913
        }
1✔
914

915
        /**
916
         * Returns the value of the named property. Will return null if the property does not exist.
917
         **/
918
        @Override
919
        public Object get(String propertyName) {
920
            if (propertyName.equals("name")) {
1✔
921
                return getName();
1✔
922
            }
923
            if (propertyName.equalsIgnoreCase("top")) {
1✔
924
                return _window.getFrameContents(WebRequest.TOP_FRAME).getScriptableObject();
1✔
925
            }
926
            if (propertyName.equalsIgnoreCase("parent")) {
1✔
927
                return _window.getParentFrameContents(_frame).getScriptableObject();
1✔
928
            }
929
            if (propertyName.equalsIgnoreCase("opener")) {
1✔
930
                return getFrameName().equals(WebRequest.TOP_FRAME) ? getScriptable(_window.getOpener()) : null;
1!
931
            }
932
            if (propertyName.equalsIgnoreCase("closed")) {
1✔
933
                return getFrameName().equals(WebRequest.TOP_FRAME) && _window.isClosed() ? Boolean.TRUE : Boolean.FALSE;
1!
934
            }
935
            try {
936
                return getSubframeContents(propertyName).getScriptableObject();
1✔
937
            } catch (NoSuchFrameException e) {
1✔
938
                return super.get(propertyName);
1✔
939
            }
940
        }
941

942
        @Override
943
        public String getName() {
944
            String windowName = getFrameName().equals(WebRequest.TOP_FRAME) ? _window.getName() : getFrameName();
1✔
945
            return windowName.startsWith(WebWindow.NO_NAME) ? "" : windowName;
1✔
946
        }
947

948
        private Scriptable getScriptable(WebResponse opener) {
949
            return opener == null ? null : opener.getScriptableObject();
1✔
950
        }
951

952
        /**
953
         * Sets the value of the named property. Will throw a runtime exception if the property does not exist or cannot
954
         * accept the specified value.
955
         **/
956
        @Override
957
        public void set(String propertyName, Object value) {
958
            if (propertyName.equals("name")) {
1!
959
                if (value == null) {
1!
960
                    value = "";
×
961
                }
962
                if (getFrameName().equals(WebRequest.TOP_FRAME)) {
1!
963
                    _window.setName(value.toString());
1✔
964
                }
965
            } else {
966
                super.set(propertyName, value);
×
967
            }
968
        }
1✔
969

970
        public void setLocation(String relativeURL) throws IOException, SAXException {
971
            getWindow().getResponse(new GetMethodWebRequest(_pageURL, relativeURL, _frame.getName()));
1✔
972
        }
1✔
973

974
        public URL getURL() {
975
            return WebResponse.this._pageURL;
1✔
976
        }
977
    }
978

979
    // ---------------------------------------- Object methods --------------------------------------------
980

981
    @Override
982
    public abstract String toString();
983

984
    // ----------------------------------------- protected members -----------------------------------------------
985

986
    /**
987
     * Constructs a response object. see [ 1159858 ] patch for RFE 1159844 (parsing intercepted pages)
988
     *
989
     * @param frame
990
     *            the frame to hold the response
991
     * @param url
992
     *            the url from which the response was received
993
     **/
994
    protected WebResponse(WebClient client, FrameSelector frame, URL url) {
1✔
995
        _client = client;
1✔
996
        _baseURL = _pageURL = url;
1✔
997
        _baseTarget = frame.getName();
1✔
998
        _frame = frame;
1✔
999
        // intialize window for interception as described in
1000
        // https://sourceforge.net/tracker/index.php?func=detail&aid=1159844&group_id=6550&atid=356550
1001
        if (client != null) {
1✔
1002
            _window = client.getMainWindow();
1✔
1003
        }
1004
    }
1✔
1005

1006
    /**
1007
     * Constructs a response object.
1008
     *
1009
     * @param frame
1010
     *            the frame to hold the response
1011
     * @param url
1012
     *            the url from which the response was received
1013
     **/
1014
    protected WebResponse(WebClient client, FrameSelector frame, URL url, String text) {
1015
        this(client, frame, url);
1✔
1016
        _responseText = text;
1✔
1017
    }
1✔
1018

1019
    protected final void defineRawInputStream(InputStream inputStream) throws IOException {
1020
        if (_inputStream != null || _responseText != null) {
1!
1021
            throw new IllegalStateException("Must be called before response text is defined.");
×
1022
        }
1023

1024
        // please note bug report [ 1119205 ] EOFExceptions while using a Proxy
1025
        // and patch proposal below
1026
        // by Ralf Bust
1027
        /*
1028
         * original 1.6.2 code if (encodedUsingGZIP()) { byte[] compressedData = readFromStream( inputStream,
1029
         * getContentLength() ); _inputStream = new GZIPInputStream( new ByteArrayInputStream( compressedData ) ); }
1030
         * else { _inputStream = inputStream; }
1031
         */
1032

1033
        if (encodedUsingGZIP()) {
1✔
1034
            try {
1035
                _inputStream = new GZIPInputStream(inputStream);
1✔
1036
            } catch (EOFException eof) {
×
1037
                _inputStream = inputStream;
×
1038
            }
1✔
1039
        } else {
1040
            _inputStream = inputStream;
1✔
1041
        }
1042
    }
1✔
1043

1044
    private boolean encodedUsingGZIP() {
1045
        String encoding = getHeaderField("Content-Encoding");
1✔
1046
        return encoding != null && encoding.indexOf("gzip") >= 0;
1!
1047
    }
1048

1049
    /**
1050
     * Overwrites the current value (if any) of the content type header.
1051
     **/
1052
    protected void setContentTypeHeader(String value) {
1053
        _contentHeader = value;
1✔
1054
    }
1✔
1055

1056
    // ------------------------------------------ package members ------------------------------------------------
1057

1058
    static final String BLANK_HTML = "";
1059

1060
    static WebResponse createBlankResponse() {
1061
        return new DefaultWebResponse(BLANK_HTML);
1✔
1062
    }
1063

1064
    WebWindow getWindow() {
1065
        return _window;
1✔
1066
    }
1067

1068
    void setWindow(WebWindow window) {
1069
        _window = window;
1✔
1070
    }
1✔
1071

1072
    /**
1073
     * replace the given text
1074
     *
1075
     * @param text
1076
     *            - the text to replace
1077
     * @param contentType
1078
     *            - the contenttype
1079
     */
1080
    @Override
1081
    public boolean replaceText(String text, String contentType) {
1082
        if (_parsingPage) {
1✔
1083
            return false;
1✔
1084
        }
1085
        _responseText = text;
1✔
1086
        _inputStream = null;
1✔
1087
        _page = null;
1✔
1088
        _contentType = contentType;
1✔
1089
        _baseURL = null;
1✔
1090
        _baseTarget = _frame.getName();
1✔
1091
        _refreshHeader = null;
1✔
1092

1093
        try {
1094
            readTags(text.getBytes(StandardCharsets.UTF_8));
1✔
NEW
1095
        } catch (MalformedURLException e) {
×
1096
            throw new RuntimeException("Failure while attempting to reparse text: " + e);
×
1097
        }
1✔
1098
        return true;
1✔
1099
    }
1100

1101
    /**
1102
     * Returns the frames found in the page in the order in which they appear.
1103
     **/
1104
    WebRequest[] getFrameRequests() throws SAXException {
1105
        WebFrame[] frames = getFrames();
1✔
1106
        Vector requests = new Vector<>();
1✔
1107
        for (WebFrame frame : frames) {
1✔
1108
            if (frame.hasInitialRequest()) {
1✔
1109
                requests.addElement(frame.getInitialRequest());
1✔
1110
            }
1111
        }
1112

1113
        WebRequest[] result = new WebRequest[requests.size()];
1✔
1114
        requests.copyInto(result);
1✔
1115
        return result;
1✔
1116
    }
1117

1118
    // --------------------------------- private members --------------------------------------
1119

1120
    private WebWindow _window;
1121

1122
    private HTMLPage _page;
1123

1124
    private String _contentHeader;
1125

1126
    private int _contentLength = UNINITIALIZED_INT;
1✔
1127

1128
    private String _contentType;
1129

1130
    private String _characterSet;
1131

1132
    private WebRequest _refreshRequest;
1133

1134
    private int _refreshDelay = -1; // initialized to invalid value
1✔
1135

1136
    /**
1137
     * the response as a String
1138
     */
1139
    private String _responseText;
1140

1141
    /**
1142
     * the response as a byte array
1143
     */
1144
    private byte[] _bytes;
1145

1146
    private InputStream _inputStream;
1147

1148
    private final URL _pageURL;
1149

1150
    private final WebClient _client;
1151

1152
    /**
1153
     * getter for the WebClient
1154
     *
1155
     * @since 1.7
1156
     *
1157
     * @return the web client for this WebResponse (if any)
1158
     */
1159
    public WebClient getClient() {
1160
        return _client;
×
1161
    }
1162

1163
    private ScriptingHandler _scriptingHandler;
1164

1165
    protected void loadResponseText() throws IOException {
1166
        if (_responseText != null) {
1!
1167
            throw new IllegalStateException("May only invoke loadResponseText once");
×
1168
        }
1169
        _responseText = "";
1✔
1170

1171
        try (InputStream inputStream = getInputStream()) {
1✔
1172
            final int contentLength = this.encodedUsingGZIP() ? -1 : getContentLength();
1✔
1173
            int bytesRemaining = contentLength < 0 ? Integer.MAX_VALUE : contentLength;
1✔
1174
            _bytes = readFromStream(inputStream, bytesRemaining);
1✔
1175

1176
            readTags(_bytes);
1✔
1177
            _responseText = new String(_bytes, getCharacterSet());
1✔
1178
            _inputStream = new ByteArrayInputStream(_bytes);
1✔
1179

1180
            if (HttpUnitOptions.isCheckContentLength() && contentLength >= 0 && _bytes.length != contentLength) {
1!
1181
                throw new IOException(
1✔
1182
                        "Truncated message. Expected length: " + contentLength + ", Actual length: " + _bytes.length);
1183
            }
1184
        }
1185
    }
1✔
1186

1187
    private byte[] readFromStream(InputStream inputStream, int maxBytes) throws IOException {
1188
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1✔
1189
        byte[] buffer = new byte[8 * 1024];
1✔
1190
        int count = 0;
1✔
1191
        if (maxBytes > 0) {
1✔
1192
            do {
1193
                outputStream.write(buffer, 0, count);
1✔
1194
                maxBytes -= count;
1✔
1195
                if (maxBytes <= 0) {
1✔
1196
                    break;
1✔
1197
                }
1198
                count = inputStream.read(buffer, 0, Math.min(maxBytes, buffer.length));
1✔
1199
            } while (count != -1);
1✔
1200
        } else {
1201
            do {
1202
                outputStream.write(buffer, 0, count);
1✔
1203
                int available = getAvailableBytes(inputStream);
1✔
1204
                count = available == 0 ? -1 : inputStream.read(buffer, 0, buffer.length);
1!
1205
            } while (count != -1);
1!
1206
        }
1207

1208
        return outputStream.toByteArray();
1✔
1209
    }
1210

1211
    private int getAvailableBytes(InputStream inputStream) throws IOException {
1212
        int timeLeft = UNKNOWN_LENGTH_TIMEOUT;
1✔
1213
        int available;
1214
        do {
1215
            timeLeft -= UNKNOWN_LENGTH_RETRY_INTERVAL;
1✔
1216
            try {
1217
                Thread.sleep(UNKNOWN_LENGTH_RETRY_INTERVAL);
1✔
1218
            } catch (InterruptedException e) {
×
NEW
1219
                Thread.interrupted();
×
1220
                /* do nothing */
1221
            }
1✔
1222
            available = inputStream.available();
1✔
1223
        } while (available == 0 && timeLeft > 0);
1!
1224
        return available;
1✔
1225
    }
1226

1227
    /**
1228
     * read the tags from the given message
1229
     *
1230
     * @param rawMessage
1231
     *
1232
     * @throws MalformedURLException
1233
     */
1234
    private void readTags(byte[] rawMessage) throws MalformedURLException {
1235
        ByteTagParser parser = new ByteTagParser(rawMessage);
1✔
1236
        ByteTag tag = parser.getNextTag();
1✔
1237
        while (tag != null) {
1✔
1238
            if (tag.getName().equalsIgnoreCase("meta")) {
1✔
1239
                processMetaTag(tag);
1✔
1240
            }
1241
            if (tag.getName().equalsIgnoreCase("base")) {
1✔
1242
                processBaseTag(tag);
1✔
1243
            }
1244
            // loop over a noscript region
1245
            if (tag.getName().equalsIgnoreCase("noscript") && HttpUnitOptions.isScriptingEnabled()) {
1✔
1246
                do {
1247
                    tag = parser.getNextTag();
1✔
1248
                } while (!tag.getName().equalsIgnoreCase("/noscript"));
1✔
1249
            }
1250
            tag = parser.getNextTag();
1✔
1251
        }
1252
    }
1✔
1253

1254
    private void processBaseTag(ByteTag tag) throws MalformedURLException {
1255
        if (tag.getAttribute("href") != null) {
1✔
1256
            _baseURL = new URL(getURL(), tag.getAttribute("href"));
1✔
1257
        }
1258
        if (tag.getAttribute("target") != null) {
1✔
1259
            _baseTarget = tag.getAttribute("target");
1✔
1260
        }
1261
    }
1✔
1262

1263
    /**
1264
     * process MetaTags based on the tag
1265
     *
1266
     * @param tag
1267
     */
1268
    private void processMetaTag(ByteTag tag) {
1269
        if (isHttpEquivMetaTag(tag, "content-type")) {
1✔
1270
            inferContentType(tag.getAttribute("content"));
1✔
1271
        } else if (isHttpEquivMetaTag(tag, "refresh")) {
1✔
1272
            inferRefreshHeader(tag.getAttribute("content"));
1✔
1273
        }
1274
    }
1✔
1275

1276
    /**
1277
     * check whether the given tag is a http equiv meta tag
1278
     *
1279
     * @param tag
1280
     * @param headerName
1281
     *
1282
     * @return
1283
     */
1284
    private boolean isHttpEquivMetaTag(ByteTag tag, String headerName) {
1285
        String equiv1 = tag.getAttribute("http_equiv");
1✔
1286
        String equiv2 = tag.getAttribute("http-equiv");
1✔
1287
        return headerName.equalsIgnoreCase(equiv1) || headerName.equalsIgnoreCase(equiv2);
1✔
1288
    }
1289

1290
    /**
1291
     * infer the refresh Header
1292
     *
1293
     * @param refreshHeader
1294
     */
1295
    private void inferRefreshHeader(String refreshHeader) {
1296
        String originalHeader = getHeaderField("Refresh");
1✔
1297
        // System.err.println("original='"+originalHeader+"'\nrefreshHeader='"+refreshHeader+"'");
1298
        if (originalHeader == null) {
1!
1299
            _refreshHeader = refreshHeader;
1✔
1300
        }
1301
    }
1✔
1302

1303
    /**
1304
     * read the Refresh Request
1305
     */
1306
    private void readRefreshRequest() {
1307
        if (_refreshDelay >= 0) {
1✔
1308
            return;
1✔
1309
        }
1310
        _refreshDelay = 0;
1✔
1311
        String refreshHeader = _refreshHeader != null ? _refreshHeader : getHeaderField("Refresh");
1✔
1312
        if (refreshHeader == null) {
1✔
1313
            return;
1✔
1314
        }
1315

1316
        int semicolonIndex = refreshHeader.indexOf(';');
1✔
1317
        if (semicolonIndex < 0) {
1✔
1318
            interpretRefreshHeaderElement(refreshHeader, refreshHeader);
1✔
1319
        } else {
1320
            interpretRefreshHeaderElement(refreshHeader.substring(0, semicolonIndex), refreshHeader);
1✔
1321
            interpretRefreshHeaderElement(refreshHeader.substring(semicolonIndex + 1), refreshHeader);
1✔
1322
        }
1323
        if (_refreshRequest == null) {
1✔
1324
            _refreshRequest = new GetMethodWebRequest(_pageURL, _pageURL.toString(), _frame.getName());
1✔
1325
        }
1326
    }
1✔
1327

1328
    private void interpretRefreshHeaderElement(String token, String refreshHeader) {
1329
        if (token.isEmpty()) {
1!
1330
            return;
×
1331
        }
1332
        try {
1333
            if (Character.isDigit(token.charAt(0))) {
1✔
1334
                _refreshDelay = Integer.parseInt(token);
1✔
1335
            } else {
1336
                _refreshRequest = new GetMethodWebRequest(_pageURL, getRefreshURL(token), _frame.getName());
1✔
1337
            }
1338
        } catch (NumberFormatException e) {
×
1339
            System.out.println("Unable to interpret refresh tag: \"" + refreshHeader + '"');
×
1340
        }
1✔
1341
    }
1✔
1342

1343
    private String getRefreshURL(String text) {
1344
        text = text.trim();
1✔
1345
        if (!text.toUpperCase().startsWith("URL")) {
1✔
1346
            return HttpUnitUtils.stripQuotes(text);
1✔
1347
        }
1348
        int splitIndex = text.indexOf('=');
1✔
1349
        String value = text.substring(splitIndex + 1).trim();
1✔
1350
        return HttpUnitUtils.replaceEntities(HttpUnitUtils.stripQuotes(value));
1✔
1351
    }
1352

1353
    private void inferContentType(String contentTypeHeader) {
1354
        String originalHeader = getHeaderField("Content-type");
1✔
1355
        if (originalHeader == null || originalHeader.indexOf("charset") < 0) {
1!
1356
            setContentTypeHeader(contentTypeHeader);
1✔
1357
        }
1358
    }
1✔
1359

1360
    CookieJar getCookieJar() {
1361
        if (_cookies == null) {
1✔
1362
            _cookies = new CookieJar(this);
1✔
1363
        }
1364
        return _cookies;
1✔
1365
    }
1366

1367
    private CookieJar _cookies;
1368

1369
    private void readContentTypeHeader() {
1370
        String contentHeader = _contentHeader != null ? _contentHeader : getHeaderField("Content-type");
1✔
1371
        if (contentHeader == null) {
1!
1372
            _contentType = HttpUnitOptions.getDefaultContentType();
×
1373
            setCharacterSet(HttpUnitOptions.getDefaultCharacterSet());
×
1374
            _contentHeader = _contentType + ";charset=" + _characterSet;
×
1375
        } else {
1376
            String[] parts = HttpUnitUtils.parseContentTypeHeader(contentHeader);
1✔
1377
            if (null != _client && null != _client.getClientProperties().getOverrideContentType()) {
1✔
1378
                _contentType = _client.getClientProperties().getOverrideContentType();
1✔
1379
            } else {
1380
                _contentType = parts[0];
1✔
1381
            }
1382
            if (parts[1] != null) {
1✔
1383
                setCharacterSet(parts[1]);
1✔
1384
            }
1385
        }
1386
    }
1✔
1387

1388
    private WebFrame[] getFrames() throws SAXException {
1389
        if (isWithParse()) {
1✔
1390
            return getReceivedPage().getFrames();
1✔
1391
        }
1392
        return new WebFrame[0];
1✔
1393
    }
1394

1395
    /**
1396
     * get the received Page
1397
     *
1398
     * @return the received page
1399
     *
1400
     * @throws SAXException
1401
     */
1402
    HTMLPage getReceivedPage() throws SAXException {
1403
        if (_page == null) {
1✔
1404
            try {
1405
                _parsingPage = true;
1✔
1406
                if (HttpUnitOptions.isCheckHtmlContentType() && !isHTML()) {
1✔
1407
                    throw new NotHTMLException(getContentType());
1✔
1408
                }
1409
                _page = new HTMLPage(this, _frame, _baseURL, _baseTarget, getCharacterSet());
1✔
1410
                if (_withParse) {
1!
1411
                    _page.parse(getText(), _pageURL);
1✔
1412
                    if (_page == null) {
1!
1413
                        throw new IllegalStateException("replaceText called in the middle of getReceivedPage()");
×
1414
                    }
1415
                    ((HTMLDocumentImpl) _page.getRootNode()).getWindow().setProxy(this);
1✔
1416
                }
1417
            } catch (IOException e) {
×
1418
                HttpUnitUtils.handleException(e);
×
1419
                throw new RuntimeException(e.toString());
×
1420
            } finally {
1421
                _parsingPage = false;
1✔
1422
            }
1423
        }
1424
        return _page;
1✔
1425
    }
1426

1427
    private static String _defaultEncoding;
1428

1429
    private static final String[] DEFAULT_ENCODING_CANDIDATES = { StandardCharsets.ISO_8859_1.name(),
1✔
1430
            StandardCharsets.US_ASCII.name() };
1✔
1431

1432
    static String getDefaultEncoding() {
1433
        if (_defaultEncoding == null) {
1✔
1434
            for (String element : DEFAULT_ENCODING_CANDIDATES) {
1!
1435
                if (isSupportedCharacterSet(element)) {
1!
1436
                    return _defaultEncoding = element;
1✔
1437
                }
1438
            }
1439
            _defaultEncoding = Charset.defaultCharset().displayName();
×
1440
        }
1441
        return _defaultEncoding;
1✔
1442
    }
1443

1444
    private void setCharacterSet(String characterSet) {
1445
        if (characterSet == null) {
1✔
1446
            return;
1✔
1447
        }
1448

1449
        _characterSet = isSupportedCharacterSet(characterSet) ? characterSet : getDefaultEncoding();
1✔
1450
    }
1✔
1451

1452
    private static boolean isSupportedCharacterSet(String characterSet) {
1453
        try {
1454
            return "abcd".getBytes(Charset.forName(characterSet)).length > 0;
1!
1455
        } catch (UnsupportedCharsetException e) {
1✔
1456
            return false;
1✔
1457
        }
1458
    }
1459

1460
    void setCookie(String name, String value) {
1461
        _client.putCookie(name, value);
1✔
1462
    }
1✔
1463

1464
    String getCookieHeader() {
1465
        return _client.getCookieJar().getCookieHeaderField(getURL());
1✔
1466
    }
1467

1468
    String getReferer() {
1469
        return null;
1✔
1470
    }
1471

1472
    // =======================================================================================
1473

1474
    static class ByteTag {
1475

1476
        ByteTag(byte[] buffer, int start, int length) {
1✔
1477
            _buffer = new String(buffer, start, length, Charset.forName(WebResponse.getDefaultEncoding()))
1✔
1478
                    .toCharArray();
1✔
1479
            _name = nextToken();
1✔
1480

1481
            String attribute = "";
1✔
1482
            String token = nextToken();
1✔
1483
            while (!token.isEmpty()) {
1✔
1484
                if (token.equals("=") && !attribute.isEmpty()) {
1✔
1485
                    getAttributes().put(attribute.toLowerCase(), nextToken());
1✔
1486
                    attribute = "";
1✔
1487
                } else {
1488
                    if (!attribute.isEmpty()) {
1✔
1489
                        getAttributes().put(attribute.toLowerCase(), "");
1✔
1490
                    }
1491
                    attribute = token;
1✔
1492
                }
1493
                token = nextToken();
1✔
1494
            }
1495
        }
1✔
1496

1497
        public String getName() {
1498
            return _name;
1✔
1499
        }
1500

1501
        public String getAttribute(String attributeName) {
1502
            return (String) getAttributes().get(attributeName);
1✔
1503
        }
1504

1505
        @Override
1506
        public String toString() {
1507
            return "ByteTag[ name=" + _name + ";attributes = " + _attributes + ']';
×
1508
        }
1509

1510
        private Hashtable getAttributes() {
1511
            if (_attributes == null) {
1✔
1512
                _attributes = new Hashtable<>();
1✔
1513
            }
1514
            return _attributes;
1✔
1515
        }
1516

1517
        private String _name = "";
1✔
1518
        private Hashtable _attributes;
1519

1520
        private char[] _buffer;
1521
        private int _end = -1;
1✔
1522

1523
        private String nextToken() {
1524
            int start = _end + 1;
1✔
1525
            while (start < _buffer.length && Character.isWhitespace(_buffer[start])) {
1✔
1526
                start++;
1✔
1527
            }
1528
            if (start >= _buffer.length) {
1✔
1529
                return "";
1✔
1530
            }
1531
            if (_buffer[start] == '"') {
1✔
1532
                for (_end = start + 1; _end < _buffer.length && _buffer[_end] != '"'; _end++) {
1✔
1533

1534
                }
1535
                return new String(_buffer, start + 1, _end - start - 1);
1✔
1536
            }
1537
            if (_buffer[start] == '\'') {
1✔
1538
                for (_end = start + 1; _end < _buffer.length && _buffer[_end] != '\''; _end++) {
1✔
1539

1540
                }
1541
                return new String(_buffer, start + 1, _end - start - 1);
1✔
1542
            }
1543
            if (_buffer[start] == '=') {
1✔
1544
                _end = start;
1✔
1545
                return "=";
1✔
1546
            }
1547
            for (_end = start + 1; _end < _buffer.length && _buffer[_end] != '='
1✔
1548
                    && !Character.isWhitespace(_buffer[_end]); _end++) {
1✔
1549

1550
            }
1551
            return new String(_buffer, start, _end-- - start);
1✔
1552
        }
1553
    }
1554

1555
    // =======================================================================================
1556

1557
    static class ByteTagParser {
1558
        ByteTagParser(byte[] buffer) {
1✔
1559
            _buffer = buffer;
1✔
1560
        }
1✔
1561

1562
        ByteTag getNextTag() {
1563
            ByteTag byteTag = null;
1✔
1564
            do {
1565
                int _start = _end + 1;
1✔
1566
                while (_start < _buffer.length && _buffer[_start] != '<') {
1✔
1567
                    _start++;
1✔
1568
                }
1569
                // proposed patch for bug report
1570
                // [ 1376739 ] iframe tag not recognized if Javascript code contains '<'
1571
                // by Nathan Jakubiak
1572
                // uncommented since it doesn't seem to fix the test in WebFrameTest.java
1573
                // if (_scriptDepth > 0 && _start+1 < _buffer.length &&
1574
                // _buffer[ _start+1 ] != '/') {
1575
                // _end = _start+1;
1576
                // continue;
1577
                // }
1578
                for (_end = _start + 1; _end < _buffer.length && _buffer[_end] != '>'; _end++) {
1✔
1579

1580
                }
1581
                if (_end >= _buffer.length || _end < _start) {
1!
1582
                    return null;
1✔
1583
                }
1584
                byteTag = new ByteTag(_buffer, _start + 1, _end - _start - 1);
1✔
1585
                if (byteTag.getName().equalsIgnoreCase("script")) {
1✔
1586
                    _scriptDepth++;
1✔
1587
                    return byteTag;
1✔
1588
                }
1589
                if (byteTag.getName().equalsIgnoreCase("/script")) {
1✔
1590
                    _scriptDepth--;
1✔
1591
                }
1592
            } while (_scriptDepth > 0);
1✔
1593
            return byteTag;
1✔
1594
        }
1595

1596
        private int _scriptDepth = 0;
1✔
1597
        private int _end = -1;
1✔
1598

1599
        private byte[] _buffer;
1600
    }
1601

1602
    /**
1603
     * allow access to the valid content Types
1604
     *
1605
     * @since 1.7
1606
     *
1607
     * @return the validContentTypes
1608
     */
1609
    public static String[] getValidContentTypes() {
1610
        return validContentTypes;
×
1611
    }
1612

1613
    /**
1614
     * allow modification of the valid content Types use with care
1615
     *
1616
     * @since 1.7
1617
     *
1618
     * @param validContentTypes
1619
     *            the validContentTypes to set
1620
     */
1621
    protected static void setValidContentTypes(String[] validContentTypes) {
1622
        WebResponse.validContentTypes = validContentTypes;
×
1623
    }
×
1624

1625
}
1626

1627
// =======================================================================================
1628

1629
class DefaultWebResponse extends WebResponse {
1630

1631
    DefaultWebResponse(String text) {
1632
        this(null, null, text);
1✔
1633
    }
1✔
1634

1635
    DefaultWebResponse(WebClient client, URL url, String text) {
1636
        this(client, FrameSelector.TOP_FRAME, url, text);
1✔
1637
    }
1✔
1638

1639
    DefaultWebResponse(WebClient client, FrameSelector frame, URL url, String text) {
1640
        super(client, frame, url, text);
1✔
1641
    }
1✔
1642

1643
    /**
1644
     * Returns the response code associated with this response.
1645
     **/
1646
    @Override
1647
    public int getResponseCode() {
1648
        return HttpURLConnection.HTTP_OK;
1✔
1649
    }
1650

1651
    /**
1652
     * Returns the response message associated with this response.
1653
     **/
1654
    @Override
1655
    public String getResponseMessage() {
1656
        return "OK";
×
1657
    }
1658

1659
    @Override
1660
    public String[] getHeaderFieldNames() {
1661
        return new String[] { "Content-type" };
×
1662
    }
1663

1664
    /**
1665
     * Returns the value for the specified header field. If no such field is defined, will return null.
1666
     **/
1667
    @Override
1668
    public String getHeaderField(String fieldName) {
1669
        if (fieldName.equalsIgnoreCase("Content-type")) {
1✔
1670
            return "text/html; charset=us-ascii";
1✔
1671
        }
1672
        return null;
1✔
1673
    }
1674

1675
    @Override
1676
    public String[] getHeaderFields(String fieldName) {
1677
        String value = getHeaderField(fieldName);
1✔
1678
        return value == null ? new String[0] : new String[] { value };
1!
1679
    }
1680

1681
    @Override
1682
    public String toString() {
1683
        try {
1684
            return "DefaultWebResponse [" + getText() + "]";
×
1685
        } catch (IOException e) { // should never happen
×
1686
            return "DefaultWebResponse [???]";
×
1687
        }
1688
    }
1689
}
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