• 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

83.9
/src/main/java/com/meterware/pseudoserver/WebResource.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.pseudoserver;
9

10
import com.meterware.httpunit.HttpUnitUtils;
11

12
import java.io.IOException;
13
import java.io.InputStream;
14
import java.io.OutputStream;
15
import java.net.HttpURLConnection;
16
import java.nio.charset.Charset;
17
import java.nio.charset.StandardCharsets;
18
import java.util.ArrayList;
19
import java.util.List;
20
import java.util.Locale;
21

22
/**
23
 * A resource to be returned from the simulated server.
24
 **/
25
public class WebResource {
26

27
    /** The Constant DEFAULT_CONTENT_TYPE. */
28
    static final String DEFAULT_CONTENT_TYPE = "text/html";
29

30
    /** The closes connection. */
31
    private boolean _closesConnection;
32

33
    /** The contents. */
34
    private byte[] _contents;
35

36
    /** The string. */
37
    private String _string;
38

39
    /** The stream. */
40
    private InputStream _stream;
41

42
    /** The response code. */
43
    private int _responseCode;
44

45
    /** The send character set. */
46
    private boolean _sendCharacterSet;
47

48
    /** The content type. */
49
    private String _contentType = DEFAULT_CONTENT_TYPE;
1✔
50

51
    /** The character set. */
52
    private String _characterSet = StandardCharsets.ISO_8859_1.name();
1✔
53

54
    /** The has explicit content type header. */
55
    private boolean _hasExplicitContentTypeHeader;
56

57
    /** The has explicit content length header. */
58
    private boolean _hasExplicitContentLengthHeader;
59

60
    /** The headers. */
61
    private List<String> _headers = new ArrayList<>();
1✔
62

63
    /** The is chunked. */
64
    private boolean _isChunked;
65

66
    /**
67
     * Instantiates a new web resource.
68
     *
69
     * @param contents
70
     *            the contents
71
     */
72
    public WebResource(String contents) {
73
        this(contents, DEFAULT_CONTENT_TYPE);
1✔
74
    }
1✔
75

76
    /**
77
     * Instantiates a new web resource.
78
     *
79
     * @param contents
80
     *            the contents
81
     * @param contentType
82
     *            the content type
83
     */
84
    public WebResource(String contents, String contentType) {
85
        this(contents, contentType, HttpURLConnection.HTTP_OK);
1✔
86
    }
1✔
87

88
    /**
89
     * Instantiates a new web resource.
90
     *
91
     * @param contents
92
     *            the contents
93
     * @param contentType
94
     *            the content type
95
     */
96
    public WebResource(byte[] contents, String contentType) {
97
        this(contents, contentType, HttpURLConnection.HTTP_OK);
1✔
98
    }
1✔
99

100
    /**
101
     * Adds the header.
102
     *
103
     * @param header
104
     *            the header
105
     */
106
    public void addHeader(String header) {
107
        _headers.add(header);
1✔
108
        if (header.toLowerCase(Locale.ENGLISH).startsWith("content-type")) {
1✔
109
            _hasExplicitContentTypeHeader = true;
1✔
110
        }
111
        if (header.toLowerCase(Locale.ENGLISH).startsWith("content-length")) {
1✔
112
            _hasExplicitContentLengthHeader = true;
1✔
113
        }
114
        if (header.trim().toLowerCase(Locale.ENGLISH).startsWith("connection")
1✔
115
                && header.trim().toLowerCase(Locale.ENGLISH).endsWith("close")) {
1!
116
            _closesConnection = true;
1✔
117
        }
118
        if (header.trim().toLowerCase(Locale.ENGLISH).startsWith("transfer-encoding")
1✔
119
                && header.trim().toLowerCase(Locale.ENGLISH).endsWith("chunked")) {
1!
120
            _isChunked = true;
1✔
121
        }
122
    }
1✔
123

124
    /**
125
     * Sets the character set.
126
     *
127
     * @param characterSet
128
     *            the new character set
129
     */
130
    public void setCharacterSet(String characterSet) {
131
        _characterSet = characterSet;
1✔
132
    }
1✔
133

134
    /**
135
     * Sets the send character set.
136
     *
137
     * @param enabled
138
     *            the new send character set
139
     */
140
    public void setSendCharacterSet(boolean enabled) {
141
        _sendCharacterSet = enabled;
1✔
142
    }
1✔
143

144
    /**
145
     * Suppress automatic length header.
146
     */
147
    public void suppressAutomaticLengthHeader() {
148
        _hasExplicitContentLengthHeader = true;
×
149
    }
×
150

151
    /**
152
     * Suppress automatic content type header.
153
     */
154
    public void suppressAutomaticContentTypeHeader() {
155
        _hasExplicitContentTypeHeader = true;
1✔
156
    }
1✔
157

158
    /**
159
     * Instantiates a new web resource.
160
     *
161
     * @param contents
162
     *            the contents
163
     * @param responseCode
164
     *            the response code
165
     */
166
    public WebResource(String contents, int responseCode) {
167
        this(contents, DEFAULT_CONTENT_TYPE, responseCode);
1✔
168
    }
1✔
169

170
    /**
171
     * Instantiates a new web resource.
172
     *
173
     * @param contents
174
     *            the contents
175
     * @param contentType
176
     *            the content type
177
     * @param responseCode
178
     *            the response code
179
     */
180
    public WebResource(String contents, String contentType, int responseCode) {
1✔
181
        _string = contents;
1✔
182
        _contentType = contentType;
1✔
183
        _responseCode = responseCode;
1✔
184
    }
1✔
185

186
    /**
187
     * Instantiates a new web resource.
188
     *
189
     * @param contents
190
     *            the contents
191
     * @param contentType
192
     *            the content type
193
     * @param responseCode
194
     *            the response code
195
     */
196
    public WebResource(byte[] contents, String contentType, int responseCode) {
1✔
197
        _contents = contents;
1✔
198
        _contentType = contentType;
1✔
199
        _responseCode = responseCode;
1✔
200
    }
1✔
201

202
    /**
203
     * Instantiates a new web resource.
204
     *
205
     * @param stream
206
     *            the stream
207
     * @param contentType
208
     *            the content type
209
     * @param responseCode
210
     *            the response code
211
     */
212
    public WebResource(InputStream stream, String contentType, int responseCode) {
1✔
213
        _stream = stream;
1✔
214
        _contentType = contentType;
1✔
215
        _responseCode = responseCode;
1✔
216
        addHeader("Connection: close");
1✔
217
    }
1✔
218

219
    /**
220
     * Gets the headers.
221
     *
222
     * @return the headers
223
     */
224
    String[] getHeaders() {
225
        final List<String> effectiveHeaders = new ArrayList<>(_headers);
1✔
226
        if (!_hasExplicitContentTypeHeader) {
1✔
227
            effectiveHeaders.add(getContentTypeHeader());
1✔
228
        }
229
        if (_stream == null && !_hasExplicitContentLengthHeader && !isChunked()) {
1✔
230
            effectiveHeaders.add(getContentLengthHeader());
1✔
231
        }
232
        return effectiveHeaders.toArray(new String[0]);
1✔
233
    }
234

235
    /**
236
     * Checks if is chunked.
237
     *
238
     * @return true, if is chunked
239
     */
240
    private boolean isChunked() {
241
        return _isChunked;
1✔
242
    }
243

244
    /**
245
     * Closes connection.
246
     *
247
     * @return true, if successful
248
     */
249
    boolean closesConnection() {
250
        return _closesConnection;
1✔
251
    }
252

253
    /**
254
     * Write to.
255
     *
256
     * @param outputStream
257
     *            the output stream
258
     *
259
     * @throws IOException
260
     *             Signals that an I/O exception has occurred.
261
     */
262
    void writeTo(OutputStream outputStream) throws IOException {
263
        if (_stream == null) {
1✔
264
            outputStream.write(getContentsAsBytes());
1✔
265
        } else {
266
            byte[] buffer = new byte[8 * 1024];
1✔
267
            int count = 0;
1✔
268
            do {
269
                outputStream.write(buffer, 0, count);
1✔
270
                count = _stream.read(buffer, 0, buffer.length);
1✔
271
            } while (count != -1);
1✔
272
        }
273
    }
1✔
274

275
    /**
276
     * To string.
277
     *
278
     * @param contentsAsBytes
279
     *            the contents as bytes
280
     *
281
     * @return the string
282
     */
283
    static String toString(byte[] contentsAsBytes) {
284
        StringBuilder sb = new StringBuilder();
×
285
        for (byte contentsAsByte : contentsAsBytes) {
×
286
            sb.append(Integer.toHexString(contentsAsByte)).append(' ');
×
287
        }
288
        return sb.toString();
×
289
    }
290

291
    /**
292
     * Gets the contents as bytes.
293
     *
294
     * @return the contents as bytes
295
     */
296
    private byte[] getContentsAsBytes() {
297
        if (_contents != null) {
1✔
298
            return _contents;
1✔
299
        }
300
        if (_string != null) {
1!
301
            return _string.getBytes(getCharacterSet());
1✔
302
        }
303
        throw new IllegalStateException("Cannot get bytes from stream");
×
304
    }
305

306
    /**
307
     * Gets the content type header.
308
     *
309
     * @return the content type header
310
     */
311
    private String getContentTypeHeader() {
312
        return "Content-Type: " + _contentType + getCharacterSetParameter();
1✔
313
    }
314

315
    /**
316
     * Gets the content length header.
317
     *
318
     * @return the content length header
319
     */
320
    private String getContentLengthHeader() {
321
        return "Content-Length: " + getContentsAsBytes().length;
1✔
322
    }
323

324
    /**
325
     * Gets the character set.
326
     *
327
     * @return the character set
328
     */
329
    Charset getCharacterSet() {
330
        return Charset.forName(HttpUnitUtils.stripQuotes(_characterSet));
1✔
331
    }
332

333
    /**
334
     * Gets the character set parameter.
335
     *
336
     * @return the character set parameter
337
     */
338
    String getCharacterSetParameter() {
339
        if (!_sendCharacterSet) {
1✔
340
            return "";
1✔
341
        }
342
        return "; charset=" + _characterSet;
1✔
343
    }
344

345
    /**
346
     * Gets the response code.
347
     *
348
     * @return the response code
349
     */
350
    int getResponseCode() {
351
        return _responseCode;
1✔
352
    }
353

354
    @Override
355
    public String toString() {
356
        return "WebResource [code=" + _responseCode + "; type = " + _contentType + "; charset = " + _characterSet
×
357
                + "]\n" + getContentsAsString();
×
358
    }
359

360
    /**
361
     * Gets the contents as string.
362
     *
363
     * @return the contents as string
364
     */
365
    private String getContentsAsString() {
366
        if (_string != null) {
×
367
            return _string;
×
368
        }
369
        return "<< hex bytes >>";
×
370
    }
371

372
}
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