BodyProcessor.java

1
package com.renomad.minum.web;
2
3
import com.renomad.minum.logging.ILogger;
4
import com.renomad.minum.security.ForbiddenUseException;
5
import com.renomad.minum.state.Constants;
6
import com.renomad.minum.state.Context;
7
import com.renomad.minum.utils.StringUtils;
8
9
import java.io.ByteArrayOutputStream;
10
import java.io.IOException;
11
import java.io.InputStream;
12
import java.nio.charset.StandardCharsets;
13
import java.util.*;
14
import java.util.regex.Matcher;
15
import java.util.regex.Pattern;
16
17
/**
18
 * This code is responsible for extracting the {@link Body} from
19
 * an HTTP request.
20
 */
21
final class BodyProcessor implements IBodyProcessor {
22
23
    private final ILogger logger;
24
    private final IInputStreamUtils inputStreamUtils;
25
    private final Constants constants;
26
27
    BodyProcessor(Context context) {
28
        this.constants = context.getConstants();
29
        this.logger = context.getLogger();
30
        this.inputStreamUtils = new InputStreamUtils(constants.maxReadLineSizeBytes);
31
    }
32
33
    @Override
34
    public Body extractData(InputStream is, Headers h) {
35
        final var contentType = h.contentType();
36
37 2 1. extractData : negated conditional → KILLED
2. extractData : changed conditional boundary → KILLED
        if (h.contentLength() >= 0) {
38 2 1. extractData : negated conditional → KILLED
2. extractData : changed conditional boundary → KILLED
            if (h.contentLength() >= constants.maxReadSizeBytes) {
39
                throw new ForbiddenUseException("It is disallowed to process a body with a length more than " + constants.maxReadSizeBytes + " bytes");
40
            }
41
        } else {
42
            // we don't process chunked transfer encodings.  just bail.
43
            List<String> transferEncodingHeaders = h.valueByKey("transfer-encoding");
44 1 1. extractData : negated conditional → KILLED
            if (List.of("chunked").equals(transferEncodingHeaders)) {
45
                throw new BadRequestException("client sent chunked transfer-encoding.  Minum does not automatically read bodies of this type.");
46
            }
47 1 1. extractData : replaced return value with null for com/renomad/minum/web/BodyProcessor::extractData → KILLED
            return Body.EMPTY;
48
        }
49
50 1 1. extractData : replaced return value with null for com/renomad/minum/web/BodyProcessor::extractData → KILLED
        return extractBodyFromInputStream(h.contentLength(), contentType, is);
51
    }
52
53
    /**
54
     * Handles the parsing of the body data for either form-urlencoded or
55
     * multipart/form-data
56
     *
57
     * @param contentType a mime value which must be either application/x-www-form-urlencoded
58
     *                    or multipart/form-data.  Anything else will cause a new Body to
59
     *                    be created with the body bytes, unparsed.  There are a number of
60
     *                    cases where this makes sense - if the user is sending us plain text,
61
     *                    html, json, or css, we want to simply accept the data and not try to parse it.
62
     */
63
    Body extractBodyFromInputStream(int contentLength, String contentType, InputStream is) {
64
        // if the body is zero bytes long, just return
65 1 1. extractBodyFromInputStream : negated conditional → KILLED
        if (contentLength == 0) {
66
            logger.logDebug(() -> "the length of the body was 0, returning an empty Body");
67 1 1. extractBodyFromInputStream : replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED
            return Body.EMPTY;
68
        }
69
70 1 1. extractBodyFromInputStream : negated conditional → KILLED
        if (contentType.contains("application/x-www-form-urlencoded")) {
71 1 1. extractBodyFromInputStream : replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED
            return parseUrlEncodedForm(is, contentLength);
72 1 1. extractBodyFromInputStream : negated conditional → KILLED
        } else if (contentType.contains("multipart/form-data")) {
73
            String boundaryValue = determineBoundaryValue(contentType);
74 1 1. extractBodyFromInputStream : replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED
            return parseMultipartForm(contentLength, boundaryValue, is);
75
        } else {
76
            logger.logDebug(() -> "did not recognize a key-value pattern content-type, returning the raw bytes for the body.  Content-Type was: " + contentType);
77
            // we can return the whole byte array here because we never read from it
78
            try {
79 1 1. extractBodyFromInputStream : replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED
                return new Body(Map.of(), inputStreamUtils.read(contentLength, is), List.of(), BodyType.UNRECOGNIZED);
80
            } catch (IOException e) {
81
                throw new WebServerException("Error in BodyProcessor.extractBodyFromInputStream", e);
82
            }
83
        }
84
    }
85
86
    /**
87
     * Parse multipart/form protocol.
88
     * @param contentLength the length of incoming data, found in the "content-length" header
89
     * @param boundaryValue the randomly-generated boundary value between the partitions.  Research
90
     *                      multipart/form data protocol for further information.
91
     * @param inputStream A stream of bytes coming from the socket.
92
     */
93
    private Body parseMultipartForm(int contentLength, String boundaryValue, InputStream inputStream) {
94
95 1 1. parseMultipartForm : negated conditional → KILLED
        if (boundaryValue.isBlank()) {
96
            throw new BadRequestException("The boundary value was blank for the multipart input");
97
        }
98
99
        List<Partition> partitions = new ArrayList<>();
100
101
        try {
102
            int countOfPartitions = 0;
103
            for (StreamingMultipartPartition p : getMultiPartIterable(inputStream, boundaryValue, contentLength)) {
104 1 1. parseMultipartForm : Changed increment from 1 to -1 → KILLED
                countOfPartitions += 1;
105 2 1. parseMultipartForm : changed conditional boundary → TIMED_OUT
2. parseMultipartForm : negated conditional → KILLED
                if (countOfPartitions >= MAX_BODY_KEYS_URL_ENCODED) {
106
                    throw new WebServerException("Error: body had excessive number of partitions (" + countOfPartitions + ").  Maximum allowed: " + MAX_BODY_KEYS_URL_ENCODED);
107
                }
108
                partitions.add(new Partition(p.getHeaders(), p.readAllBytes(), p.getContentDisposition()));
109
            }
110
111
112
        } catch (Exception ex) {
113
            throw new BadRequestException("Unable to parse the body as a multipart/form-data content-type", ex);
114
        }
115
116 1 1. parseMultipartForm : negated conditional → KILLED
        if (partitions.isEmpty()) {
117 1 1. parseMultipartForm : replaced return value with null for com/renomad/minum/web/BodyProcessor::parseMultipartForm → KILLED
            return new Body(Map.of(), new byte[0], List.of(), BodyType.UNRECOGNIZED);
118
        } else {
119 1 1. parseMultipartForm : replaced return value with null for com/renomad/minum/web/BodyProcessor::parseMultipartForm → KILLED
            return new Body(Map.of(), new byte[0], partitions, BodyType.MULTIPART);
120
        }
121
    }
122
123
    /**
124
     * Given the "content-type" header, determine the boundary value.  A typical
125
     * multipart content-type header might look like this: <pre>Content-Type: multipart/form-data; boundary=i_am_a_boundary</pre>
126
     */
127
    private static String determineBoundaryValue(String contentType) {
128
        String boundaryKey = "boundary=";
129
        String boundaryValue = "";
130
        int indexOfBoundaryKey = contentType.indexOf(boundaryKey);
131 2 1. determineBoundaryValue : negated conditional → KILLED
2. determineBoundaryValue : changed conditional boundary → KILLED
        if (indexOfBoundaryKey >= 0) {
132
            // grab all the text after the key to obtain the boundary value
133 1 1. determineBoundaryValue : Replaced integer addition with subtraction → KILLED
            boundaryValue = contentType.substring(indexOfBoundaryKey + boundaryKey.length());
134
            // the index after the end of the boundary value, used to trim the string if necessary
135
            int indexEndOfBoundaryValue = 0;
136
            for (char c : boundaryValue.toCharArray()) {
137 1 1. determineBoundaryValue : negated conditional → KILLED
                if (c == ' ') break;
138 1 1. determineBoundaryValue : negated conditional → KILLED
                if (c == ';') break;
139 1 1. determineBoundaryValue : Changed increment from 1 to -1 → KILLED
                indexEndOfBoundaryValue += 1;
140
            }
141
            boundaryValue = boundaryValue.substring(0, indexEndOfBoundaryValue);
142
        }
143 1 1. determineBoundaryValue : replaced return value with "" for com/renomad/minum/web/BodyProcessor::determineBoundaryValue → KILLED
        return boundaryValue;
144
    }
145
146
147
    /**
148
     * Parse data formatted by application/x-www-form-urlencoded
149
     * See <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST">...</a>
150
     * <p>
151
     * See here for the encoding: <a href="https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding">...</a>
152
     * <p>
153
     * for example, {@code valuea=3&valueb=this+is+something}
154
     */
155
    Body parseUrlEncodedForm(InputStream is, int contentLength) {
156 1 1. parseUrlEncodedForm : negated conditional → KILLED
        if (contentLength == 0) {
157 1 1. parseUrlEncodedForm : replaced return value with null for com/renomad/minum/web/BodyProcessor::parseUrlEncodedForm → KILLED
            return Body.EMPTY;
158
        }
159
        final var postedPairs = new HashMap<String, byte[]>();
160
161
        try {
162
            int countOfPartitions = 0;
163
            for (final var keyValue : getUrlEncodedDataIterable(is, contentLength)) {
164 1 1. parseUrlEncodedForm : Changed increment from 1 to -1 → KILLED
                countOfPartitions += 1;
165 2 1. parseUrlEncodedForm : changed conditional boundary → TIMED_OUT
2. parseUrlEncodedForm : negated conditional → KILLED
                if (countOfPartitions >= MAX_BODY_KEYS_URL_ENCODED) {
166
                    throw new WebServerException("Error: body had excessive number of partitions ("+countOfPartitions+").  Maximum allowed: " + MAX_BODY_KEYS_URL_ENCODED);
167
                }
168
                String value = new String(keyValue.getUedg().readAllBytes(), StandardCharsets.US_ASCII);
169
                String key = keyValue.getKey();
170
                final var decodedValue = StringUtils.decode(value);
171 1 1. parseUrlEncodedForm : negated conditional → KILLED
                final var convertedValue = decodedValue == null ? "".getBytes(StandardCharsets.UTF_8) : decodedValue.getBytes(StandardCharsets.UTF_8);
172
173
                final var result = postedPairs.put(key, convertedValue);
174
175 1 1. parseUrlEncodedForm : negated conditional → KILLED
                if (result != null) {
176
                    throw new BadRequestException("Unexpected: key (" +key + ") was duplicated in the post body - previous value was " + new String(result, StandardCharsets.US_ASCII) + " and was overwritten by " + decodedValue);
177
                }
178
            }
179
        } catch (Exception ex) {
180
            throw new BadRequestException("Unable to parse the request body as a URL-encoded content type", ex);
181
        }
182
        // we return nothing for the raw bytes because the code for parsing the streaming data
183
        // doesn't begin with a fully-read byte array - it pulls data off the stream one byte
184
        // at a time.
185 1 1. parseUrlEncodedForm : replaced return value with null for com/renomad/minum/web/BodyProcessor::parseUrlEncodedForm → KILLED
        return new Body(postedPairs, new byte[0], List.of(), BodyType.FORM_URL_ENCODED);
186
    }
187
188
    /**
189
     * A regex used to extract the name value from the headers in multipart/form
190
     * For example, in the following code, you can see that the name is "image_uploads"
191
     * <pre>
192
     * {@code
193
     * --i_am_a_boundary
194
     * Content-Type: text/plain
195
     * Content-Disposition: form-data; name="text1"
196
     *
197
     * I am a value that is text
198
     * --i_am_a_boundary
199
     * Content-Type: application/octet-stream
200
     * Content-Disposition: form-data; name="image_uploads"; filename="photo_preview.jpg"
201
     * }
202
     * </pre>
203
     */
204
    private static final Pattern multiformNameRegex = Pattern.compile("\\bname\\b=\"(?<namevalue>.*?)\"");
205
    private static final Pattern multiformFilenameRegex = Pattern.compile("\\bfilename\\b=\"(?<namevalue>.*?)\"");
206
207
    @Override
208
    public Iterable<UrlEncodedKeyValue> getUrlEncodedDataIterable(InputStream inputStream, long contentLength) {
209 2 1. getUrlEncodedDataIterable : replaced return value with Collections.emptyList for com/renomad/minum/web/BodyProcessor::getUrlEncodedDataIterable → KILLED
2. lambda$getUrlEncodedDataIterable$2 : replaced return value with null for com/renomad/minum/web/BodyProcessor::lambda$getUrlEncodedDataIterable$2 → KILLED
        return () -> new Iterator<>() {
210
211
            final CountBytesRead countBytesRead = new CountBytesRead();
212
213
            @Override
214
            public boolean hasNext() {
215 3 1. hasNext : negated conditional → KILLED
2. hasNext : replaced boolean return with true for com/renomad/minum/web/BodyProcessor$1::hasNext → KILLED
3. hasNext : changed conditional boundary → KILLED
                return countBytesRead.getCount() < contentLength;
216
            }
217
218
            @Override
219
            public UrlEncodedKeyValue next() {
220 1 1. next : negated conditional → KILLED
                if (!hasNext()) {
221
                    throw new NoSuchElementException();
222
                }
223
                String key = "";
224
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
225
                while(true) {
226
                    int result = 0;
227
                    try {
228
                        result = inputStream.read();
229 1 1. next : removed call to com/renomad/minum/web/CountBytesRead::increment → KILLED
                        countBytesRead.increment();
230
                    } catch (IOException e) {
231
                        throw new WebServerException("Error in getUrlEncodedDataIterable.next", e);
232
                    }
233
                    // if this is true, the inputstream is closed
234 1 1. next : negated conditional → KILLED
                    if (result == -1) break;
235
                    byte myByte = (byte) result;
236
                    // if this is true, we're done with the key
237 1 1. next : negated conditional → KILLED
                    if (myByte == '=') {
238
                        // URL encoding is in ASCII only.
239
                        key = byteArrayOutputStream.toString(StandardCharsets.US_ASCII);
240
                        break;
241
                    } else {
242 2 1. next : changed conditional boundary → TIMED_OUT
2. next : negated conditional → KILLED
                        if (byteArrayOutputStream.size() >= MAX_KEY_SIZE_BYTES) {
243
                            throw new WebServerException("Maximum size for name attribute is " + MAX_KEY_SIZE_BYTES + " ascii characters");
244
                        }
245 1 1. next : removed call to java/io/ByteArrayOutputStream::write → KILLED
                        byteArrayOutputStream.write(myByte);
246
                    }
247
                }
248
249 1 1. next : negated conditional → KILLED
                if (key.isBlank()) {
250
                    throw new WebServerException("Unable to parse this body. no key found during parsing");
251 1 1. next : negated conditional → KILLED
                } else if (countBytesRead.getCount() == contentLength) {
252
                    // if the only thing sent was the key and there's no further data, return the key with a null input stream
253
                    // that will immediately return
254 1 1. next : replaced return value with null for com/renomad/minum/web/BodyProcessor$1::next → KILLED
                    return new UrlEncodedKeyValue(key, new UrlEncodedDataGetter(InputStream.nullInputStream(), countBytesRead, contentLength));
255
                } else {
256 1 1. next : replaced return value with null for com/renomad/minum/web/BodyProcessor$1::next → KILLED
                    return new UrlEncodedKeyValue(key, new UrlEncodedDataGetter(inputStream, countBytesRead, contentLength));
257
                }
258
            }
259
        };
260
    }
261
262
263
    @Override
264
    public Iterable<StreamingMultipartPartition> getMultiPartIterable(InputStream inputStream, String boundaryValue, int contentLength) {
265 2 1. getMultiPartIterable : replaced return value with Collections.emptyList for com/renomad/minum/web/BodyProcessor::getMultiPartIterable → KILLED
2. lambda$getMultiPartIterable$3 : replaced return value with null for com/renomad/minum/web/BodyProcessor::lambda$getMultiPartIterable$3 → KILLED
        return () -> new Iterator<>() {
266
267
            final CountBytesRead countBytesRead = new CountBytesRead();
268
            boolean hasReadFirstPartition = false;
269
270
            @Override
271
            public boolean hasNext() {
272
                // determining if we have more to read is a little tricky because we have a buffer
273
                // filled by reading ahead, looking for the boundary value
274 4 1. hasNext : changed conditional boundary → TIMED_OUT
2. hasNext : Replaced integer subtraction with addition → KILLED
3. hasNext : replaced boolean return with true for com/renomad/minum/web/BodyProcessor$2::hasNext → KILLED
4. hasNext : negated conditional → KILLED
                return (contentLength - countBytesRead.getCount()) > boundaryValue.length();
275
            }
276
277
            @Override
278
            public StreamingMultipartPartition next() {
279
                try {
280 1 1. next : negated conditional → KILLED
                    if (!hasNext()) {
281
                        throw new NoSuchElementException();
282
                    }
283
                    // confirm that the boundary value is as expected, as a sanity check,
284
                    // and avoid including the boundary value in the first set of headers
285 1 1. next : negated conditional → KILLED
                    if (!hasReadFirstPartition) {
286
                        String s;
287
                        s = inputStreamUtils.readLine(inputStream);
288 1 1. next : negated conditional → KILLED
                        if (s == null) {
289
                            throw new BadRequestException("Unexpectedly encountered end of stream while reading in BodyProcessor.next()");
290
                        }
291 2 1. next : Replaced integer addition with subtraction → TIMED_OUT
2. next : removed call to com/renomad/minum/web/CountBytesRead::incrementBy → KILLED
                        countBytesRead.incrementBy(s.length() + 2);
292
                        hasReadFirstPartition = true;
293
294 1 1. next : negated conditional → KILLED
                        if (!s.contains(boundaryValue)) {
295
                            throw new BadRequestException("Error: First line must contain the expected boundary value. Expected to find: " + boundaryValue + " in: " + s);
296
                        }
297
                    }
298
                    List<String> allHeaders = null;
299
                    allHeaders = Headers.getAllHeaders(inputStream, inputStreamUtils);
300
                    int lengthOfHeaders = allHeaders.stream().map(String::length).reduce(0, Integer::sum);
301
                    // each line has a CR + LF (that's two bytes) and the headers end with a second pair of CR+LF.
302 2 1. next : Replaced integer multiplication with division → TIMED_OUT
2. next : Replaced integer addition with subtraction → TIMED_OUT
                    int extraCrLfs = (2 * allHeaders.size()) + 2;
303 2 1. next : removed call to com/renomad/minum/web/CountBytesRead::incrementBy → KILLED
2. next : Replaced integer addition with subtraction → KILLED
                    countBytesRead.incrementBy(lengthOfHeaders + extraCrLfs);
304
305
                    Headers headers = new Headers(allHeaders);
306
307
                    List<String> cds = headers.valueByKey("Content-Disposition");
308 1 1. next : negated conditional → KILLED
                    if (cds == null) {
309
                        throw new WebServerException("Error: no Content-Disposition header on partition in Multipart/form data");
310
                    }
311
                    String contentDisposition = String.join(";", cds);
312
313
                    Matcher nameMatcher = multiformNameRegex.matcher(contentDisposition);
314
                    Matcher filenameMatcher = multiformFilenameRegex.matcher(contentDisposition);
315
316
                    String name = "";
317 1 1. next : negated conditional → KILLED
                    if (nameMatcher.find()) {
318
                        name = nameMatcher.group("namevalue");
319
                    } else {
320
                        throw new WebServerException("Error: No name value set on multipart partition");
321
                    }
322
                    String filename = "";
323 1 1. next : negated conditional → KILLED
                    if (filenameMatcher.find()) {
324
                        filename = filenameMatcher.group("namevalue");
325
                    }
326
327
                    // at this point our inputstream pointer is at the beginning of the
328
                    // body data.  From here until the end it's pure data.
329
330 1 1. next : replaced return value with null for com/renomad/minum/web/BodyProcessor$2::next → KILLED
                    return new StreamingMultipartPartition(headers, inputStream, new ContentDisposition(name, filename), boundaryValue, countBytesRead, contentLength);
331
                } catch (IOException ex) {
332
                    throw new WebServerException("Error in BodyProcessor.getMultiPartIterable.next", ex);
333
                }
334
            }
335
336
337
        };
338
    }
339
340
}

Mutations

37

1.1
Location : extractData
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

2.2
Location : extractData
Killed by : com.renomad.minum.web.BodyProcessorTests
changed conditional boundary → KILLED

38

1.1
Location : extractData
Killed by : com.renomad.minum.web.RequestTests
negated conditional → KILLED

2.2
Location : extractData
Killed by : com.renomad.minum.FunctionalTests.test_EdgeCase_Response_MultiCookies(com.renomad.minum.FunctionalTests)
changed conditional boundary → KILLED

44

1.1
Location : extractData
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

47

1.1
Location : extractData
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractData → KILLED

50

1.1
Location : extractData
Killed by : com.renomad.minum.web.RequestTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractData → KILLED

65

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

67

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED

70

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

71

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED

72

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

74

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED

79

1.1
Location : extractBodyFromInputStream
Killed by : com.renomad.minum.web.RequestTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → KILLED

95

1.1
Location : parseMultipartForm
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

104

1.1
Location : parseMultipartForm
Killed by : com.renomad.minum.web.BodyProcessorTests
Changed increment from 1 to -1 → KILLED

105

1.1
Location : parseMultipartForm
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

2.2
Location : parseMultipartForm
Killed by : none
changed conditional boundary → TIMED_OUT

116

1.1
Location : parseMultipartForm
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

117

1.1
Location : parseMultipartForm
Killed by : com.renomad.minum.web.RequestTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::parseMultipartForm → KILLED

119

1.1
Location : parseMultipartForm
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced return value with null for com/renomad/minum/web/BodyProcessor::parseMultipartForm → KILLED

131

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

2.2
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BodyProcessorTests
changed conditional boundary → KILLED

133

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
Replaced integer addition with subtraction → KILLED

137

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

138

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

139

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
Changed increment from 1 to -1 → KILLED

143

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced return value with "" for com/renomad/minum/web/BodyProcessor::determineBoundaryValue → KILLED

156

1.1
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

157

1.1
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.WebTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::parseUrlEncodedForm → KILLED

164

1.1
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.RequestTests
Changed increment from 1 to -1 → KILLED

165

1.1
Location : parseUrlEncodedForm
Killed by : none
changed conditional boundary → TIMED_OUT

2.2
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

171

1.1
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

175

1.1
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

185

1.1
Location : parseUrlEncodedForm
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::parseUrlEncodedForm → KILLED

209

1.1
Location : getUrlEncodedDataIterable
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with Collections.emptyList for com/renomad/minum/web/BodyProcessor::getUrlEncodedDataIterable → KILLED

2.2
Location : lambda$getUrlEncodedDataIterable$2
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with null for com/renomad/minum/web/BodyProcessor::lambda$getUrlEncodedDataIterable$2 → KILLED

215

1.1
Location : hasNext
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

2.2
Location : hasNext
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced boolean return with true for com/renomad/minum/web/BodyProcessor$1::hasNext → KILLED

3.3
Location : hasNext
Killed by : com.renomad.minum.web.BodyProcessorTests
changed conditional boundary → KILLED

220

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

229

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
removed call to com/renomad/minum/web/CountBytesRead::increment → KILLED

234

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

237

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

242

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

2.2
Location : next
Killed by : none
changed conditional boundary → TIMED_OUT

245

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
removed call to java/io/ByteArrayOutputStream::write → KILLED

249

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

251

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
negated conditional → KILLED

254

1.1
Location : next
Killed by : com.renomad.minum.web.WebTests
replaced return value with null for com/renomad/minum/web/BodyProcessor$1::next → KILLED

256

1.1
Location : next
Killed by : com.renomad.minum.web.BodyProcessorTests
replaced return value with null for com/renomad/minum/web/BodyProcessor$1::next → KILLED

265

1.1
Location : getMultiPartIterable
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced return value with Collections.emptyList for com/renomad/minum/web/BodyProcessor::getMultiPartIterable → KILLED

2.2
Location : lambda$getMultiPartIterable$3
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced return value with null for com/renomad/minum/web/BodyProcessor::lambda$getMultiPartIterable$3 → KILLED

274

1.1
Location : hasNext
Killed by : com.renomad.minum.web.BoundaryBugTest
Replaced integer subtraction with addition → KILLED

2.2
Location : hasNext
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced boolean return with true for com/renomad/minum/web/BodyProcessor$2::hasNext → KILLED

3.3
Location : hasNext
Killed by : none
changed conditional boundary → TIMED_OUT

4.4
Location : hasNext
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

280

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

285

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

288

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

291

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
removed call to com/renomad/minum/web/CountBytesRead::incrementBy → KILLED

2.2
Location : next
Killed by : none
Replaced integer addition with subtraction → TIMED_OUT

294

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

302

1.1
Location : next
Killed by : none
Replaced integer multiplication with division → TIMED_OUT

2.2
Location : next
Killed by : none
Replaced integer addition with subtraction → TIMED_OUT

303

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
removed call to com/renomad/minum/web/CountBytesRead::incrementBy → KILLED

2.2
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
Replaced integer addition with subtraction → KILLED

308

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

317

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

323

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
negated conditional → KILLED

330

1.1
Location : next
Killed by : com.renomad.minum.web.BoundaryBugTest
replaced return value with null for com/renomad/minum/web/BodyProcessor$2::next → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0