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

hazendaz / httpunit / 656

06 Dec 2025 09:11PM UTC coverage: 80.452% (+0.02%) from 80.435%
656

push

github

hazendaz
[maven-release-plugin] prepare for next development iteration

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8245 of 10137 relevant lines covered (81.34%)

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
 * 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 java.io.ByteArrayOutputStream;
23
import java.io.IOException;
24
import java.io.InputStream;
25
import java.nio.charset.Charset;
26
import java.nio.charset.StandardCharsets;
27
import java.util.Locale;
28
import java.util.StringTokenizer;
29

30
import javax.xml.parsers.DocumentBuilder;
31
import javax.xml.parsers.DocumentBuilderFactory;
32
import javax.xml.parsers.ParserConfigurationException;
33

34
import org.w3c.dom.Document;
35
import org.xml.sax.EntityResolver;
36
import org.xml.sax.InputSource;
37
import org.xml.sax.SAXException;
38

39
/**
40
 * Utility code shared by httpunit and servletunit.
41
 **/
42
public class HttpUnitUtils {
×
43

44
    /** The Constant DEFAULT_TEXT_BUFFER_SIZE. */
45
    public static final int DEFAULT_TEXT_BUFFER_SIZE = 2048;
46

47
    /** set to true to debug Exception handling. */
48
    private static boolean EXCEPTION_DEBUG = true;
1✔
49

50
    /**
51
     * handle Exceptions and thowables.
52
     *
53
     * @param th
54
     *            the th
55
     */
56
    public static void handleException(Throwable th) {
57
        if (EXCEPTION_DEBUG) {
1!
58
            th.printStackTrace();
×
59
        }
60
    }
1✔
61

62
    /**
63
     * are we running in the Eclipse IDE?.
64
     *
65
     * @return whether we are running in the Eclipse environment
66
     */
67
    public static boolean isEclipse() {
68
        StackTraceElement[] ste = new Throwable().getStackTrace();
×
69
        return ste[ste.length - 1].getClassName().startsWith("org.eclipse.jdt");
×
70
    }
71

72
    /**
73
     * Returns the content type and encoding as a pair of strings. If no character set is specified, the second entry
74
     * will be null.
75
     *
76
     * @param header
77
     *            the header to parse
78
     *
79
     * @return a string array with the content type and the content char set
80
     **/
81
    public static String[] parseContentTypeHeader(String header) {
82
        String[] result = { "text/plain", null };
1✔
83
        if (header.trim().length() > 0) {
1✔
84
            StringTokenizer st = new StringTokenizer(header, ";= ");
1✔
85
            result[0] = st.nextToken();
1✔
86
            while (st.hasMoreTokens()) {
1✔
87
                String parameter = st.nextToken();
1✔
88
                if (st.hasMoreTokens()) {
1!
89
                    String value = stripQuotes(st.nextToken());
1✔
90
                    if (parameter.trim().equalsIgnoreCase("charset")) {
1✔
91
                        result[1] = value;
1✔
92
                    }
93
                }
94
            }
1✔
95
        }
96
        return result;
1✔
97
    }
98

99
    /**
100
     * strip the quotes from a value.
101
     *
102
     * @param value
103
     *            the value
104
     *
105
     * @return the stripped value
106
     */
107
    public static String stripQuotes(String value) {
108
        if (value.startsWith("'") || value.startsWith("\"")) {
1!
109
            value = value.substring(1);
1✔
110
        }
111
        if (value.endsWith("'") || value.endsWith("\"")) {
1!
112
            value = value.substring(0, value.length() - 1);
1✔
113
        }
114
        return value;
1✔
115
    }
116

117
    /**
118
     * Returns an interpretation of the specified URL-encoded string, using the ISO-8859-1 character set.
119
     *
120
     * @param byteString
121
     *            the byte string
122
     *
123
     * @return the string
124
     */
125
    public static String decode(String byteString) {
126
        return decode(byteString, "ISO-8859-1");
1✔
127
    }
128

129
    /**
130
     * Returns a string representation of a number, trimming off any trailing decimal zeros.
131
     *
132
     * @param number
133
     *            the number
134
     *
135
     * @return the string
136
     */
137
    static String trimmedValue(Number number) {
138
        String rawNumber = number.toString();
1✔
139
        if (rawNumber.indexOf('.') == -1) {
1!
140
            return rawNumber;
×
141
        }
142

143
        int index = rawNumber.length();
1✔
144
        while (rawNumber.charAt(index - 1) == '0') {
1✔
145
            index--;
1✔
146
        }
147
        if (rawNumber.charAt(index - 1) == '.') {
1!
148
            index--;
1✔
149
        }
150
        return rawNumber.substring(0, index);
1✔
151
    }
152

153
    /**
154
     * Decodes a URL safe string into its original form using the specified character set. Escaped characters are
155
     * converted back to their original representation. This method is copied from the <b>Jakarta Commons Codec</b>;
156
     * <code>org.apache.commons.codec.net.URLCodec</code> class.
157
     *
158
     * @param string
159
     *            URL safe string to convert into its original form
160
     * @param charset
161
     *            the charset
162
     *
163
     * @return original string
164
     *
165
     * @throws IllegalArgumentException
166
     *             thrown if URL decoding is unsuccessful,
167
     */
168
    public static String decode(String string, String charset) {
169
        if (string == null) {
1✔
170
            return null;
1✔
171
        }
172

173
        return new String(decodeUrl(string.getBytes(StandardCharsets.US_ASCII)), Charset.forName(charset));
1✔
174
    }
175

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

215
    /**
216
     * parse an InputStream to a string (for debugging).
217
     *
218
     * @param is
219
     *            the is
220
     *
221
     * @return the string gotten from the inputString
222
     */
223
    public static String parseISToString(java.io.InputStream is) {
224
        java.io.DataInputStream din = new java.io.DataInputStream(is);
×
225
        StringBuilder sb = new StringBuilder();
×
226
        try {
227
            String line = null;
×
228
            while ((line = din.readLine()) != null) {
×
229
                sb.append(line + "\n");
×
230
            }
231
        } catch (Exception ex) {
×
232
            // TODO handle exception properly here
233
            ex.getMessage();
×
234
        } finally {
235
            try {
236
                is.close();
×
237
            } catch (Exception ex) {
×
238
            }
×
239
        }
240
        return sb.toString();
×
241
    }
242

243
    /**
244
     * parse the given inputSource with a new Parser.
245
     *
246
     * @param inputSource
247
     *            the input source
248
     *
249
     * @return the document parsed from the input Source
250
     *
251
     * @throws SAXException
252
     *             the SAX exception
253
     * @throws IOException
254
     *             Signals that an I/O exception has occurred.
255
     */
256
    public static Document parse(InputSource inputSource) throws SAXException, IOException {
257
        DocumentBuilder db = newParser();
1✔
258
        try {
259
            return db.parse(inputSource);
1✔
260
        } catch (java.net.MalformedURLException mue) {
×
261
            if (EXCEPTION_DEBUG) {
×
262
                String msg = mue.getMessage();
×
263
                if (msg != null) {
×
264
                    System.err.println(msg);
×
265
                }
266
                InputStream is = inputSource.getByteStream();
×
267
                is.reset();
×
268
                String content = parseISToString(is);
×
269
                System.err.println(content);
×
270
            }
271
            throw mue;
×
272
        }
273
    }
274

275
    /**
276
     * parse the given inputStream with a new Parser.
277
     *
278
     * @param inputStream
279
     *            the input stream
280
     *
281
     * @return the document parsed from the input Stream
282
     *
283
     * @throws SAXException
284
     *             the SAX exception
285
     * @throws IOException
286
     *             Signals that an I/O exception has occurred.
287
     */
288
    public static Document parse(InputStream inputStream) throws SAXException, IOException {
289
        DocumentBuilder db = newParser();
1✔
290
        try {
291
            return db.parse(inputStream);
1✔
292
        } catch (java.net.MalformedURLException mue) {
×
293
            if (EXCEPTION_DEBUG) {
×
294
                String msg = mue.getMessage();
×
295
                if (msg != null) {
×
296
                    System.err.println(msg);
×
297
                }
298
                InputStream is = inputStream;
×
299
                is.reset();
×
300
                String content = parseISToString(is);
×
301
                System.err.println(content);
×
302
            }
303
            throw mue;
×
304
        }
305
    }
306

307
    /**
308
     * creates a parser using JAXP API.
309
     *
310
     * @return the document builder
311
     *
312
     * @throws SAXException
313
     *             the SAX exception
314
     */
315
    public static DocumentBuilder newParser() throws SAXException {
316
        try {
317
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
1✔
318
            DocumentBuilder builder = factory.newDocumentBuilder();
1✔
319
            builder.setEntityResolver(new HttpUnitUtils.ClasspathEntityResolver());
1✔
320
            return builder;
1✔
321
        } catch (ParserConfigurationException ex) {
×
322
            // redirect the new exception for code compatibility
323
            throw new SAXException(ex);
×
324
        }
325
    }
326

327
    /**
328
     * Returns a string array created by appending a string to an existing array. The existing array may be null.
329
     *
330
     * @param oldValue
331
     *            the old value
332
     * @param newValue
333
     *            the new value
334
     *
335
     * @return the string[]
336
     */
337
    static String[] withNewValue(String[] oldValue, String newValue) {
338
        String[] result;
339
        if (oldValue == null) {
1✔
340
            result = new String[] { newValue };
1✔
341
        } else {
342
            result = new String[oldValue.length + 1];
1✔
343
            System.arraycopy(oldValue, 0, result, 0, oldValue.length);
1✔
344
            result[oldValue.length] = newValue;
1✔
345
        }
346
        return result;
1✔
347
    }
348

349
    /**
350
     * Returns a string array created by appending an object to an existing array. The existing array may be null.
351
     *
352
     * @param oldValue
353
     *            the old value
354
     * @param newValue
355
     *            the new value
356
     *
357
     * @return the object[]
358
     */
359
    static Object[] withNewValue(Object[] oldValue, Object newValue) {
360
        Object[] result;
361
        if (oldValue == null) {
1!
362
            result = new Object[] { newValue };
1✔
363
        } else {
364
            result = new Object[oldValue.length + 1];
×
365
            System.arraycopy(oldValue, 0, result, 0, oldValue.length);
×
366
            result[oldValue.length] = newValue;
×
367
        }
368
        return result;
1✔
369
    }
370

371
    /**
372
     * Return true if the first string contains the second. Case sensitivity is according to the setting of
373
     * HttpUnitOptions.matchesIgnoreCase
374
     *
375
     * @param string
376
     *            the string
377
     * @param substring
378
     *            the substring
379
     *
380
     * @return true, if successful
381
     */
382
    static boolean contains(String string, String substring) {
383
        if (HttpUnitOptions.getMatchesIgnoreCase()) {
1!
384
            return string.toUpperCase().indexOf(substring.toUpperCase()) >= 0;
1✔
385
        }
386
        return string.indexOf(substring) >= 0;
×
387
    }
388

389
    /**
390
     * Return true if the first string starts with the second. Case sensitivity is according to the setting of
391
     * HttpUnitOptions.matchesIgnoreCase
392
     *
393
     * @param string
394
     *            the string
395
     * @param prefix
396
     *            the prefix
397
     *
398
     * @return true, if successful
399
     */
400
    static boolean hasPrefix(String string, String prefix) {
401
        if (HttpUnitOptions.getMatchesIgnoreCase()) {
1!
402
            return string.toUpperCase().startsWith(prefix.toUpperCase());
1✔
403
        }
404
        return string.startsWith(prefix);
×
405
    }
406

407
    /**
408
     * Return true if the first string equals the second. Case sensitivity is according to the setting of
409
     * HttpUnitOptions.matchesIgnoreCase
410
     *
411
     * @param string1
412
     *            the string 1
413
     * @param string2
414
     *            the string 2
415
     *
416
     * @return true, if successful
417
     */
418
    static boolean matches(String string1, String string2) {
419
        if (HttpUnitOptions.getMatchesIgnoreCase()) {
1!
420
            return string1.equalsIgnoreCase(string2);
1✔
421
        }
422
        return string1.equals(string2);
×
423
    }
424

425
    /**
426
     * check whether the URL is a java script url.
427
     *
428
     * @param urlString
429
     *            - the string to analyze
430
     *
431
     * @return - true if this is a javascript url
432
     */
433
    static boolean isJavaScriptURL(String urlString) {
434
        return urlString.toLowerCase(Locale.ENGLISH).startsWith("javascript:");
1✔
435
    }
436

437
    /**
438
     * Trims whitespace from the ends, and encodes from the middle. Spaces within quotes are respected.
439
     *
440
     * @param s
441
     *            the s
442
     *
443
     * @return the string
444
     */
445
    static String encodeSpaces(String s) {
446
        s = s.trim();
1✔
447
        // if no spaces we are fine
448
        if (s.indexOf(' ') < 0) {
1✔
449
            return s;
1✔
450
        }
451

452
        boolean inQuotes = false;
1✔
453
        StringBuilder sb = new StringBuilder();
1✔
454
        char[] chars = s.toCharArray();
1✔
455
        // loop over oper the chars of the URL
456
        for (char aChar : chars) {
1✔
457
            // toggle quotation and add quote
458
            if (aChar == '"' || aChar == '\'') {
1!
459
                inQuotes = !inQuotes;
×
460
                sb.append(aChar);
×
461
                // append everything in quotes and printable chars above space
462
            } else if (inQuotes || aChar > ' ') {
1!
463
                sb.append(aChar);
1✔
464
            } else if (aChar == ' ') {
1!
465
                // encode spaces
466
                // TODO check what to do about breaking testLinkUrlAcrossLineBreaks then ...
467
                // sb.append("%20");
468
            }
469
        }
470
        return sb.toString();
1✔
471
    }
472

473
    /**
474
     * Replace entities.
475
     *
476
     * @param string
477
     *            the string
478
     *
479
     * @return the string
480
     */
481
    static String replaceEntities(String string) {
482
        int i = 0;
1✔
483
        int ampIndex;
484
        while ((ampIndex = string.indexOf('&', i)) >= 0) {
1✔
485
            int semiColonIndex = string.indexOf(';', ampIndex + 1);
1✔
486
            if (semiColonIndex < 0) {
1✔
487
                break;
1✔
488
            }
489
            i = ampIndex + 1;
1✔
490

491
            String entityName = string.substring(ampIndex + 1, semiColonIndex);
1✔
492
            if (entityName.equalsIgnoreCase("amp")) {
1✔
493
                string = string.substring(0, ampIndex) + '&' + string.substring(semiColonIndex + 1);
1✔
494
            }
495

496
        }
1✔
497
        return string;
1✔
498
    }
499

500
    /**
501
     * Strips the fragment identifier (if any) from the Url.
502
     *
503
     * @param rawUrl
504
     *            the raw url
505
     *
506
     * @return the string
507
     */
508
    static String trimFragment(String rawUrl) {
509
        if (isJavaScriptURL(rawUrl)) {
1!
510
            return rawUrl;
×
511
        }
512
        final int hashIndex = rawUrl.indexOf('#');
1✔
513
        return hashIndex < 0 ? rawUrl : rawUrl.substring(0, hashIndex);
1✔
514
    }
515

516
    /**
517
     * The Class ClasspathEntityResolver.
518
     */
519
    static class ClasspathEntityResolver implements EntityResolver {
1✔
520

521
        @Override
522
        public InputSource resolveEntity(String publicID, String systemID) {
523
            if (systemID == null) {
×
524
                return null;
×
525
            }
526

527
            String localName = systemID;
×
528
            if (localName.indexOf("/") > 0) {
×
529
                localName = localName.substring(localName.lastIndexOf("/") + 1);
×
530
            }
531

532
            try {
533
                return new InputSource(getClass().getClassLoader().getResourceAsStream(localName));
×
534
            } catch (Exception e) {
×
535
                // proposed patch for bug report
536
                // [ 1264706 ] [patch] replace ClasspathEntityResolver
537
                // by fabrizio giustina
538
                // even to return this in all cases!
539
                // return new InputSource( new ByteArrayInputStream( new byte[0] ) );
540
                return null;
×
541
            }
542
        }
543
    }
544

545
    /**
546
     * Checks if is exception debug.
547
     *
548
     * @return the eXCEPTION_DEBUG
549
     */
550
    protected static boolean isEXCEPTION_DEBUG() {
551
        return EXCEPTION_DEBUG;
×
552
    }
553

554
    /**
555
     * Sets the EXCEPTIO N DEBUG.
556
     *
557
     * @param exception_debug
558
     *            the eXCEPTION_DEBUG to set
559
     *
560
     * @return true, if successful
561
     */
562
    public static boolean setEXCEPTION_DEBUG(boolean exception_debug) {
563
        boolean oldExceptionDebug = exception_debug;
1✔
564
        EXCEPTION_DEBUG = exception_debug;
1✔
565
        return oldExceptionDebug;
1✔
566
    }
567
}
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