HttpCallerServiceImpl.java

package space.sunqian.common.net.http;

import space.sunqian.annotations.Nonnull;
import space.sunqian.annotations.Nullable;
import space.sunqian.common.Fs;
import space.sunqian.common.base.math.MathKit;
import space.sunqian.common.io.IOKit;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.util.List;
import java.util.Map;

enum HttpCallerServiceImpl implements HttpCallerService {
    INST;

    @Override
    public @Nonnull HttpCaller newCaller(int bufSize, @Nonnull Proxy proxy) throws HttpNetException {
        return new CallerImpl(bufSize, proxy);
    }

    private static final class CallerImpl implements HttpCaller {

        // private final @Nonnull IOOperator io;
        private final int bufSize;
        private final @Nonnull Proxy proxy;

        CallerImpl(int bufSize, @Nonnull Proxy proxy) throws HttpNetException {
            this.bufSize = bufSize;
            this.proxy = proxy;
        }

        @Override
        public @Nonnull HttpResp request(@Nonnull HttpReq req) throws HttpNetException {
            return Fs.uncheck(() -> request0(req), HttpNetException::new);
        }

        private @Nonnull HttpResp request0(@Nonnull HttpReq req) throws Exception {
            HttpURLConnection connection = (HttpURLConnection) req.url().openConnection(proxy);
            connection.setConnectTimeout(MathKit.intValue(req.timeout().toMillis()));
            connection.setReadTimeout(MathKit.intValue(req.timeout().toMillis()));
            req.headers().forEach((k, list) -> {
                for (String s : list) {
                    connection.addRequestProperty(k, s);
                }
            });
            // connection.setDoInput(true);
            connection.setRequestMethod(req.method());
            HttpReq.Body body = req.body();
            if (body != null) {
                InputStream in = body.toInputStream();
                int firstByte = in.read();
                if (firstByte != -1) {
                    connection.setDoOutput(true);
                    OutputStream out = connection.getOutputStream();
                    out.write(firstByte);
                    IOKit.readTo(in, out, new byte[bufSize]);
                }
            }
            int respCode = connection.getResponseCode();
            String respMsg = connection.getResponseMessage();
            String firstLine = Fs.nonnull(connection.getHeaderField(null), "");
            String protocol = firstLine.substring(0, Math.max(firstLine.indexOf(' '), 0));
            Map<String, List<String>> respHeaders = connection.getHeaderFields();
            String respContentType = connection.getContentType();
            InputStream errBody = connection.getErrorStream();
            InputStream respBody = errBody != null ? errBody : connection.getInputStream();
            return new HttpResp() {

                @Override
                public @Nonnull String protocolVersion() {
                    return protocol;
                }

                @Override
                public @Nonnull String statusCode() {
                    return Integer.toString(respCode);
                }

                @Override
                public @Nonnull String statusText() {
                    return respMsg;
                }

                @Override
                public @Nonnull Map<String, List<String>> headers() {
                    return respHeaders;
                }

                @Override
                public @Nonnull InputStream body() {
                    return respBody;
                }

                @Override
                public @Nullable String contentType() {
                    return respContentType;
                }
            };
        }
    }
}