1 package wjhk.jupload2.upload.helper;
2
3 import java.io.IOException;
4 import java.io.PushbackInputStream;
5 import java.util.regex.Matcher;
6 import java.util.regex.Pattern;
7
8 import wjhk.jupload2.exception.JUploadEOFException;
9 import wjhk.jupload2.exception.JUploadException;
10 import wjhk.jupload2.exception.JUploadIOException;
11 import wjhk.jupload2.policies.UploadPolicy;
12
13
14
15
16
17
18 public class HTTPInputStreamReader {
19
20
21
22
23
24
25
26 private UploadPolicy uploadPolicy = null;
27
28 private HTTPConnectionHelper httpConnectionHelper = null;
29
30
31
32
33
34 String stringResponseBody = null;
35
36 private byte[] bytearrayResponseBody = new byte[0];
37
38
39
40
41 String responseHeaders = null;
42
43
44
45
46 String responseMsg = null;
47
48
49
50
51
52 private CookieJar cookies = null;
53
54 boolean gotClose = false;
55
56 private boolean gotChunked = false;
57
58 private boolean gotContentLength = false;
59
60 private int clen = 0;
61
62
63
64
65 private int httpStatusCode = 0;
66
67 private String line = "";
68
69 private String charset = "ISO-8859-1";
70
71
72
73
74 private final static int CHUNKBUF_SIZE = 4096;
75
76 private final byte chunkbuf[] = new byte[CHUNKBUF_SIZE];
77
78 private final static Pattern pChunked = Pattern.compile(
79 "^Transfer-Encoding:\\s+chunked", Pattern.CASE_INSENSITIVE);
80
81 private final static Pattern pClose = Pattern.compile(
82 "^Connection:\\s+close", Pattern.CASE_INSENSITIVE);
83
84 private final static Pattern pProxyClose = Pattern.compile(
85 "^Proxy-Connection:\\s+close", Pattern.CASE_INSENSITIVE);
86
87 private final static Pattern pHttpStatus = Pattern
88 .compile("^HTTP/\\d\\.\\d\\s+((\\d+)\\s+.*)$");
89
90 private final static Pattern pContentLen = Pattern.compile(
91 "^Content-Length:\\s+(\\d+)$", Pattern.CASE_INSENSITIVE);
92
93 private final static Pattern pContentTypeCs = Pattern.compile(
94 "^Content-Type:\\s+.*;\\s*charset=([^;\\s]+).*$",
95 Pattern.CASE_INSENSITIVE);
96
97 private final static Pattern pSetCookie = Pattern.compile(
98 "^Set-Cookie:\\s+(.*)$", Pattern.CASE_INSENSITIVE);
99
100
101
102
103
104
105
106
107
108 public HTTPInputStreamReader(HTTPConnectionHelper httpConnectionHelper,
109 UploadPolicy uploadPolicy) {
110 this.httpConnectionHelper = httpConnectionHelper;
111 this.uploadPolicy = uploadPolicy;
112 this.cookies = new CookieJar(uploadPolicy);
113 }
114
115
116
117
118
119
120 public int gethttpStatusCode() {
121 return this.httpStatusCode;
122 }
123
124
125
126
127
128
129 public String getResponseBody() {
130 return this.stringResponseBody;
131 }
132
133
134
135
136
137
138
139
140 public byte[] getResponseBodyAsByteArray() {
141 return this.bytearrayResponseBody;
142 }
143
144
145
146
147
148
149
150 public String getResponseCharset() {
151 return this.charset;
152 }
153
154
155
156
157
158
159 public String getResponseHeaders() {
160 return this.responseHeaders;
161 }
162
163
164
165
166
167
168
169 public String getResponseMsg() {
170 return this.responseMsg;
171 }
172
173
174
175
176
177
178
179 public int readHttpResponse() throws JUploadException {
180 PushbackInputStream httpDataIn = this.httpConnectionHelper
181 .getInputStream();
182
183 try {
184
185 readHeaders(httpDataIn);
186
187
188
189
190 if (this.httpConnectionHelper.getMethod().equals("HEAD")) {
191 this.uploadPolicy
192 .displayDebug(
193 "This is a HEAD request: we don't care about the bytearrayResponseBody",
194 70);
195 this.stringResponseBody = "";
196 } else {
197 readBody(httpDataIn);
198 }
199 } catch (JUploadException e) {
200 throw e;
201 } catch (Exception e) {
202 throw new JUploadException(e);
203 }
204
205 return this.httpStatusCode;
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219 static byte[] byteAppend(byte[] buf1, byte[] buf2) {
220 byte[] ret = new byte[buf1.length + buf2.length];
221 System.arraycopy(buf1, 0, ret, 0, buf1.length);
222 System.arraycopy(buf2, 0, ret, buf1.length, buf2.length);
223 return ret;
224 }
225
226
227
228
229
230
231
232
233
234 static byte[] byteAppend(byte[] buf1, byte[] buf2, int len) {
235 if (len > buf2.length)
236 len = buf2.length;
237 byte[] ret = new byte[buf1.length + len];
238 System.arraycopy(buf1, 0, ret, 0, buf1.length);
239 System.arraycopy(buf2, 0, ret, buf1.length, len);
240 return ret;
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256 public static String readLine(PushbackInputStream inputStream,
257 String charset, boolean includeCR) throws IOException,
258 JUploadException {
259 byte[] line = readLine(inputStream, includeCR);
260 return (null == line) ? null : new String(line, charset);
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287 public static byte[] readLine(PushbackInputStream inputStream,
288 boolean includeCR) throws IOException, JUploadException {
289 final byte EOS = -1;
290 final byte CR = 13;
291 final byte LF = 10;
292 int len = 0;
293 int buflen = 128;
294 byte[] buf = new byte[buflen];
295 byte[] ret = null;
296 int b;
297 boolean lineRead = false;
298
299 while (!lineRead) {
300 try {
301 b = inputStream.read();
302 } catch (IOException ioe) {
303 throw new JUploadIOException(ioe.getClass().getName() + ": "
304 + ioe.getMessage()
305 + " (while reading server response )", ioe);
306 } catch (Exception e) {
307 throw new JUploadException(e.getClass().getName() + ": "
308 + e.getMessage() + " (while reading server response )",
309 e);
310 }
311 switch (b) {
312 case EOS:
313
314
315 if (len == 0) {
316 return null;
317 }
318 lineRead = true;
319 break;
320
321
322
323
324 case LF:
325
326 lineRead = true;
327 break;
328 case CR:
329
330
331 b = inputStream.read();
332
333 if (b != LF) {
334
335
336 inputStream.unread(b);
337 }
338 lineRead = true;
339 break;
340 default:
341 buf[len++] = (byte) b;
342
343
344 if (len + 2 >= buflen) {
345 buflen *= 2;
346 byte[] tmp = new byte[buflen];
347 System.arraycopy(buf, 0, tmp, 0, len);
348 buf = tmp;
349 }
350 }
351 }
352
353
354 while (len > 0 && (buf[len] == CR || buf[len] == LF)) {
355 len -= 1;
356 }
357
358
359
360 if (includeCR) {
361
362
363 buf[len++] = CR;
364 buf[len++] = LF;
365 }
366
367 if (len > 0) {
368 ret = new byte[len];
369 if (len > 0)
370 System.arraycopy(buf, 0, ret, 0, len);
371 } else {
372
373
374
375 ret = new byte[0];
376 }
377 return ret;
378 }
379
380
381
382
383
384
385
386
387 private void readHeaders(PushbackInputStream httpDataIn)
388 throws IOException, JUploadException {
389 StringBuffer sbHeaders = new StringBuffer();
390
391 String tmp;
392
393 this.uploadPolicy.displayDebug(
394 "-------- Response Headers Start --------", 80);
395
396 do {
397 tmp = readLine(httpDataIn, "US-ASCII", false);
398 if (null == tmp) {
399 throw new JUploadEOFException(this.uploadPolicy,
400 "reading headers");
401 }
402 if (this.httpStatusCode == 0) {
403 Matcher m = pHttpStatus.matcher(tmp);
404 if (m.matches()) {
405 this.httpStatusCode = Integer.parseInt(m.group(2));
406 this.responseMsg = m.group(1);
407 } else {
408
409
410
411
412
413 this.uploadPolicy.displayDebug("First line of response: '"
414 + tmp + "'", 80);
415
416 throw new JUploadException(
417 "HTTP response did not begin with status line.");
418 }
419 }
420
421
422
423 if (tmp.startsWith(" ") || tmp.startsWith("\t"))
424 this.line += " " + tmp.trim();
425 else
426 this.line = tmp;
427
428
429 this.uploadPolicy.displayDebug(this.line, 80);
430 sbHeaders.append(tmp).append("\n");
431
432 if (pClose.matcher(this.line).matches())
433 this.gotClose = true;
434 if (pProxyClose.matcher(this.line).matches())
435 this.gotClose = true;
436 if (pChunked.matcher(this.line).matches())
437 this.gotChunked = true;
438 Matcher m = pContentLen.matcher(this.line);
439 if (m.matches()) {
440 this.gotContentLength = true;
441 this.clen = Integer.parseInt(m.group(1));
442 }
443 m = pContentTypeCs.matcher(this.line);
444 if (m.matches())
445 this.charset = m.group(1);
446 m = pSetCookie.matcher(this.line);
447 if (m.matches()) {
448 this.uploadPolicy.displayDebug(
449 "Calling this.cookies.parseCookieHeader, with parameter: "
450 + m.group(1), 80);
451 this.cookies.parseCookieHeader(m.group(1));
452 this.uploadPolicy.displayDebug("Cookie header parsed.", 80);
453 }
454
455
456 } while (this.line.length() > 0);
457
458 this.responseHeaders = sbHeaders.toString();
459 this.uploadPolicy.displayDebug(
460 "--------- Response Headers End ---------", 80);
461 }
462
463
464
465
466
467
468
469
470
471 private void readBody(PushbackInputStream httpDataIn) throws IOException,
472 JUploadException {
473
474 while ((!this.gotContentLength) || (this.clen > 0)) {
475 if (this.gotChunked) {
476
477
478 this.line = readLine(httpDataIn, "US-ASCII", false);
479 if (null == this.line)
480 throw new JUploadEOFException(this.uploadPolicy,
481 "reading HTTP Body, HTTP chunked mode (1)");
482
483
484
485 int len = Integer.parseInt(this.line.replaceFirst(";.*", "")
486 .trim(), 16);
487 this.uploadPolicy.displayDebug("Chunk: " + this.line + " dec: "
488 + len, 70);
489 if (len == 0) {
490
491
492
493
494
495
496
497
498
499 break;
500 }
501
502
503 while (len > 0) {
504 int rlen = (len > CHUNKBUF_SIZE) ? CHUNKBUF_SIZE : len;
505 int ofs = 0;
506 if (rlen > 0) {
507 while (ofs < rlen) {
508 int res = httpDataIn.read(this.chunkbuf, ofs, rlen
509 - ofs);
510 if (res < 0)
511 throw new JUploadEOFException(
512 this.uploadPolicy,
513 "reading body, HTTP chunk mode (2)");
514 len -= res;
515 ofs += res;
516 }
517 if (ofs < rlen)
518 throw new JUploadException("short read");
519 if (rlen < CHUNKBUF_SIZE)
520 this.bytearrayResponseBody = byteAppend(
521 this.bytearrayResponseBody, this.chunkbuf,
522 rlen);
523 else
524 this.bytearrayResponseBody = byteAppend(
525 this.bytearrayResponseBody, this.chunkbuf);
526 }
527 }
528
529 readLine(httpDataIn, false);
530 } else {
531
532
533 if (this.gotContentLength) {
534
535
536 while (this.clen > 0) {
537 int rlen = (this.clen > CHUNKBUF_SIZE) ? CHUNKBUF_SIZE
538 : this.clen;
539 int ofs = 0;
540 if (rlen > 0) {
541 while (ofs < rlen) {
542 int res = httpDataIn.read(this.chunkbuf, ofs,
543 rlen - ofs);
544 if (res < 0)
545 throw new JUploadEOFException(
546 this.uploadPolicy,
547 "reading HTTP bytearrayResponseBody, not chunked mode");
548 this.clen -= res;
549 ofs += res;
550 }
551 if (ofs < rlen)
552 throw new JUploadException("short read");
553 if (rlen < CHUNKBUF_SIZE)
554 this.bytearrayResponseBody = byteAppend(
555 this.bytearrayResponseBody,
556 this.chunkbuf, rlen);
557 else
558 this.bytearrayResponseBody = byteAppend(
559 this.bytearrayResponseBody,
560 this.chunkbuf);
561 }
562 }
563 } else {
564
565
566 while (true) {
567 byte[] lbuf = readLine(httpDataIn, true);
568 if (null == lbuf)
569 break;
570 this.bytearrayResponseBody = byteAppend(
571 this.bytearrayResponseBody, lbuf);
572 }
573 break;
574 }
575 }
576 }
577
578
579
580
581
582 this.stringResponseBody = new String(this.bytearrayResponseBody,
583 this.charset);
584
585
586 this.uploadPolicy.displayDebug("-------- Response Body Start --------",
587 99);
588 this.uploadPolicy.displayDebug(this.stringResponseBody, 99);
589 this.uploadPolicy.displayDebug("-------- Response Body End --------",
590 99);
591 }
592 }