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

hazendaz / httpunit / 636

05 Dec 2025 03:27AM UTC coverage: 80.509%. Remained the same
636

push

github

hazendaz
Cleanup more old since tags

you guessed it, at this point going to jautodoc the rest so the warnings on builds go away ;)

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
public abstract class WebResponse implements HTMLSegment, CookieSource, DomWindowProxy {
60

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

70
    private static final int UNINITIALIZED_INT = -2;
71
    private static final int UNKNOWN_LENGTH_TIMEOUT = 500;
72
    private static final int UNKNOWN_LENGTH_RETRY_INTERVAL = 10;
73

74
    private FrameSelector _frame;
75
    // allow to switch off parsing e.g. for method="HEAD"
76
    private boolean _withParse = true;
1✔
77
    private String _baseTarget;
78
    private String _refreshHeader;
79
    private URL _baseURL;
80
    private boolean _parsingPage;
81

82
    /**
83
     * is parsing on?
84
     *
85
     * @return true if parsing is enabled
86
     */
87
    public boolean isWithParse() {
88
        return _withParse;
1✔
89
    }
90

91
    /**
92
     * set the parsing switch
93
     *
94
     * @param doParse
95
     */
96
    public void setWithParse(boolean doParse) {
97
        _withParse = doParse;
1✔
98
    }
1✔
99

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

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

126
    /**
127
     * Returns the URL which invoked this response.
128
     **/
129
    @Override
130
    public URL getURL() {
131
        return _pageURL;
1✔
132
    }
133

134
    /**
135
     * Returns the title of the page.
136
     *
137
     * @exception SAXException
138
     *                thrown if there is an error parsing this response
139
     **/
140
    public String getTitle() throws SAXException {
141
        return getReceivedPage().getTitle();
1✔
142
    }
143

144
    /**
145
     * Returns the stylesheet linked in the head of the page. <code> <link type="text/css" rel="stylesheet"
146
     * href="/mystyle.css" /> </code> will return "/mystyle.css".
147
     *
148
     * @exception SAXException
149
     *                thrown if there is an error parsing this response
150
     **/
151
    public String getExternalStyleSheet() throws SAXException {
152
        return getReceivedPage().getExternalStyleSheet();
1✔
153
    }
154

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

168
    /**
169
     * Returns the name of the frame containing this page.
170
     **/
171
    public String getFrameName() {
172
        return _frame.getName();
1✔
173
    }
174

175
    void setFrame(FrameSelector frame) {
176
        if (!_frame.getName().equals(frame.getName())) {
1!
177
            throw new IllegalArgumentException("May not modify the frame name");
×
178
        }
179
        _frame = frame;
1✔
180
    }
1✔
181

182
    /**
183
     * Returns the frame containing this page.
184
     */
185
    FrameSelector getFrame() {
186
        return _frame;
1✔
187
    }
188

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

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

207
    /**
208
     * Returns the response code associated with this response.
209
     **/
210
    public abstract int getResponseCode();
211

212
    /**
213
     * Returns the response message associated with this response.
214
     **/
215
    public abstract String getResponseMessage();
216

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

230
    /**
231
     * Returns the content type of this response.
232
     **/
233
    public String getContentType() {
234
        if (_contentType == null) {
1✔
235
            readContentTypeHeader();
1✔
236
        }
237
        return _contentType;
1✔
238
    }
239

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

256
    /**
257
     * Returns a list of new cookie names defined as part of this response.
258
     **/
259
    public String[] getNewCookieNames() {
260
        return getCookieJar().getCookieNames();
1✔
261
    }
262

263
    /**
264
     * Returns the new cookie value defined as part of this response.
265
     **/
266
    public String getNewCookieValue(String name) {
267
        return getCookieJar().getCookieValue(name);
×
268
    }
269

270
    /**
271
     * Returns the names of the header fields found in the response.
272
     **/
273
    public abstract String[] getHeaderFieldNames();
274

275
    /**
276
     * Returns the value for the specified header field. If no such field is defined, will return null. If more than one
277
     * header is defined for the specified name, returns only the first found.
278
     **/
279
    public abstract String getHeaderField(String fieldName);
280

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

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

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

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

331
        return result;
1✔
332
    }
333

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

347
        return result;
1✔
348
    }
349

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

363
    // ---------------------- HTMLSegment methods -----------------------------
364

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

376
    /**
377
     * return the HTMLElements with the specified tag name
378
     *
379
     * @param tagName
380
     *            e.g. "div" or "table"
381
     *
382
     * @return a list of all HTMLElements with that tag name
383
     *
384
     * @throws SAXException
385
     */
386
    public HTMLElement[] getElementsByTagName(String tagName) throws SAXException {
387
        return getReceivedPage().getElementsByTagName(getDOM(), tagName);
1✔
388
    }
389

390
    /**
391
     * Returns a list of HTML element names contained in this HTML section.
392
     */
393
    @Override
394
    public String[] getElementNames() throws SAXException {
395
        return getReceivedPage().getElementNames();
1✔
396
    }
397

398
    /**
399
     * Returns the HTMLElements found in this segment with the specified name.
400
     */
401
    @Override
402
    public HTMLElement[] getElementsWithName(String name) throws SAXException {
403
        return getReceivedPage().getElementsWithName(name);
1✔
404
    }
405

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

413
    /**
414
     * Returns the HTMLElements found with the specified attribute value.
415
     */
416
    @Override
417
    public HTMLElement[] getElementsWithAttribute(String name, String value) throws SAXException {
418
        return getReceivedPage().getElementsWithAttribute(name, value);
1✔
419
    }
420

421
    /**
422
     * Returns the forms found in the page in the order in which they appear.
423
     *
424
     * @exception SAXException
425
     *                thrown if there is an error parsing the response.
426
     **/
427
    @Override
428
    public WebForm[] getForms() throws SAXException {
429
        return getReceivedPage().getForms();
1✔
430
    }
431

432
    /**
433
     * Returns the form found in the page with the specified name.
434
     *
435
     * @exception SAXException
436
     *                thrown if there is an error parsing the response.
437
     **/
438
    @Override
439
    public WebForm getFormWithName(String name) throws SAXException {
440
        return getReceivedPage().getFormWithName(name);
1✔
441
    }
442

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

454
    /**
455
     * Returns the first form found in the page matching the specified criteria.
456
     *
457
     * @exception SAXException
458
     *                thrown if there is an error parsing the response.
459
     **/
460
    @Override
461
    public WebForm getFirstMatchingForm(HTMLElementPredicate predicate, Object criteria) throws SAXException {
462
        return getReceivedPage().getFirstMatchingForm(predicate, criteria);
1✔
463
    }
464

465
    /**
466
     * Returns all forms found in the page matching the specified criteria.
467
     *
468
     * @exception SAXException
469
     *                thrown if there is an error parsing the response.
470
     **/
471
    @Override
472
    public WebForm[] getMatchingForms(HTMLElementPredicate predicate, Object criteria) throws SAXException {
473
        return getReceivedPage().getMatchingForms(predicate, criteria);
×
474
    }
475

476
    /**
477
     * Returns the links found in the page in the order in which they appear.
478
     *
479
     * @exception SAXException
480
     *                thrown if there is an error parsing the response.
481
     **/
482
    @Override
483
    public WebLink[] getLinks() throws SAXException {
484
        return getReceivedPage().getLinks();
1✔
485
    }
486

487
    /**
488
     * Returns the first link which contains the specified text.
489
     *
490
     * @exception SAXException
491
     *                thrown if there is an error parsing the response.
492
     **/
493
    @Override
494
    public WebLink getLinkWith(String text) throws SAXException {
495
        return getReceivedPage().getLinkWith(text);
1✔
496
    }
497

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

509
    /**
510
     * Returns the link found in the page with the specified name.
511
     *
512
     * @exception SAXException
513
     *                thrown if there is an error parsing the response.
514
     **/
515
    public WebLink getLinkWithName(String name) throws SAXException {
516
        return getReceivedPage().getLinkWithName(name);
1✔
517
    }
518

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

529
    /**
530
     * Returns the first link found in the page matching the specified criteria.
531
     *
532
     * @exception SAXException
533
     *                thrown if there is an error parsing the response.
534
     **/
535
    @Override
536
    public WebLink getFirstMatchingLink(HTMLElementPredicate predicate, Object criteria) throws SAXException {
537
        return getReceivedPage().getFirstMatchingLink(predicate, criteria);
1✔
538
    }
539

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

551
    /**
552
     * Returns the images found in the page in the order in which they appear.
553
     *
554
     * @exception SAXException
555
     *                thrown if there is an error parsing the response.
556
     **/
557
    @Override
558
    public WebImage[] getImages() throws SAXException {
559
        return getReceivedPage().getImages();
1✔
560
    }
561

562
    /**
563
     * Returns the image found in the page with the specified name attribute.
564
     *
565
     * @exception SAXException
566
     *                thrown if there is an error parsing the response.
567
     **/
568
    @Override
569
    public WebImage getImageWithName(String source) throws SAXException {
570
        return getReceivedPage().getImageWithName(source);
1✔
571
    }
572

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

584
    /**
585
     * Returns the first image found in the page with the specified alt attribute.
586
     **/
587
    @Override
588
    public WebImage getImageWithAltText(String altText) throws SAXException {
589
        return getReceivedPage().getImageWithAltText(altText);
1✔
590
    }
591

592
    @Override
593
    public WebApplet[] getApplets() throws SAXException {
594
        return getReceivedPage().getApplets();
1✔
595
    }
596

597
    /**
598
     * Returns an array of text blocks found in the page.
599
     */
600
    @Override
601
    public TextBlock[] getTextBlocks() throws SAXException {
602
        return getReceivedPage().getTextBlocks();
1✔
603
    }
604

605
    /**
606
     * Returns the text block after the specified block, if any.
607
     */
608
    public TextBlock getNextTextBlock(TextBlock block) throws SAXException {
609
        return getReceivedPage().getNextTextBlock(block);
1✔
610
    }
611

612
    /**
613
     * Returns the first link found in the page matching the specified criteria.
614
     *
615
     * @exception SAXException
616
     *                thrown if there is an error parsing the response.
617
     **/
618
    public TextBlock getFirstMatchingTextBlock(HTMLElementPredicate predicate, Object criteria) throws SAXException {
619
        return getReceivedPage().getFirstMatchingTextBlock(predicate, criteria);
1✔
620
    }
621

622
    /**
623
     * Returns a copy of the domain object model tree associated with this response. If the response is HTML, it will
624
     * use a special parser which can transform HTML into an XML DOM.
625
     *
626
     * @exception SAXException
627
     *                thrown if there is an error parsing the response.
628
     **/
629
    public Document getDOM() throws SAXException {
630
        if (isHTML()) {
1!
631
            return (Document) getReceivedPage().getDOM();
1✔
632
        }
633
        try {
634
            return HttpUnitUtils.parse(new InputSource(new StringReader(getText())));
×
635
        } catch (IOException e) {
×
636
            throw new SAXException(e);
×
637
        }
638
    }
639

640
    /**
641
     * Returns the top-level tables found in this page in the order in which they appear.
642
     *
643
     * @exception SAXException
644
     *                thrown if there is an error parsing the response.
645
     **/
646
    @Override
647
    public WebTable[] getTables() throws SAXException {
648
        return getReceivedPage().getTables();
1✔
649
    }
650

651
    /**
652
     * Returns the first table in the response which matches the specified predicate and value. Will recurse into any
653
     * nested tables, as needed.
654
     *
655
     * @return the selected table, or null if none is found
656
     **/
657
    @Override
658
    public WebTable getFirstMatchingTable(HTMLElementPredicate predicate, Object criteria) throws SAXException {
659
        return getReceivedPage().getFirstMatchingTable(predicate, criteria);
×
660
    }
661

662
    /**
663
     * Returns all tables found in the page matching the specified criteria.
664
     *
665
     * @exception SAXException
666
     *                thrown if there is an error parsing the response.
667
     **/
668
    @Override
669
    public WebTable[] getMatchingTables(HTMLElementPredicate predicate, Object criteria) throws SAXException {
670
        return getReceivedPage().getMatchingTables(predicate, criteria);
×
671
    }
672

673
    /**
674
     * Returns the first table in the response which has the specified text as the full text of its first non-blank row
675
     * and non-blank column. Will recurse into any nested tables, as needed. Case is ignored.
676
     *
677
     * @exception SAXException
678
     *                thrown if there is an error parsing the response.
679
     *
680
     * @return the selected table, or null if none is found
681
     **/
682
    @Override
683
    public WebTable getTableStartingWith(String text) throws SAXException {
684
        return getReceivedPage().getTableStartingWith(text);
1✔
685
    }
686

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

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

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

729
    // ---------------------------------------- JavaScript methods ----------------------------------------
730

731
    /**
732
     * get the scriptable object for this WebResponse
733
     */
734
    public Scriptable getScriptableObject() {
735
        ScriptingHandler result = this.getScriptingHandler();
1✔
736
        if (!(result instanceof Scriptable)) {
1!
737
            throw new RuntimeException(
×
738
                    "getScriptableObject failed for " + result.getClass().getName() + " - not a Scriptable");
×
739
        }
740
        return (Scriptable) result;
1✔
741
    }
742

743
    public void setScriptingHandler(ScriptingHandler scriptingHandler) {
744
        _scriptingHandler = scriptingHandler;
×
745
    }
×
746

747
    @Override
748
    public ScriptingHandler getScriptingHandler() {
749
        if (_scriptingHandler == null) {
1✔
750
            _scriptingHandler = HttpUnitOptions.getScriptingEngine().createHandler(this);
1✔
751
        }
752
        return _scriptingHandler;
1✔
753
    }
754

755
    public ScriptingHandler createJavascriptScriptingHandler() {
756
        return new Scriptable();
1✔
757
    }
758

759
    /**
760
     * create a DOMScriptingHandler
761
     *
762
     * @return the DOM scripting handler (the window)
763
     */
764
    public ScriptingHandler createDomScriptingHandler() {
765
        if (!isHTML()) {
×
766
            return new DomWindow(this);
×
767
        }
768
        try {
769
            HTMLPage page = this.getReceivedPage();
×
770
            Node rootNode = page.getRootNode();
×
771
            HTMLDocumentImpl document = (HTMLDocumentImpl) rootNode;
×
772
            DomWindow result = document.getWindow();
×
773
            result.setProxy(this);
×
774
            return result;
×
775
        } catch (SAXException e) {
×
776
            return new DomWindow(this);
×
777
        }
778
    }
779

780
    public static ScriptableDelegate newDelegate(String delegateClassName) {
781
        if (delegateClassName.equalsIgnoreCase("Option")) {
1!
782
            return FormControl.newSelectionOption();
1✔
783
        }
784
        throw new IllegalArgumentException("No such scripting class supported: " + delegateClassName);
×
785
    }
786

787
    HTMLPage.Scriptable getDocumentScriptable() {
788
        return getScriptableObject().getDocument();
1✔
789
    }
790

791
    /**
792
     * open a a new Window with the given name and relative URL
793
     *
794
     * @param name
795
     *            - the name of the window
796
     * @param relativeUrl
797
     *            - the relative URL to be used
798
     *
799
     * @return the WebResponse as a DomWindowProxy
800
     */
801
    @Override
802
    public DomWindowProxy openNewWindow(String name, String relativeUrl) throws IOException, SAXException {
803
        if (relativeUrl == null || relativeUrl.trim().isEmpty()) {
1✔
804
            relativeUrl = "about:";
1✔
805
        }
806
        GetMethodWebRequest request = new GetMethodWebRequest(getURL(), relativeUrl, _frame, name);
1✔
807
        return _window.getResponse(request);
1✔
808
    }
809

810
    @Override
811
    public DomWindowProxy submitRequest(HTMLElementImpl sourceElement, String method, String location, String target,
812
            MessageBody requestBody) throws IOException, SAXException {
813
        if (method.equalsIgnoreCase("get")) {
1!
814
            return getWindow().sendRequest(new GetMethodWebRequest(this, sourceElement, getURL(), location, target));
1✔
815
        }
816
        return null;
×
817
    }
818

819
    @Override
820
    public void close() {
821
        if (getFrameName().equals(WebRequest.TOP_FRAME)) {
1!
822
            _window.close();
1✔
823
        }
824
    }
1✔
825

826
    @Override
827
    public void alert(String message) {
828
        _client.postAlert(message);
1✔
829
    }
1✔
830

831
    @Override
832
    public boolean confirm(String message) {
833
        return _client.getConfirmationResponse(message);
1✔
834
    }
835

836
    @Override
837
    public String prompt(String prompt, String defaultResponse) {
838
        return _client.getUserResponse(prompt, defaultResponse);
1✔
839
    }
840

841
    String getBaseTarget() {
842
        return _baseTarget;
1✔
843
    }
844

845
    public class Scriptable extends ScriptableDelegate implements NamedDelegate {
1✔
846

847
        public void alertUser(String message) {
848
            alert(message);
1✔
849
        }
1✔
850

851
        public boolean getConfirmationResponse(String message) {
852
            return confirm(message);
1✔
853
        }
854

855
        public String getUserResponse(String prompt, String defaultResponse) {
856
            return prompt(prompt, defaultResponse);
1✔
857
        }
858

859
        public ClientProperties getClientProperties() {
860
            return _client == null ? ClientProperties.getDefaultProperties() : _client.getClientProperties();
1✔
861
        }
862

863
        public HTMLPage.Scriptable getDocument() {
864
            try {
865
                if (!isHTML()) {
1✔
866
                    replaceText(BLANK_HTML, HTML_CONTENT);
1✔
867
                }
868
                return getReceivedPage().getScriptableObject();
1✔
869
            } catch (SAXException e) {
×
870
                throw new RuntimeException(e.toString());
×
871
            }
872
        }
873

874
        public Scriptable[] getFrames() throws SAXException {
875
            String[] names = getFrameNames();
1✔
876
            Scriptable[] frames = new Scriptable[names.length];
1✔
877
            for (int i = 0; i < frames.length; i++) {
1✔
878
                frames[i] = getSubframeContents(names[i]).getScriptableObject();
1✔
879
            }
880
            return frames;
1✔
881
        }
882

883
        public void load() throws SAXException {
884
            if (isHTML() && isWithParse()) {
1!
885
                getReceivedPage().getForms(); // TODO be more explicit here - don't care about forms, after all
1✔
886
                doEventScript(getReceivedPage().getOnLoadEvent());
1✔
887
            }
888
        }
1✔
889

890
        public Scriptable open(String urlString, String name, String features, boolean replace)
891
                throws IOException, SAXException {
892
            WebResponse response = (WebResponse) openNewWindow(name, urlString);
1✔
893
            return response == null ? null : response.getScriptableObject();
1✔
894
        }
895

896
        public void closeWindow() {
897
            close();
1✔
898
        }
1✔
899

900
        /**
901
         * Returns the value of the named property. Will return null if the property does not exist.
902
         **/
903
        @Override
904
        public Object get(String propertyName) {
905
            if (propertyName.equals("name")) {
1✔
906
                return getName();
1✔
907
            }
908
            if (propertyName.equalsIgnoreCase("top")) {
1✔
909
                return _window.getFrameContents(WebRequest.TOP_FRAME).getScriptableObject();
1✔
910
            }
911
            if (propertyName.equalsIgnoreCase("parent")) {
1✔
912
                return _window.getParentFrameContents(_frame).getScriptableObject();
1✔
913
            }
914
            if (propertyName.equalsIgnoreCase("opener")) {
1✔
915
                return getFrameName().equals(WebRequest.TOP_FRAME) ? getScriptable(_window.getOpener()) : null;
1!
916
            }
917
            if (propertyName.equalsIgnoreCase("closed")) {
1✔
918
                return getFrameName().equals(WebRequest.TOP_FRAME) && _window.isClosed() ? Boolean.TRUE : Boolean.FALSE;
1!
919
            }
920
            try {
921
                return getSubframeContents(propertyName).getScriptableObject();
1✔
922
            } catch (NoSuchFrameException e) {
1✔
923
                return super.get(propertyName);
1✔
924
            }
925
        }
926

927
        @Override
928
        public String getName() {
929
            String windowName = getFrameName().equals(WebRequest.TOP_FRAME) ? _window.getName() : getFrameName();
1✔
930
            return windowName.startsWith(WebWindow.NO_NAME) ? "" : windowName;
1✔
931
        }
932

933
        private Scriptable getScriptable(WebResponse opener) {
934
            return opener == null ? null : opener.getScriptableObject();
1✔
935
        }
936

937
        /**
938
         * Sets the value of the named property. Will throw a runtime exception if the property does not exist or cannot
939
         * accept the specified value.
940
         **/
941
        @Override
942
        public void set(String propertyName, Object value) {
943
            if (propertyName.equals("name")) {
1!
944
                if (value == null) {
1!
945
                    value = "";
×
946
                }
947
                if (getFrameName().equals(WebRequest.TOP_FRAME)) {
1!
948
                    _window.setName(value.toString());
1✔
949
                }
950
            } else {
951
                super.set(propertyName, value);
×
952
            }
953
        }
1✔
954

955
        public void setLocation(String relativeURL) throws IOException, SAXException {
956
            getWindow().getResponse(new GetMethodWebRequest(_pageURL, relativeURL, _frame.getName()));
1✔
957
        }
1✔
958

959
        public URL getURL() {
960
            return WebResponse.this._pageURL;
1✔
961
        }
962
    }
963

964
    // ---------------------------------------- Object methods --------------------------------------------
965

966
    @Override
967
    public abstract String toString();
968

969
    // ----------------------------------------- protected members -----------------------------------------------
970

971
    /**
972
     * Constructs a response object. see [ 1159858 ] patch for RFE 1159844 (parsing intercepted pages)
973
     *
974
     * @param frame
975
     *            the frame to hold the response
976
     * @param url
977
     *            the url from which the response was received
978
     **/
979
    protected WebResponse(WebClient client, FrameSelector frame, URL url) {
1✔
980
        _client = client;
1✔
981
        _baseURL = _pageURL = url;
1✔
982
        _baseTarget = frame.getName();
1✔
983
        _frame = frame;
1✔
984
        // intialize window for interception as described in
985
        // https://sourceforge.net/tracker/index.php?func=detail&aid=1159844&group_id=6550&atid=356550
986
        if (client != null) {
1✔
987
            _window = client.getMainWindow();
1✔
988
        }
989
    }
1✔
990

991
    /**
992
     * Constructs a response object.
993
     *
994
     * @param frame
995
     *            the frame to hold the response
996
     * @param url
997
     *            the url from which the response was received
998
     **/
999
    protected WebResponse(WebClient client, FrameSelector frame, URL url, String text) {
1000
        this(client, frame, url);
1✔
1001
        _responseText = text;
1✔
1002
    }
1✔
1003

1004
    protected final void defineRawInputStream(InputStream inputStream) throws IOException {
1005
        if (_inputStream != null || _responseText != null) {
1!
1006
            throw new IllegalStateException("Must be called before response text is defined.");
×
1007
        }
1008

1009
        // please note bug report [ 1119205 ] EOFExceptions while using a Proxy
1010
        // and patch proposal below
1011
        // by Ralf Bust
1012
        /*
1013
         * original 1.6.2 code if (encodedUsingGZIP()) { byte[] compressedData = readFromStream( inputStream,
1014
         * getContentLength() ); _inputStream = new GZIPInputStream( new ByteArrayInputStream( compressedData ) ); }
1015
         * else { _inputStream = inputStream; }
1016
         */
1017

1018
        if (encodedUsingGZIP()) {
1✔
1019
            try {
1020
                _inputStream = new GZIPInputStream(inputStream);
1✔
1021
            } catch (EOFException eof) {
×
1022
                _inputStream = inputStream;
×
1023
            }
1✔
1024
        } else {
1025
            _inputStream = inputStream;
1✔
1026
        }
1027
    }
1✔
1028

1029
    private boolean encodedUsingGZIP() {
1030
        String encoding = getHeaderField("Content-Encoding");
1✔
1031
        return encoding != null && encoding.indexOf("gzip") >= 0;
1!
1032
    }
1033

1034
    /**
1035
     * Overwrites the current value (if any) of the content type header.
1036
     **/
1037
    protected void setContentTypeHeader(String value) {
1038
        _contentHeader = value;
1✔
1039
    }
1✔
1040

1041
    // ------------------------------------------ package members ------------------------------------------------
1042

1043
    static final String BLANK_HTML = "";
1044

1045
    static WebResponse createBlankResponse() {
1046
        return new DefaultWebResponse(BLANK_HTML);
1✔
1047
    }
1048

1049
    WebWindow getWindow() {
1050
        return _window;
1✔
1051
    }
1052

1053
    void setWindow(WebWindow window) {
1054
        _window = window;
1✔
1055
    }
1✔
1056

1057
    /**
1058
     * replace the given text
1059
     *
1060
     * @param text
1061
     *            - the text to replace
1062
     * @param contentType
1063
     *            - the contenttype
1064
     */
1065
    @Override
1066
    public boolean replaceText(String text, String contentType) {
1067
        if (_parsingPage) {
1✔
1068
            return false;
1✔
1069
        }
1070
        _responseText = text;
1✔
1071
        _inputStream = null;
1✔
1072
        _page = null;
1✔
1073
        _contentType = contentType;
1✔
1074
        _baseURL = null;
1✔
1075
        _baseTarget = _frame.getName();
1✔
1076
        _refreshHeader = null;
1✔
1077

1078
        try {
1079
            readTags(text.getBytes(StandardCharsets.UTF_8));
1✔
1080
        } catch (MalformedURLException e) {
×
1081
            throw new RuntimeException("Failure while attempting to reparse text: " + e);
×
1082
        }
1✔
1083
        return true;
1✔
1084
    }
1085

1086
    /**
1087
     * Returns the frames found in the page in the order in which they appear.
1088
     **/
1089
    WebRequest[] getFrameRequests() throws SAXException {
1090
        WebFrame[] frames = getFrames();
1✔
1091
        List<WebRequest> requests = new ArrayList<>();
1✔
1092
        for (WebFrame frame : frames) {
1✔
1093
            if (frame.hasInitialRequest()) {
1✔
1094
                requests.add(frame.getInitialRequest());
1✔
1095
            }
1096
        }
1097

1098
        return requests.toArray(new WebRequest[requests.size()]);
1✔
1099
    }
1100

1101
    // --------------------------------- private members --------------------------------------
1102

1103
    private WebWindow _window;
1104

1105
    private HTMLPage _page;
1106

1107
    private String _contentHeader;
1108

1109
    private int _contentLength = UNINITIALIZED_INT;
1✔
1110

1111
    private String _contentType;
1112

1113
    private String _characterSet;
1114

1115
    private WebRequest _refreshRequest;
1116

1117
    private int _refreshDelay = -1; // initialized to invalid value
1✔
1118

1119
    /**
1120
     * the response as a String
1121
     */
1122
    private String _responseText;
1123

1124
    /**
1125
     * the response as a byte array
1126
     */
1127
    private byte[] _bytes;
1128

1129
    private InputStream _inputStream;
1130

1131
    private final URL _pageURL;
1132

1133
    private final WebClient _client;
1134

1135
    /**
1136
     * getter for the WebClient
1137
     *
1138
     * @return the web client for this WebResponse (if any)
1139
     */
1140
    public WebClient getClient() {
1141
        return _client;
×
1142
    }
1143

1144
    private ScriptingHandler _scriptingHandler;
1145

1146
    protected void loadResponseText() throws IOException {
1147
        if (_responseText != null) {
1!
1148
            throw new IllegalStateException("May only invoke loadResponseText once");
×
1149
        }
1150
        _responseText = "";
1✔
1151

1152
        try (InputStream inputStream = getInputStream()) {
1✔
1153
            final int contentLength = this.encodedUsingGZIP() ? -1 : getContentLength();
1✔
1154
            int bytesRemaining = contentLength < 0 ? Integer.MAX_VALUE : contentLength;
1✔
1155
            _bytes = readFromStream(inputStream, bytesRemaining);
1✔
1156

1157
            readTags(_bytes);
1✔
1158
            _responseText = new String(_bytes, getCharacterSet());
1✔
1159
            _inputStream = new ByteArrayInputStream(_bytes);
1✔
1160

1161
            if (HttpUnitOptions.isCheckContentLength() && contentLength >= 0 && _bytes.length != contentLength) {
1!
1162
                throw new IOException(
×
1163
                        "Truncated message. Expected length: " + contentLength + ", Actual length: " + _bytes.length);
1164
            }
1165
        }
1166
    }
1✔
1167

1168
    private byte[] readFromStream(InputStream inputStream, int maxBytes) throws IOException {
1169
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
1✔
1170
        byte[] buffer = new byte[8 * 1024];
1✔
1171
        int count = 0;
1✔
1172
        if (maxBytes > 0) {
1✔
1173
            do {
1174
                outputStream.write(buffer, 0, count);
1✔
1175
                maxBytes -= count;
1✔
1176
                if (maxBytes <= 0) {
1✔
1177
                    break;
1✔
1178
                }
1179
                count = inputStream.read(buffer, 0, Math.min(maxBytes, buffer.length));
1✔
1180
            } while (count != -1);
1✔
1181
        } else {
1182
            do {
1183
                outputStream.write(buffer, 0, count);
1✔
1184
                int available = getAvailableBytes(inputStream);
1✔
1185
                count = available == 0 ? -1 : inputStream.read(buffer, 0, buffer.length);
1!
1186
            } while (count != -1);
1!
1187
        }
1188

1189
        return outputStream.toByteArray();
1✔
1190
    }
1191

1192
    private int getAvailableBytes(InputStream inputStream) throws IOException {
1193
        int timeLeft = UNKNOWN_LENGTH_TIMEOUT;
1✔
1194
        int available;
1195
        do {
1196
            timeLeft -= UNKNOWN_LENGTH_RETRY_INTERVAL;
1✔
1197
            try {
1198
                Thread.sleep(UNKNOWN_LENGTH_RETRY_INTERVAL);
1✔
1199
            } catch (InterruptedException e) {
×
1200
                Thread.interrupted();
×
1201
                /* do nothing */
1202
            }
1✔
1203
            available = inputStream.available();
1✔
1204
        } while (available == 0 && timeLeft > 0);
1!
1205
        return available;
1✔
1206
    }
1207

1208
    /**
1209
     * read the tags from the given message
1210
     *
1211
     * @param rawMessage
1212
     *
1213
     * @throws MalformedURLException
1214
     */
1215
    private void readTags(byte[] rawMessage) throws MalformedURLException {
1216
        ByteTagParser parser = new ByteTagParser(rawMessage);
1✔
1217
        ByteTag tag = parser.getNextTag();
1✔
1218
        while (tag != null) {
1✔
1219
            if (tag.getName().equalsIgnoreCase("meta")) {
1✔
1220
                processMetaTag(tag);
1✔
1221
            }
1222
            if (tag.getName().equalsIgnoreCase("base")) {
1✔
1223
                processBaseTag(tag);
1✔
1224
            }
1225
            // loop over a noscript region
1226
            if (tag.getName().equalsIgnoreCase("noscript") && HttpUnitOptions.isScriptingEnabled()) {
1✔
1227
                do {
1228
                    tag = parser.getNextTag();
1✔
1229
                } while (!tag.getName().equalsIgnoreCase("/noscript"));
1✔
1230
            }
1231
            tag = parser.getNextTag();
1✔
1232
        }
1233
    }
1✔
1234

1235
    private void processBaseTag(ByteTag tag) throws MalformedURLException {
1236
        if (tag.getAttribute("href") != null) {
1✔
1237
            _baseURL = new URL(getURL(), tag.getAttribute("href"));
1✔
1238
        }
1239
        if (tag.getAttribute("target") != null) {
1✔
1240
            _baseTarget = tag.getAttribute("target");
1✔
1241
        }
1242
    }
1✔
1243

1244
    /**
1245
     * process MetaTags based on the tag
1246
     *
1247
     * @param tag
1248
     */
1249
    private void processMetaTag(ByteTag tag) {
1250
        if (isHttpEquivMetaTag(tag, "content-type")) {
1✔
1251
            inferContentType(tag.getAttribute("content"));
1✔
1252
        } else if (isHttpEquivMetaTag(tag, "refresh")) {
1✔
1253
            inferRefreshHeader(tag.getAttribute("content"));
1✔
1254
        }
1255
    }
1✔
1256

1257
    /**
1258
     * check whether the given tag is a http equiv meta tag
1259
     *
1260
     * @param tag
1261
     * @param headerName
1262
     *
1263
     * @return
1264
     */
1265
    private boolean isHttpEquivMetaTag(ByteTag tag, String headerName) {
1266
        String equiv1 = tag.getAttribute("http_equiv");
1✔
1267
        String equiv2 = tag.getAttribute("http-equiv");
1✔
1268
        return headerName.equalsIgnoreCase(equiv1) || headerName.equalsIgnoreCase(equiv2);
1✔
1269
    }
1270

1271
    /**
1272
     * infer the refresh Header
1273
     *
1274
     * @param refreshHeader
1275
     */
1276
    private void inferRefreshHeader(String refreshHeader) {
1277
        String originalHeader = getHeaderField("Refresh");
1✔
1278
        // System.err.println("original='"+originalHeader+"'\nrefreshHeader='"+refreshHeader+"'");
1279
        if (originalHeader == null) {
1!
1280
            _refreshHeader = refreshHeader;
1✔
1281
        }
1282
    }
1✔
1283

1284
    /**
1285
     * read the Refresh Request
1286
     */
1287
    private void readRefreshRequest() {
1288
        if (_refreshDelay >= 0) {
1✔
1289
            return;
1✔
1290
        }
1291
        _refreshDelay = 0;
1✔
1292
        String refreshHeader = _refreshHeader != null ? _refreshHeader : getHeaderField("Refresh");
1✔
1293
        if (refreshHeader == null) {
1✔
1294
            return;
1✔
1295
        }
1296

1297
        int semicolonIndex = refreshHeader.indexOf(';');
1✔
1298
        if (semicolonIndex < 0) {
1✔
1299
            interpretRefreshHeaderElement(refreshHeader, refreshHeader);
1✔
1300
        } else {
1301
            interpretRefreshHeaderElement(refreshHeader.substring(0, semicolonIndex), refreshHeader);
1✔
1302
            interpretRefreshHeaderElement(refreshHeader.substring(semicolonIndex + 1), refreshHeader);
1✔
1303
        }
1304
        if (_refreshRequest == null) {
1✔
1305
            _refreshRequest = new GetMethodWebRequest(_pageURL, _pageURL.toString(), _frame.getName());
1✔
1306
        }
1307
    }
1✔
1308

1309
    private void interpretRefreshHeaderElement(String token, String refreshHeader) {
1310
        if (token.isEmpty()) {
1!
1311
            return;
×
1312
        }
1313
        try {
1314
            if (Character.isDigit(token.charAt(0))) {
1✔
1315
                _refreshDelay = Integer.parseInt(token);
1✔
1316
            } else {
1317
                _refreshRequest = new GetMethodWebRequest(_pageURL, getRefreshURL(token), _frame.getName());
1✔
1318
            }
1319
        } catch (NumberFormatException e) {
×
1320
            System.out.println("Unable to interpret refresh tag: \"" + refreshHeader + '"');
×
1321
        }
1✔
1322
    }
1✔
1323

1324
    private String getRefreshURL(String text) {
1325
        text = text.trim();
1✔
1326
        if (!text.toUpperCase().startsWith("URL")) {
1✔
1327
            return HttpUnitUtils.stripQuotes(text);
1✔
1328
        }
1329
        int splitIndex = text.indexOf('=');
1✔
1330
        String value = text.substring(splitIndex + 1).trim();
1✔
1331
        return HttpUnitUtils.replaceEntities(HttpUnitUtils.stripQuotes(value));
1✔
1332
    }
1333

1334
    private void inferContentType(String contentTypeHeader) {
1335
        String originalHeader = getHeaderField("Content-type");
1✔
1336
        if (originalHeader == null || originalHeader.indexOf("charset") < 0) {
1!
1337
            setContentTypeHeader(contentTypeHeader);
1✔
1338
        }
1339
    }
1✔
1340

1341
    CookieJar getCookieJar() {
1342
        if (_cookies == null) {
1✔
1343
            _cookies = new CookieJar(this);
1✔
1344
        }
1345
        return _cookies;
1✔
1346
    }
1347

1348
    private CookieJar _cookies;
1349

1350
    private void readContentTypeHeader() {
1351
        String contentHeader = _contentHeader != null ? _contentHeader : getHeaderField("Content-type");
1✔
1352
        if (contentHeader == null) {
1!
1353
            _contentType = HttpUnitOptions.getDefaultContentType();
×
1354
            setCharacterSet(HttpUnitOptions.getDefaultCharacterSet());
×
1355
            _contentHeader = _contentType + ";charset=" + _characterSet;
×
1356
        } else {
1357
            String[] parts = HttpUnitUtils.parseContentTypeHeader(contentHeader);
1✔
1358
            if (null != _client && null != _client.getClientProperties().getOverrideContentType()) {
1✔
1359
                _contentType = _client.getClientProperties().getOverrideContentType();
1✔
1360
            } else {
1361
                _contentType = parts[0];
1✔
1362
            }
1363
            if (parts[1] != null) {
1✔
1364
                setCharacterSet(parts[1]);
1✔
1365
            }
1366
        }
1367
    }
1✔
1368

1369
    private WebFrame[] getFrames() throws SAXException {
1370
        if (isWithParse()) {
1✔
1371
            return getReceivedPage().getFrames();
1✔
1372
        }
1373
        return new WebFrame[0];
1✔
1374
    }
1375

1376
    /**
1377
     * get the received Page
1378
     *
1379
     * @return the received page
1380
     *
1381
     * @throws SAXException
1382
     */
1383
    HTMLPage getReceivedPage() throws SAXException {
1384
        if (_page == null) {
1✔
1385
            try {
1386
                _parsingPage = true;
1✔
1387
                if (HttpUnitOptions.isCheckHtmlContentType() && !isHTML()) {
1✔
1388
                    throw new NotHTMLException(getContentType());
1✔
1389
                }
1390
                _page = new HTMLPage(this, _frame, _baseURL, _baseTarget, getCharacterSet());
1✔
1391
                if (_withParse) {
1!
1392
                    _page.parse(getText(), _pageURL);
1✔
1393
                    if (_page == null) {
1!
1394
                        throw new IllegalStateException("replaceText called in the middle of getReceivedPage()");
×
1395
                    }
1396
                    ((HTMLDocumentImpl) _page.getRootNode()).getWindow().setProxy(this);
1✔
1397
                }
1398
            } catch (IOException e) {
×
1399
                HttpUnitUtils.handleException(e);
×
1400
                throw new RuntimeException(e.toString());
×
1401
            } finally {
1402
                _parsingPage = false;
1✔
1403
            }
1404
        }
1405
        return _page;
1✔
1406
    }
1407

1408
    private static String _defaultEncoding;
1409

1410
    private static final String[] DEFAULT_ENCODING_CANDIDATES = { StandardCharsets.ISO_8859_1.name(),
1✔
1411
            StandardCharsets.US_ASCII.name() };
1✔
1412

1413
    static String getDefaultEncoding() {
1414
        if (_defaultEncoding == null) {
1✔
1415
            for (String element : DEFAULT_ENCODING_CANDIDATES) {
1!
1416
                if (isSupportedCharacterSet(element)) {
1!
1417
                    return _defaultEncoding = element;
1✔
1418
                }
1419
            }
1420
            _defaultEncoding = Charset.defaultCharset().displayName();
×
1421
        }
1422
        return _defaultEncoding;
1✔
1423
    }
1424

1425
    private void setCharacterSet(String characterSet) {
1426
        if (characterSet == null) {
1✔
1427
            return;
1✔
1428
        }
1429

1430
        _characterSet = isSupportedCharacterSet(characterSet) ? characterSet : getDefaultEncoding();
1✔
1431
    }
1✔
1432

1433
    private static boolean isSupportedCharacterSet(String characterSet) {
1434
        try {
1435
            return "abcd".getBytes(Charset.forName(characterSet)).length > 0;
1!
1436
        } catch (UnsupportedCharsetException e) {
1✔
1437
            return false;
1✔
1438
        }
1439
    }
1440

1441
    void setCookie(String name, String value) {
1442
        _client.putCookie(name, value);
1✔
1443
    }
1✔
1444

1445
    String getCookieHeader() {
1446
        return _client.getCookieJar().getCookieHeaderField(getURL());
1✔
1447
    }
1448

1449
    String getReferer() {
1450
        return null;
1✔
1451
    }
1452

1453
    // =======================================================================================
1454

1455
    static class ByteTag {
1456

1457
        ByteTag(byte[] buffer, int start, int length) {
1✔
1458
            _buffer = new String(buffer, start, length, Charset.forName(WebResponse.getDefaultEncoding()))
1✔
1459
                    .toCharArray();
1✔
1460
            _name = nextToken();
1✔
1461

1462
            String attribute = "";
1✔
1463
            String token = nextToken();
1✔
1464
            while (!token.isEmpty()) {
1✔
1465
                if (token.equals("=") && !attribute.isEmpty()) {
1✔
1466
                    getAttributes().put(attribute.toLowerCase(), nextToken());
1✔
1467
                    attribute = "";
1✔
1468
                } else {
1469
                    if (!attribute.isEmpty()) {
1✔
1470
                        getAttributes().put(attribute.toLowerCase(), "");
1✔
1471
                    }
1472
                    attribute = token;
1✔
1473
                }
1474
                token = nextToken();
1✔
1475
            }
1476
        }
1✔
1477

1478
        public String getName() {
1479
            return _name;
1✔
1480
        }
1481

1482
        public String getAttribute(String attributeName) {
1483
            return (String) getAttributes().get(attributeName);
1✔
1484
        }
1485

1486
        @Override
1487
        public String toString() {
1488
            return "ByteTag[ name=" + _name + ";attributes = " + _attributes + ']';
×
1489
        }
1490

1491
        private Hashtable getAttributes() {
1492
            if (_attributes == null) {
1✔
1493
                _attributes = new Hashtable<>();
1✔
1494
            }
1495
            return _attributes;
1✔
1496
        }
1497

1498
        private String _name = "";
1✔
1499
        private Hashtable _attributes;
1500

1501
        private char[] _buffer;
1502
        private int _end = -1;
1✔
1503

1504
        private String nextToken() {
1505
            int start = _end + 1;
1✔
1506
            while (start < _buffer.length && Character.isWhitespace(_buffer[start])) {
1✔
1507
                start++;
1✔
1508
            }
1509
            if (start >= _buffer.length) {
1✔
1510
                return "";
1✔
1511
            }
1512
            if (_buffer[start] == '"') {
1✔
1513
                for (_end = start + 1; _end < _buffer.length && _buffer[_end] != '"'; _end++) {
1✔
1514

1515
                }
1516
                return new String(_buffer, start + 1, _end - start - 1);
1✔
1517
            }
1518
            if (_buffer[start] == '\'') {
1✔
1519
                for (_end = start + 1; _end < _buffer.length && _buffer[_end] != '\''; _end++) {
1✔
1520

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

1531
            }
1532
            return new String(_buffer, start, _end-- - start);
1✔
1533
        }
1534
    }
1535

1536
    // =======================================================================================
1537

1538
    static class ByteTagParser {
1539
        ByteTagParser(byte[] buffer) {
1✔
1540
            _buffer = buffer;
1✔
1541
        }
1✔
1542

1543
        ByteTag getNextTag() {
1544
            ByteTag byteTag = null;
1✔
1545
            do {
1546
                int _start = _end + 1;
1✔
1547
                while (_start < _buffer.length && _buffer[_start] != '<') {
1✔
1548
                    _start++;
1✔
1549
                }
1550
                // proposed patch for bug report
1551
                // [ 1376739 ] iframe tag not recognized if Javascript code contains '<'
1552
                // by Nathan Jakubiak
1553
                // uncommented since it doesn't seem to fix the test in WebFrameTest.java
1554
                // if (_scriptDepth > 0 && _start+1 < _buffer.length &&
1555
                // _buffer[ _start+1 ] != '/') {
1556
                // _end = _start+1;
1557
                // continue;
1558
                // }
1559
                for (_end = _start + 1; _end < _buffer.length && _buffer[_end] != '>'; _end++) {
1✔
1560

1561
                }
1562
                if (_end >= _buffer.length || _end < _start) {
1!
1563
                    return null;
1✔
1564
                }
1565
                byteTag = new ByteTag(_buffer, _start + 1, _end - _start - 1);
1✔
1566
                if (byteTag.getName().equalsIgnoreCase("script")) {
1✔
1567
                    _scriptDepth++;
1✔
1568
                    return byteTag;
1✔
1569
                }
1570
                if (byteTag.getName().equalsIgnoreCase("/script")) {
1✔
1571
                    _scriptDepth--;
1✔
1572
                }
1573
            } while (_scriptDepth > 0);
1✔
1574
            return byteTag;
1✔
1575
        }
1576

1577
        private int _scriptDepth = 0;
1✔
1578
        private int _end = -1;
1✔
1579

1580
        private byte[] _buffer;
1581
    }
1582

1583
    /**
1584
     * allow access to the valid content Types
1585
     *
1586
     * @return the validContentTypes
1587
     */
1588
    public static String[] getValidContentTypes() {
1589
        return validContentTypes;
×
1590
    }
1591

1592
    /**
1593
     * allow modification of the valid content Types use with care
1594
     *
1595
     * @param validContentTypes
1596
     *            the validContentTypes to set
1597
     */
1598
    protected static void setValidContentTypes(String[] validContentTypes) {
1599
        WebResponse.validContentTypes = validContentTypes;
×
1600
    }
×
1601

1602
}
1603

1604
// =======================================================================================
1605

1606
class DefaultWebResponse extends WebResponse {
1607

1608
    DefaultWebResponse(String text) {
1609
        this(null, null, text);
1✔
1610
    }
1✔
1611

1612
    DefaultWebResponse(WebClient client, URL url, String text) {
1613
        this(client, FrameSelector.TOP_FRAME, url, text);
1✔
1614
    }
1✔
1615

1616
    DefaultWebResponse(WebClient client, FrameSelector frame, URL url, String text) {
1617
        super(client, frame, url, text);
1✔
1618
    }
1✔
1619

1620
    /**
1621
     * Returns the response code associated with this response.
1622
     **/
1623
    @Override
1624
    public int getResponseCode() {
1625
        return HttpURLConnection.HTTP_OK;
1✔
1626
    }
1627

1628
    /**
1629
     * Returns the response message associated with this response.
1630
     **/
1631
    @Override
1632
    public String getResponseMessage() {
1633
        return "OK";
×
1634
    }
1635

1636
    @Override
1637
    public String[] getHeaderFieldNames() {
1638
        return new String[] { "Content-type" };
×
1639
    }
1640

1641
    /**
1642
     * Returns the value for the specified header field. If no such field is defined, will return null.
1643
     **/
1644
    @Override
1645
    public String getHeaderField(String fieldName) {
1646
        if (fieldName.equalsIgnoreCase("Content-type")) {
1✔
1647
            return "text/html; charset=us-ascii";
1✔
1648
        }
1649
        return null;
1✔
1650
    }
1651

1652
    @Override
1653
    public String[] getHeaderFields(String fieldName) {
1654
        String value = getHeaderField(fieldName);
1✔
1655
        return value == null ? new String[0] : new String[] { value };
1!
1656
    }
1657

1658
    @Override
1659
    public String toString() {
1660
        try {
1661
            return "DefaultWebResponse [" + getText() + "]";
×
1662
        } catch (IOException e) { // should never happen
×
1663
            return "DefaultWebResponse [???]";
×
1664
        }
1665
    }
1666
}
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