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

hazendaz / httpunit / 755

14 Feb 2026 07:14PM UTC coverage: 80.526%. Remained the same
755

push

github

hazendaz
[ci] Fix badge

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10124 relevant lines covered (81.44%)

0.81 hits per line

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

62.4
/src/main/java/com/meterware/httpunit/HttpUnitUtils.java
1
/*
2
 * SPDX-License-Identifier: MIT
3
 * See LICENSE file for details.
4
 *
5
 * Copyright 2000-2026 Russell Gold
6
 * Copyright 2021-2000 hazendaz
7
 */
8
package com.meterware.httpunit;
9

10
import java.io.ByteArrayOutputStream;
11
import java.io.IOException;
12
import java.io.InputStream;
13
import java.nio.charset.Charset;
14
import java.nio.charset.StandardCharsets;
15
import java.util.Locale;
16
import java.util.StringTokenizer;
17

18
import javax.xml.parsers.DocumentBuilder;
19
import javax.xml.parsers.DocumentBuilderFactory;
20
import javax.xml.parsers.ParserConfigurationException;
21

22
import org.w3c.dom.Document;
23
import org.xml.sax.EntityResolver;
24
import org.xml.sax.InputSource;
25
import org.xml.sax.SAXException;
26

27
/**
28
 * Utility code shared by httpunit and servletunit.
29
 **/
30
public class HttpUnitUtils {
×
31

32
    /** The Constant DEFAULT_TEXT_BUFFER_SIZE. */
33
    public static final int DEFAULT_TEXT_BUFFER_SIZE = 2048;
34

35
    /** set to true to debug Exception handling. */
36
    private static boolean EXCEPTION_DEBUG = true;
1✔
37

38
    /**
39
     * handle Exceptions and thowables.
40
     *
41
     * @param th
42
     *            the th
43
     */
44
    public static void handleException(Throwable th) {
45
        if (EXCEPTION_DEBUG) {
1!
46
            th.printStackTrace();
×
47
        }
48
    }
1✔
49

50
    /**
51
     * are we running in the Eclipse IDE?.
52
     *
53
     * @return whether we are running in the Eclipse environment
54
     */
55
    public static boolean isEclipse() {
56
        StackTraceElement[] ste = new Throwable().getStackTrace();
×
57
        return ste[ste.length - 1].getClassName().startsWith("org.eclipse.jdt");
×
58
    }
59

60
    /**
61
     * Returns the content type and encoding as a pair of strings. If no character set is specified, the second entry
62
     * will be null.
63
     *
64
     * @param header
65
     *            the header to parse
66
     *
67
     * @return a string array with the content type and the content char set
68
     **/
69
    public static String[] parseContentTypeHeader(String header) {
70
        String[] result = { "text/plain", null };
1✔
71
        if (header.trim().length() > 0) {
1✔
72
            StringTokenizer st = new StringTokenizer(header, ";= ");
1✔
73
            result[0] = st.nextToken();
1✔
74
            while (st.hasMoreTokens()) {
1✔
75
                String parameter = st.nextToken();
1✔
76
                if (st.hasMoreTokens()) {
1!
77
                    String value = stripQuotes(st.nextToken());
1✔
78
                    if (parameter.trim().equalsIgnoreCase("charset")) {
1✔
79
                        result[1] = value;
1✔
80
                    }
81
                }
82
            }
1✔
83
        }
84
        return result;
1✔
85
    }
86

87
    /**
88
     * strip the quotes from a value.
89
     *
90
     * @param value
91
     *            the value
92
     *
93
     * @return the stripped value
94
     */
95
    public static String stripQuotes(String value) {
96
        if (value.startsWith("'") || value.startsWith("\"")) {
1!
97
            value = value.substring(1);
1✔
98
        }
99
        if (value.endsWith("'") || value.endsWith("\"")) {
1!
100
            value = value.substring(0, value.length() - 1);
1✔
101
        }
102
        return value;
1✔
103
    }
104

105
    /**
106
     * Returns an interpretation of the specified URL-encoded string, using the ISO-8859-1 character set.
107
     *
108
     * @param byteString
109
     *            the byte string
110
     *
111
     * @return the string
112
     */
113
    public static String decode(String byteString) {
114
        return decode(byteString, "ISO-8859-1");
1✔
115
    }
116

117
    /**
118
     * Returns a string representation of a number, trimming off any trailing decimal zeros.
119
     *
120
     * @param number
121
     *            the number
122
     *
123
     * @return the string
124
     */
125
    static String trimmedValue(Number number) {
126
        String rawNumber = number.toString();
1✔
127
        if (rawNumber.indexOf('.') == -1) {
1!
128
            return rawNumber;
×
129
        }
130

131
        int index = rawNumber.length();
1✔
132
        while (rawNumber.charAt(index - 1) == '0') {
1✔
133
            index--;
1✔
134
        }
135
        if (rawNumber.charAt(index - 1) == '.') {
1!
136
            index--;
1✔
137
        }
138
        return rawNumber.substring(0, index);
1✔
139
    }
140

141
    /**
142
     * Decodes a URL safe string into its original form using the specified character set. Escaped characters are
143
     * converted back to their original representation. This method is copied from the <b>Jakarta Commons Codec</b>;
144
     * <code>org.apache.commons.codec.net.URLCodec</code> class.
145
     *
146
     * @param string
147
     *            URL safe string to convert into its original form
148
     * @param charset
149
     *            the charset
150
     *
151
     * @return original string
152
     *
153
     * @throws IllegalArgumentException
154
     *             thrown if URL decoding is unsuccessful,
155
     */
156
    public static String decode(String string, String charset) {
157
        if (string == null) {
1✔
158
            return null;
1✔
159
        }
160

161
        return new String(decodeUrl(string.getBytes(StandardCharsets.US_ASCII)), Charset.forName(charset));
1✔
162
    }
163

164
    /**
165
     * Decodes an array of URL safe 7-bit characters into an array of original bytes. Escaped characters are converted
166
     * back to their original representation. This method is copied from the <b>Jakarta Commons Codec</b>;
167
     * <code>org.apache.commons.codec.net.URLCodec</code> class.
168
     *
169
     * @param pArray
170
     *            array of URL safe characters
171
     *
172
     * @return array of original bytes
173
     *
174
     * @throws IllegalArgumentException
175
     *             the illegal argument exception
176
     */
177
    private static final byte[] decodeUrl(byte[] pArray) throws IllegalArgumentException {
178
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
1✔
179
        for (int i = 0; i < pArray.length; i++) {
1✔
180
            int b = pArray[i];
1✔
181
            if (b == '+') {
1✔
182
                buffer.write(' ');
1✔
183
            } else if (b != '%') {
1✔
184
                buffer.write(b);
1✔
185
            } else {
186
                try {
187
                    i++;
1✔
188
                    int u = Character.digit((char) pArray[i], 16);
1✔
189
                    i++;
1✔
190
                    int l = Character.digit((char) pArray[i], 16);
1✔
191
                    if (u == -1 || l == -1) {
1!
192
                        throw new IllegalArgumentException("Invalid URL encoding");
×
193
                    }
194
                    buffer.write((char) ((u << 4) + l));
1✔
195
                } catch (ArrayIndexOutOfBoundsException e) {
×
196
                    throw new IllegalArgumentException("Invalid URL encoding");
×
197
                }
1✔
198
            }
199
        }
200
        return buffer.toByteArray();
1✔
201
    }
202

203
    /**
204
     * parse an InputStream to a string (for debugging).
205
     *
206
     * @param is
207
     *            the is
208
     *
209
     * @return the string gotten from the inputString
210
     */
211
    public static String parseISToString(java.io.InputStream is) {
212
        java.io.DataInputStream din = new java.io.DataInputStream(is);
×
213
        StringBuilder sb = new StringBuilder();
×
214
        try {
215
            String line = null;
×
216
            while ((line = din.readLine()) != null) {
×
217
                sb.append(line + "\n");
×
218
            }
219
        } catch (Exception ex) {
×
220
            // TODO handle exception properly here
221
            ex.getMessage();
×
222
        } finally {
223
            try {
224
                is.close();
×
225
            } catch (Exception ex) {
×
226
            }
×
227
        }
228
        return sb.toString();
×
229
    }
230

231
    /**
232
     * parse the given inputSource with a new Parser.
233
     *
234
     * @param inputSource
235
     *            the input source
236
     *
237
     * @return the document parsed from the input Source
238
     *
239
     * @throws SAXException
240
     *             the SAX exception
241
     * @throws IOException
242
     *             Signals that an I/O exception has occurred.
243
     */
244
    public static Document parse(InputSource inputSource) throws SAXException, IOException {
245
        DocumentBuilder db = newParser();
1✔
246
        try {
247
            return db.parse(inputSource);
1✔
248
        } catch (java.net.MalformedURLException mue) {
×
249
            if (EXCEPTION_DEBUG) {
×
250
                String msg = mue.getMessage();
×
251
                if (msg != null) {
×
252
                    System.err.println(msg);
×
253
                }
254
                InputStream is = inputSource.getByteStream();
×
255
                is.reset();
×
256
                String content = parseISToString(is);
×
257
                System.err.println(content);
×
258
            }
259
            throw mue;
×
260
        }
261
    }
262

263
    /**
264
     * parse the given inputStream with a new Parser.
265
     *
266
     * @param inputStream
267
     *            the input stream
268
     *
269
     * @return the document parsed from the input Stream
270
     *
271
     * @throws SAXException
272
     *             the SAX exception
273
     * @throws IOException
274
     *             Signals that an I/O exception has occurred.
275
     */
276
    public static Document parse(InputStream inputStream) throws SAXException, IOException {
277
        DocumentBuilder db = newParser();
1✔
278
        try {
279
            return db.parse(inputStream);
1✔
280
        } catch (java.net.MalformedURLException mue) {
×
281
            if (EXCEPTION_DEBUG) {
×
282
                String msg = mue.getMessage();
×
283
                if (msg != null) {
×
284
                    System.err.println(msg);
×
285
                }
286
                InputStream is = inputStream;
×
287
                is.reset();
×
288
                String content = parseISToString(is);
×
289
                System.err.println(content);
×
290
            }
291
            throw mue;
×
292
        }
293
    }
294

295
    /**
296
     * creates a parser using JAXP API.
297
     *
298
     * @return the document builder
299
     *
300
     * @throws SAXException
301
     *             the SAX exception
302
     */
303
    public static DocumentBuilder newParser() throws SAXException {
304
        try {
305
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1✔
306
            DocumentBuilder builder = factory.newDocumentBuilder();
1✔
307
            builder.setEntityResolver(new HttpUnitUtils.ClasspathEntityResolver());
1✔
308
            return builder;
1✔
309
        } catch (ParserConfigurationException ex) {
×
310
            // redirect the new exception for code compatibility
311
            throw new SAXException(ex);
×
312
        }
313
    }
314

315
    /**
316
     * Returns a string array created by appending a string to an existing array. The existing array may be null.
317
     *
318
     * @param oldValue
319
     *            the old value
320
     * @param newValue
321
     *            the new value
322
     *
323
     * @return the string[]
324
     */
325
    static String[] withNewValue(String[] oldValue, String newValue) {
326
        String[] result;
327
        if (oldValue == null) {
1✔
328
            result = new String[] { newValue };
1✔
329
        } else {
330
            result = new String[oldValue.length + 1];
1✔
331
            System.arraycopy(oldValue, 0, result, 0, oldValue.length);
1✔
332
            result[oldValue.length] = newValue;
1✔
333
        }
334
        return result;
1✔
335
    }
336

337
    /**
338
     * Returns a string array created by appending an object to an existing array. The existing array may be null.
339
     *
340
     * @param oldValue
341
     *            the old value
342
     * @param newValue
343
     *            the new value
344
     *
345
     * @return the object[]
346
     */
347
    static Object[] withNewValue(Object[] oldValue, Object newValue) {
348
        Object[] result;
349
        if (oldValue == null) {
1!
350
            result = new Object[] { newValue };
1✔
351
        } else {
352
            result = new Object[oldValue.length + 1];
×
353
            System.arraycopy(oldValue, 0, result, 0, oldValue.length);
×
354
            result[oldValue.length] = newValue;
×
355
        }
356
        return result;
1✔
357
    }
358

359
    /**
360
     * Return true if the first string contains the second. Case sensitivity is according to the setting of
361
     * HttpUnitOptions.matchesIgnoreCase
362
     *
363
     * @param string
364
     *            the string
365
     * @param substring
366
     *            the substring
367
     *
368
     * @return true, if successful
369
     */
370
    static boolean contains(String string, String substring) {
371
        if (HttpUnitOptions.getMatchesIgnoreCase()) {
1!
372
            return string.toUpperCase().indexOf(substring.toUpperCase()) >= 0;
1✔
373
        }
374
        return string.indexOf(substring) >= 0;
×
375
    }
376

377
    /**
378
     * Return true if the first string starts with the second. Case sensitivity is according to the setting of
379
     * HttpUnitOptions.matchesIgnoreCase
380
     *
381
     * @param string
382
     *            the string
383
     * @param prefix
384
     *            the prefix
385
     *
386
     * @return true, if successful
387
     */
388
    static boolean hasPrefix(String string, String prefix) {
389
        if (HttpUnitOptions.getMatchesIgnoreCase()) {
1!
390
            return string.toUpperCase().startsWith(prefix.toUpperCase());
1✔
391
        }
392
        return string.startsWith(prefix);
×
393
    }
394

395
    /**
396
     * Return true if the first string equals the second. Case sensitivity is according to the setting of
397
     * HttpUnitOptions.matchesIgnoreCase
398
     *
399
     * @param string1
400
     *            the string 1
401
     * @param string2
402
     *            the string 2
403
     *
404
     * @return true, if successful
405
     */
406
    static boolean matches(String string1, String string2) {
407
        if (HttpUnitOptions.getMatchesIgnoreCase()) {
1!
408
            return string1.equalsIgnoreCase(string2);
1✔
409
        }
410
        return string1.equals(string2);
×
411
    }
412

413
    /**
414
     * check whether the URL is a java script url.
415
     *
416
     * @param urlString
417
     *            - the string to analyze
418
     *
419
     * @return - true if this is a javascript url
420
     */
421
    static boolean isJavaScriptURL(String urlString) {
422
        return urlString.toLowerCase(Locale.ENGLISH).startsWith("javascript:");
1✔
423
    }
424

425
    /**
426
     * Trims whitespace from the ends, and encodes from the middle. Spaces within quotes are respected.
427
     *
428
     * @param s
429
     *            the s
430
     *
431
     * @return the string
432
     */
433
    static String encodeSpaces(String s) {
434
        s = s.trim();
1✔
435
        // if no spaces we are fine
436
        if (s.indexOf(' ') < 0) {
1✔
437
            return s;
1✔
438
        }
439

440
        boolean inQuotes = false;
1✔
441
        StringBuilder sb = new StringBuilder();
1✔
442
        char[] chars = s.toCharArray();
1✔
443
        // loop over oper the chars of the URL
444
        for (char aChar : chars) {
1✔
445
            // toggle quotation and add quote
446
            if (aChar == '"' || aChar == '\'') {
1!
447
                inQuotes = !inQuotes;
×
448
                sb.append(aChar);
×
449
                // append everything in quotes and printable chars above space
450
            } else if (inQuotes || aChar > ' ') {
1!
451
                sb.append(aChar);
1✔
452
            } else if (aChar == ' ') {
1!
453
                // encode spaces
454
                // TODO check what to do about breaking testLinkUrlAcrossLineBreaks then ...
455
                // sb.append("%20");
456
            }
457
        }
458
        return sb.toString();
1✔
459
    }
460

461
    /**
462
     * Replace entities.
463
     *
464
     * @param string
465
     *            the string
466
     *
467
     * @return the string
468
     */
469
    static String replaceEntities(String string) {
470
        int i = 0;
1✔
471
        int ampIndex;
472
        while ((ampIndex = string.indexOf('&', i)) >= 0) {
1✔
473
            int semiColonIndex = string.indexOf(';', ampIndex + 1);
1✔
474
            if (semiColonIndex < 0) {
1✔
475
                break;
1✔
476
            }
477
            i = ampIndex + 1;
1✔
478

479
            String entityName = string.substring(ampIndex + 1, semiColonIndex);
1✔
480
            if (entityName.equalsIgnoreCase("amp")) {
1✔
481
                string = string.substring(0, ampIndex) + '&' + string.substring(semiColonIndex + 1);
1✔
482
            }
483

484
        }
1✔
485
        return string;
1✔
486
    }
487

488
    /**
489
     * Strips the fragment identifier (if any) from the Url.
490
     *
491
     * @param rawUrl
492
     *            the raw url
493
     *
494
     * @return the string
495
     */
496
    static String trimFragment(String rawUrl) {
497
        if (isJavaScriptURL(rawUrl)) {
1!
498
            return rawUrl;
×
499
        }
500
        final int hashIndex = rawUrl.indexOf('#');
1✔
501
        return hashIndex < 0 ? rawUrl : rawUrl.substring(0, hashIndex);
1✔
502
    }
503

504
    /**
505
     * The Class ClasspathEntityResolver.
506
     */
507
    static class ClasspathEntityResolver implements EntityResolver {
1✔
508

509
        @Override
510
        public InputSource resolveEntity(String publicID, String systemID) {
511
            if (systemID == null) {
×
512
                return null;
×
513
            }
514

515
            String localName = systemID;
×
516
            if (localName.indexOf("/") > 0) {
×
517
                localName = localName.substring(localName.lastIndexOf("/") + 1);
×
518
            }
519

520
            try {
521
                return new InputSource(getClass().getClassLoader().getResourceAsStream(localName));
×
522
            } catch (Exception e) {
×
523
                // proposed patch for bug report
524
                // [ 1264706 ] [patch] replace ClasspathEntityResolver
525
                // by fabrizio giustina
526
                // even to return this in all cases!
527
                // return new InputSource( new ByteArrayInputStream( new byte[0] ) );
528
                return null;
×
529
            }
530
        }
531
    }
532

533
    /**
534
     * Checks if is exception debug.
535
     *
536
     * @return the eXCEPTION_DEBUG
537
     */
538
    protected static boolean isEXCEPTION_DEBUG() {
539
        return EXCEPTION_DEBUG;
×
540
    }
541

542
    /**
543
     * Sets the EXCEPTIO N DEBUG.
544
     *
545
     * @param exception_debug
546
     *            the eXCEPTION_DEBUG to set
547
     *
548
     * @return true, if successful
549
     */
550
    public static boolean setEXCEPTION_DEBUG(boolean exception_debug) {
551
        boolean oldExceptionDebug = exception_debug;
1✔
552
        EXCEPTION_DEBUG = exception_debug;
1✔
553
        return oldExceptionDebug;
1✔
554
    }
555
}
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