1 19 20 package com.maverick.http; 21 22 import java.io.ByteArrayInputStream ; 23 import java.io.ByteArrayOutputStream ; 24 import java.io.EOFException ; 25 import java.io.IOException ; 26 import java.io.InputStream ; 27 import java.text.MessageFormat ; 28 import java.util.NoSuchElementException ; 29 import java.util.StringTokenizer ; 30 31 35 public class HttpResponse extends HttpHeader { 36 37 static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(HttpResponse.class); 39 41 protected ByteArrayOutputStream chunked; 42 protected String begin; 43 44 private String version = ""; private int status; 46 private String reason = ""; 48 boolean closeConnection = false; 49 int contentLength = 0; 50 51 boolean foundContinue = false; 52 boolean release = true; 53 54 InputStream in = new ByteArrayInputStream (new byte[] {}); 55 56 HttpConnection con; 57 HttpContinue cont; 58 59 public HttpResponse(HttpConnection con) throws IOException { 60 this(con, false); 61 } 62 63 public HttpResponse(HttpConnection con, HttpContinue cont) throws IOException { 64 this.con = con; 65 this.cont = cont; 66 doResponse(false); 67 } 68 69 public HttpResponse(HttpConnection con, boolean headerOnly) throws IOException { 70 this.con = con; 71 doResponse(headerOnly); 72 } 73 74 private void doResponse(boolean headerOnly) throws IOException { 75 76 do { 77 78 begin = readLine(con.getInputStream()); 79 80 while (begin.trim().length() == 0) { 81 begin = readLine(con.getInputStream()); 82 } 83 84 log.debug(MessageFormat.format(Messages.getString("HttpResponse.startLine"), new Object [] { begin })); processResponse(); 88 89 if (status == 100) { 90 if (cont != null) 91 cont.continueRequest(con); 92 foundContinue = true; 93 94 String tmp; 95 while (true) { 96 97 101 tmp = readLine(con.getInputStream()); 102 103 log.debug(MessageFormat.format(Messages.getString("HttpResponse.received100"), new Object [] { tmp })); 107 if (tmp.equals("")) break; 109 } 110 111 } 112 } while (status >= 100 && status < 200); 113 114 processHeaderFields(con.getInputStream()); 115 116 if (!headerOnly) { 117 118 123 if (getHeaderField("transfer-encoding") != null) { if (getHeaderField("transfer-encoding").equalsIgnoreCase( "chunked")) { in = new ChunkedInputStream(con.getInputStream()); 127 removeFields("transfer-encoding"); } 130 } else if (getHeaderField("Content-Length") != null) { contentLength = Integer.parseInt(getHeaderField("Content-Length")); in = new ContentInputStream(contentLength); 133 } else if (getHeaderField("Connection") != null && getHeaderField("Connection").equalsIgnoreCase("close")) { 136 con.canReuse = false; 138 139 145 in = con.getInputStream(); 146 } else if (getHeaderField("Proxy-Connection") != null && getHeaderField("Proxy-Connection").equalsIgnoreCase("close")) { 149 con.canReuse = false; 151 152 158 in = con.getInputStream(); 159 } else { 160 163 in = new ByteArrayInputStream (new byte[] {}); 164 } 165 } 166 167 171 if (getHeaderField("Connection") != null && getHeaderField("Connection").equalsIgnoreCase("close")) { 174 con.canReuse = false; 176 177 } else if (getHeaderField("Proxy-Connection") != null && getHeaderField("Proxy-Connection").equalsIgnoreCase("close")) { 180 con.canReuse = false; 182 } 183 184 } 185 186 public void close() { 187 close(true); 188 } 189 190 public boolean hasContinue() { 191 return foundContinue; 192 } 193 194 public synchronized void close(boolean release) { 195 196 205 if (con == null) 206 return; 207 208 try { 209 if (in instanceof ChunkedInputStream) { 210 ((ChunkedInputStream) in).drain(); 211 } else if (in instanceof ContentInputStream) { 212 ((ContentInputStream) in).drain(); 213 } 214 } catch (IOException ex) { 215 con.canReuse = false; 217 } finally { 218 if (release) { 219 con.release(); 220 con = null; 221 } 222 } 223 } 224 225 public String getContentType() { 226 return getHeaderField("Content-Type"); 227 } 228 229 public String getContentTypeWithoutParameter() { 230 String contentType = getContentType(); 231 if(contentType == null) { 232 return null; 233 } 234 int idx = contentType.indexOf(';'); 235 return idx == -1 ? contentType : contentType.substring(0, idx); 236 } 237 238 protected String readLine(InputStream in) throws IOException { 239 StringBuffer lineBuf = new StringBuffer (); 240 int c; 241 242 while (true) { 243 c = in.read(); 244 245 if (c == -1) { 246 if (lineBuf.length() == 0) 247 throw new EOFException (Messages.getString("HttpResponse.unexpectedEOF")); 249 break; 250 } 251 252 if (c != '\n') { 253 lineBuf.append((char) c); 254 } else { 255 break; 256 } 257 } 258 259 return lineBuf.toString().trim(); 260 } 261 262 public String getStartLine() { 263 return begin; 264 } 265 266 protected void processHeaderFields(InputStream in) throws IOException { 267 clearHeaderFields(); 268 269 StringBuffer lineBuf = new StringBuffer (); 270 String lastHeaderName = null; 271 int c; 272 273 while (true) { 274 c = in.read(); 275 276 if (c == -1) { 277 throw new IOException (Messages.getString("HttpResponse.headerCorrupt")); } 279 280 if (c != '\n') { 281 lineBuf.append((char) c); 282 } else { 283 String line = lineBuf.toString().trim(); 284 lineBuf.setLength(0); 285 if (line.length() != 0) { 286 lastHeaderName = processNextLine(line, lastHeaderName); 287 } else { 288 break; 289 } 290 } 291 } 292 } 293 294 private String processNextLine(String line, String lastHeaderName) throws IOException { 295 String name; 296 String value; 297 char c = line.charAt(0); 298 299 if ((c == ' ') || (c == '\t')) { 300 name = lastHeaderName; 301 value = getHeaderField(lastHeaderName) + " " + line.trim(); } else { 303 int n = line.indexOf(':'); 304 305 if (n == -1) { 306 throw new IOException (MessageFormat.format(Messages.getString("HttpResponse.corruptField"), new Object [] { line })); } 308 309 name = line.substring(0, n); 310 value = line.substring(n + 1).trim(); 311 } 312 313 log.debug(MessageFormat.format(Messages.getString("HttpResponse.receivedHeader"), new Object [] { name, value })); addHeaderField(name, value); 317 318 return name; 319 } 320 321 public InputStream getInputStream() { 322 return in; 323 } 324 325 public HttpConnection getConnection() { 326 return con; 327 } 328 329 public String getVersion() { 330 return version; 331 } 332 333 public int getStatus() { 334 return status; 335 } 336 337 public String getReason() { 338 return reason; 339 } 340 341 private void processResponse() throws IOException { 342 StringTokenizer tokens = new StringTokenizer (begin, WHITE_SPACE, false); 343 reason = ""; try { 345 version = tokens.nextToken(); 346 status = Integer.parseInt(tokens.nextToken()); 347 while (tokens.hasMoreTokens()) { 348 reason += tokens.nextToken() + " "; } 350 reason = reason.trim(); 351 } catch (NoSuchElementException e) { 352 throw new IOException (MessageFormat.format(Messages.getString("HttpResponse.failedToReadResponse"), new Object [] { begin })); } catch (NumberFormatException e) { 354 throw new IOException (MessageFormat.format(Messages.getString("HttpResponse.failedToReadResponse"), new Object [] { begin })); } 356 } 357 358 362 class ChunkedInputStream extends InputStream { 363 364 long chunkLength; 365 InputStream in; 366 367 ChunkedInputStream(InputStream in) throws IOException { 368 369 this.in = in; 370 chunkLength = Long.parseLong(readLine(in), 16); 372 } 373 374 public int read() throws IOException { 375 byte[] b = new byte[1]; 376 int read = read(b, 0, 1); 377 if (read == -1) 378 return -1; 379 else 380 return b[0] & 0xFF; 381 } 382 383 public void drain() throws IOException { 384 long len; 385 byte[] buf = new byte[65535]; 386 while (contentLength > 0) { 387 len = con.getInputStream().read(buf, 0, buf.length); 388 389 if (contentLength > 0) 390 contentLength -= len; 391 else 392 break; 393 } 394 } 395 396 public synchronized int read(byte[] buf, int off, int len) throws IOException { 397 398 if (chunkLength == 0 || con == null) { 399 return -1; 400 } 401 int read; 402 int count = 0; 403 while (len > 0 && chunkLength > 0) { 404 405 read = in.read(buf, off, (int) (len > chunkLength ? chunkLength : len)); 406 407 if (read == -1) 408 throw new EOFException (Messages.getString("HttpResponse.unexpectedEOFDuringChunking")); 410 chunkLength -= read; 411 len -= read; 412 off += read; 413 count += read; 414 415 if (chunkLength == 0) { 416 readLine(in); 417 chunkLength = Long.parseLong(readLine(in), 16); 418 if (chunkLength == 0) 419 close(); 420 } 421 } 422 423 return count; 424 } 425 426 public synchronized void close() throws IOException { 427 428 if (con != null) { 429 readLine(in); 430 HttpResponse.this.close(true); 431 } 432 } 433 } 434 435 438 class ContentInputStream extends InputStream { 439 440 long contentLength; 441 442 ContentInputStream(long contentLength) { 443 this.contentLength = contentLength; 444 } 445 446 public synchronized int available() { 447 return (int) contentLength; 448 } 449 450 public synchronized long skip(long length) throws IOException { 451 return con.getInputStream().skip(length); 452 } 453 454 public void drain() throws IOException { 455 long len; 456 457 while (contentLength > 0) { 458 len = con.getInputStream().skip(contentLength); 459 460 if (contentLength > 0) 461 contentLength -= len; 462 else 463 break; 464 } 465 } 466 467 public synchronized int read() throws IOException { 468 469 if (contentLength == 0 || con == null) { 470 return -1; 471 } else { 472 int b = con.getInputStream().read(); 473 if (b == -1) 474 throw new EOFException (MessageFormat.format(Messages.getString("HttpResponse.unexpectedEOFInResponseExpected"), new Object [] { new Long (contentLength) })); contentLength--; 476 477 if (contentLength == 0) 478 close(); 479 480 return b; 481 } 482 } 483 484 public synchronized int read(byte[] buf, int off, int len) throws IOException { 485 if (contentLength == 0 || con == null) { 486 return -1; 487 } else { 488 int read = con.getInputStream().read(buf, off, (contentLength > len ? len : (int) contentLength)); 489 if (read == -1) 490 throw new EOFException (MessageFormat.format(Messages.getString("HttpResponse.unexpectedEOFInResponseExpected"), new Object [] { new Long (contentLength) })); contentLength -= read; 492 493 if (contentLength == 0) 494 close(); 495 496 return read; 497 } 498 499 } 500 501 public synchronized void close() { 502 if (con != null) 504 HttpResponse.this.close(true); 505 } 506 } 507 508 }
| Popular Tags
|