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

com.iwbfly.myhttp.http.Response Maven / Gradle / Ivy

The newest version!
package com.iwbfly.myhttp.http;

import com.iwbfly.myhttp.utils.Util;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;


/**
 * @author pangyajun
 * @since 2021/11/23 09:52
 **/
public final class Response implements Closeable {

    private final int status;
    private final String reason;
    private final Map> headers;
    private final Body body;
    private final Request request;

    private Response(Builder builder) {
        Util.checkState(builder.request != null, "original request is required");
        this.status = builder.status;
        this.request = builder.request;
        this.reason = builder.reason; // nullable
        this.headers = (builder.headers != null)
                ? Collections.unmodifiableMap(caseInsensitiveCopyOf(builder.headers))
                : new LinkedHashMap<>();
        this.body = builder.body; // nullable
    }

    public Builder toBuilder() {
        return new Builder(this);
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        int status;
        String reason;
        Map> headers;
        Body body;
        Request request;

        Builder() {}

        Builder(Response source) {
            this.status = source.status;
            this.reason = source.reason;
            this.headers = source.headers;
            this.body = source.body;
            this.request = source.request;
        }

        /** @see Response#status */
        public Builder status(int status) {
            this.status = status;
            return this;
        }

        /** @see Response#reason */
        public Builder reason(String reason) {
            this.reason = reason;
            return this;
        }

        /** @see Response#headers */
        public Builder headers(Map> headers) {
            this.headers = headers;
            return this;
        }

        /** @see Response#body */
        public Builder body(Body body) {
            this.body = body;
            return this;
        }

        /** @see Response#body */
        public Builder body(InputStream inputStream, Integer length) {
            this.body = InputStreamBody.orNull(inputStream, length);
            return this;
        }

        /** @see Response#body */
        public Builder body(byte[] data) {
            this.body = ByteArrayBody.orNull(data);
            return this;
        }

        /** @see Response#body */
        public Builder body(String text, Charset charset) {
            this.body = ByteArrayBody.orNull(text, charset);
            return this;
        }

        /**
         * @see Response#request
         */
        public Builder request(Request request) {
            Util.checkNotNull(request, "request is required");
            this.request = request;
            return this;
        }

        public Response build() {
            return new Response(this);
        }
    }

    /**
     * status code. ex {@code 200}
     *
     * See rfc2616
     * @return status
     */

    public int status() {
        return status;
    }

    /**
     * Nullable and not set when using http/2
     *
     * See https://github.com/http2/http2-spec/issues/202
     * @return reason
     */
    public String reason() {
        return reason;
    }

    /**
     * Returns a case-insensitive mapping of header names to their values.
     * @return headers
     */
    public Map> headers() {
        return headers;
    }

    /**
     * if present, the response had a body
     * @return body
     */
    public Body body() {
        return body;
    }

    /**
     * the request that generated this response
     * @return request
     */
    public Request request() {
        return request;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder("HTTP/1.1 ").append(status);
        if (reason != null)
            builder.append(' ').append(reason);
        builder.append('\n');
        for (String field : headers.keySet()) {
            for (String value : Util.valuesOrEmpty(headers, field)) {
                builder.append(field).append(": ").append(value).append('\n');
            }
        }
        if (body != null)
            builder.append('\n').append(body);
        return builder.toString();
    }

    @Override
    public void close() {
        Util.ensureClosed(body);
    }

    public interface Body extends Closeable {

        /**
         * length in bytes, if known. Null if unknown or greater than {@link Integer#MAX_VALUE}.
         *
         * 
*
*
* Note
* This is an integer as most implementations cannot do bodies greater than 2GB. */ Integer length(); /** * True if {@link #asInputStream()} and {@link #asReader()} can be called more than once. */ boolean isRepeatable(); /** * It is the responsibility of the caller to close the stream. */ InputStream asInputStream() throws IOException; /** * It is the responsibility of the caller to close the stream. */ Reader asReader() throws IOException; /** * */ Reader asReader(Charset charset) throws IOException; public String string() throws IOException; } private static final class InputStreamBody implements Response.Body { private final InputStream inputStream; private final Integer length; private InputStreamBody(InputStream inputStream, Integer length) { this.inputStream = inputStream; this.length = length; } private static Body orNull(InputStream inputStream, Integer length) { if (inputStream == null) { return null; } return new InputStreamBody(inputStream, length); } @Override public Integer length() { return length; } @Override public boolean isRepeatable() { return false; } @Override public InputStream asInputStream() throws IOException { return inputStream; } @Override public Reader asReader() throws IOException { return new InputStreamReader(inputStream, Util.UTF_8); } @Override public Reader asReader(Charset charset) throws IOException { Util.checkNotNull(charset, "charset should not be null"); return new InputStreamReader(inputStream, charset); } @Override public String string() throws IOException { int readSize; byte[] bytes = null; bytes = new byte[length]; long length_tmp = length; long index = 0;// start from zero while ((readSize = inputStream.read(bytes, (int) index, (int) length_tmp)) != -1) { length_tmp -= readSize; if (length_tmp == 0) { break; } index = index + readSize; } return new String(bytes); } @Override public void close() throws IOException { inputStream.close(); } } private static final class ByteArrayBody implements Response.Body { private final byte[] data; public ByteArrayBody(byte[] data) { this.data = data; } private static Body orNull(byte[] data) { if (data == null) { return null; } return new ByteArrayBody(data); } private static Body orNull(String text, Charset charset) { if (text == null) { return null; } Util.checkNotNull(charset, "charset"); return new ByteArrayBody(text.getBytes(charset)); } @Override public Integer length() { return data.length; } @Override public boolean isRepeatable() { return true; } @Override public InputStream asInputStream() throws IOException { return new ByteArrayInputStream(data); } @Override public Reader asReader() throws IOException { return new InputStreamReader(asInputStream(), Util.UTF_8); } @Override public Reader asReader(Charset charset) throws IOException { Util.checkNotNull(charset, "charset should not be null"); return new InputStreamReader(asInputStream(), charset); } @Override public String string() throws IOException { return new String(data); } @Override public void close() throws IOException {} @Override public String toString() { return Util.decodeOrDefault(data, Util.UTF_8, "Binary data"); } } private static Map> caseInsensitiveCopyOf(Map> headers) { Map> result = new TreeMap>(String.CASE_INSENSITIVE_ORDER); for (Map.Entry> entry : headers.entrySet()) { String headerName = entry.getKey(); if (!result.containsKey(headerName)) { result.put(headerName.toLowerCase(Locale.ROOT), new LinkedList()); } result.get(headerName).addAll(entry.getValue()); } return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy