| 1 | package com.renomad.minum.web; | |
| 2 | ||
| 3 | import com.renomad.minum.security.ForbiddenUseException; | |
| 4 | import com.renomad.minum.utils.UtilsException; | |
| 5 | ||
| 6 | import java.io.ByteArrayOutputStream; | |
| 7 | import java.io.IOException; | |
| 8 | import java.io.InputStream; | |
| 9 | import java.nio.charset.StandardCharsets; | |
| 10 | import java.util.Objects; | |
| 11 | ||
| 12 | /** | |
| 13 | * Handy helpful utilities for working with input streams. | |
| 14 | */ | |
| 15 | final class InputStreamUtils implements IInputStreamUtils { | |
| 16 | ||
| 17 | private final int maxReadLineSizeBytes; | |
| 18 | ||
| 19 | public InputStreamUtils(int maxReadLineSizeBytes) { | |
| 20 | this.maxReadLineSizeBytes = maxReadLineSizeBytes; | |
| 21 | } | |
| 22 | ||
| 23 | @Override | |
| 24 | public String readLine(InputStream inputStream) throws IOException { | |
| 25 | final int NEWLINE_DECIMAL = 10; | |
| 26 | final int CARRIAGE_RETURN_DECIMAL = 13; | |
| 27 | ||
| 28 |
1
1. readLine : Replaced integer division with multiplication → TIMED_OUT |
final var result = new ByteArrayOutputStream(maxReadLineSizeBytes / 3); |
| 29 | int bytesRead = 0; | |
| 30 |
1
1. readLine : Changed increment from 1 to -1 → TIMED_OUT |
for (int i = 0;; i++) { |
| 31 |
2
1. readLine : changed conditional boundary → KILLED 2. readLine : negated conditional → KILLED |
if (i >= maxReadLineSizeBytes) { |
| 32 |
1
1. readLine : removed call to java/io/InputStream::close → SURVIVED |
inputStream.close(); |
| 33 | throw new ForbiddenUseException("client sent more bytes than allowed for a single line. max: " + maxReadLineSizeBytes); | |
| 34 | } | |
| 35 | int a = inputStream.read(); | |
| 36 |
1
1. readLine : negated conditional → KILLED |
if (a == -1) { |
| 37 |
2
1. readLine : negated conditional → KILLED 2. readLine : changed conditional boundary → KILLED |
if (bytesRead > 0) { |
| 38 |
1
1. readLine : replaced return value with "" for com/renomad/minum/web/InputStreamUtils::readLine → SURVIVED |
return result.toString(StandardCharsets.UTF_8); |
| 39 | } else { | |
| 40 | /* | |
| 41 | it could be unclear whether we read a line that's an empty string, or we | |
| 42 | reached the end of stream. With this code, if we get an empty string, | |
| 43 | that means the line we read is just an empty string, and if we get a null, | |
| 44 | that means we didn't have any characters read into our ByteArrayOutputStream, | |
| 45 | and tried reading at the end of stream. | |
| 46 | */ | |
| 47 |
1
1. readLine : replaced return value with "" for com/renomad/minum/web/InputStreamUtils::readLine → KILLED |
return null; |
| 48 | } | |
| 49 | } | |
| 50 |
1
1. readLine : negated conditional → KILLED |
if (a == CARRIAGE_RETURN_DECIMAL) continue; |
| 51 |
1
1. readLine : negated conditional → KILLED |
if (a == NEWLINE_DECIMAL) break; |
| 52 |
1
1. readLine : removed call to java/io/ByteArrayOutputStream::write → KILLED |
result.write(a); |
| 53 |
1
1. readLine : Changed increment from 1 to -1 → KILLED |
bytesRead += 1; |
| 54 | } | |
| 55 |
1
1. readLine : replaced return value with "" for com/renomad/minum/web/InputStreamUtils::readLine → KILLED |
return result.toString(StandardCharsets.UTF_8); |
| 56 | } | |
| 57 | ||
| 58 | @Override | |
| 59 | public byte[] read(int lengthToRead, InputStream inputStream) { | |
| 60 | final int typicalBufferSize = 1024 * 8; | |
| 61 | byte[] buf = new byte[Math.min(lengthToRead, typicalBufferSize)]; // 8k buffer is my understanding of a decent size. Fast, doesn't waste too much space. | |
| 62 | byte[] data; | |
| 63 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); | |
| 64 | int read; | |
| 65 | int totalRead = 0; | |
| 66 | try { | |
| 67 |
2
1. read : changed conditional boundary → KILLED 2. read : negated conditional → KILLED |
while ((read = inputStream.read(buf)) >= 0) { |
| 68 |
1
1. read : Replaced integer addition with subtraction → TIMED_OUT |
totalRead += read; |
| 69 |
2
1. read : negated conditional → KILLED 2. read : changed conditional boundary → KILLED |
if (totalRead < lengthToRead) { |
| 70 | // if we haven't gotten everything we wanted, write this to the output and loop again | |
| 71 |
1
1. read : removed call to java/io/ByteArrayOutputStream::write → KILLED |
baos.write(buf, 0, read); |
| 72 | } else { | |
| 73 |
3
1. read : Replaced integer subtraction with addition → KILLED 2. read : Replaced integer subtraction with addition → KILLED 3. read : removed call to java/io/ByteArrayOutputStream::write → KILLED |
baos.write(buf, 0, read - (totalRead - lengthToRead)); |
| 74 | break; | |
| 75 | } | |
| 76 | } | |
| 77 | } catch (IOException ex) { | |
| 78 | throw new UtilsException(ex); | |
| 79 | } | |
| 80 | data = baos.toByteArray(); | |
| 81 | ||
| 82 |
1
1. read : negated conditional → KILLED |
if (data.length != lengthToRead) { |
| 83 | String message = String.format("length of bytes read (%d) must be what we expected (%d)", data.length, lengthToRead); | |
| 84 | throw new ForbiddenUseException(message); | |
| 85 | } | |
| 86 |
1
1. read : replaced return value with null for com/renomad/minum/web/InputStreamUtils::read → KILLED |
return data; |
| 87 | } | |
| 88 | ||
| 89 | @Override | |
| 90 | public boolean equals(Object o) { | |
| 91 |
2
1. equals : negated conditional → KILLED 2. equals : replaced boolean return with false for com/renomad/minum/web/InputStreamUtils::equals → KILLED |
if (this == o) return true; |
| 92 |
3
1. equals : negated conditional → TIMED_OUT 2. equals : negated conditional → KILLED 3. equals : replaced boolean return with true for com/renomad/minum/web/InputStreamUtils::equals → KILLED |
if (o == null || getClass() != o.getClass()) return false; |
| 93 | InputStreamUtils that = (InputStreamUtils) o; | |
| 94 |
2
1. equals : negated conditional → KILLED 2. equals : replaced boolean return with true for com/renomad/minum/web/InputStreamUtils::equals → KILLED |
return maxReadLineSizeBytes == that.maxReadLineSizeBytes; |
| 95 | } | |
| 96 | ||
| 97 | @Override | |
| 98 | public int hashCode() { | |
| 99 |
1
1. hashCode : replaced int return with 0 for com/renomad/minum/web/InputStreamUtils::hashCode → KILLED |
return Objects.hash(maxReadLineSizeBytes); |
| 100 | } | |
| 101 | } | |
Mutations | ||
| 28 |
1.1 |
|
| 30 |
1.1 |
|
| 31 |
1.1 2.2 |
|
| 32 |
1.1 |
|
| 36 |
1.1 |
|
| 37 |
1.1 2.2 |
|
| 38 |
1.1 |
|
| 47 |
1.1 |
|
| 50 |
1.1 |
|
| 51 |
1.1 |
|
| 52 |
1.1 |
|
| 53 |
1.1 |
|
| 55 |
1.1 |
|
| 67 |
1.1 2.2 |
|
| 68 |
1.1 |
|
| 69 |
1.1 2.2 |
|
| 71 |
1.1 |
|
| 73 |
1.1 2.2 3.3 |
|
| 82 |
1.1 |
|
| 86 |
1.1 |
|
| 91 |
1.1 2.2 |
|
| 92 |
1.1 2.2 3.3 |
|
| 94 |
1.1 2.2 |
|
| 99 |
1.1 |