001    package com.nimbusds.openid.connect.sdk.util;
002    
003    
004    import java.io.BufferedReader;
005    import java.io.InputStreamReader;
006    import java.io.IOException;
007    import java.net.HttpURLConnection;
008    import java.net.URL;
009    
010    import javax.mail.internet.ContentType;
011    import javax.mail.internet.ParseException;
012    
013    import 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
024    public 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                    
180                    return new Resource(sb.toString(), contentType);
181            }
182    }