001package com.nimbusds.openid.connect.sdk.util;
002
003
004import java.io.BufferedReader;
005import java.io.InputStreamReader;
006import java.io.IOException;
007import java.net.HttpURLConnection;
008import java.net.URL;
009
010import javax.mail.internet.ContentType;
011import javax.mail.internet.ParseException;
012
013import net.jcip.annotations.ThreadSafe;
014
015
016/**
017 * The default retriever of resources specified by URL. Provides setting of
018 * HTTP connect and read timeouts. Caching header directives are not honoured.
019 */
020@ThreadSafe
021public class DefaultResourceRetriever implements ResourceRetriever {
022
023
024        /**
025         * The system line separator.
026         */
027        private final String lineSeparator;
028        
029        
030        /**
031         * The HTTP connect timeout, in milliseconds.
032         */
033        private int connectTimeout;
034        
035        
036        /**
037         * The HTTP read timeout, in milliseconds.
038         */
039        private int readTimeout;
040        
041        
042        /**
043         * Creates a new resource retriever. The HTTP connect and read timeouts
044         * are set to zero (infinite).
045         */
046        public DefaultResourceRetriever() {
047        
048                this(0, 0);     
049        }
050        
051        
052        /**
053         * Creates a new resource retriever.
054         *
055         * @param connectTimeout The HTTP connects timeout, in milliseconds, 
056         *                       zero for infinite. Must not be negative.
057         * @param readTimeout    The HTTP read timeout, in milliseconds, zero 
058         *                       for infinite. Must not be negative.
059         */
060        public DefaultResourceRetriever(final int connectTimeout, final int readTimeout) {
061        
062                setConnectTimeout(connectTimeout);
063                setReadTimeout(readTimeout);
064                
065                lineSeparator = System.getProperty("line.separator");
066        }
067        
068        
069        /**
070         * Gets the HTTP connect timeout.
071         *
072         * @return The HTTP connect timeout, in milliseconds, zero for 
073         *         infinite.
074         */
075        public int getConnectTimeout() {
076        
077                return connectTimeout;
078        }
079        
080        
081        /**
082         * Sets the HTTP connect timeout.
083         *
084         * @param connectTimeout The HTTP connect timeout, in milliseconds, 
085         *                       zero for infinite. Must not be negative.
086         */
087        public void setConnectTimeout(final int connectTimeout) {
088        
089                if (connectTimeout < 0)
090                        throw new IllegalArgumentException("The connect timeout must not be negative");
091                
092                this.connectTimeout = connectTimeout;
093        }
094        
095        
096        /**
097         * Gets the HTTP read timeout.
098         *
099         * @return The HTTP read timeout, in milliseconds, zero for infinite.
100         */
101        public int getReadTimeout() {
102        
103                return readTimeout;
104        }
105        
106        
107        /**
108         * Sets the HTTP read timeout.
109         *
110         * @param readTimeout The HTTP read timeout, in milliseconds, zero for
111         *                    infinite. Must not be negative.
112         */
113        public void setReadTimeout(final int readTimeout) {
114        
115                if (readTimeout < 0)
116                        throw new IllegalArgumentException("The read timeout must not be negative");
117                
118                this.readTimeout = readTimeout;
119        }
120        
121        
122        @Override       
123        public Resource retrieveResource(final URL url)
124                throws IOException {
125                
126                HttpURLConnection con;
127
128                try {
129                        con = (HttpURLConnection)url.openConnection();
130
131                } catch (ClassCastException e) {
132
133                        throw new IOException("Couldn't open HTTP(S) connection: " + e.getMessage(), e);
134                }
135
136                con.setConnectTimeout(connectTimeout);
137                con.setReadTimeout(readTimeout);
138                
139                StringBuilder sb = new StringBuilder();
140                
141                BufferedReader input = new BufferedReader(new InputStreamReader(con.getInputStream()));
142                
143                String line;
144                
145                while ((line = input.readLine()) != null) {
146                
147                        sb.append(line);
148                        sb.append(lineSeparator);
149                }
150                
151                input.close();
152                
153                // Check HTTP code + message
154                final int statusCode = con.getResponseCode();
155                final String statusMessage = con.getResponseMessage();
156
157                if (statusCode != HttpURLConnection.HTTP_OK) {
158
159                        throw new IOException("HTTP " + statusCode + ": " + statusMessage);
160                }
161
162                // Parse the Content-Type header
163                ContentType contentType = null;
164
165                if (con.getContentType() != null) {
166
167                        try {
168                                contentType = new ContentType(con.getContentType());
169
170                        } catch (ParseException e) {
171
172                                throw new IOException("Couldn't parse Content-Type header: " + e.getMessage(), e);
173                        }
174                }
175                
176                return new Resource(sb.toString(), contentType);
177        }
178}