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 : changed conditional boundary → TIMED_OUT
2. extractData : negated conditional → KILLED
        if (h.contentLength() >= 0L) {
38 2 1. extractData : negated conditional → TIMED_OUT
2. extractData : changed conditional boundary → TIMED_OUT
            if (h.contentLength() >= (long) 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 → TIMED_OUT
        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(long contentLength, String contentType, InputStream is) {
64
        // if the body is zero bytes long, just return
65 1 1. extractBodyFromInputStream : negated conditional → KILLED
        if (contentLength == 0L) {
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 → TIMED_OUT
                return new Body(Map.of(), inputStreamUtils.read((int) 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(long contentLength, String boundaryValue, InputStream inputStream) {
94
        List<Partition> partitions = new ArrayList<>();
95
96
        try {
97
            int countOfPartitions = 0;
98
            for (StreamingMultipartPartition p : getMultiPartIterable(inputStream, boundaryValue, contentLength)) {
99 1 1. parseMultipartForm : Changed increment from 1 to -1 → TIMED_OUT
                countOfPartitions += 1;
100 2 1. parseMultipartForm : changed conditional boundary → TIMED_OUT
2. parseMultipartForm : negated conditional → KILLED
                if (countOfPartitions >= MAX_BODY_KEYS_URL_ENCODED) {
101
                    throw new WebServerException("Error: body had excessive number of partitions (" + countOfPartitions + ").  Maximum allowed: " + MAX_BODY_KEYS_URL_ENCODED);
102
                }
103
                partitions.add(new Partition(p.getHeaders(), p.readAllBytes(), p.getContentDisposition()));
104
            }
105
106
107
        } catch (Exception ex) {
108
            throw new BadRequestException("Unable to parse the body as a multipart/form-data content-type", ex);
109
        }
110
111 1 1. parseMultipartForm : negated conditional → KILLED
        if (partitions.isEmpty()) {
112 1 1. parseMultipartForm : replaced return value with null for com/renomad/minum/web/BodyProcessor::parseMultipartForm → TIMED_OUT
            return new Body(Map.of(), new byte[0], List.of(), BodyType.UNRECOGNIZED);
113
        } else {
114 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);
115
        }
116
    }
117
118
    /**
119
     * Given the "content-type" header, determine the boundary value.  A typical
120
     * multipart content-type header might look like this: <pre>Content-Type: multipart/form-data; boundary=i_am_a_boundary</pre>
121
     */
122
    static String determineBoundaryValue(String contentType) {
123
        String boundaryKey = "boundary=";
124
        String boundaryValue;
125
        int indexOfBoundaryKey = contentType.indexOf(boundaryKey);
126 2 1. determineBoundaryValue : changed conditional boundary → TIMED_OUT
2. determineBoundaryValue : negated conditional → KILLED
        if (indexOfBoundaryKey >= 0) {
127
            // grab all the text after the key to obtain the boundary value
128 1 1. determineBoundaryValue : Replaced integer addition with subtraction → KILLED
            boundaryValue = contentType.substring(indexOfBoundaryKey + boundaryKey.length());
129
            // the index after the end of the boundary value, used to trim the string if necessary
130
            int indexEndOfBoundaryValue = 0;
131 2 1. determineBoundaryValue : negated conditional → KILLED
2. determineBoundaryValue : changed conditional boundary → KILLED
            for (int i = 0; i < boundaryValue.length(); i++) {
132
                char c = boundaryValue.charAt(i);
133 1 1. determineBoundaryValue : negated conditional → KILLED
                if (c == ' ') break;
134 1 1. determineBoundaryValue : negated conditional → KILLED
                if (c == ';') break;
135 1 1. determineBoundaryValue : Changed increment from 1 to -1 → KILLED
                indexEndOfBoundaryValue += 1;
136
            }
137 1 1. determineBoundaryValue : replaced return value with "" for com/renomad/minum/web/BodyProcessor::determineBoundaryValue → KILLED
            return boundaryValue.substring(0, indexEndOfBoundaryValue);
138
        }
139
        throw new BadRequestException("Did not find a valid boundary value for the multipart input. Header was: " + contentType);
140
    }
141
142
143
    /**
144
     * Parse data formatted by application/x-www-form-urlencoded
145
     * See <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST">...</a>
146
     * <p>
147
     * See here for the encoding: <a href="https://developer.mozilla.org/en-US/docs/Glossary/percent-encoding">...</a>
148
     * <p>
149
     * for example, {@code valuea=3&valueb=this+is+something}
150
     */
151
    Body parseUrlEncodedForm(InputStream is, long contentLength) {
152 1 1. parseUrlEncodedForm : negated conditional → KILLED
        if (contentLength == 0L) {
153 1 1. parseUrlEncodedForm : replaced return value with null for com/renomad/minum/web/BodyProcessor::parseUrlEncodedForm → KILLED
            return Body.EMPTY;
154
        }
155
        final var postedPairs = new HashMap<String, byte[]>();
156
157
        try {
158
            int countOfPartitions = 0;
159
            for (final var keyValue : getUrlEncodedDataIterable(is, contentLength)) {
160 1 1. parseUrlEncodedForm : Changed increment from 1 to -1 → TIMED_OUT
                countOfPartitions += 1;
161 2 1. parseUrlEncodedForm : changed conditional boundary → TIMED_OUT
2. parseUrlEncodedForm : negated conditional → KILLED
                if (countOfPartitions >= MAX_BODY_KEYS_URL_ENCODED) {
162
                    throw new WebServerException("Error: body had excessive number of partitions ("+countOfPartitions+").  Maximum allowed: " + MAX_BODY_KEYS_URL_ENCODED);
163
                }
164
                String value = new String(keyValue.getUedg().readAllBytes(), StandardCharsets.US_ASCII);
165
                String key = keyValue.getKey();
166
                final var decodedValue = StringUtils.decode(value);
167 1 1. parseUrlEncodedForm : negated conditional → KILLED
                final var convertedValue = decodedValue == null ? "".getBytes(StandardCharsets.UTF_8) : decodedValue.getBytes(StandardCharsets.UTF_8);
168
169
                final var result = postedPairs.put(key, convertedValue);
170
171 1 1. parseUrlEncodedForm : negated conditional → KILLED
                if (result != null) {
172
                    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);
173
                }
174
            }
175
        } catch (Exception ex) {
176
            throw new BadRequestException("Unable to parse the request body as a URL-encoded content type", ex);
177
        }
178
        // we return nothing for the raw bytes because the code for parsing the streaming data
179
        // doesn't begin with a fully-read byte array - it pulls data off the stream one byte
180
        // at a time.
181 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);
182
    }
183
184
    /**
185
     * A regex used to extract the name value from the headers in multipart/form
186
     * For example, in the following code, you can see that the name is "image_uploads"
187
     * <pre>
188
     * {@code
189
     * --i_am_a_boundary
190
     * Content-Type: text/plain
191
     * Content-Disposition: form-data; name="text1"
192
     *
193
     * I am a value that is text
194
     * --i_am_a_boundary
195
     * Content-Type: application/octet-stream
196
     * Content-Disposition: form-data; name="image_uploads"; filename="photo_preview.jpg"
197
     * }
198
     * </pre>
199
     */
200
    private static final Pattern multiformNameRegex = Pattern.compile("\\bname\\b=\"(?<namevalue>.*?)\"");
201
    private static final Pattern multiformFilenameRegex = Pattern.compile("\\bfilename\\b=\"(?<namevalue>.*?)\"");
202
203
    @Override
204
    public Iterable<UrlEncodedKeyValue> getUrlEncodedDataIterable(InputStream inputStream, long contentLength) {
205 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<>() {
206
207
            final CountBytesRead countBytesRead = new CountBytesRead();
208
209
            @Override
210
            public boolean hasNext() {
211 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;
212
            }
213
214
            @Override
215
            public UrlEncodedKeyValue next() {
216 1 1. next : negated conditional → KILLED
                if (!hasNext()) {
217
                    throw new NoSuchElementException();
218
                }
219
                String key = "";
220
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
221
                while(true) {
222
                    int result;
223
                    try {
224
                        result = inputStream.read();
225 1 1. next : removed call to com/renomad/minum/web/CountBytesRead::increment → KILLED
                        countBytesRead.increment();
226
                    } catch (IOException e) {
227
                        throw new WebServerException("Error in getUrlEncodedDataIterable.next", e);
228
                    }
229
                    // if this is true, the inputstream is closed
230 1 1. next : negated conditional → KILLED
                    if (result == -1) break;
231
                    byte myByte = (byte) result;
232
                    // if this is true, we're done with the key
233 1 1. next : negated conditional → KILLED
                    if (myByte == '=') {
234
                        // URL encoding is in ASCII only.
235
                        key = byteArrayOutputStream.toString(StandardCharsets.US_ASCII);
236
                        break;
237
                    } else {
238 2 1. next : changed conditional boundary → TIMED_OUT
2. next : negated conditional → KILLED
                        if (byteArrayOutputStream.size() >= MAX_KEY_SIZE_BYTES) {
239
                            throw new WebServerException("Maximum size for name attribute is " + MAX_KEY_SIZE_BYTES + " ascii characters");
240
                        }
241 1 1. next : removed call to java/io/ByteArrayOutputStream::write → KILLED
                        byteArrayOutputStream.write(myByte);
242
                    }
243
                }
244
245 1 1. next : negated conditional → KILLED
                if (key.isBlank()) {
246
                    throw new WebServerException("Unable to parse this body. no key found during parsing");
247 1 1. next : negated conditional → KILLED
                } else if (countBytesRead.getCount() == contentLength) {
248
                    // if the only thing sent was the key and there's no further data, return the key with a null input stream
249
                    // that will immediately return
250 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));
251
                } else {
252 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));
253
                }
254
            }
255
        };
256
    }
257
258
259
    @Override
260
    public Iterable<StreamingMultipartPartition> getMultiPartIterable(InputStream inputStream, String boundaryValue, long contentLength) {
261 2 1. lambda$getMultiPartIterable$3 : replaced return value with null for com/renomad/minum/web/BodyProcessor::lambda$getMultiPartIterable$3 → KILLED
2. getMultiPartIterable : replaced return value with Collections.emptyList for com/renomad/minum/web/BodyProcessor::getMultiPartIterable → KILLED
        return () -> new Iterator<>() {
262
263
            final CountBytesRead countBytesRead = new CountBytesRead();
264
            boolean hasReadFirstPartition = false;
265
266
            @Override
267
            public boolean hasNext() {
268
                // determining if we have more to read is a little tricky because we have a buffer
269
                // filled by reading ahead, looking for the boundary value
270 4 1. hasNext : changed conditional boundary → TIMED_OUT
2. hasNext : Replaced long subtraction with addition → KILLED
3. hasNext : negated conditional → KILLED
4. hasNext : replaced boolean return with true for com/renomad/minum/web/BodyProcessor$2::hasNext → KILLED
                return (contentLength - countBytesRead.getCount()) > (long) boundaryValue.length();
271
            }
272
273
            @Override
274
            public StreamingMultipartPartition next() {
275
                try {
276 1 1. next : negated conditional → KILLED
                    if (!hasNext()) {
277
                        throw new NoSuchElementException();
278
                    }
279
                    // confirm that the boundary value is as expected, as a sanity check,
280
                    // and avoid including the boundary value in the first set of headers
281 1 1. next : negated conditional → KILLED
                    if (!hasReadFirstPartition) {
282
                        String s;
283
                        s = inputStreamUtils.readLine(inputStream);
284 1 1. next : negated conditional → KILLED
                        if (s == null) {
285
                            throw new BadRequestException("Unexpectedly encountered end of stream while reading in BodyProcessor.next()");
286
                        }
287 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.getBytes(StandardCharsets.UTF_8).length + 2);
288
                        hasReadFirstPartition = true;
289
290 1 1. next : negated conditional → KILLED
                        if (!s.contains(boundaryValue)) {
291
                            throw new BadRequestException("Error: First line must contain the expected boundary value. Expected to find: " + boundaryValue + " in: " + s);
292
                        }
293
                    }
294
                    List<String> allHeaders;
295
                    allHeaders = Headers.getAllHeaders(inputStream, inputStreamUtils);
296 1 1. lambda$next$0 : replaced Integer return value with 0 for com/renomad/minum/web/BodyProcessor$2::lambda$next$0 → KILLED
                    int lengthOfHeaders = allHeaders.stream().map(x -> x.getBytes(StandardCharsets.UTF_8).length).reduce(0, Integer::sum);
297
                    // each line has a CR + LF (that's two bytes) and the headers end with a second pair of CR+LF.
298 2 1. next : Replaced integer addition with subtraction → TIMED_OUT
2. next : Replaced integer multiplication with division → TIMED_OUT
                    int extraCrLfs = (2 * allHeaders.size()) + 2;
299 2 1. next : Replaced integer addition with subtraction → KILLED
2. next : removed call to com/renomad/minum/web/CountBytesRead::incrementBy → KILLED
                    countBytesRead.incrementBy(lengthOfHeaders + extraCrLfs);
300
301
                    Headers headers = new Headers(allHeaders);
302
303
                    List<String> cds = headers.valueByKey("Content-Disposition");
304 1 1. next : negated conditional → KILLED
                    if (cds == null) {
305
                        throw new WebServerException("Error: no Content-Disposition header on partition in Multipart/form data");
306
                    }
307
                    String contentDisposition = String.join(";", cds);
308
309
                    Matcher nameMatcher = multiformNameRegex.matcher(contentDisposition);
310
                    Matcher filenameMatcher = multiformFilenameRegex.matcher(contentDisposition);
311
312
                    String name;
313 1 1. next : negated conditional → KILLED
                    if (nameMatcher.find()) {
314
                        name = nameMatcher.group("namevalue");
315
                    } else {
316
                        throw new WebServerException("Error: No name value set on multipart partition");
317
                    }
318
                    String filename = "";
319 1 1. next : negated conditional → KILLED
                    if (filenameMatcher.find()) {
320
                        filename = filenameMatcher.group("namevalue");
321
                    }
322
323
                    // at this point our inputstream pointer is at the beginning of the
324
                    // body data.  From here until the end it's pure data.
325
326 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);
327
                } catch (IOException ex) {
328
                    throw new WebServerException("Error in BodyProcessor.getMultiPartIterable.next", ex);
329
                }
330
            }
331
332
333
        };
334
    }
335
336
}

Mutations

37

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

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

38

1.1
Location : extractData
Killed by : none
negated conditional → TIMED_OUT

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

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 : none
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractData → TIMED_OUT

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 : none
replaced return value with null for com/renomad/minum/web/BodyProcessor::extractBodyFromInputStream → TIMED_OUT

99

1.1
Location : parseMultipartForm
Killed by : none
Changed increment from 1 to -1 → TIMED_OUT

100

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

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

111

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

112

1.1
Location : parseMultipartForm
Killed by : none
replaced return value with null for com/renomad/minum/web/BodyProcessor::parseMultipartForm → TIMED_OUT

114

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

126

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

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

128

1.1
Location : determineBoundaryValue
Killed by : com.renomad.minum.web.BoundaryBugTest
Replaced integer addition with subtraction → 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
negated conditional → KILLED

134

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

135

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

137

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

152

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

153

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

160

1.1
Location : parseUrlEncodedForm
Killed by : none
Changed increment from 1 to -1 → TIMED_OUT

161

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

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

167

1.1
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

181

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

205

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

211

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

216

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

225

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

230

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

233

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

238

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

241

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

245

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

247

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

250

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

252

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

261

1.1
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

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

270

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

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

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

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

276

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

281

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

284

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

287

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

290

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

296

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

298

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

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

299

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

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

304

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

313

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

319

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

326

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