/** * Create a new instance of {@link RawHttpDuplex} that uses the given options. * <p> * This is the most general constructor, as {@link RawHttpDuplexOptions} can provide all options other constructors * accept. * * @param options to use for this instance */ public RawHttpDuplex(RawHttpDuplexOptions options) { this.okResponse = new RawHttp().parseResponse("200 OK"); this.options = options; }
/** * Parses the HTTP request produced by the given stream. * * @param inputStream producing a HTTP request * @return a parsed HTTP request object * @throws InvalidHttpRequest if the request is invalid * @throws IOException if a problem occurs accessing the stream */ public RawHttpRequest parseRequest(InputStream inputStream) throws IOException { return parseRequest(inputStream, null); }
private BodyReader createBodyReader(InputStream inputStream, StartLine startLine, RawHttpHeaders headers) { return new LazyBodyReader(getFramedBody(startLine, headers), inputStream); }
@Test public void goingRawWithoutFancyClient() throws IOException { RawHttp rawHttp = new RawHttp(); RawHttpRequest request = rawHttp.parseRequest(String.format("GET localhost:%d/hello HTTP/1.0", PORT)); Socket socket = new Socket("localhost", PORT); request.writeTo(socket.getOutputStream()); EagerHttpResponse<?> response = rawHttp.parseResponse(socket.getInputStream()).eagerly(); assertThat(response.getStatusCode(), is(200)); assertThat(response.getBody().map(EagerBodyReader::toString) .orElseThrow(() -> new RuntimeException("No body")), equalTo("Hello")); }
@Test(expected = FileNotFoundException.class) public void replacingBodyWithFile() throws Throwable { RawHttp http = new RawHttp(); RawHttpRequest request = http.parseRequest("POST http://example.com/hello"); try { RawHttpRequest requestWithBody = request.withBody( new FileBody(new File("hello.request"), "text/plain")); System.out.println(requestWithBody.eagerly()); } catch (RuntimeException e) { throw e.getCause(); } }
@Test public void rudimentaryHttpServerCalledFromHttpComponentsClient() throws Exception { RawHttp http = new RawHttp(); ServerSocket server = new ServerSocket(8084); try { Socket client = server.accept(); RawHttpRequest request = http.parseRequest(client.getInputStream()); System.out.println("REQUEST:\n" + request); if (request.getUri().getPath().equals("/saysomething")) { http.parseResponse("HTTP/1.1 200 OK\n" + "Content-Type: text/plain\n" + "Content-Length: 9\n" + "something").writeTo(client.getOutputStream()); } else { http.parseResponse("HTTP/1.1 404 Not Found\n" + "Content-Type: text/plain\n" + "Content-Length: 0\n" + RawHttp.waitForPortToBeTaken(8084, Duration.ofSeconds(2));
/** * Parses the HTTP response produced by the given stream. * * @param inputStream producing a HTTP response * @return a parsed HTTP response object * @throws InvalidHttpResponse if the response is invalid * @throws IOException if a problem occurs accessing the stream */ public final RawHttpResponse<Void> parseResponse(InputStream inputStream) throws IOException { return parseResponse(inputStream, null); }
public RawHttpComponentsClient(CloseableHttpClient httpClient) { this(httpClient, new RawHttp()); }
/** * Parses the HTTP request produced by the given stream. * * @param inputStream producing a HTTP request * @param senderAddress the address of the request sender, if known * @return a parsed HTTP request object * @throws InvalidHttpRequest if the request is invalid * @throws IOException if a problem occurs accessing the stream */ public RawHttpRequest parseRequest(InputStream inputStream, @Nullable InetAddress senderAddress) throws IOException { RequestLine requestLine = metadataParser.parseRequestLine(inputStream); RawHttpHeaders originalHeaders = metadataParser.parseHeaders(inputStream, (message, lineNumber) -> // add 1 to the line number to correct for the start-line new InvalidHttpRequest(message, lineNumber + 1)); RawHttpHeaders.Builder modifiableHeaders = RawHttpHeaders.newBuilder(originalHeaders); // do a little cleanup to make sure the request is actually valid requestLine = verifyHost(requestLine, modifiableHeaders); RawHttpHeaders headers = modifiableHeaders.build(); @Nullable BodyReader bodyReader = requestHasBody(headers) ? createBodyReader(inputStream, requestLine, headers) : null; return new RawHttpRequest(requestLine, headers, bodyReader, senderAddress); }
/** * 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); }
/** * Determines whether a response with the given status-line should have a body. * <p> * This method ignores the request-line of the request which produced such response. If the request * is known, use the {@link #responseHasBody(StatusLine, RequestLine)} method instead. * * @param statusLine status-line of response * @return true if such response has a body, false otherwise */ public static boolean responseHasBody(StatusLine statusLine) { return responseHasBody(statusLine, null); }
/** * Determines whether a response with the given status-line should have a body. * <p> * If provided, the request-line of the request which produced such response is taken into * consideration. See <a href="https://tools.ietf.org/html/rfc7230#section-3.3">Section 3.3</a> * of RFC-7230 for details. * * @param statusLine status-line of response * @param requestLine request-line of request, if any * @return true if such response has a body, false otherwise */ public static boolean responseHasBody(StatusLine statusLine, @Nullable RequestLine requestLine) { if (requestLine != null) { if (requestLine.getMethod().equalsIgnoreCase("HEAD")) { return false; // HEAD response must never have a body } if (requestLine.getMethod().equalsIgnoreCase("CONNECT") && startsWith(2, statusLine.getStatusCode())) { return false; // CONNECT successful means start tunelling } } int statusCode = statusLine.getStatusCode(); // All 1xx (Informational), 204 (No Content), and 304 (Not Modified) // responses do not include a message body. boolean hasNoBody = startsWith(1, statusCode) || statusCode == 204 || statusCode == 304; return !hasNoBody; }
@Test public void frontPageExample() throws IOException { RawHttp rawHttp = new RawHttp(); RawHttpRequest request = rawHttp.parseRequest( "GET /hello.txt HTTP/1.1\r\n" + "User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3\r\n" + "Host: www.example.com\r\n" + "Accept-Language: en, mi"); Socket socket = new Socket("www.example.com", 80); request.writeTo(socket.getOutputStream()); EagerHttpResponse<?> response = rawHttp.parseResponse(socket.getInputStream()).eagerly(); // call "eagerly()" in order to download the body System.out.println(response.eagerly()); assertThat(response.getStatusCode(), equalTo(404)); assertTrue(response.getBody().isPresent()); File responseFile = Files.createTempFile("rawhttp", ".http").toFile(); try (OutputStream out = Files.newOutputStream(responseFile.toPath())) { response.writeTo(out); } System.out.printf("Response parsed from file (%s):", responseFile); System.out.println(rawHttp.parseResponse(responseFile).eagerly()); }
@Test public void replacingBodyWithChunkedEncodedMessage() throws IOException { InputStream stream = new ByteArrayInputStream("Hello RawHTTTP".getBytes()); int chunkSize = 4; RawHttp http = new RawHttp(); RawHttpRequest request = http.parseRequest("POST http://example.com/hello"); RawHttpRequest requestWithBody = request.withBody( new ChunkedBody(stream, "text/plain", chunkSize)); System.out.println(requestWithBody.eagerly()); }
/** * Parses the HTTP response produced by the given stream. * * @param inputStream producing a HTTP response * @return a parsed HTTP response object * @throws InvalidHttpResponse if the response is invalid * @throws IOException if a problem occurs accessing the stream */ public final RawHttpResponse<Void> parseResponse(InputStream inputStream) throws IOException { return parseResponse(inputStream, null); }
/** * Create a new {@link TcpRawHttpClient} using the given options, which can give a custom * socketProvider and onClose callback. * <p> * If no options are given, {@link DefaultOptions} is used. * * @param options configuration for this client */ public TcpRawHttpClient(@Nullable TcpRawHttpClientOptions options) { this(options == null ? new DefaultOptions() : options, new RawHttp(RawHttpOptions.newBuilder() .doNotAllowNewLineWithoutReturn() .build())); }
/** * Parses the HTTP request produced by the given stream. * * @param inputStream producing a HTTP request * @param senderAddress the address of the request sender, if known * @return a parsed HTTP request object * @throws InvalidHttpRequest if the request is invalid * @throws IOException if a problem occurs accessing the stream */ public RawHttpRequest parseRequest(InputStream inputStream, @Nullable InetAddress senderAddress) throws IOException { RequestLine requestLine = metadataParser.parseRequestLine(inputStream); RawHttpHeaders originalHeaders = metadataParser.parseHeaders(inputStream, (message, lineNumber) -> // add 1 to the line number to correct for the start-line new InvalidHttpRequest(message, lineNumber + 1)); RawHttpHeaders.Builder modifiableHeaders = RawHttpHeaders.newBuilder(originalHeaders); // do a little cleanup to make sure the request is actually valid requestLine = verifyHost(requestLine, modifiableHeaders); RawHttpHeaders headers = modifiableHeaders.build(); @Nullable BodyReader bodyReader = requestHasBody(headers) ? createBodyReader(inputStream, requestLine, headers) : null; return new RawHttpRequest(requestLine, headers, bodyReader, senderAddress); }
/** * 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); }
/** * Determines whether a response with the given status-line should have a body. * <p> * This method ignores the request-line of the request which produced such response. If the request * is known, use the {@link #responseHasBody(StatusLine, RequestLine)} method instead. * * @param statusLine status-line of response * @return true if such response has a body, false otherwise */ public static boolean responseHasBody(StatusLine statusLine) { return responseHasBody(statusLine, null); }
/** * Determines whether a response with the given status-line should have a body. * <p> * If provided, the request-line of the request which produced such response is taken into * consideration. See <a href="https://tools.ietf.org/html/rfc7230#section-3.3">Section 3.3</a> * of RFC-7230 for details. * * @param statusLine status-line of response * @param requestLine request-line of request, if any * @return true if such response has a body, false otherwise */ public static boolean responseHasBody(StatusLine statusLine, @Nullable RequestLine requestLine) { if (requestLine != null) { if (requestLine.getMethod().equalsIgnoreCase("HEAD")) { return false; // HEAD response must never have a body } if (requestLine.getMethod().equalsIgnoreCase("CONNECT") && startsWith(2, statusLine.getStatusCode())) { return false; // CONNECT successful means start tunelling } } int statusCode = statusLine.getStatusCode(); // All 1xx (Informational), 204 (No Content), and 304 (Not Modified) // responses do not include a message body. boolean hasNoBody = startsWith(1, statusCode) || statusCode == 204 || statusCode == 304; return !hasNoBody; }