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

hazendaz / httpunit / 552

30 Oct 2025 07:58PM UTC coverage: 80.509% (-0.04%) from 80.553%
552

push

github

hazendaz
[gha] Update actions

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8249 of 10132 relevant lines covered (81.42%)

0.81 hits per line

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

86.88
/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.ArrayList;
47
import java.util.Hashtable;
48
import java.util.List;
49
import java.util.zip.GZIPInputStream;
50

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

337
        return result;
1✔
338
    }
339

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

353
        return result;
1✔
354
    }
355

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

369
    // ---------------------- HTMLSegment methods -----------------------------
370

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

745
    // ---------------------------------------- JavaScript methods ----------------------------------------
746

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

980
    // ---------------------------------------- Object methods --------------------------------------------
981

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

985
    // ----------------------------------------- protected members -----------------------------------------------
986

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

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

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

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

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

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

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

1057
    // ------------------------------------------ package members ------------------------------------------------
1058

1059
    static final String BLANK_HTML = "";
1060

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

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

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

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

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

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

1114
        return requests.toArray(new WebRequest[requests.size()]);
1✔
1115
    }
1116

1117
    // --------------------------------- private members --------------------------------------
1118

1119
    private WebWindow _window;
1120

1121
    private HTMLPage _page;
1122

1123
    private String _contentHeader;
1124

1125
    private int _contentLength = UNINITIALIZED_INT;
1✔
1126

1127
    private String _contentType;
1128

1129
    private String _characterSet;
1130

1131
    private WebRequest _refreshRequest;
1132

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

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

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

1145
    private InputStream _inputStream;
1146

1147
    private final URL _pageURL;
1148

1149
    private final WebClient _client;
1150

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

1162
    private ScriptingHandler _scriptingHandler;
1163

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1366
    private CookieJar _cookies;
1367

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

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

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

1426
    private static String _defaultEncoding;
1427

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

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

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

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

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

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

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

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

1471
    // =======================================================================================
1472

1473
    static class ByteTag {
1474

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

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

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

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

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

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

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

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

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

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

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

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

1554
    // =======================================================================================
1555

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

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

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

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

1598
        private byte[] _buffer;
1599
    }
1600

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

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

1624
}
1625

1626
// =======================================================================================
1627

1628
class DefaultWebResponse extends WebResponse {
1629

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

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

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

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

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

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

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

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

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