• 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

78.57
/src/main/java/com/meterware/pseudoserver/ReceivedHttpMessage.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.pseudoserver;
21

22
import java.io.ByteArrayInputStream;
23
import java.io.ByteArrayOutputStream;
24
import java.io.IOException;
25
import java.io.InputStream;
26
import java.io.InputStreamReader;
27
import java.io.Reader;
28
import java.util.Enumeration;
29
import java.util.Hashtable;
30

31
/**
32
 * The Class ReceivedHttpMessage.
33
 */
34
abstract class ReceivedHttpMessage {
35

36
    /** The Constant CR. */
37
    private static final int CR = 13;
38

39
    /** The Constant LF. */
40
    private static final int LF = 10;
41

42
    /** The reader. */
43
    private Reader _reader;
44

45
    /** The headers. */
46
    private Hashtable _headers = new Hashtable<>();
1✔
47

48
    /** The request body. */
49
    private byte[] _requestBody;
50

51
    /**
52
     * Instantiates a new received http message.
53
     *
54
     * @param inputStream
55
     *            the input stream
56
     *
57
     * @throws IOException
58
     *             Signals that an I/O exception has occurred.
59
     */
60
    ReceivedHttpMessage(InputStream inputStream) throws IOException {
1✔
61
        interpretMessageHeader(readHeaderLine(inputStream));
1✔
62
        readHeaders(inputStream);
1✔
63
        readMessageBody(inputStream);
1✔
64
    }
1✔
65

66
    @Override
67
    public String toString() {
68
        StringBuilder sb = new StringBuilder(getClassName()).append("[ ");
1✔
69
        appendMessageHeader(sb);
1✔
70
        sb.append("\n");
1✔
71
        for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
1✔
72
            Object key = e.nextElement();
1✔
73
            sb.append("      ").append(key).append(": ").append(_headers.get(key)).append("\n");
1✔
74
        }
1✔
75
        sb.append("   body contains ").append(getBody().length).append(" byte(s)]");
1✔
76
        return sb.toString();
1✔
77
    }
78

79
    /**
80
     * Read header line.
81
     *
82
     * @param inputStream
83
     *            the input stream
84
     *
85
     * @return the string
86
     *
87
     * @throws IOException
88
     *             Signals that an I/O exception has occurred.
89
     */
90
    private String readHeaderLine(InputStream inputStream) throws IOException {
91
        return new String(readDelimitedChunk(inputStream));
1✔
92
    }
93

94
    /**
95
     * Read delimited chunk.
96
     *
97
     * @param inputStream
98
     *            the input stream
99
     *
100
     * @return the byte[]
101
     *
102
     * @throws IOException
103
     *             Signals that an I/O exception has occurred.
104
     */
105
    private byte[] readDelimitedChunk(InputStream inputStream) throws IOException {
106
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
1✔
107
        int b = inputStream.read();
1✔
108
        while (b != CR) {
1✔
109
            baos.write(b);
1✔
110
            b = inputStream.read();
1✔
111
        }
112

113
        b = inputStream.read();
1✔
114
        if (b != LF) {
1!
115
            throw new IOException("Bad header line termination: " + b);
×
116
        }
117
        return baos.toByteArray();
1✔
118
    }
119

120
    /**
121
     * Append contents.
122
     *
123
     * @param sb
124
     *            the sb
125
     */
126
    void appendContents(StringBuilder sb) {
127
        for (Enumeration e = _headers.keys(); e.hasMoreElements();) {
×
128
            Object key = e.nextElement();
×
129
            sb.append("      ").append(key).append(": ").append(_headers.get(key)).append("\n");
×
130
        }
×
131
        sb.append("   body contains ").append(getBody().length).append(" byte(s)");
×
132
    }
×
133

134
    /**
135
     * Gets the reader.
136
     *
137
     * @return the reader
138
     */
139
    Reader getReader() {
140
        return _reader;
×
141
    }
142

143
    /**
144
     * Gets the header.
145
     *
146
     * @param name
147
     *            the name
148
     *
149
     * @return the header
150
     */
151
    String getHeader(String name) {
152
        return (String) _headers.get(name.toUpperCase());
1✔
153
    }
154

155
    /**
156
     * Gets the body.
157
     *
158
     * @return the body
159
     */
160
    byte[] getBody() {
161
        return _requestBody;
1✔
162
    }
163

164
    /**
165
     * Read message body.
166
     *
167
     * @param inputStream
168
     *            the input stream
169
     *
170
     * @throws IOException
171
     *             Signals that an I/O exception has occurred.
172
     */
173
    private void readMessageBody(InputStream inputStream) throws IOException {
174
        if ("chunked".equalsIgnoreCase(getHeader("Transfer-Encoding"))) {
1✔
175
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
1✔
176
            while (getNextChunkLength(inputStream) > 0) {
1✔
177
                baos.write(readDelimitedChunk(inputStream));
1✔
178
            }
179
            flushChunkTrailer(inputStream);
1✔
180
            _requestBody = baos.toByteArray();
1✔
181
        } else {
1✔
182
            int totalExpected = getContentLength();
1✔
183
            ByteArrayOutputStream baos = new ByteArrayOutputStream(totalExpected);
1✔
184
            byte[] buffer = new byte[1024];
1✔
185
            int total = 0;
1✔
186
            int count = -1;
1✔
187
            while (total < totalExpected && (count = inputStream.read(buffer)) != -1) {
1!
188
                baos.write(buffer, 0, count);
1✔
189
                total += count;
1✔
190
            }
191
            baos.flush();
1✔
192
            _requestBody = baos.toByteArray();
1✔
193
        }
194
        _reader = new InputStreamReader(new ByteArrayInputStream(_requestBody));
1✔
195
    }
1✔
196

197
    /**
198
     * Flush chunk trailer.
199
     *
200
     * @param inputStream
201
     *            the input stream
202
     *
203
     * @throws IOException
204
     *             Signals that an I/O exception has occurred.
205
     */
206
    private void flushChunkTrailer(InputStream inputStream) throws IOException {
207
        byte[] line;
208
        do {
209
            line = readDelimitedChunk(inputStream);
1✔
210
        } while (line.length > 0);
1!
211
    }
1✔
212

213
    /**
214
     * Gets the next chunk length.
215
     *
216
     * @param inputStream
217
     *            the input stream
218
     *
219
     * @return the next chunk length
220
     *
221
     * @throws IOException
222
     *             Signals that an I/O exception has occurred.
223
     */
224
    private int getNextChunkLength(InputStream inputStream) throws IOException {
225
        try {
226
            return Integer.parseInt(readHeaderLine(inputStream), 16);
1✔
227
        } catch (NumberFormatException e) {
×
228
            throw new IOException("Unabled to read chunk length: " + e);
×
229
        }
230
    }
231

232
    /**
233
     * Gets the content length.
234
     *
235
     * @return the content length
236
     */
237
    private int getContentLength() {
238
        try {
239
            return Integer.parseInt(getHeader("Content-Length"));
1✔
240
        } catch (NumberFormatException e) {
1✔
241
            return 0;
1✔
242
        }
243
    }
244

245
    /**
246
     * Read headers.
247
     *
248
     * @param inputStream
249
     *            the input stream
250
     *
251
     * @throws IOException
252
     *             Signals that an I/O exception has occurred.
253
     */
254
    private void readHeaders(InputStream inputStream) throws IOException {
255
        String lastHeader = null;
1✔
256

257
        String header = readHeaderLine(inputStream);
1✔
258
        while (header.length() > 0) {
1✔
259
            if (header.charAt(0) <= ' ') {
1!
260
                if (lastHeader == null) {
×
261
                    continue;
×
262
                }
263
                _headers.put(lastHeader, _headers.get(lastHeader) + header.trim());
×
264
            } else {
265
                lastHeader = header.substring(0, header.indexOf(':')).toUpperCase();
1✔
266
                _headers.put(lastHeader, header.substring(header.indexOf(':') + 1).trim());
1✔
267
            }
268
            header = readHeaderLine(inputStream);
1✔
269
        }
270
    }
1✔
271

272
    /**
273
     * Gets the class name.
274
     *
275
     * @return the class name
276
     */
277
    private String getClassName() {
278
        return getClass().getName().substring(getClass().getName().lastIndexOf('.') + 1);
1✔
279
    }
280

281
    /**
282
     * Append message header.
283
     *
284
     * @param sb
285
     *            the sb
286
     */
287
    abstract void appendMessageHeader(StringBuilder sb);
288

289
    /**
290
     * Interpret message header.
291
     *
292
     * @param messageHeader
293
     *            the message header
294
     */
295
    abstract void interpretMessageHeader(String messageHeader);
296

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