001package io.freefair.spring.okhttp.client;
002
003import okhttp3.*;
004import okio.Buffer;
005import okio.ByteString;
006import org.springframework.http.HttpHeaders;
007import org.springframework.http.HttpMethod;
008import org.springframework.http.StreamingHttpOutputMessage;
009import org.springframework.http.client.AbstractClientHttpRequest;
010import org.springframework.http.client.ClientHttpRequest;
011import org.springframework.http.client.ClientHttpResponse;
012import org.springframework.lang.Nullable;
013import org.springframework.util.Assert;
014import org.springframework.util.StringUtils;
015import java.io.IOException;
016import java.io.OutputStream;
017import java.net.MalformedURLException;
018import java.net.URI;
019
020/**
021 * OkHttp based {@link ClientHttpRequest} implementation.
022 *
023 * @author Lars Grefer
024 * @see OkHttpClientRequestFactory
025 */
026public class OkHttpClientRequest extends AbstractClientHttpRequest implements StreamingHttpOutputMessage {
027    private final OkHttpClient okHttpClient;
028    private final URI uri;
029    private final HttpMethod method;
030    @Nullable
031    private Body streamingBody;
032    @Nullable
033    private Buffer bufferBody;
034
035    @Override
036    public HttpMethod getMethod() {
037        return method;
038    }
039
040    @Override
041    public URI getURI() {
042        return uri;
043    }
044
045    @Override
046    public void setBody(Body body) {
047        Assert.notNull(body, "body must not be null");
048        assertNotExecuted();
049        Assert.state(bufferBody == null, "getBody has already been used.");
050        this.streamingBody = body;
051    }
052
053    @Override
054    protected OutputStream getBodyInternal(HttpHeaders headers) {
055        Assert.state(this.streamingBody == null, "setBody has already been used.");
056        if (bufferBody == null) {
057            bufferBody = new Buffer();
058        }
059        return bufferBody.outputStream();
060    }
061
062    @Override
063    protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
064        Request okHttpRequest = buildRequest(headers);
065        Response okHttpResponse = this.okHttpClient.newCall(okHttpRequest).execute();
066        return new OkHttpClientResponse(okHttpResponse);
067    }
068
069    private Request buildRequest(HttpHeaders headers) throws MalformedURLException {
070        Request.Builder builder = new Request.Builder();
071        builder.url(uri.toURL());
072        MediaType contentType = null;
073        String contentTypeHeader = headers.getFirst(HttpHeaders.CONTENT_TYPE);
074        if (StringUtils.hasText(contentTypeHeader)) {
075            contentType = MediaType.parse(contentTypeHeader);
076        }
077        RequestBody body = null;
078        if (bufferBody != null) {
079            ByteString bodyData = bufferBody.readByteString();
080            if (headers.getContentLength() < 0) {
081                headers.setContentLength(bodyData.size());
082            }
083            body = RequestBody.create(bodyData, contentType);
084        } else if (streamingBody != null) {
085            body = new StreamingBodyRequestBody(streamingBody, contentType, headers.getContentLength());
086        } else if (okhttp3.internal.http.HttpMethod.requiresRequestBody(method.name())) {
087            body = RequestBody.create(new byte[0], contentType);
088        }
089        builder.method(getMethod().name(), body);
090        headers.forEach((name, values) -> {
091            for (String value : values) {
092                builder.addHeader(name, value);
093            }
094        });
095        return builder.build();
096    }
097
098    public OkHttpClientRequest(final OkHttpClient okHttpClient, final URI uri, final HttpMethod method) {
099        this.okHttpClient = okHttpClient;
100        this.uri = uri;
101        this.method = method;
102    }
103}