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