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

hazendaz / httpunit / 345

07 Jun 2025 10:27PM UTC coverage: 80.575% (+0.05%) from 80.528%
345

push

github

hazendaz
Cleanup encoding

3216 of 4105 branches covered (78.34%)

Branch coverage included in aggregate %.

33 of 37 new or added lines in 12 files covered. (89.19%)

8253 of 10129 relevant lines covered (81.48%)

0.81 hits per line

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

87.57
/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().length() == 0) {
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) {
×
1219
                /* do nothing */ }
1✔
1220
            available = inputStream.available();
1✔
1221
        } while (available == 0 && timeLeft > 0);
1!
1222
        return available;
1✔
1223
    }
1224

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

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

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

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

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

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

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

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

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

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

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

1365
    private CookieJar _cookies;
1366

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

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

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

1425
    private static String _defaultEncoding;
1426

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

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

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

1447
        _characterSet = isSupportedCharacterSet(characterSet) ? characterSet : getDefaultEncoding();
1✔
1448
    }
1✔
1449

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

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

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

1466
    String getReferer() {
1467
        return null;
1✔
1468
    }
1469

1470
    // =======================================================================================
1471

1472
    static class ByteTag {
1473

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

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

1495
        public String getName() {
1496
            return _name;
1✔
1497
        }
1498

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

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

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

1515
        private String _name = "";
1✔
1516
        private Hashtable _attributes;
1517

1518
        private char[] _buffer;
1519
        private int _end = -1;
1✔
1520

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

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

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

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

1553
    // =======================================================================================
1554

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

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

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

1594
        private int _scriptDepth = 0;
1✔
1595
        private int _end = -1;
1✔
1596

1597
        private byte[] _buffer;
1598
    }
1599

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

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

1623
}
1624

1625
// =======================================================================================
1626

1627
class DefaultWebResponse extends WebResponse {
1628

1629
    DefaultWebResponse(String text) {
1630
        this(null, null, text);
1✔
1631
    }
1✔
1632

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

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

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

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

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

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

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

1679
    @Override
1680
    public String toString() {
1681
        try {
1682
            return "DefaultWebResponse [" + getText() + "]";
×
1683
        } catch (IOException e) { // should never happen
×
1684
            return "DefaultWebResponse [???]";
×
1685
        }
1686
    }
1687
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc