All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.dongliu.cute.http.ResponseContext Maven / Gradle / Ivy

The newest version!
package net.dongliu.cute.http;

import net.dongliu.commons.io.InputStreams;
import net.dongliu.commons.io.Readers;
import net.dongliu.commons.reflect.TypeInfer;
import net.dongliu.cute.http.exception.IllegalStatusCodeException;
import net.dongliu.cute.http.exception.JsonMarshallerNotFoundException;
import net.dongliu.cute.http.json.JsonMarshaller;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.IntPredicate;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Objects.requireNonNull;

/**
 * Raw blocking http response. The http headers already received, the http body not consumed, can be get as InputStream.
 * It you do not consume http response body, with {@link #readToString()}, {@link #readToBytes()},
 * {@link #writeTo(Path)} etc.., you need to close this raw response manually.
 * 

* This class is not thread-safe. * * @author Liu Dong */ public class ResponseContext implements AutoCloseable { private final RawResponse resp; // user specified charset private boolean decompressBody = true; @Nullable private final JsonMarshaller jsonMarshaller; ResponseContext(RawResponse resp, @Nullable JsonMarshaller jsonMarshaller) { this.resp = requireNonNull(resp); this.jsonMarshaller = jsonMarshaller; } /** * If decompress http response body automatically. Default True. */ public ResponseContext decompressBody(boolean decompressBody) { this.decompressBody = decompressBody; return this; } /** * Check the response status code. If not pass the predicate, throw {@link IllegalStatusCodeException} * * @throws IllegalArgumentException if check failed. */ public ResponseContext checkStatusCode(IntPredicate predicate) throws IllegalStatusCodeException { var info = resp.info(); if (!predicate.test(info.statusCode())) { throw new IllegalStatusCodeException(info.statusCode()); } return this; } /** * Handle response body with handler, return a new response with content as handler result. * HTTPResponseHandler should consume all InputStream data, or connection may close and cannot reuse. * The response is closed whether this call succeed or failed with exception. */ public Response handle(ResponseHandler responseHandler) { var info = this.resp.info(); InputStream body; if (decompressBody) { body = wrapCompressedInput(resp.method(), info.statusCode(), info.headers(), resp.body()); } else { body = resp.body(); } var respInfo = new ResponseInfo(info.statusCode(), info.headers()); try (body) { T result = responseHandler.handle(respInfo, body); return new Response<>(resp.url(), info.statusCode(), info.headers(), result); } catch (IOException e) { throw new UncheckedIOException(e); } } /** * Handle response content as reader. The reader passed to handler would be closed when handler finished or error occurred. * * @param charset the set used to decode the response stream * @param handler the handler * @param the response body type to convert to */ public Response handleAsReader(ResponseHandler handler, Charset charset) { return handle((info, body) -> { try (Reader reader = new InputStreamReader(body, charset)) { return handler.handle(info, reader); } }); } /** * Handle response content as reader. * This method will try to get response charset from header, if not set, will use UTF8. * The reader passed to handler would be closed when handler finished or error occurred. * * @param handler the handler * @param the response body type to convert to */ public Response handleAsReader(ResponseHandler handler) { var info = resp.info(); return handleAsReader(handler, info.getCharset().orElse(UTF_8)); } /** * Wrap response input stream if it is compressed */ private InputStream wrapCompressedInput(Method method, int status, Headers headers, InputStream input) { // if has no body, some server still set content-encoding header, // GZIPInputStream wrap empty input stream will cause exception. we should check this if (noBody(method, status)) { return input; } var contentEncoding = headers.getHeader(HeaderNames.CONTENT_ENCODING).orElse(""); //we should remove the content-encoding header here? switch (contentEncoding) { case "gzip": try { return new GZIPInputStream(input); } catch (IOException e) { throw new UncheckedIOException(e); } case "deflate": // Note: deflate implements may or may not wrap in zlib due to rfc confusing. // here deal with deflate without zlib header return new InflaterInputStream(input, new Inflater(true)); case "identity": default: return input; } } private boolean noBody(Method method, int status) { return method.equals(Method.HEAD) || (status >= 100 && status < 200) || status == StatusCodes.NOT_MODIFIED || status == StatusCodes.NO_CONTENT; } /** * Convert to response, with body as text. * This method will try to get response charset from header, if not set, will use UTF8. */ public Response readToString() { return handleAsReader((info, body) -> Readers.readAll(body)); } /** * Convert to response, with body as text. * * @param charset the charset to decode response body */ public Response readToString(Charset charset) { return handleAsReader((info, body) -> Readers.readAll(body), charset); } /** * Convert to response, with body as byte array */ public Response readToBytes() { return handle((info, body) -> body.readAllBytes()); } /** * Unmarshal response body as json. * This method will try to get response charset from header, if not set, will use UTF8. * * @param The json value type */ public Response decodeJson(Class type) { requireNonNull(type); if (jsonMarshaller == null) { throw new JsonMarshallerNotFoundException(); } return handleAsReader((info, body) -> jsonMarshaller.unmarshal(body, type)); } /** * Unmarshal response body as json. * This method will try to get response charset from header, if not set, will use UTF8. * * @param typeInfer for getting actual generic type * @param The json value type */ public Response decodeJson(TypeInfer typeInfer) { requireNonNull(typeInfer); if (jsonMarshaller == null) { throw new JsonMarshallerNotFoundException(); } return handleAsReader((info, body) -> jsonMarshaller.unmarshal(body, typeInfer.getType())); } /** * Unmarshal response body as json * * @param The json value type * @param charset the charset to decode response body */ public Response decodeJson(Class type, Charset charset) { requireNonNull(type); requireNonNull(charset); if (jsonMarshaller == null) { throw new JsonMarshallerNotFoundException(); } return handleAsReader((info, body) -> jsonMarshaller.unmarshal(body, type), charset); } /** * Unmarshal response body as json * * @param typeInfer for getting actual generic type * @param charset the charset to decode response body * @param The json value type */ public Response decodeJson(TypeInfer typeInfer, Charset charset) { requireNonNull(typeInfer); requireNonNull(charset); if (jsonMarshaller == null) { throw new JsonMarshallerNotFoundException(); } return handleAsReader((info, body) -> jsonMarshaller.unmarshal(body, typeInfer.getType()), charset); } /** * Write response body to file */ public Response writeTo(Path path) { return handle((info, body) -> { try (var out = Files.newOutputStream(path)) { body.transferTo(out); } return null; }); } /** * Write response body to OutputStream. OutputStream will not be closed. */ public Response writeTo(OutputStream out) { return handle((info, body) -> { body.transferTo(out); return null; }); } /** * Write response body to Writer, use charset detected from response header to decode response body. * If charset in header not set, will use utf-8 * The Writer will be leaved unclosed when finished or exception occurred. */ public Response writeTo(Writer writer) { requireNonNull(writer); return handleAsReader((info, body) -> { Readers.transferTo(body, writer); return null; }); } /** * Write response body to Writer. * The Writer will be leaved unclosed when finished or exception occurred. * * @param charset the charset to decode response body */ public Response writeTo(Writer writer, Charset charset) { requireNonNull(writer); requireNonNull(charset); return handleAsReader((info, body) -> { Readers.transferTo(body, writer); return null; }); } /** * Consume and discard this response body. */ public Response discard() { return handle((info, body) -> { InputStreams.discardAll(body); return null; }); } @Override public void close() { discard(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy