001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jose.util;
019
020
021import java.io.IOException;
022import java.io.InputStream;
023import java.net.HttpURLConnection;
024import java.net.URL;
025import java.nio.charset.Charset;
026
027import net.jcip.annotations.ThreadSafe;
028
029
030/**
031 * The default retriever of resources specified by URL. Provides setting of
032 * HTTP connect and read timeouts as well as a size limit of the retrieved
033 * entity. Caching header directives are not honoured.
034 *
035 * @author Vladimir Dzhuvinov
036 * @version 2016-11-28
037 */
038@ThreadSafe
039public class DefaultResourceRetriever extends AbstractRestrictedResourceRetriever implements RestrictedResourceRetriever {
040        
041        
042        /**
043         * Creates a new resource retriever. The HTTP timeouts and entity size
044         * limit are set to zero (infinite).
045         */
046        public DefaultResourceRetriever() {
047        
048                this(0, 0);     
049        }
050        
051        
052        /**
053         * Creates a new resource retriever. The HTTP entity size limit is set
054         * to zero (infinite).
055         *
056         * @param connectTimeout The HTTP connects timeout, in milliseconds, 
057         *                       zero for infinite. Must not be negative.
058         * @param readTimeout    The HTTP read timeout, in milliseconds, zero 
059         *                       for infinite. Must not be negative.
060         */
061        public DefaultResourceRetriever(final int connectTimeout, final int readTimeout) {
062
063                this(connectTimeout, readTimeout, 0);
064        }
065
066
067        /**
068         * Creates a new resource retriever.
069         *
070         * @param connectTimeout The HTTP connects timeout, in milliseconds,
071         *                       zero for infinite. Must not be negative.
072         * @param readTimeout    The HTTP read timeout, in milliseconds, zero
073         *                       for infinite. Must not be negative.
074         * @param sizeLimit      The HTTP entity size limit, in bytes, zero for
075         *                       infinite. Must not be negative.
076         */
077        public DefaultResourceRetriever(final int connectTimeout, final int readTimeout, final int sizeLimit) {
078        
079                super(connectTimeout, readTimeout, sizeLimit);
080        }
081
082
083        @Override
084        public Resource retrieveResource(final URL url)
085                throws IOException {
086                
087                HttpURLConnection con;
088                try {
089                        con = (HttpURLConnection)url.openConnection();
090                } catch (ClassCastException e) {
091                        throw new IOException("Couldn't open HTTP(S) connection: " + e.getMessage(), e);
092                }
093
094                con.setConnectTimeout(getConnectTimeout());
095                con.setReadTimeout(getReadTimeout());
096
097                InputStream inputStream = con.getInputStream();
098
099                if (getSizeLimit() > 0) {
100                        inputStream = new BoundedInputStream(inputStream, getSizeLimit());
101                }
102                
103                final String content;
104                try {
105                        content = IOUtils.readInputStreamToString(inputStream, Charset.forName("UTF-8"));
106                } finally {
107                        inputStream.close();
108                }
109
110                // Check HTTP code + message
111                final int statusCode = con.getResponseCode();
112                final String statusMessage = con.getResponseMessage();
113
114                // Ensure 2xx status code
115                if (statusCode > 299 || statusCode < 200) {
116                        throw new IOException("HTTP " + statusCode + ": " + statusMessage);
117                }
118
119                return new Resource(content, con.getContentType());
120        }
121}