Source code: com/sonalb/net/http/HTTPRedirectHandler.java
1 /*
2 * -*- mode: java; c-basic-indent: 4; indent-tabs-mode: nil -*-
3 * :indentSize=4:noTabs=true:tabSize=4:indentOnTab=true:indentOnEnter=true:mode=java:
4 * ex: set tabstop=4 expandtab:
5 *
6 * MrPostman - webmail <-> email gateway
7 * Copyright (C) 2002-2003 MrPostman Development Group
8 * Projectpage: http://mrbook.org/mrpostman/
9 *
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 * In particular, this implies that users are responsible for
21 * using MrPostman after reading the terms and conditions given
22 * by their web-mail provider.
23 *
24 * You should have received a copy of the GNU General Public License
25 * Named LICENSE in the base directory of this distribution,
26 * if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 */
29
30 package com.sonalb.net.http;
31
32 import com.sonalb.net.http.cookie.Client;
33 import com.sonalb.net.http.cookie.CookieJar;
34 import com.sonalb.net.http.cookie.MalformedCookieException;
35
36 import java.io.IOException;
37 import java.io.InputStream;
38
39 import java.net.HttpURLConnection;
40 import java.net.URL;
41
42 import java.util.Map;
43 import java.util.Iterator;
44 import java.util.List;
45
46 /**
47
48 * Convenience class that combines cookie-handling and redirect-handling logic. When the connect()
49
50 * method is invoked, the handler cyclically processes HTTP Redirects (if any), and also takes care of
51
52 * cookie-handling while doing so. The maximum number of redirects defaults to 10. The handler determines
53
54 * a successful run, when the HTTP response code is equal to a specified success code. This code defaults
55
56 * to 200 (OK).
57
58 * @author Sonal Bansal
59
60 */
61 public class HTTPRedirectHandler {
62 public static final String CVSID = "$Id: HTTPRedirectHandler.java,v 1.7 2003/02/22 09:17:19 lbruand Exp $";
63 HttpURLConnection huc;
64 CookieJar cj = new CookieJar();
65 Client client = new Client();
66 boolean bCookies = true;
67 int successCode = 200;
68 int maxRedirects = 10;
69 boolean bConnected = false;
70 boolean bConnectMethodAlreadyCalled = false;
71 InputStream is;
72
73 /**
74
75 * Creates a handler for the input HttpURLConnection. The HttpURLConnection must NOT be
76
77 * connected yet.
78
79 */
80 public HTTPRedirectHandler(HttpURLConnection huc) {
81 if (huc == null) {
82 throw new NullPointerException();
83 }
84
85 this.huc = huc;
86 }
87
88 /**
89
90 * Sets the Client to be used for this handler.
91
92 */
93 public void setClient(Client cl) {
94 if (cl == null) {
95 throw new IllegalArgumentException("Null argument.");
96 }
97
98 synchronized (client) {
99 client = cl;
100 }
101 }
102
103 /**
104
105 * Enables/Disables automated cookie-handling.
106
107 */
108 public void handleCookies(boolean b) {
109 bCookies = b;
110 }
111
112 /**
113
114 * Sets the HTTP response code designating a successful run.
115
116 * @param i the code; non-positive values ignored
117
118 */
119 public void setSuccessCode(int i) {
120 if (i > 0) {
121 successCode = i;
122 }
123 }
124
125 /**
126
127 * Sets the CookieJar containing Cookies to be used during cookie-handling.
128
129 */
130 public void setCookieJar(CookieJar cj) {
131 if ((cj == null) || cj.isEmpty()) {
132 return;
133 }
134
135 this.cj = cj;
136 }
137
138 /**
139
140 * Adds some Cookies to the existing Cookies in an HTTPRedirectHandler.
141
142 * @param cj the Cookies to be added
143
144 */
145 public void addCookies(CookieJar cj) {
146 if ((cj == null) || cj.isEmpty()) {
147 return;
148 }
149
150 this.cj.addAll(cj);
151 }
152
153 /**
154
155 * Gets the CookieJar containing any pre-existing Cookies, as well as new ones extracted
156
157 * during processing.
158
159 * @return the CookieJar; always non-null
160
161 */
162 public CookieJar getCookieJar() {
163 return (cj);
164 }
165
166 /**
167
168 * Gets the InputStream for the final successful response.
169
170 * @throws IllegalStateException when called before successful connection.
171
172 */
173 public InputStream getInputStream() {
174 if (!bConnected) {
175 throw new IllegalStateException("Not Connected");
176 }
177
178 return (is);
179 }
180
181 /**
182
183 * Gets the HttpURLConnection for the final successful response.
184
185 */
186 public HttpURLConnection getConnection() {
187 if (!bConnected) {
188 throw new IllegalStateException("Not Connected");
189 }
190
191 return (huc);
192 }
193
194 /**
195
196 * Sets the maximum number of redirects that will be followed.
197
198 * @param i the max number; non-positive values are ignored
199
200 */
201 public void setMaxRedirects(int i) {
202 if (i > 0) {
203 maxRedirects = i;
204 }
205 }
206
207 /**
208
209 * Connects to initial HttpURLConnection (specified during construction), and initiates
210
211 * cookie-handling and redirect-handling. It can only be called once per instance.
212
213 * @throws IOException if there is an I/O problem
214
215 * @throws MalformedCookieException if there was a problem with cookie-handling
216
217 * @throws IllegalStateException if this method has already been called
218
219 */
220 public void connect() throws IOException, MalformedCookieException {
221 if (bConnectMethodAlreadyCalled) {
222 throw new IllegalStateException("No can do.");
223 }
224 Map requestProps = huc.getRequestProperties();
225 bConnectMethodAlreadyCalled = true;
226
227 int code;
228
229 URL url;
230
231 huc.setFollowRedirects(false);
232
233 if (!cj.isEmpty()) {
234 client.setCookies(huc, cj);
235 }
236
237 is = huc.getInputStream();
238
239 cj.addAll(client.getCookies(huc));
240
241 while (((code = huc.getResponseCode()) != successCode) && (maxRedirects > 0)) {
242 if ((code < 300) || (code > 399)) {
243 throw new IOException("Can't deal with this response code (" + code + ").");
244 }
245
246 is.close();
247
248 is = null;
249
250 url = new URL(huc.getHeaderField("location"));
251
252
253 huc.disconnect();
254
255 huc = null;
256
257 huc = (HttpURLConnection) url.openConnection();
258 if (requestProps != null) {
259 StringBuffer sb;
260 for (Iterator i = requestProps.entrySet().iterator(); i.hasNext();) {
261 Map.Entry e = (Map.Entry) i.next();
262 sb = new StringBuffer();
263 for (Iterator j = ((List) e.getValue()).iterator(); j.hasNext();) {
264 if (sb.length() != 0) {
265 sb.append(", ");
266 }
267 sb.append((String) j.next());
268 }
269 huc.setRequestProperty((String) e.getKey(), sb.toString());
270 }
271 requestProps = null;
272 }
273
274
275 client.setCookies(huc, cj);
276
277 huc.setFollowRedirects(false);
278
279 huc.connect();
280
281 is = huc.getInputStream();
282
283 cj.addAll(client.getCookies(huc));
284
285 maxRedirects--;
286 }
287
288 if ((maxRedirects <= 0) && (code != successCode)) {
289 throw new IOException("Max redirects exhausted.");
290 }
291
292 bConnected = true;
293 }
294
295 /**
296
297 * Checks whether this handler has successfully connected.
298
299 */
300 public boolean isConnected() {
301 return (bConnected);
302 }
303 }