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

org.zalando.logbook.okhttp.LocalRequest Maven / Gradle / Ivy

package org.zalando.logbook.okhttp;

import lombok.AllArgsConstructor;
import lombok.Getter;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.RequestBody;
import okio.Buffer;
import org.zalando.logbook.HttpHeaders;
import org.zalando.logbook.HttpRequest;
import org.zalando.logbook.Origin;

import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

import static java.nio.charset.StandardCharsets.UTF_8;
import static okhttp3.HttpUrl.defaultPort;
import static okhttp3.RequestBody.create;
import static org.zalando.fauxpas.FauxPas.throwingUnaryOperator;

@AllArgsConstructor
final class LocalRequest implements HttpRequest {

    private final AtomicReference state = new AtomicReference<>(new Unbuffered());

    private final Request request;

    private interface State {

        default State with() {
            return this;
        }

        default State without() {
            return this;
        }

        default State buffer() throws IOException {
            return this;
        }

        Request getRequest();

        default byte[] getBody() {
            return new byte[0];
        }

    }

    @AllArgsConstructor
    private abstract class AbstractState implements State {

        @Override
        public Request getRequest() {
            return request;
        }

    }

    private final class Unbuffered extends AbstractState {

        @Override
        public State with() {
            return new Offering();
        }

    }

    private final class Offering extends AbstractState {

        @Override
        public State without() {
            return new Unbuffered();
        }

        @Override
        public State buffer() throws IOException {
            @Nullable final RequestBody entity = request.body();

            if (entity == null) {
                return new Passing();
            } else {
                final Buffer buffer = new Buffer();
                entity.writeTo(buffer);
                final byte[] body = buffer.readByteArray();

                final Request copy = request.newBuilder()
                        .method(request.method(), create(body, entity.contentType()))
                        .build();

                return new Buffering(copy, body);
            }
        }

    }

    @AllArgsConstructor
    private static final class Buffering implements State {

        @Getter
        private final Request request;
        private final byte[] body;

        @Override
        public State without() {
            return new Ignoring(request, body);
        }

        @Override
        public byte[] getBody() {
            return body;
        }

    }

    @AllArgsConstructor
    private static final class Ignoring implements State {

        @Getter
        private final Request request;
        private final byte[] body;

        @Override
        public State with() {
            return new Buffering(request, body);
        }

    }

    private final class Passing extends AbstractState {

    }

    @Override
    public String getRemote() {
        return "localhost";
    }

    @Override
    public String getMethod() {
        return request.method();
    }

    @Override
    public String getScheme() {
        return request.url().scheme();
    }

    @Override
    public String getHost() {
        return request.url().host();
    }

    @Override
    public Optional getPort() {
        final int port = request.url().port();
        final int defaultPort = defaultPort(request.url().scheme());
        return port == defaultPort ? Optional.empty() : Optional.of(port);
    }

    @Override
    public String getPath() {
        return request.url().encodedPath();
    }

    @Override
    public String getQuery() {
        return Optional.ofNullable(request.url().query()).orElse("");
    }

    @Override
    public String getProtocolVersion() {
        // TODO find the real thing
        return "HTTP/1.1";
    }

    @Override
    public Origin getOrigin() {
        return Origin.LOCAL;
    }

    @Override
    public HttpHeaders getHeaders() {
        return HttpHeaders.of(request.headers().toMultimap());
    }

    @Override
    public String getContentType() {
        return contentType().map(MediaType::toString).orElse("");
    }

    @Override
    public Charset getCharset() {
        return contentType().map(MediaType::charset).orElse(UTF_8);
    }

    private Optional contentType() {
        return Optional.ofNullable(request.body())
                .map(RequestBody::contentType);
    }

    @Override
    public HttpRequest withBody() {
        state.updateAndGet(State::with);
        return this;
    }

    @Override
    public HttpRequest withoutBody() {
        state.updateAndGet(State::without);
        return this;
    }

    Request toRequest() {
        return buffer().getRequest();
    }

    @Override
    public byte[] getBody() {
        return buffer().getBody();
    }

    private State buffer() {
        return state.updateAndGet(throwingUnaryOperator(State::buffer));
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy