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

com.mycila.hc.transport.JdkHttpClient.groovy Maven / Gradle / Ivy

/**
 * Copyright (C) 2013 Mycila ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mycila.hc.transport

import com.mycila.hc.*
import com.mycila.hc.io.ContentDecoder
import com.mycila.hc.io.DeferredContentProvider
import com.mycila.hc.io.GZIPContentDecoder
import com.mycila.hc.io.NoContentDecoder

import javax.net.ssl.HostnameVerifier
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLSession
import java.nio.ByteBuffer

/**
 * @author Mathieu Carbou ([email protected])
 * @date 2014-02-12
 */
class JdkHttpClient extends HttpClient {

    @Override
    protected HttpResult send(HttpExchange exchange) throws Exception {

        DeferredContentProvider deferredContentProvider = null
        HttpRequest request = exchange.request
        HttpResponse response = exchange.response
        HttpURLConnection connection = ((HttpURLConnection) request.requestUrl.openConnection(config.proxy))

        try {
            connection.with {
                doInput = true
                doOutput = request.hasContent()
                requestMethod = request.method
                useCaches = config.allowCache
                instanceFollowRedirects = config.followRedirects
                connectTimeout = config.connectTimeout
                readTimeout = config.readTimeout
                long contentLength = request.headers.contentLength
                if (contentLength >= 0) {
                    fixedLengthStreamingMode = contentLength
                }
                if (request.headers.chunked) {
                    chunkedStreamingMode = config.maxBufferSize
                }
                for (Map.Entry header : request.headers.stringIterator(',')) {
                    setRequestProperty(header.key, header.value)
                }
            }

            // ssl verifier
            if (request.secured && !config.sslVerification) {
                HttpsURLConnection httpsCon = (HttpsURLConnection) connection
                httpsCon.hostnameVerifier = new HostnameVerifier() {
                    @Override
                    boolean verify(String s, SSLSession sslSession) { true }
                }
            }

            Log.trace('[%s] JdkHttpClient Connecting...', null, request.url)

            boolean done = false
            try {
                connection.connect()
            } catch (IOException ignored) {
                // in case of status >= 400
                done = true
            }

            // write request content
            if (!done && request.hasContent()) {

                Log.trace('[%s] JdkHttpClient Sending request body...', null, request.url)

                try {
                    OutputStream out = connection.outputStream
                    byte[] bytes = new byte[config.maxBufferSize]
                    for (ByteBuffer buffer : request.contentProvider) {
                        while (buffer.hasRemaining()) {

                            // gives a chance to abort request
                            if (Thread.currentThread().interrupted) {
                                throw new InterruptedException()
                            }

                            int len = Math.min(bytes.length, buffer.remaining())
                            buffer.get(bytes, 0, len)
                            out.write(bytes, 0, len)
                        }
                    }
                    out.close()
                } finally {
                    request.contentProvider.close()
                }
            }

            // get response code and headers
            response.status = connection.responseCode
            response.reason = connection.responseMessage
            connection.headerFields.each { k, list ->
                if (k != null) {
                    response.headers.add(k, list)
                }
            }

            Log.trace('[%s] JdkHttpClient onHeaders: %s %s', null, request.url, response.status, response.reason)

            request.listeners.onHeaders(exchange)

            // gives a chance to abort request
            if (Thread.currentThread().interrupted) {
                throw new InterruptedException()
            }

            // do not read response if not needed
            if (request.responseContentHandling == HttpClientConfig.ResponseContentHandling.IGNORED) {
                connection.disconnect()
                return new HttpResult(request, response)
            }

            // otherwise try to get fast an input stream
            InputStream stream = connection.errorStream
            if (stream == null) {
                stream = connection.inputStream
            }

            boolean gzip = response.headers.getFirst(HttpHeader.CONTENT_ENCODING) == 'gzip'
            boolean buffered = request.responseContentHandling == HttpClientConfig.ResponseContentHandling.BUFFERED

            Log.trace('[%s] JdkHttpClient Reading response content (gzip=%s, buffered=%s)...', null, request.url, gzip, buffered)

            // gzip decoding ?
            ContentDecoder decoder = gzip ? new GZIPContentDecoder(config.maxBufferSize) : new NoContentDecoder()

            // create a byte-capturing content provider if buffered
            if (buffered) {
                deferredContentProvider = new DeferredContentProvider(connection.contentLengthLong, true)
                request.listeners.addFirst(deferredContentProvider)
            }

            // reader task
            int read = 0
            while (read != -1 && !Thread.currentThread().interrupted) {
                byte[] bytes = new byte[config.maxBufferSize]
                int offset = 0
                read = stream.read(bytes, 0, bytes.length)
                while (read > 0 && offset + read <= bytes.length && !Thread.currentThread().interrupted) {
                    offset += read
                    read = stream.read(bytes, offset, bytes.length - offset)
                }
                if (Thread.currentThread().interrupted) {
                    throw new InterruptedException()
                }
                if (offset > 0) {
                    ByteBuffer data = decoder.decode(ByteBuffer.wrap(bytes, 0, offset))
                    if (data.hasRemaining()) {
                        Log.trace('[%s] JdkHttpClient onContent: %s bytes', null, request.url, data.remaining())
                        exchange.request.listeners.onContent(new HttpContent(exchange, data))
                    }
                    // gives a chance to abort request
                    if (Thread.currentThread().interrupted) {
                        throw new InterruptedException()
                    }
                }
            }

            return deferredContentProvider ?
                new HttpResult(request, response, config.deserializers, deferredContentProvider) :
                new HttpResult(request, response)

        } catch (InterruptedException e) {
            Log.trace('[%s] JdkHttpClient interrupted', null, request.url)
            Thread.currentThread().interrupt()
            throw e
        } catch (Exception e) {
            Log.trace('[%s] JdkHttpClient error: %s', e, request.url, e.message)
            throw e
        } finally {
            Log.trace('[%s] JdkHttpClient cleanup', null, request.url)
            deferredContentProvider?.close()
            request.listeners.remove(deferredContentProvider)
            connection.disconnect()
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy