/** * Create a copy of this HTTP response, replacing its statusLine with the provided one. * * @param statusLine to replace * @return copy of this HTTP message with the provided statusLine */ public RawHttpResponse<Response> withStatusLine(StatusLine statusLine) { return new RawHttpResponse<>(libResponse, request, statusLine, getHeaders(), getBody().orElse(null)); }
@Override public RawHttpResponse<Void> onResponse(Socket socket, URI uri, RawHttpResponse<Void> httpResponse) throws IOException { if (httpResponse.getHeaders() .getFirst("Connection") .orElse("") .equalsIgnoreCase("close") || httpResponse.getStartLine().getHttpVersion().isOlderThan(HttpVersion.HTTP_1_1)) { socketByHost.remove(uri.getHost()); // resolve the full response before closing the socket return httpResponse.eagerly(false); } return httpResponse; }
@Override public RawHttpResponse<Response> withHeaders(RawHttpHeaders headers) { return withHeaders(headers, true); }
@Override public RawHttpResponse<Response> withBody(HttpMessageBody body) { return new RawHttpResponse<>(libResponse, request, statusLine, body.headersFrom(getHeaders()), body.toBodyReader()); }
@Nullable EagerBodyReader bodyReader = response.getBody().isPresent() ? response.getBody().get().eager() : null; .map(ChunkedBodyContents::getTrailerHeaders) .orElse(emptyRawHttpHeaders()); headers = RawHttpHeaders.newBuilder(response.getHeaders()) .merge(trailingHeaders) .build(); } else { headers = response.getHeaders(); return new EagerHttpResponse<>(response.getLibResponse().orElse(null), response.getRequest().orElse(null), response.getStartLine(), headers, bodyReader);
"\r\n" + body); response.writeTo(client.getOutputStream()); } catch (IOException e) { e.printStackTrace(); RawHttpResponse<?> response = http.parseResponse(socket.getInputStream()).eagerly(); System.out.println("RESPONSE:\n" + response); assertEquals(200, response.getStatusCode()); assertTrue(response.getBody().isPresent()); assertEquals("Hello RawHTTP!", response.getBody().get().decodeBodyToString(UTF_8));
@Override public void logRequest(RawHttpRequest request, RawHttpResponse<?> response) { executor.submit(() -> { if (request.getSenderAddress().isPresent()) { InetAddress senderAddress = request.getSenderAddress().get(); System.out.print(senderAddress.getHostAddress() + " "); } Long bytes = response.getBody() .map(b -> b.getLengthIfKnown().orElse(-1L)) .orElse(-1L); System.out.println("[" + LocalDateTime.now().format(dateFormat) + "] \"" + request.getStartLine() + "\" " + response.getStatusCode() + " " + bytes); }); }
RawHttpResponse<?> response = http.parseResponse(socket.getInputStream()).eagerly(); System.out.println("RESPONSE:\n" + response); assertEquals(200, response.getStatusCode()); assertTrue(response.getBody().isPresent()); String textBody = response.getBody().get().decodeBodyToString(UTF_8); assertTrue(textBody.contains(jsonBody.replaceAll(" ", ""))); response.getHeaders().getFirst("Content-Type").orElse("")); response.getHeaders().get("Content-Type"));
@Test public void useHttpServer() throws InterruptedException, IOException { RawHttpServer server = new TcpRawHttpServer(8086); RawHttp http = new RawHttp(); server.start(request -> { System.out.println("Got Request:\n" + request); String body = "Hello RawHTTP!"; String dateString = RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)); RawHttpResponse<?> response = http.parseResponse("HTTP/1.1 200 OK\r\n" + "Content-Type: plain/text\r\n" + "Content-Length: " + body.length() + "\r\n" + "Server: RawHTTP\r\n" + "Date: " + dateString + "\r\n" + "\r\n" + body); return Optional.of(response); }); // wait for the socket get bound Thread.sleep(150L); RawHttpRequest request = http.parseRequest("GET /\r\nHost: localhost"); Socket socket = new Socket(InetAddress.getLoopbackAddress(), 8086); request.writeTo(socket.getOutputStream()); // get the response RawHttpResponse<?> response = http.parseResponse(socket.getInputStream()).eagerly(); System.out.println("RESPONSE:\n" + response); assertEquals(200, response.getStatusCode()); assertTrue(response.getBody().isPresent()); assertEquals("Hello RawHTTP!", response.getBody().get().decodeBodyToString(UTF_8)); server.stop(); }
@Override public Optional<EagerBodyReader> getBody() { Optional<? extends BodyReader> body = super.getBody(); return body.map(b -> (EagerBodyReader) b); }
/** * Ensure that this response is read eagerly, downloading the full body if necessary. * <p> * The returned object can be safely passed around after the connection used to receive * this response has been closed. * <p> * The connection or stream used to download the response is NOT closed after a call to * this method. Use {@link #eagerly(boolean)} if a different behaviour is required. * * @return this response, after eagerly downloading all of its contents. * @throws IOException if an error occurs while reading this response */ public EagerHttpResponse<Response> eagerly() throws IOException { return eagerly(true); }
@Override public Optional<RawHttpResponse<?>> route(RawHttpRequest request) { final Optional<RawHttpResponse<?>> response; if (request.getMethod().equals("GET")) { String path = request.getStartLine().getUri() .normalize().getPath() .replaceAll(DIR_BACK_PATTERN, ""); // provide the index.html file at the root path if (path.isEmpty() || path.equals("/")) { path = "index.html"; } Optional<FileResult> resource = fileLocator.find(path, request.getHeaders().get("Accept")); response = resource.map(fileResult -> HttpResponses.getOkResponse(request.getStartLine().getHttpVersion()) .withHeaders(fileResult.contentTypeHeader) .withBody(new FileBody(fileResult.file))); } else { response = Optional.of(HttpResponses.getMethodNotAllowedResponse(request.getStartLine().getHttpVersion())); } return response; }
try { response = route(request); response.writeTo(client.getOutputStream()); } finally { closeBodyOf(response);
/** * Parses the HTTP response produced by the given stream. * * @param inputStream producing a HTTP response * @param requestLine optional {@link RequestLine} of the request which results in this response. * If provided, it is taken into consideration when deciding whether the response contains * a body. See <a href="https://tools.ietf.org/html/rfc7230#section-3.3">Section 3.3</a> * of RFC-7230 for details. * @return a parsed HTTP response object * @throws InvalidHttpResponse if the response is invalid * @throws IOException if a problem occurs accessing the stream */ public RawHttpResponse<Void> parseResponse(InputStream inputStream, @Nullable RequestLine requestLine) throws IOException { StatusLine statusLine = metadataParser.parseStatusLine(inputStream); RawHttpHeaders headers = metadataParser.parseHeaders(inputStream, (message, lineNumber) -> // add 1 to the line number to correct for the start-line new InvalidHttpResponse(message, lineNumber + 1)); @Nullable BodyReader bodyReader = responseHasBody(statusLine, requestLine) ? createBodyReader(inputStream, statusLine, headers) : null; return new RawHttpResponse<>(null, null, statusLine, headers, bodyReader); }
@Nullable EagerBodyReader bodyReader = response.getBody().isPresent() ? response.getBody().get().eager() : null; .map(ChunkedBodyContents::getTrailerHeaders) .orElse(emptyRawHttpHeaders()); headers = RawHttpHeaders.newBuilder(response.getHeaders()) .merge(trailingHeaders) .build(); } else { headers = response.getHeaders(); return new EagerHttpResponse<>(response.getLibResponse().orElse(null), response.getRequest().orElse(null), response.getStartLine(), headers, bodyReader);
.withBody(new StreamedChunkedBody(sender.getChunkStream()))); if (response.getStatusCode() != 200) { if (client instanceof Closeable) { ((Closeable) client).close(); throw new RuntimeException("Server response status code is not 200: " + response.getStatusCode()); InputStream responseStream = response.getBody().map(b -> b.isChunked() ? b.asRawStream() : null) .orElseThrow(() -> new IllegalStateException("HTTP response does not contain a chunked body"));
@Override public Optional<EagerBodyReader> getBody() { Optional<? extends BodyReader> body = super.getBody(); return body.map(b -> (EagerBodyReader) b); }
/** * Ensure that this response is read eagerly, downloading the full body if necessary. * <p> * The returned object can be safely passed around after the connection used to receive * this response has been closed. * <p> * The connection or stream used to download the response is NOT closed after a call to * this method. Use {@link #eagerly(boolean)} if a different behaviour is required. * * @return this response, after eagerly downloading all of its contents. * @throws IOException if an error occurs while reading this response */ public EagerHttpResponse<Response> eagerly() throws IOException { return eagerly(true); }
@Override public RawHttpResponse<Response> withBody(HttpMessageBody body) { return new RawHttpResponse<>(libResponse, request, statusLine, body.headersFrom(getHeaders()), body.toBodyReader()); }