Response.java

1
package com.renomad.minum.web;
2
3
import com.renomad.minum.utils.FileUtils;
4
5
import java.io.ByteArrayOutputStream;
6
import java.io.IOException;
7
import java.io.RandomAccessFile;
8
import java.net.URI;
9
import java.nio.ByteBuffer;
10
import java.nio.channels.FileChannel;
11
import java.nio.charset.StandardCharsets;
12
import java.nio.file.Files;
13
import java.nio.file.Path;
14
import java.util.Arrays;
15
import java.util.HashMap;
16
import java.util.Map;
17
import java.util.Objects;
18
import java.util.zip.GZIPOutputStream;
19
20
import static com.renomad.minum.web.StatusLine.StatusCode.CODE_200_OK;
21
import static com.renomad.minum.web.StatusLine.StatusCode.CODE_206_PARTIAL_CONTENT;
22
23
/**
24
 * Represents an HTTP response. This is what will get sent back to the
25
 * client (that is, to the browser).  There are a variety of overloads
26
 * of this record for different situations.  The overarching paradigm is
27
 * to provide you high flexibility.
28
 * <p>
29
 * A response message is sent by a server to a client as a reply to its former request message.
30
 * </p>
31
 * <h3>
32
 * Response syntax
33
 * </h3>
34
 * <p>
35
 * A server sends response messages to the client, which consist of:
36
 * </p>
37
 * <ul>
38
 * <li>
39
 * a status line, consisting of the protocol version, a space, the
40
 * response status code, another space, a possibly empty reason
41
 * phrase, a carriage return and a line feed, e.g.:
42
 * <pre>
43
 * HTTP/1.1 200 OK
44
 * </pre>
45
 * </li>
46
 *
47
 * <li>
48
 * zero or more response header fields, each consisting of the case-insensitive
49
 * field name, a colon, optional leading whitespace, the field value, an
50
 * optional trailing whitespace and ending with a carriage return and a line feed, e.g.:
51
 * <pre>
52
 * Content-Type: text/html
53
 * </pre>
54
 * </li>
55
 *
56
 * <li>
57
 * an empty line, consisting of a carriage return and a line feed;
58
 * </li>
59
 *
60
 * <li>
61
 * an optional message body.
62
 * </li>
63
 *</ul>
64
65
 */
66
public final class Response implements IResponse {
67
68
    private final StatusLine.StatusCode statusCode;
69
    private final Map<String, String> extraHeaders;
70
    private final byte[] body;
71
    private final ThrowingConsumer<ISocketWrapper> outputGenerator;
72
    private final long bodyLength;
73
74
    /**
75
     * If true, the body is text, and therefore would probably benefit
76
     * from being compressed before sending (presuming sufficiently large
77
     * to make it worth the time for compression)
78
     */
79
    private final boolean isBodyText;
80
81
    /**
82
     * This is the constructor that provides access to all fields.  It is not intended
83
     * to be used from outside this class.
84
     * @param extraHeaders extra headers we want to return with the response.
85
     * @param outputGenerator a {@link ThrowingConsumer} that will use a {@link ISocketWrapper} parameter
86
     *                        to send bytes on the wire back to the client.  See the static factory methods
87
     *                        such as {@link #buildResponse(StatusLine.StatusCode, Map, byte[])} for more details on this.
88
     * @param bodyLength this is used to set the content-length header for the response.  If this is
89
     *                   not provided, we set the header to "transfer-encoding: chunked", or in other words, streaming.
90
     */
91
    Response(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, byte[] body,
92
             ThrowingConsumer<ISocketWrapper> outputGenerator, long bodyLength, boolean isBodyText) {
93
        this.statusCode = statusCode;
94
        this.extraHeaders = new HashMap<>(extraHeaders);
95
        this.body = body;
96
        this.outputGenerator = outputGenerator;
97
        this.bodyLength = bodyLength;
98
        this.isBodyText = isBodyText;
99
    }
100
101
    /**
102
     * This factory method is intended for situations where the user wishes to stream data
103
     * but lacks the content length.  This is only for unusual situations where the developer
104
     * needs the extra control.  In most cases, other methods are more suitable.
105
     * @param extraHeaders any extra headers for the response, such as the content-type
106
     * @param outputGenerator a function that will be given a {@link ISocketWrapper}, providing the
107
     *                        ability to send bytes on the socket.
108
     */
109
    public static IResponse buildStreamingResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, ThrowingConsumer<ISocketWrapper> outputGenerator) {
110 1 1. buildStreamingResponse : replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED
        return new Response(statusCode, extraHeaders, null, outputGenerator, 0, false);
111
    }
112
113
    /**
114
     * Similar to {@link Response#buildStreamingResponse(StatusLine.StatusCode, Map, ThrowingConsumer)} but here we know
115
     * the body length, so that will be sent to the client as content-length.
116
     * @param extraHeaders any extra headers for the response, such as the content-type
117
     * @param outputGenerator a function that will be given a {@link ISocketWrapper}, providing the
118
     *                        ability to send bytes on the socket.
119
     * @param bodyLength the length, in bytes, of the data to be sent to the client
120
     */
121
    public static IResponse buildStreamingResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, ThrowingConsumer<ISocketWrapper> outputGenerator, long bodyLength) {
122 1 1. buildStreamingResponse : replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED
        return new Response(statusCode, extraHeaders, null, outputGenerator, bodyLength, false);
123
    }
124
125
    /**
126
     * A constructor for situations where the developer wishes to send a small (less than a megabyte) byte array
127
     * to the client.  If there is need to send something of larger size, choose one these
128
     * alternate constructors:
129
     * FileChannel - for sending a large file: {@link Response#buildLargeFileResponse(Map, String, Headers)}
130
     * Streaming - for more custom streaming control with a known body size: {@link Response#buildStreamingResponse(StatusLine.StatusCode, Map, ThrowingConsumer, long)}
131
     * Streaming - for more custom streaming control with body size unknown: {@link Response#buildStreamingResponse(StatusLine.StatusCode, Map, ThrowingConsumer)}
132
     *
133
     * @param extraHeaders any extra headers for the response, such as the content-type.
134
     */
135
    public static IResponse buildResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, byte[] body) {
136 2 1. lambda$buildResponse$0 : removed call to com/renomad/minum/web/Response::sendByteArrayResponse → TIMED_OUT
2. buildResponse : replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED
        return new Response(statusCode, extraHeaders, body, socketWrapper -> sendByteArrayResponse(socketWrapper, body), body.length, false);
137
    }
138
139
    /**
140
     * Build an ordinary response, with a known body
141
     * @param extraHeaders extra HTTP headers, like <pre>content-type: text/html</pre>
142
     */
143
    public static IResponse buildResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders, String body) {
144
        byte[] bytes = body.getBytes(StandardCharsets.UTF_8);
145 2 1. buildResponse : replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED
2. lambda$buildResponse$1 : removed call to com/renomad/minum/web/Response::sendByteArrayResponse → KILLED
        return new Response(statusCode, extraHeaders, bytes, socketWrapper -> sendByteArrayResponse(socketWrapper, bytes), bytes.length, true);
146
    }
147
148
    /**
149
     * This will send a large file to the client as a stream.
150
     * <em>Note that this does not check that the requested file is within a directory. If you expect the
151
     * path value to be untrusted - that is, set by a user - use {@link #buildLargeFileResponse(Map, String, String, Headers)}</em>
152
     * @param extraHeaders extra headers you would like in the response, as key-value pairs in a map - for
153
     *                     example "Content-Type: video/mp4" or "Content-Type: application/zip"
154
     * @param filePath the string value of the path to this file. <em>Caution! if this is set by the user.  See notes above</em>
155
     * @param requestHeaders these are the headers from the request, which is needed here to set proper
156
     *                       response headers in some cases.
157
     */
158
    public static IResponse buildLargeFileResponse(Map<String, String> extraHeaders, String filePath, Headers requestHeaders) throws IOException {
159
        Map<String, String> adjustedHeaders = new HashMap<>(extraHeaders);
160
        long fileSize = Files.size(Path.of(filePath));
161
        var range = new Range(requestHeaders, fileSize);
162
        StatusLine.StatusCode responseCode = CODE_200_OK;
163
        long length = fileSize;
164 1 1. buildLargeFileResponse : negated conditional → TIMED_OUT
        if (range.hasRangeHeader()) {
165
            long offset = range.getOffset();
166
            length = range.getLength();
167 2 1. buildLargeFileResponse : Replaced long addition with subtraction → SURVIVED
2. buildLargeFileResponse : Replaced long subtraction with addition → KILLED
            var lastIndex = (offset + length) - 1;
168
            adjustedHeaders.put("Content-Range", String.format("bytes %d-%d/%d", offset, lastIndex, fileSize));
169
            responseCode = CODE_206_PARTIAL_CONTENT;
170
        }
171
172
        ThrowingConsumer<ISocketWrapper> outputGenerator = socketWrapper -> {
173
            try (RandomAccessFile reader = new RandomAccessFile(filePath, "r")) {
174 1 1. lambda$buildLargeFileResponse$2 : removed call to java/io/RandomAccessFile::seek → KILLED
                reader.seek(range.getOffset());
175
                var fileChannel = reader.getChannel();
176 1 1. lambda$buildLargeFileResponse$2 : removed call to com/renomad/minum/web/Response::sendFileChannelResponse → TIMED_OUT
                sendFileChannelResponse(socketWrapper, fileChannel, range.getLength());
177
            }
178
        };
179
180 1 1. buildLargeFileResponse : replaced return value with null for com/renomad/minum/web/Response::buildLargeFileResponse → KILLED
        return new Response(responseCode, adjustedHeaders, null, outputGenerator, length, false);
181
    }
182
183
    /**
184
     * This will send a large file to the client as a stream.
185
     * This is a safe version of the method, for when the file path is set by a user, meaning it is untrusted.
186
     * By setting a parentDirectory, the system will ensure that the requested path is within that directory, thus
187
     * disallowing path strings that could escape it, like prefixing with a slash.
188
     * @param extraHeaders extra headers you would like in the response, as key-value pairs in a map - for
189
     *                     example "Content-Type: video/mp4" or "Content-Type: application/zip"
190
     * @param filePath the string value of the path to this file. <em>Caution! if this is set by the user.  See notes above</em>
191
     * @param requestHeaders these are the headers from the request, which is needed here to set proper
192
     *                       response headers in some cases.
193
     * @param parentDirectory this value is presumed to be set by the developer, and thus isn't checked for safety. This
194
     *                        allows the developer to use directories anywhere in the system.
195
     */
196
    public static IResponse buildLargeFileResponse(Map<String, String> extraHeaders, String filePath, String parentDirectory, Headers requestHeaders) throws IOException {
197
        Path path = FileUtils.safeResolve(parentDirectory, filePath);
198 1 1. buildLargeFileResponse : replaced return value with null for com/renomad/minum/web/Response::buildLargeFileResponse → SURVIVED
        return buildLargeFileResponse(extraHeaders, path.toString(), requestHeaders);
199
    }
200
201
    /**
202
     * Build a {@link Response} with just a status code and headers, without a body
203
     * @param extraHeaders extra HTTP headers
204
     */
205
    public static IResponse buildLeanResponse(StatusLine.StatusCode statusCode, Map<String, String> extraHeaders) {
206 1 1. buildLeanResponse : replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED
        return new Response(statusCode, extraHeaders, null, socketWrapper -> {}, 0, false);
207
    }
208
209
    /**
210
     * Build a {@link Response} with only a status code, with no body and no extra headers.
211
     */
212
    public static IResponse buildLeanResponse(StatusLine.StatusCode statusCode) {
213 1 1. buildLeanResponse : replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED
        return new Response(statusCode, Map.of(), null, socketWrapper -> {}, 0, false);
214
    }
215
216
    /**
217
     * A helper method to create a response that returns a
218
     * 303 status code ("see other").  Provide a url that will
219
     * be handed to the browser.  This url may be relative or absolute.
220
     */
221
    public static IResponse redirectTo(String locationUrl) {
222
        try {
223
            // check if we can create a URL from this - any failures indicate invalid syntax.
224
            URI.create(locationUrl);
225
        } catch (Exception ex) {
226
            throw new WebServerException("Failure in redirect to (" + locationUrl + "). Exception: " + ex);
227
        }
228 1 1. redirectTo : replaced return value with null for com/renomad/minum/web/Response::redirectTo → KILLED
        return buildResponse(StatusLine.StatusCode.CODE_303_SEE_OTHER, Map.of("location", locationUrl, "Content-Type", "text/html; charset=UTF-8"), "<p>See <a href=\""+locationUrl+"\">this link</a></p>");
229
    }
230
231
    /**
232
     * If you are returning HTML text with a 200 ok, this is a helper that
233
     * lets you skip some of the boilerplate. This version of the helper
234
     * lets you add extra headers on top of the basic content-type headers
235
     * that are needed to specify this is HTML.
236
     */
237
    public static IResponse htmlOk(String body, Map<String, String> extraHeaders) {
238
        var headers = new HashMap<String, String>();
239
        headers.put("Content-Type", "text/html; charset=UTF-8");
240 1 1. htmlOk : removed call to java/util/HashMap::putAll → KILLED
        headers.putAll(extraHeaders);
241 1 1. htmlOk : replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED
        return buildResponse(StatusLine.StatusCode.CODE_200_OK, headers, body);
242
    }
243
244
    /**
245
     * If you are returning HTML text with a 200 ok, this is a helper that
246
     * lets you skip some of the boilerplate.
247
     */
248
    public static IResponse htmlOk(String body) {
249 1 1. htmlOk : replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED
        return htmlOk(body, Map.of());
250
    }
251
252
    @Override
253
    public Map<String, String> getExtraHeaders() {
254 1 1. getExtraHeaders : replaced return value with Collections.emptyMap for com/renomad/minum/web/Response::getExtraHeaders → KILLED
        return new HashMap<>(extraHeaders);
255
    }
256
257
    @Override
258
    public StatusLine.StatusCode getStatusCode() {
259 1 1. getStatusCode : replaced return value with null for com/renomad/minum/web/Response::getStatusCode → KILLED
        return statusCode;
260
    }
261
262
    @Override
263
    public boolean isBodyText() {
264 2 1. isBodyText : replaced boolean return with true for com/renomad/minum/web/Response::isBodyText → KILLED
2. isBodyText : replaced boolean return with false for com/renomad/minum/web/Response::isBodyText → KILLED
        return isBodyText;
265
    }
266
267
    @Override
268
    public long getBodyLength() {
269 1 1. getBodyLength : negated conditional → KILLED
        if (body != null) {
270 1 1. getBodyLength : replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED
            return body.length;
271
        } else {
272 1 1. getBodyLength : replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED
            return bodyLength;
273
        }
274
    }
275
276
    @Override
277
    public void sendBody(ISocketWrapper sw) throws IOException {
278
        try {
279 1 1. sendBody : removed call to com/renomad/minum/web/ThrowingConsumer::accept → KILLED
            outputGenerator.accept(sw);
280
        } catch (Exception ex) {
281
            throw new IOException(ex.getMessage(), ex);
282
        }
283
    }
284
285
    /**
286
     * put bytes from a file into the socket, sending to the client
287
     * @param fileChannel the file we are reading from, based on a {@link RandomAccessFile}
288
     * @param length the number of bytes to send.  May be less than the full length of this {@link FileChannel}
289
     */
290
    private static void sendFileChannelResponse(ISocketWrapper sw, FileChannel fileChannel, long length) throws IOException {
291
        try {
292
            int bufferSize = 8 * 1024;
293
            ByteBuffer buff = ByteBuffer.allocate(bufferSize);
294
            long countBytesLeftToSend = length;
295
            while (true) {
296
                int countBytesRead = fileChannel.read(buff);
297 2 1. sendFileChannelResponse : changed conditional boundary → KILLED
2. sendFileChannelResponse : negated conditional → KILLED
                if (countBytesRead <= 0) {
298
                    break;
299
                } else {
300 2 1. sendFileChannelResponse : changed conditional boundary → SURVIVED
2. sendFileChannelResponse : negated conditional → KILLED
                    if (countBytesLeftToSend < countBytesRead) {
301 1 1. sendFileChannelResponse : removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED
                        sw.send(buff.array(), 0, (int)countBytesLeftToSend);
302
                        break;
303
                    } else {
304 1 1. sendFileChannelResponse : removed call to com/renomad/minum/web/ISocketWrapper::send → TIMED_OUT
                        sw.send(buff.array(), 0, countBytesRead);
305
                    }
306
                    buff.clear();
307
                }
308 1 1. sendFileChannelResponse : Replaced long subtraction with addition → SURVIVED
                countBytesLeftToSend -= countBytesRead;
309
            }
310
        } finally {
311 1 1. sendFileChannelResponse : removed call to java/nio/channels/FileChannel::close → KILLED
            fileChannel.close();
312
        }
313
    }
314
315
    private static void sendByteArrayResponse(ISocketWrapper sw, byte[] body) throws IOException {
316 1 1. sendByteArrayResponse : removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED
        sw.send(body);
317
    }
318
319
    @Override
320
    public byte[] getBody() {
321 1 1. getBody : replaced return value with null for com/renomad/minum/web/Response::getBody → KILLED
        return body;
322
    }
323
324
    /**
325
     * Compress the data in this body using gzip.
326
     * <br>
327
     * This operates by getting the body field from this instance of {@link Response} and
328
     * creating a new Response with the compressed data.
329
     */
330
    Response compressBody() throws IOException {
331
332
        ByteArrayOutputStream out = new ByteArrayOutputStream();
333
        var gos = new GZIPOutputStream(out);
334 1 1. compressBody : removed call to java/util/zip/GZIPOutputStream::write → SURVIVED
        gos.write(body);
335 1 1. compressBody : removed call to java/util/zip/GZIPOutputStream::finish → SURVIVED
        gos.finish();
336 1 1. compressBody : replaced return value with null for com/renomad/minum/web/Response::compressBody → KILLED
        return (Response)Response.buildResponse(
337
                statusCode,
338
                extraHeaders,
339
                out.toByteArray()
340
        );
341
    }
342
343
    @Override
344
    public boolean equals(Object o) {
345 2 1. equals : replaced boolean return with false for com/renomad/minum/web/Response::equals → KILLED
2. equals : negated conditional → KILLED
        if (this == o) return true;
346 3 1. equals : negated conditional → KILLED
2. equals : replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED
3. equals : negated conditional → KILLED
        if (o == null || getClass() != o.getClass()) return false;
347
        Response response = (Response) o;
348 6 1. equals : replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED
2. equals : negated conditional → KILLED
3. equals : negated conditional → KILLED
4. equals : negated conditional → KILLED
5. equals : negated conditional → KILLED
6. equals : negated conditional → KILLED
        return bodyLength == response.bodyLength && isBodyText == response.isBodyText && statusCode == response.statusCode && Objects.equals(extraHeaders, response.extraHeaders) && Arrays.equals(body, response.body);
349
    }
350
351
    @Override
352
    public int hashCode() {
353
        int result = Objects.hash(statusCode, extraHeaders, bodyLength, isBodyText);
354 2 1. hashCode : Replaced integer addition with subtraction → SURVIVED
2. hashCode : Replaced integer multiplication with division → KILLED
        result = 31 * result + Arrays.hashCode(body);
355 1 1. hashCode : replaced int return with 0 for com/renomad/minum/web/Response::hashCode → KILLED
        return result;
356
    }
357
358
    @Override
359
    public String toString() {
360 1 1. toString : replaced return value with "" for com/renomad/minum/web/Response::toString → KILLED
        return "Response{" +
361
                "statusCode=" + statusCode +
362
                ", extraHeaders=" + extraHeaders +
363
                ", bodyLength=" + bodyLength +
364
                ", isBodyText=" + isBodyText +
365
                '}';
366
    }
367
}

Mutations

110

1.1
Location : buildStreamingResponse
Killed by : com.renomad.minum.web.ResponseTests.testResponse_Streaming(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::buildStreamingResponse → KILLED

122

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

136

1.1
Location : buildResponse
Killed by : com.renomad.minum.web.WebFrameworkTests.test_compressIfRequested(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED

2.2
Location : lambda$buildResponse$0
Killed by : none
removed call to com/renomad/minum/web/Response::sendByteArrayResponse → TIMED_OUT

145

1.1
Location : buildResponse
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::buildResponse → KILLED

2.2
Location : lambda$buildResponse$1
Killed by : com.renomad.minum.web.ResponseTests.testResponse_EdgeCase_SendBodyWithException(com.renomad.minum.web.ResponseTests)
removed call to com/renomad/minum/web/Response::sendByteArrayResponse → KILLED

164

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

167

1.1
Location : buildLargeFileResponse
Killed by : none
Replaced long addition with subtraction → SURVIVED
Covering tests

2.2
Location : buildLargeFileResponse
Killed by : com.renomad.minum.FunctionalTests
Replaced long subtraction with addition → KILLED

174

1.1
Location : lambda$buildLargeFileResponse$2
Killed by : com.renomad.minum.FunctionalTests
removed call to java/io/RandomAccessFile::seek → KILLED

176

1.1
Location : lambda$buildLargeFileResponse$2
Killed by : none
removed call to com/renomad/minum/web/Response::sendFileChannelResponse → TIMED_OUT

180

1.1
Location : buildLargeFileResponse
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Response::buildLargeFileResponse → KILLED

198

1.1
Location : buildLargeFileResponse
Killed by : none
replaced return value with null for com/renomad/minum/web/Response::buildLargeFileResponse → SURVIVED
Covering tests

206

1.1
Location : buildLeanResponse
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED

213

1.1
Location : buildLeanResponse
Killed by : com.renomad.minum.web.WebFrameworkTests.test_readStaticFile_Edge_Colon(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::buildLeanResponse → KILLED

228

1.1
Location : redirectTo
Killed by : com.renomad.minum.FunctionalTests
replaced return value with null for com/renomad/minum/web/Response::redirectTo → KILLED

240

1.1
Location : htmlOk
Killed by : com.renomad.minum.web.WebTests
removed call to java/util/HashMap::putAll → KILLED

241

1.1
Location : htmlOk
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED

249

1.1
Location : htmlOk
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with null for com/renomad/minum/web/Response::htmlOk → KILLED

254

1.1
Location : getExtraHeaders
Killed by : com.renomad.minum.web.WebFrameworkTests.test_readStaticFile_HTML(com.renomad.minum.web.WebFrameworkTests)
replaced return value with Collections.emptyMap for com/renomad/minum/web/Response::getExtraHeaders → KILLED

259

1.1
Location : getStatusCode
Killed by : com.renomad.minum.web.WebFrameworkTests.test_readStaticFile_Edge_Colon(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::getStatusCode → KILLED

264

1.1
Location : isBodyText
Killed by : com.renomad.minum.web.WebPerformanceTests.test3(com.renomad.minum.web.WebPerformanceTests)
replaced boolean return with true for com/renomad/minum/web/Response::isBodyText → KILLED

2.2
Location : isBodyText
Killed by : com.renomad.minum.web.WebTests
replaced boolean return with false for com/renomad/minum/web/Response::isBodyText → KILLED

269

1.1
Location : getBodyLength
Killed by : com.renomad.minum.web.WebPerformanceTests.test1(com.renomad.minum.web.WebPerformanceTests)
negated conditional → KILLED

270

1.1
Location : getBodyLength
Killed by : com.renomad.minum.web.WebFrameworkTests.test_compressIfRequested(com.renomad.minum.web.WebFrameworkTests)
replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED

272

1.1
Location : getBodyLength
Killed by : com.renomad.minum.web.WebTests
replaced long return with 0 for com/renomad/minum/web/Response::getBodyLength → KILLED

279

1.1
Location : sendBody
Killed by : com.renomad.minum.web.ResponseTests.testResponse_Streaming(com.renomad.minum.web.ResponseTests)
removed call to com/renomad/minum/web/ThrowingConsumer::accept → KILLED

297

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
changed conditional boundary → KILLED

2.2
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
negated conditional → KILLED

300

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
negated conditional → KILLED

2.2
Location : sendFileChannelResponse
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

301

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED

304

1.1
Location : sendFileChannelResponse
Killed by : none
removed call to com/renomad/minum/web/ISocketWrapper::send → TIMED_OUT

308

1.1
Location : sendFileChannelResponse
Killed by : none
Replaced long subtraction with addition → SURVIVED
Covering tests

311

1.1
Location : sendFileChannelResponse
Killed by : com.renomad.minum.FunctionalTests
removed call to java/nio/channels/FileChannel::close → KILLED

316

1.1
Location : sendByteArrayResponse
Killed by : com.renomad.minum.web.ResponseTests.testResponse_EdgeCase_SendBodyWithException(com.renomad.minum.web.ResponseTests)
removed call to com/renomad/minum/web/ISocketWrapper::send → KILLED

321

1.1
Location : getBody
Killed by : com.renomad.minum.web.EndpointTests.test_Endpoint_HappyPath(com.renomad.minum.web.EndpointTests)
replaced return value with null for com/renomad/minum/web/Response::getBody → KILLED

334

1.1
Location : compressBody
Killed by : none
removed call to java/util/zip/GZIPOutputStream::write → SURVIVED
Covering tests

335

1.1
Location : compressBody
Killed by : none
removed call to java/util/zip/GZIPOutputStream::finish → SURVIVED
Covering tests

336

1.1
Location : compressBody
Killed by : com.renomad.minum.web.WebFrameworkTests.test_compressIfRequested(com.renomad.minum.web.WebFrameworkTests)
replaced return value with null for com/renomad/minum/web/Response::compressBody → KILLED

345

1.1
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with false for com/renomad/minum/web/Response::equals → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
negated conditional → KILLED

346

1.1
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED

3.3
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

348

1.1
Location : equals
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced boolean return with true for com/renomad/minum/web/Response::equals → KILLED

2.2
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

3.3
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

4.4
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

5.5
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

6.6
Location : equals
Killed by : com.renomad.minum.web.ResponseTests.testUseResponseAsKey(com.renomad.minum.web.ResponseTests)
negated conditional → KILLED

354

1.1
Location : hashCode
Killed by : none
Replaced integer addition with subtraction → SURVIVED
Covering tests

2.2
Location : hashCode
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
Replaced integer multiplication with division → KILLED

355

1.1
Location : hashCode
Killed by : com.renomad.minum.EqualsTests.equalsTest(com.renomad.minum.EqualsTests)
replaced int return with 0 for com/renomad/minum/web/Response::hashCode → KILLED

360

1.1
Location : toString
Killed by : com.renomad.minum.web.ResponseTests.testToString(com.renomad.minum.web.ResponseTests)
replaced return value with "" for com/renomad/minum/web/Response::toString → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0