1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 package HTTPClient;
38
39 import java.net.ProtocolException;
40 import java.io.IOException;
41 import java.util.Hashtable;
42
43
44
45
46
47
48
49
50
51 class RedirectionModule implements HTTPClientModule
52 {
53
54 private static Hashtable perm_redir_cntxt_list = new Hashtable();
55
56
57 private static Hashtable deferred_redir_list = new Hashtable();
58
59
60 private int level;
61
62
63 private URI lastURI;
64
65
66 private boolean new_con;
67
68
69 private Request saved_req;
70
71
72
73
74
75
76
77 RedirectionModule()
78 {
79 level = 0;
80 lastURI = null;
81 saved_req = null;
82 }
83
84
85
86
87
88
89
90 public int requestHandler(Request req, Response[] resp)
91 {
92 HTTPConnection con = req.getConnection();
93 URI new_loc,
94 cur_loc;
95
96
97
98
99 HttpOutputStream out = req.getStream();
100 if (out != null && deferred_redir_list.get(out) != null)
101 {
102 copyFrom((RedirectionModule) deferred_redir_list.remove(out));
103 req.copyFrom(saved_req);
104
105 if (new_con)
106 return REQ_NEWCON_RST;
107 else
108 return REQ_RESTART;
109 }
110
111
112
113
114 try
115 {
116 cur_loc = new URI(new URI(con.getProtocol(), con.getHost(), con.getPort(), null),
117 req.getRequestURI());
118 }
119 catch (ParseException pe)
120 {
121 throw new Error("HTTPClient Internal Error: unexpected exception '"
122 + pe + "'");
123 }
124
125
126
127
128 Hashtable perm_redir_list = Util.getList(perm_redir_cntxt_list,
129 req.getConnection().getContext());
130 if ((new_loc = (URI) perm_redir_list.get(cur_loc)) != null)
131 {
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 String nres = new_loc.getPathAndQuery();
148 req.setRequestURI(nres);
149
150 try
151 { lastURI = new URI(new_loc, nres); }
152 catch (ParseException pe)
153 { }
154
155 Log.write(Log.MODS, "RdirM: matched request in permanent " +
156 "redirection list - redoing request to " +
157 lastURI.toExternalForm());
158
159 if (!con.isCompatibleWith(new_loc))
160 {
161 try
162 { con = new HTTPConnection(new_loc); }
163 catch (Exception e)
164 {
165 throw new Error("HTTPClient Internal Error: unexpected " +
166 "exception '" + e + "'");
167 }
168
169 con.setSSLSocketFactory(req.getConnection().getSSLSocketFactory());
170 con.setContext(req.getConnection().getContext());
171 req.setConnection(con);
172 return REQ_NEWCON_RST;
173 }
174 else
175 {
176 return REQ_RESTART;
177 }
178 }
179
180 return REQ_CONTINUE;
181 }
182
183
184
185
186
187 public void responsePhase1Handler(Response resp, RoRequest req)
188 throws IOException
189 {
190 int sts = resp.getStatusCode();
191 if (sts < 301 || sts > 307 || sts == 304)
192 {
193 if (lastURI != null)
194 resp.setEffectiveURI(lastURI);
195 }
196 }
197
198
199
200
201
202 public int responsePhase2Handler(Response resp, Request req)
203 throws IOException
204 {
205
206
207 int sts = resp.getStatusCode();
208 switch(sts)
209 {
210 case 302:
211
212
213
214
215
216
217
218
219 if (req.getMethod().equals("POST") ||
220 req.getMethod().equals("PUT"))
221 {
222 Log.write(Log.MODS, "RdirM: Received status: " + sts +
223 " " + resp.getReasonLine() +
224 " - treating as 303");
225
226 sts = 303;
227 }
228
229 case 301:
230 case 303:
231 case 307:
232
233 Log.write(Log.MODS, "RdirM: Handling status: " + sts +
234 " " + resp.getReasonLine());
235
236
237
238 if (!req.getMethod().equals("GET") &&
239 !req.getMethod().equals("HEAD") &&
240 sts != 303)
241 {
242 Log.write(Log.MODS, "RdirM: not redirected because " +
243 "method is neither HEAD nor GET");
244
245 if (sts == 301 && resp.getHeader("Location") != null)
246 update_perm_redir_list(req,
247 resLocHdr(resp.getHeader("Location"), req));
248
249 resp.setEffectiveURI(lastURI);
250 return RSP_CONTINUE;
251 }
252
253 case 305:
254 case 306:
255
256 if (sts == 305 || sts == 306)
257 Log.write(Log.MODS, "RdirM: Handling status: " + sts +
258 " " + resp.getReasonLine());
259
260
261 if (sts == 305 && req.getConnection().getProxyHost() != null)
262 {
263 Log.write(Log.MODS, "RdirM: 305 ignored because " +
264 "a proxy is already in use");
265
266 resp.setEffectiveURI(lastURI);
267 return RSP_CONTINUE;
268 }
269
270
271
272
273
274
275
276
277
278 if (level >= 15 || resp.getHeader("Location") == null)
279 {
280 if (level >= 15)
281 Log.write(Log.MODS, "RdirM: not redirected because "+
282 "of too many levels of redirection");
283 else
284 Log.write(Log.MODS, "RdirM: not redirected because "+
285 "no Location header was present");
286
287 resp.setEffectiveURI(lastURI);
288 return RSP_CONTINUE;
289 }
290 level++;
291
292 URI loc = resLocHdr(resp.getHeader("Location"), req);
293
294 HTTPConnection mvd;
295 String nres;
296 new_con = false;
297
298 if (sts == 305)
299 {
300 mvd = new HTTPConnection(req.getConnection().getProtocol(),
301 req.getConnection().getHost(),
302 req.getConnection().getPort());
303 mvd.setCurrentProxy(loc.getHost(), loc.getPort());
304 mvd.setSSLSocketFactory(req.getConnection().getSSLSocketFactory());
305 mvd.setContext(req.getConnection().getContext());
306 new_con = true;
307
308 nres = req.getRequestURI();
309
310
311
312
313
314
315
316
317
318 req.setMethod("GET");
319 req.setData(null);
320 req.setStream(null);
321 }
322 else if (sts == 306)
323 {
324
325 return RSP_CONTINUE;
326 }
327 else
328 {
329 if (req.getConnection().isCompatibleWith(loc))
330 {
331 mvd = req.getConnection();
332 nres = loc.getPathAndQuery();
333 }
334 else
335 {
336 try
337 {
338 mvd = new HTTPConnection(loc);
339 nres = loc.getPathAndQuery();
340 }
341 catch (Exception e)
342 {
343 if (req.getConnection().getProxyHost() == null ||
344 !loc.getScheme().equalsIgnoreCase("ftp"))
345 return RSP_CONTINUE;
346
347
348
349 mvd = new HTTPConnection("http",
350 req.getConnection().getProxyHost(),
351 req.getConnection().getProxyPort());
352 mvd.setCurrentProxy(null, 0);
353 nres = loc.toExternalForm();
354 }
355
356 mvd.setSSLSocketFactory(req.getConnection().getSSLSocketFactory());
357 mvd.setContext(req.getConnection().getContext());
358
359
360 mvd.setCheckCertificates(
361 req.getConnection().getCheckCertificates());
362 mvd.setTestConnectionHealthWithBlockingRead(
363 req.getConnection().getTestConnectionHealthWithBlockingRead());
364 mvd.setTimeAuthority(req.getConnection().getTimeAuthority());
365
366
367 new_con = true;
368 }
369
370
371
372
373
374
375
376
377
378
379
380
381
382 if (sts == 303)
383 {
384
385
386 if (!req.getMethod().equals("HEAD"))
387 req.setMethod("GET");
388 req.setData(null);
389 req.setStream(null);
390 }
391 else
392 {
393
394
395 if (req.getStream() != null)
396 {
397 if (!HTTPConnection.deferStreamed)
398 {
399 Log.write(Log.MODS, "RdirM: status " + sts +
400 " not handled - request " +
401 "has an output stream");
402 return RSP_CONTINUE;
403 }
404
405 saved_req = (Request) req.clone();
406 deferred_redir_list.put(req.getStream(), this);
407 req.getStream().reset();
408 resp.setRetryRequest(true);
409 }
410
411 if (sts == 301)
412 {
413
414 try
415 {
416 update_perm_redir_list(req, new URI(loc, nres));
417 }
418 catch (ParseException pe)
419 {
420 throw new Error("HTTPClient Internal Error: " +
421 "unexpected exception '" + pe +
422 "'");
423 }
424 }
425 }
426
427
428 NVPair[] hdrs = req.getHeaders();
429 for (int idx=0; idx<hdrs.length; idx++)
430 if (hdrs[idx].getName().equalsIgnoreCase("Referer"))
431 {
432 HTTPConnection con = req.getConnection();
433 hdrs[idx] =
434 new NVPair("Referer", con+req.getRequestURI());
435 break;
436 }
437 }
438
439 req.setConnection(mvd);
440 req.setRequestURI(nres);
441
442 try { resp.getInputStream().close(); }
443 catch (IOException ioe) { }
444
445 if (sts != 305 && sts != 306)
446 {
447 try
448 { lastURI = new URI(loc, nres); }
449 catch (ParseException pe)
450 {
451
452 Log.write(Log.MODS, "RdirM: request redirected to " +
453 lastURI.toExternalForm() +
454 " using method " + req.getMethod());
455 }
456 else
457 {
458 Log.write(Log.MODS, "RdirM: resending request using " +
459 "proxy " + mvd.getProxyHost() +
460 ":" + mvd.getProxyPort());
461 }
462
463 if (req.getStream() != null)
464 return RSP_CONTINUE;
465 else if (new_con)
466 return RSP_NEWCON_REQ;
467 else
468 return RSP_REQUEST;
469
470 default:
471
472 return RSP_CONTINUE;
473 }
474 }
475
476
477
478
479
480 public void responsePhase3Handler(Response resp, RoRequest req)
481 {
482 }
483
484
485
486
487
488 public void trailerHandler(Response resp, RoRequest req)
489 {
490 }
491
492
493
494
495
496
497
498
499 private static void update_perm_redir_list(RoRequest req, URI new_loc)
500 {
501 HTTPConnection con = req.getConnection();
502 URI cur_loc = null;
503 try
504 {
505 cur_loc = new URI(new URI(con.getProtocol(), con.getHost(), con.getPort(), null),
506 req.getRequestURI());
507 }
508 catch (ParseException pe)
509 { }
510
511 if (!cur_loc.equals(new_loc))
512 {
513 Hashtable perm_redir_list =
514 Util.getList(perm_redir_cntxt_list, con.getContext());
515 perm_redir_list.put(cur_loc, new_loc);
516 }
517 }
518
519
520
521
522
523
524
525
526
527
528
529
530
531 private URI resLocHdr(String loc, RoRequest req) throws ProtocolException
532 {
533 try
534 {
535 URI base = new URI(req.getConnection().getProtocol(),
536 req.getConnection().getHost(),
537 req.getConnection().getPort(), null);
538 base = new URI(base, req.getRequestURI());
539 URI res = new URI(base, loc);
540 if (res.getHost() == null)
541 throw new ProtocolException("Malformed URL in Location header: `" + loc +
542 "' - missing host field");
543 return res;
544 }
545 catch (ParseException pe)
546 {
547 throw new ProtocolException("Malformed URL in Location header: `" + loc +
548 "' - exception was: " + pe.getMessage());
549 }
550 }
551
552
553 private void copyFrom(RedirectionModule other)
554 {
555 this.level = other.level;
556 this.lastURI = other.lastURI;
557 this.saved_req = other.saved_req;
558 }
559 }