001package com.nimbusds.oauth2.sdk.http;
002
003
004import java.io.BufferedReader;
005import java.io.IOException;
006import java.io.InputStream;
007import java.io.InputStreamReader;
008import java.net.HttpURLConnection;
009import java.net.URL;
010import javax.mail.internet.ContentType;
011import javax.mail.internet.ParseException;
012
013import net.jcip.annotations.ThreadSafe;
014import org.apache.commons.io.input.BoundedInputStream;
015
016
017/**
018 * The default retriever of resources specified by URL. Provides setting of
019 * HTTP connect and read timeouts as well as a size limit of the retrieved
020 * entity. Caching header directives are not honoured.
021 */
022@ThreadSafe
023public class DefaultResourceRetriever extends AbstractRestrictedResourceRetriever implements RestrictedResourceRetriever {
024
025
026        /**
027         * The system line separator.
028         */
029        private final String lineSeparator;
030        
031        
032        /**
033         * Creates a new resource retriever. The HTTP timeouts and entity size
034         * limit are set to zero (infinite).
035         */
036        public DefaultResourceRetriever() {
037        
038                this(0, 0);     
039        }
040        
041        
042        /**
043         * Creates a new resource retriever. The HTTP entity size limit is set
044         * to zero (infinite).
045         *
046         * @param connectTimeout The HTTP connects timeout, in milliseconds, 
047         *                       zero for infinite. Must not be negative.
048         * @param readTimeout    The HTTP read timeout, in milliseconds, zero 
049         *                       for infinite. Must not be negative.
050         */
051        public DefaultResourceRetriever(final int connectTimeout, final int readTimeout) {
052
053                this(connectTimeout, readTimeout, 0);
054        }
055
056
057        /**
058         * Creates a new resource retriever.
059         *
060         * @param connectTimeout The HTTP connects timeout, in milliseconds,
061         *                       zero for infinite. Must not be negative.
062         * @param readTimeout    The HTTP read timeout, in milliseconds, zero
063         *                       for infinite. Must not be negative.
064         * @param sizeLimit      The HTTP entity size limit, in bytes, zero for
065         *                       infinite. Must not be negative.
066         */
067        public DefaultResourceRetriever(final int connectTimeout, final int readTimeout, final int sizeLimit) {
068        
069                super(connectTimeout, readTimeout, sizeLimit);
070                lineSeparator = System.getProperty("line.separator");
071        }
072
073
074        @Override
075        public Resource retrieveResource(final URL url)
076                throws IOException {
077                
078                HttpURLConnection con;
079                try {
080                        con = (HttpURLConnection)url.openConnection();
081                } catch (ClassCastException e) {
082                        throw new IOException("Couldn't open HTTP(S) connection: " + e.getMessage(), e);
083                }
084
085                con.setConnectTimeout(getConnectTimeout());
086                con.setReadTimeout(getReadTimeout());
087
088                StringBuilder sb = new StringBuilder();
089
090                InputStream inputStream = con.getInputStream();
091
092                if (getSizeLimit() > 0) {
093                        inputStream = new BoundedInputStream(inputStream, getSizeLimit());
094                }
095
096                BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
097
098                String line;
099
100                while ((line = input.readLine()) != null) {
101
102                        sb.append(line);
103                        sb.append(lineSeparator);
104                }
105
106                input.close();
107
108                // Check HTTP code + message
109                final int statusCode = con.getResponseCode();
110                final String statusMessage = con.getResponseMessage();
111
112                // Ensure 2xx status code
113                if (statusCode > 299 || statusCode < 200) {
114                        throw new IOException("HTTP " + statusCode + ": " + statusMessage);
115                }
116
117                // Parse the Content-Type header
118                ContentType contentType = null;
119
120                if (con.getContentType() != null) {
121                        try {
122                                contentType = new ContentType(con.getContentType());
123                        } catch (ParseException e) {
124                                throw new IOException("Couldn't parse Content-Type header: " + e.getMessage(), e);
125                        }
126                }
127                
128                return new Resource(sb.toString(), contentType);
129        }
130}