Request.java

1
package com.renomad.minum.web;
2
3
import java.util.Objects;
4
5
/**
6
 * An HTTP request.
7
 * <p>
8
 * From <a href="https://en.wikipedia.org/wiki/HTTP#Request_syntax">Wikipedia</a>
9
 * </p>
10
 *<p>
11
 *     A client sends request messages to the server, which consist of:
12
 *</p>
13
 * <ul>
14
 *     <li>
15
 *      a request line, consisting of the case-sensitive request
16
 *      method, a space, the requested URL, another space, the
17
 *      protocol version, a carriage return, and a line feed, e.g.:
18
 *      <pre>
19
 *          GET /images/logo.png HTTP/1.1
20
 *      </pre>
21
 *      </li>
22
 *
23
 *      <li>
24
 *      zero or more request header fields (at least 1 or more
25
 *      headers in case of HTTP/1.1), each consisting of the case-insensitive
26
 *      field name, a colon, optional leading whitespace, the field
27
 *      value, an optional trailing whitespace and ending with a
28
 *      carriage return and a line feed, e.g.:
29
 *
30
 *      <pre>
31
 *      Host: www.example.com
32
 *      Accept-Language: en
33
 *      </pre>
34
 *      </li>
35
 *
36
 *      <li>
37
 *      an empty line, consisting of a carriage return and a line feed;
38
 *      </li>
39
 *
40
 *      <li>
41
 *      an optional message body.
42
 *      </li>
43
 *      In the HTTP/1.1 protocol, all header fields except Host: hostname are optional.
44
 * </ul>
45
 *
46
 * <p>
47
 * A request line containing only the path name is accepted by servers to
48
 * maintain compatibility with HTTP clients before the HTTP/1.0 specification in RFC 1945.
49
 *</p>
50
 *
51
 */
52
public final class Request implements IRequest {
53
54
    private final Headers headers;
55
    private final RequestLine requestLine;
56
    private Body body;
57
    private final String remoteRequester;
58
    private final ISocketWrapper socketWrapper;
59
    private final IBodyProcessor bodyProcessor;
60
    private boolean hasStartedReadingBody;
61
62
    /**
63
     * Constructor for a HTTP request
64
     * @param  remoteRequester This is the remote address making the request
65
     */
66
    public Request(Headers headers,
67
            RequestLine requestLine,
68
            String remoteRequester,
69
            ISocketWrapper socketWrapper,
70
            IBodyProcessor bodyProcessor
71
    ) {
72
        this.headers = headers;
73
        this.requestLine = requestLine;
74
        this.remoteRequester = remoteRequester;
75
        this.socketWrapper = socketWrapper;
76
        this.bodyProcessor = bodyProcessor;
77
        this.hasStartedReadingBody = false;
78
    }
79
80
    @Override
81
    public Headers getHeaders() {
82 1 1. getHeaders : replaced return value with null for com/renomad/minum/web/Request::getHeaders → KILLED
        return headers;
83
    }
84
85
    @Override
86
    public RequestLine getRequestLine() {
87 1 1. getRequestLine : replaced return value with null for com/renomad/minum/web/Request::getRequestLine → KILLED
        return requestLine;
88
    }
89
90
    @Override
91
    public Body getBody() {
92 1 1. getBody : negated conditional → KILLED
        if (hasStartedReadingBody) {
93
            throw new WebServerException("The InputStream in Request has already been accessed for reading, preventing body extraction from stream." +
94
                    " If intending to use getBody(), use it exclusively");
95
        }
96 1 1. getBody : negated conditional → KILLED
        if (body == null) {
97
            body = bodyProcessor.extractData(socketWrapper.getInputStream(), headers);
98
        }
99 1 1. getBody : replaced return value with null for com/renomad/minum/web/Request::getBody → KILLED
        return body;
100
    }
101
102
    @Override
103
    public String getRemoteRequester() {
104 1 1. getRemoteRequester : replaced return value with "" for com/renomad/minum/web/Request::getRemoteRequester → KILLED
        return remoteRequester;
105
    }
106
107
    @Override
108
    public ISocketWrapper getSocketWrapper() {
109 1 1. getSocketWrapper : removed call to com/renomad/minum/web/Request::checkForExistingBody → KILLED
        checkForExistingBody();
110
        hasStartedReadingBody = true;
111 1 1. getSocketWrapper : replaced return value with null for com/renomad/minum/web/Request::getSocketWrapper → KILLED
        return socketWrapper;
112
    }
113
114
    @Override
115
    public boolean equals(Object o) {
116 2 1. equals : replaced boolean return with false for com/renomad/minum/web/Request::equals → TIMED_OUT
2. equals : negated conditional → KILLED
        if (this == o) return true;
117 3 1. equals : negated conditional → TIMED_OUT
2. equals : replaced boolean return with true for com/renomad/minum/web/Request::equals → TIMED_OUT
3. equals : negated conditional → TIMED_OUT
        if (o == null || getClass() != o.getClass()) return false;
118
        Request request = (Request) o;
119 8 1. equals : negated conditional → TIMED_OUT
2. equals : negated conditional → TIMED_OUT
3. equals : negated conditional → TIMED_OUT
4. equals : negated conditional → TIMED_OUT
5. equals : negated conditional → TIMED_OUT
6. equals : negated conditional → TIMED_OUT
7. equals : negated conditional → TIMED_OUT
8. equals : replaced boolean return with true for com/renomad/minum/web/Request::equals → KILLED
        return hasStartedReadingBody == request.hasStartedReadingBody && Objects.equals(headers, request.headers) && Objects.equals(requestLine, request.requestLine) && Objects.equals(body, request.body) && Objects.equals(remoteRequester, request.remoteRequester) && Objects.equals(socketWrapper, request.socketWrapper) && Objects.equals(bodyProcessor, request.bodyProcessor);
120
    }
121
122
    @Override
123
    public int hashCode() {
124 1 1. hashCode : replaced int return with 0 for com/renomad/minum/web/Request::hashCode → TIMED_OUT
        return Objects.hash(headers, requestLine, body, remoteRequester, socketWrapper, bodyProcessor, hasStartedReadingBody);
125
    }
126
127
    @Override
128
    public String toString() {
129 1 1. toString : replaced return value with "" for com/renomad/minum/web/Request::toString → KILLED
        return "Request{" +
130
                "headers=" + headers +
131
                ", requestLine=" + requestLine +
132
                ", body=" + body +
133
                ", remoteRequester='" + remoteRequester + '\'' +
134
                ", socketWrapper=" + socketWrapper +
135
                ", hasStartedReadingBody=" + hasStartedReadingBody +
136
                '}';
137
    }
138
139
    @Override
140
    public Iterable<UrlEncodedKeyValue> getUrlEncodedIterable() {
141 1 1. getUrlEncodedIterable : removed call to com/renomad/minum/web/Request::checkForExistingBody → KILLED
        checkForExistingBody();
142 1 1. getUrlEncodedIterable : negated conditional → KILLED
        if (!headers.contentType().contains("application/x-www-form-urlencoded")) {
143
            throw new WebServerException("This request was not sent with a content type of application/x-www-form-urlencoded.  The content type was: " + headers.contentType());
144
        }
145 1 1. getUrlEncodedIterable : replaced return value with Collections.emptyList for com/renomad/minum/web/Request::getUrlEncodedIterable → KILLED
        return bodyProcessor.getUrlEncodedDataIterable(getSocketWrapper().getInputStream(), getHeaders().contentLength());
146
    }
147
148
    /**
149
     * This method is for verifying that the body is null, and throwing an exception
150
     * if not.  Several of the methods in this class depend on the InputStream not
151
     * having been read already - if the body was read, then the InputStream is finished,
152
     * and any further reading would be incorrect.
153
     */
154
    private void checkForExistingBody() {
155 1 1. checkForExistingBody : negated conditional → KILLED
        if (body != null) {
156
            throw new WebServerException("Requesting this after getting the body with getBody() will result in incorrect behavior.  " +
157
                    "If you intend to work with the Request at this level, do not use getBody");
158
        }
159 1 1. checkForExistingBody : negated conditional → KILLED
        if (hasStartedReadingBody) {
160
            throw new WebServerException("The InputStream has begun processing elsewhere.  Results are invalid.");
161
        }
162
    }
163
164
    @Override
165
    public Iterable<StreamingMultipartPartition> getMultipartIterable() {
166 1 1. getMultipartIterable : removed call to com/renomad/minum/web/Request::checkForExistingBody → KILLED
        checkForExistingBody();
167 1 1. getMultipartIterable : negated conditional → KILLED
        if (!headers.contentType().contains("multipart/form-data")) {
168
            throw new WebServerException("This request was not sent with a content type of multipart/form-data.  The content type was: " + headers.contentType());
169
        }
170
        String boundaryKey = "boundary=";
171
        String contentType = getHeaders().contentType();
172
        int indexOfBoundaryKey = contentType.indexOf(boundaryKey);
173
        String boundaryValue = "";
174 2 1. getMultipartIterable : changed conditional boundary → SURVIVED
2. getMultipartIterable : negated conditional → KILLED
        if (indexOfBoundaryKey > 0) {
175
            // grab all the text after the key to obtain the boundary value
176 1 1. getMultipartIterable : Replaced integer addition with subtraction → KILLED
            boundaryValue = contentType.substring(indexOfBoundaryKey + boundaryKey.length());
177
        } else {
178
            String parsingError = "Did not find a valid boundary value for the multipart input. Returning an empty map and the raw bytes for the body. Header was: " + contentType;
179
            throw new WebServerException(parsingError);
180
        }
181
182 1 1. getMultipartIterable : negated conditional → KILLED
        if (boundaryValue.isBlank()) {
183
            String parsingError = "Boundary value was blank. Returning an empty map and the raw bytes for the body. Header was: " + contentType;
184
            throw new WebServerException(parsingError);
185
        }
186
187 1 1. getMultipartIterable : replaced return value with Collections.emptyList for com/renomad/minum/web/Request::getMultipartIterable → KILLED
        return bodyProcessor.getMultiPartIterable(getSocketWrapper().getInputStream(), boundaryValue ,getHeaders().contentLength());
188
    }
189
}

Mutations

82

1.1
Location : getHeaders
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest(com.renomad.minum.web.RequestTests)
replaced return value with null for com/renomad/minum/web/Request::getHeaders → KILLED

87

1.1
Location : getRequestLine
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest(com.renomad.minum.web.RequestTests)
replaced return value with null for com/renomad/minum/web/Request::getRequestLine → KILLED

92

1.1
Location : getBody
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_5(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

96

1.1
Location : getBody
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_5(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

99

1.1
Location : getBody
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest(com.renomad.minum.web.RequestTests)
replaced return value with null for com/renomad/minum/web/Request::getBody → KILLED

104

1.1
Location : getRemoteRequester
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest(com.renomad.minum.web.RequestTests)
replaced return value with "" for com/renomad/minum/web/Request::getRemoteRequester → KILLED

109

1.1
Location : getSocketWrapper
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_9(com.renomad.minum.web.RequestTests)
removed call to com/renomad/minum/web/Request::checkForExistingBody → KILLED

111

1.1
Location : getSocketWrapper
Killed by : com.renomad.minum.web.RequestTests.test_GetSocketWrapper(com.renomad.minum.web.RequestTests)
replaced return value with null for com/renomad/minum/web/Request::getSocketWrapper → KILLED

116

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

2.2
Location : equals
Killed by : none
replaced boolean return with false for com/renomad/minum/web/Request::equals → TIMED_OUT

117

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

2.2
Location : equals
Killed by : none
replaced boolean return with true for com/renomad/minum/web/Request::equals → TIMED_OUT

3.3
Location : equals
Killed by : none
negated conditional → TIMED_OUT

119

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

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

3.3
Location : equals
Killed by : none
negated conditional → TIMED_OUT

4.4
Location : equals
Killed by : none
negated conditional → TIMED_OUT

5.5
Location : equals
Killed by : none
negated conditional → TIMED_OUT

6.6
Location : equals
Killed by : none
negated conditional → TIMED_OUT

7.7
Location : equals
Killed by : none
negated conditional → TIMED_OUT

8.8
Location : equals
Killed by : none
negated conditional → TIMED_OUT

124

1.1
Location : hashCode
Killed by : none
replaced int return with 0 for com/renomad/minum/web/Request::hashCode → TIMED_OUT

129

1.1
Location : toString
Killed by : com.renomad.minum.web.RequestTests.test_Request_ToString(com.renomad.minum.web.RequestTests)
replaced return value with "" for com/renomad/minum/web/Request::toString → KILLED

141

1.1
Location : getUrlEncodedIterable
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_7(com.renomad.minum.web.RequestTests)
removed call to com/renomad/minum/web/Request::checkForExistingBody → KILLED

142

1.1
Location : getUrlEncodedIterable
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_4(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

145

1.1
Location : getUrlEncodedIterable
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest2(com.renomad.minum.web.RequestTests)
replaced return value with Collections.emptyList for com/renomad/minum/web/Request::getUrlEncodedIterable → KILLED

155

1.1
Location : checkForExistingBody
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_7(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

159

1.1
Location : checkForExistingBody
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_7(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

166

1.1
Location : getMultipartIterable
Killed by : com.renomad.minum.web.RequestTests.testRequest_ExpectComplaintAfterBegunReading_8(com.renomad.minum.web.RequestTests)
removed call to com/renomad/minum/web/Request::checkForExistingBody → KILLED

167

1.1
Location : getMultipartIterable
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest3(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

174

1.1
Location : getMultipartIterable
Killed by : none
changed conditional boundary → SURVIVED
Covering tests

2.2
Location : getMultipartIterable
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest3(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

176

1.1
Location : getMultipartIterable
Killed by : com.renomad.minum.web.RequestTests.test_Request_getMultipartIterable_EdgeCase_No_Valid_Boundary_2(com.renomad.minum.web.RequestTests)
Replaced integer addition with subtraction → KILLED

182

1.1
Location : getMultipartIterable
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest3(com.renomad.minum.web.RequestTests)
negated conditional → KILLED

187

1.1
Location : getMultipartIterable
Killed by : com.renomad.minum.web.RequestTests.testSimplerRequest3(com.renomad.minum.web.RequestTests)
replaced return value with Collections.emptyList for com/renomad/minum/web/Request::getMultipartIterable → KILLED

Active mutators

Tests examined


Report generated by PIT 1.17.0