1 | package com.renomad.minum.web; | |
2 | ||
3 | import java.io.ByteArrayOutputStream; | |
4 | import java.io.IOException; | |
5 | import java.io.InputStream; | |
6 | ||
7 | /** | |
8 | * This class enables pulling key-value pairs one at a time | |
9 | * from the Request body. This enables the developer to pull data | |
10 | * incrementally, rather than reading it all into memory at once. | |
11 | */ | |
12 | public class UrlEncodedDataGetter extends InputStream { | |
13 | private final InputStream inputStream; | |
14 | private final CountBytesRead countBytesRead; | |
15 | private final long contentLength; | |
16 | /** | |
17 | * After we hit the boundary, we will set this flag to true, and all | |
18 | * subsequent reads will return -1. | |
19 | */ | |
20 | private boolean isFinished = false; | |
21 | ||
22 | UrlEncodedDataGetter(InputStream inputStream, CountBytesRead countBytesRead, long contentLength) { | |
23 | this.inputStream = inputStream; | |
24 | this.countBytesRead = countBytesRead; | |
25 | this.contentLength = contentLength; | |
26 | } | |
27 | ||
28 | /** | |
29 | * Mostly similar behavior to {@link InputStream#read()} | |
30 | * @throws IOException if the inputstream is closed unexpectedly while reading. | |
31 | */ | |
32 | @Override | |
33 | public int read() throws IOException { | |
34 |
1
1. read : negated conditional → KILLED |
if (isFinished) { |
35 |
1
1. read : replaced int return with 0 for com/renomad/minum/web/UrlEncodedDataGetter::read → TIMED_OUT |
return -1; |
36 | } | |
37 |
1
1. read : negated conditional → KILLED |
if (countBytesRead.getCount() == contentLength) { |
38 | isFinished = true; | |
39 |
1
1. read : replaced int return with 0 for com/renomad/minum/web/UrlEncodedDataGetter::read → KILLED |
return -1; |
40 | } | |
41 | int result = inputStream.read(); | |
42 | ||
43 |
1
1. read : negated conditional → KILLED |
if (result == -1) { |
44 | isFinished = true; | |
45 | // I know this is surprising, however: Because we always have the content length while reading the body, | |
46 | // we know exactly when we expect to read the last byte. If we read and get a -1, it means | |
47 | // the stream is closed - but that should not have happened, because we should have stopped reading when | |
48 | // we hit the limit of bytes to read. But in the real world, it will happen: You can observe it by uploading a large | |
49 | // file and using the browser's "stop" button during the upload. | |
50 | throw new IOException("Error: The inputstream has closed unexpectedly while reading"); | |
51 | } | |
52 | ||
53 |
1
1. read : removed call to com/renomad/minum/web/CountBytesRead::increment → KILLED |
countBytesRead.increment(); |
54 | char byteValue = (char) result; | |
55 |
1
1. read : negated conditional → KILLED |
if (byteValue == '&') { |
56 | isFinished = true; | |
57 |
1
1. read : replaced int return with 0 for com/renomad/minum/web/UrlEncodedDataGetter::read → KILLED |
return -1; |
58 | } | |
59 |
1
1. read : replaced int return with 0 for com/renomad/minum/web/UrlEncodedDataGetter::read → KILLED |
return result; |
60 | } | |
61 | ||
62 | @Override | |
63 | public byte[] readAllBytes() throws IOException { | |
64 | var baos = new ByteArrayOutputStream(); | |
65 | while (true) { | |
66 | int result = read(); | |
67 |
1
1. readAllBytes : negated conditional → KILLED |
if (result == -1) { |
68 | // if our read function determines we are at the end of the value, | |
69 | // because we encountered an ampersand, it will return a -1 value, | |
70 | // and we return our value - but more keys and values expected. | |
71 |
1
1. readAllBytes : replaced return value with null for com/renomad/minum/web/UrlEncodedDataGetter::readAllBytes → KILLED |
return baos.toByteArray(); |
72 | } | |
73 |
1
1. readAllBytes : removed call to java/io/ByteArrayOutputStream::write → KILLED |
baos.write((byte)result); |
74 | } | |
75 | } | |
76 | ||
77 | /** | |
78 | * By "close", we will read from the {@link InputStream} until we have finished the body, | |
79 | * so that our InputStream has been read until the start of the next partition. | |
80 | */ | |
81 | @Override | |
82 | public void close() throws IOException { | |
83 | while (true) { | |
84 | int result = read(); | |
85 |
1
1. close : negated conditional → TIMED_OUT |
if (result == -1) { |
86 | return; | |
87 | } | |
88 | } | |
89 | } | |
90 | } | |
Mutations | ||
34 |
1.1 |
|
35 |
1.1 |
|
37 |
1.1 |
|
39 |
1.1 |
|
43 |
1.1 |
|
53 |
1.1 |
|
55 |
1.1 |
|
57 |
1.1 |
|
59 |
1.1 |
|
67 |
1.1 |
|
71 |
1.1 |
|
73 |
1.1 |
|
85 |
1.1 |