1 29 30 package com.caucho.vfs; 31 32 import com.caucho.util.Alarm; 33 import com.caucho.util.CharBuffer; 34 import com.caucho.util.Log; 35 36 import javax.net.ssl.SSLContext; 37 import javax.net.ssl.SSLSocketFactory; 38 import java.io.IOException ; 39 import java.io.InputStream ; 40 import java.io.OutputStream ; 41 import java.net.ConnectException ; 42 import java.net.Socket ; 43 import java.net.SocketException ; 44 import java.util.HashMap ; 45 import java.util.Iterator ; 46 import java.util.logging.Level ; 47 import java.util.logging.Logger ; 48 49 52 class HttpStream extends StreamImpl { 53 private static final Logger log = Log.open(HttpStream.class); 54 private static HashMap <String ,String > _reserved; 56 57 private static final Object LOCK = new Object (); 58 59 private static HttpStream _savedStream; 61 private static long _saveTime; 63 64 private long _socketTimeout = 30000L; 65 66 private boolean _isSSL; 67 68 private Socket _s; 69 private InputStream _is; 70 private OutputStream _os; 71 private ReadStream _rs; 72 private WriteStream _ws; 73 74 private String _host; 76 private int _port; 78 79 private String _virtualHost; 80 81 private String _method; 83 private boolean _isHead; 85 private boolean _isPost; 87 88 private MemoryStream _tempStream; 90 91 private boolean _isKeepalive = true; 93 private boolean _didGet; 95 private int _contentLength; 97 private boolean _isChunked; 99 private int _chunkLength; 101 private boolean _isRequestDone; 103 104 private HashMap <String ,Object > _attributes; 105 106 private byte []_tempBuffer; 108 109 112 private HttpStream(Path path, String host, int port, Socket s) 113 throws IOException 114 { 115 _s = s; 116 117 _host = host; 118 _port = port; 119 120 _is = _s.getInputStream(); 121 _os = _s.getOutputStream(); 122 123 _ws = VfsStream.openWrite(_os); 124 _rs = VfsStream.openRead(_is, _ws); 125 126 _attributes = new HashMap <String ,Object >(); 127 128 init(path); 129 } 130 131 138 static HttpStreamWrapper openRead(HttpPath path) throws IOException 139 { 140 HttpStream stream = createStream(path); 141 stream._isPost = false; 142 143 return new HttpStreamWrapper(stream); 144 } 145 146 153 static HttpStreamWrapper openReadWrite(HttpPath path) throws IOException 154 { 155 HttpStream stream = createStream(path); 156 stream._isPost = true; 157 158 return new HttpStreamWrapper(stream); 159 } 160 161 169 static private HttpStream createStream(HttpPath path) throws IOException 170 { 171 String host = path.getHost(); 172 int port = path.getPort(); 173 174 HttpStream stream = null; 175 long streamTime = 0; 176 synchronized (LOCK) { 177 if (_savedStream != null && 178 host.equals(_savedStream.getHost()) && 179 port == _savedStream.getPort()) { 180 stream = _savedStream; 181 streamTime = _saveTime; 182 _savedStream = null; 183 } 184 } 185 186 if (stream == null) { 187 } 188 else if (Alarm.getCurrentTime() < streamTime + 5000) { 190 stream.init(path); 191 return stream; 192 } 193 else { 195 try { 196 stream._isKeepalive = false; 197 stream.close(); 198 } catch (IOException e) { 199 log.log(Level.FINE, e.toString(), e); 200 } 201 } 202 203 Socket s; 204 205 try { 206 s = new Socket (host, port); 207 208 if (path instanceof HttpsPath) { 209 SSLContext context = SSLContext.getInstance("TLS"); 210 211 javax.net.ssl.TrustManager tm = 212 new javax.net.ssl.X509TrustManager() { 213 public java.security.cert.X509Certificate [] 214 getAcceptedIssuers() { 215 return null; 216 } 217 public void checkClientTrusted( 218 java.security.cert.X509Certificate [] cert, String foo) { 219 } 220 public void checkServerTrusted( 221 java.security.cert.X509Certificate [] cert, String foo) { 222 } 223 }; 224 225 226 context.init(null, new javax.net.ssl.TrustManager[] { tm }, null); 227 SSLSocketFactory factory = context.getSocketFactory(); 228 229 s = factory.createSocket(s, host, port, true); 230 } 231 } catch (ConnectException e) { 232 throw new ConnectException (path.getURL() + ": " + e.getMessage()); 233 } catch (Exception e) { 234 throw new ConnectException (path.getURL() + ": " + e.toString()); 235 } 236 237 int socketTimeout = 300 * 1000; 238 239 try { 240 s.setSoTimeout(socketTimeout); 241 } catch (Exception e) { 242 } 243 244 return new HttpStream(path, host, port, s); 245 } 246 247 250 private void init(Path path) 251 { 252 _contentLength = -1; 253 _isChunked = false; 254 _isRequestDone = false; 255 _didGet = false; 256 _isPost = false; 257 _isHead = false; 258 _method = null; 259 _attributes.clear(); 260 261 setPath(path); 262 263 if (path instanceof HttpPath) 264 _virtualHost = ((HttpPath) path).getVirtualHost(); 265 } 266 267 270 public void setSSL(boolean isSSL) 271 { 272 _isSSL = isSSL; 273 } 274 275 278 public boolean isSSL() 279 { 280 return _isSSL; 281 } 282 283 286 public void setMethod(String method) 287 { 288 _method = method; 289 } 290 291 294 public void setHead(boolean isHead) 295 { 296 _isHead = isHead; 297 } 298 299 302 public String getHost() 303 { 304 return _host; 305 } 306 307 310 public int getPort() 311 { 312 return _port; 313 } 314 315 321 public Object getAttribute(String name) 322 throws IOException 323 { 324 if (! _didGet) 325 getConnInput(); 326 327 return _attributes.get(name.toLowerCase()); 328 } 329 330 333 public Iterator getAttributeNames() 334 throws IOException 335 { 336 if (! _didGet) 337 getConnInput(); 338 339 return _attributes.keySet().iterator(); 340 } 341 342 345 public void setAttribute(String name, Object value) 346 { 347 if (name.equals("method")) 348 setMethod((String ) value); 349 else if (name.equals("socket-timeout")) { 350 if (value instanceof Integer ) { 351 int socketTimeout = ((Integer ) value).intValue(); 352 353 if (socketTimeout > 0) { 354 try { 355 if (_s != null) 356 _s.setSoTimeout(socketTimeout); 357 } catch (Exception e) { 358 359 } 360 } 361 } 362 } 363 else 364 _attributes.put(name.toLowerCase(), value); 365 } 366 367 370 public void removeAttribute(String name) 371 { 372 _attributes.remove(name.toLowerCase()); 373 } 374 375 378 public void setSocketTimeout(long timeout) 379 throws SocketException 380 { 381 if (_s != null) 382 _s.setSoTimeout((int) timeout); 383 } 384 385 388 public boolean canWrite() 389 { 390 return true; 391 } 392 393 401 public void write(byte []buf, int offset, int length, boolean isEnd) 402 throws IOException 403 { 404 if (! _isPost) 405 return; 406 407 if (_tempStream == null) 408 _tempStream = new MemoryStream(); 409 410 _tempStream.write(buf, offset, length, isEnd); 411 } 412 413 416 public boolean canRead() 417 { 418 return true; 419 } 420 421 425 public int read(byte []buf, int offset, int length) throws IOException 426 { 427 try { 428 return readInt(buf, offset, length); 429 } catch (IOException e) { 430 _isKeepalive = false; 431 throw e; 432 } catch (RuntimeException e) { 433 _isKeepalive = false; 434 throw e; 435 } 436 } 437 438 442 public int readInt(byte []buf, int offset, int length) throws IOException 443 { 444 if (! _didGet) 445 getConnInput(); 446 447 if (_isRequestDone) 448 return -1; 449 450 try { 451 int len = length; 452 453 if (_isChunked) { 454 if (_chunkLength == 0) { 455 int ch; 456 457 for (ch = _rs.read(); 458 ch >= 0 && (ch == '\r' || ch == '\n' || ch == ' '); 459 ch = _rs.read()) { 460 } 461 462 for (; ch >= 0 && ch != '\n'; ch = _rs.read()) { 463 if (ch >= '0' && ch <= '9') 464 _chunkLength = 16 * _chunkLength + ch - '0'; 465 else if (ch >= 'a' && ch <= 'f') 466 _chunkLength = 16 * _chunkLength + ch - 'a' + 10; 467 else if (ch >= 'A' && ch <= 'F') 468 _chunkLength = 16 * _chunkLength + ch - 'A' + 10; 469 } 470 471 if (_chunkLength == 0) { 472 _isRequestDone = true; 473 return -1; 474 } 475 } 476 else if (_chunkLength < 0) 477 return -1; 478 479 if (_chunkLength < len) 480 len = _chunkLength; 481 } 482 else if (_contentLength < 0) { 483 } 484 else if (_contentLength == 0) { 485 _isRequestDone = true; 486 return -1; 487 } 488 else if (_contentLength < len) 489 len = _contentLength; 490 491 len = _rs.read(buf, offset, len); 492 493 if (len < 0) { 494 } 495 else if (_isChunked) 496 _chunkLength -= len; 497 else if (_contentLength > 0) 498 _contentLength -= len; 499 500 return len; 501 } catch (IOException e) { 502 _isKeepalive = false; 503 throw e; 504 } catch (RuntimeException e) { 505 _isKeepalive = false; 506 throw e; 507 } 508 } 509 510 513 private void getConnInput() throws IOException 514 { 515 if (_didGet) 516 return; 517 518 try { 519 getConnInputImpl(); 520 } catch (IOException e) { 521 _isKeepalive = false; 522 throw e; 523 } catch (RuntimeException e) { 524 _isKeepalive = false; 525 throw e; 526 } 527 } 528 529 533 private void getConnInputImpl() throws IOException 534 { 535 if (_didGet) 536 return; 537 538 _didGet = true; 539 540 if (_method != null) { 541 _ws.print(_method); 542 _ws.print(' '); 543 } 544 else if (_isPost) 545 _ws.print("POST "); 546 else if (_isHead) 547 _ws.print("HEAD "); 548 else 549 _ws.print("GET "); 550 551 _ws.print(_path.getPath()); 553 554 if (_path.getQuery() != null) { 555 _ws.print("?"); 556 _ws.print(_path.getQuery()); 557 } 558 _ws.print(" HTTP/1.1\r\n"); 559 _ws.print("Host: "); 560 if (_virtualHost != null) 561 _ws.print(_virtualHost); 562 else { 563 _ws.print(_path.getHost()); 564 if (_path.getPort() != 80) { 565 _ws.print(":"); 566 _ws.print(String.valueOf(_path.getPort())); 567 } 568 } 569 _ws.print("\r\n"); 570 571 Object userAgent = getAttribute("User-Agent"); 572 if (userAgent == null) 573 _ws.print("User-Agent: Mozilla/4.0 (compatible; Resin 1.0; JDK)\r\n"); 574 else 575 _ws.print("User-Agent: " + userAgent + "\r\n"); 576 Iterator iter = getAttributeNames(); 577 while (iter.hasNext()) { 578 String name = (String ) iter.next(); 579 if (_reserved.get(name.toLowerCase()) == null) 580 _ws.print(name + ": " + getAttribute(name) + "\r\n"); 581 } 582 if (! _isKeepalive) 583 _ws.print("Connection: close\r\n"); 584 585 if (_isPost) { 586 int length = 0; 587 if (_tempStream != null) 588 length = _tempStream.getLength(); 589 _ws.print("Content-Length: "); 590 _ws.print(length); 591 _ws.print("\r\n"); 592 } 593 _ws.print("\r\n"); 594 595 if (_isPost) { 596 MemoryStream tempStream = _tempStream; 597 _tempStream = null; 598 if (tempStream != null) { 599 tempStream.writeToStream(_ws); 600 tempStream.destroy(); 601 } 602 } 603 604 _attributes.clear(); 605 606 parseHeaders(); 607 608 if (_isHead) 609 _isRequestDone = true; 610 } 611 612 615 private void parseHeaders() throws IOException 616 { 617 CharBuffer line = new CharBuffer(); 618 619 int count = 0; 621 do { 622 line.clear(); 623 if (! _rs.readln(line)) { 624 _isKeepalive = false; 625 return; 626 } 627 } while (line.length() == 0 && ++count < 10); 628 629 if (line.length() == 0) { 630 _isKeepalive = false; 631 return; 632 } 633 634 if (line.startsWith("HTTP/1.1 100")) { 635 count = 100; 636 do { 637 line.clear(); 638 if (! _rs.readln(line)) { 639 _isKeepalive = false; 640 return; 641 } 642 } while (line.length() != 0 && count-- > 0); 643 644 count = 100; 645 do { 646 line.clear(); 647 if (! _rs.readln(line)) { 648 _isKeepalive = false; 649 return; 650 } 651 } while (line.length() == 0 && count-- > 0); 652 } 653 654 if (line.length() == 0) { 655 _isKeepalive = false; 656 return; 657 } 658 659 int i = 0; 660 for (i = 0; i < line.length() && line.charAt(i) != ' '; i++) { 661 } 662 663 for (; i < line.length() && line.charAt(i) == ' '; i++) { 664 } 665 666 int status = 0; 667 for (; i < line.length(); i++) { 668 char ch = line.charAt(i); 669 if (ch >= '0' && ch <= '9') 670 status = 10 * status + ch - '0'; 671 else 672 break; 673 } 674 675 if (status != 200) 676 _isKeepalive = false; 677 else if (! line.startsWith("HTTP/1.1 ")) 678 _isKeepalive = false; 679 680 _attributes.put("status", String.valueOf(status)); 681 682 CharBuffer key = new CharBuffer(); 683 while (true) { 684 line.clear(); 685 if (! _rs.readln(line) || line.length() == 0) 686 break; 687 688 int lineLength = line.length(); 689 690 for (i = 0; 691 i < lineLength && Character.isWhitespace(line.charAt(i)); 692 i++) { 693 } 694 695 key.clear(); 696 for (; 697 i < lineLength && ! Character.isWhitespace(line.charAt(i)) && 698 line.charAt(i) != ':'; 699 i++) { 700 key.append((char) line.charAt(i)); 701 } 702 703 for (; 704 i < lineLength && Character.isWhitespace(line.charAt(i)); 705 i++) { 706 } 707 708 if (key.length() == 0 || lineLength <= i || line.charAt(i) != ':') 709 continue; 710 711 for (i++; 712 i < lineLength && Character.isWhitespace(line.charAt(i)); 713 i++) { 714 } 715 716 key.toLowerCase(); 717 String value = line.substring(i); 718 719 if (log.isLoggable(Level.FINE)) 720 log.fine(key + ": " + value); 721 722 if (key.matches("content-length")) { 723 _contentLength = Integer.parseInt(value); 724 } 725 else if (key.matches("connection") && 726 value.equalsIgnoreCase("close")) { 727 _isKeepalive = false; 728 } 729 else if (key.matches("transfer-encoding") && 730 value.equalsIgnoreCase("chunked")) { 731 732 _isChunked = true; 733 _chunkLength = 0; 734 } 735 736 _attributes.put(key.toLowerCase().toString(), value); 737 } 738 } 739 740 743 public int getAvailable() throws IOException 744 { 745 if (! _didGet) 746 getConnInput(); 747 748 if (_contentLength > 0) 749 return _contentLength; 750 else 751 return _rs.getAvailable(); 752 } 753 754 757 public void close() throws IOException 758 { 759 if (_isKeepalive) { 760 if (! _didGet) 762 getConnInput(); 763 764 if (! _isRequestDone) { 765 if (_tempBuffer == null) 766 _tempBuffer = new byte[256]; 767 768 try { 769 while (read(_tempBuffer, 0, _tempBuffer.length) > 0) { 770 } 771 } catch (IOException e) { 772 _isKeepalive = false; 773 } 774 } 775 } 776 777 if (com.caucho.util.Alarm.isTest()) 778 _isKeepalive = false; 780 if (_isKeepalive) { 781 HttpStream oldSaved; 782 long now = Alarm.getCurrentTime(); 783 synchronized (LOCK) { 784 oldSaved = _savedStream; 785 _savedStream = this; 786 _saveTime = now; 787 } 788 789 if (oldSaved != null && oldSaved != this) { 790 oldSaved._isKeepalive = false; 791 oldSaved.close(); 792 } 793 794 return; 795 } 796 797 try { 798 try { 799 if (_ws != null) 800 _ws.close(); 801 } catch (Throwable e) { 802 } 803 _ws = null; 804 805 try { 806 if (_rs != null) 807 _rs.close(); 808 } catch (Throwable e) { 809 } 810 _rs = null; 811 812 try { 813 if (_os != null) 814 _os.close(); 815 } catch (Throwable e) { 816 } 817 _os = null; 818 819 try { 820 if (_is != null) 821 _is.close(); 822 } catch (Throwable e) { 823 } 824 _is = null; 825 } finally { 826 if (_s != null) 827 _s.close(); 828 _s = null; 829 } 830 } 831 832 static { 833 _reserved = new HashMap <String ,String >(); 834 _reserved.put("user-agent", ""); 835 _reserved.put("content-length", ""); 836 _reserved.put("content-encoding", ""); 837 _reserved.put("connection", ""); 838 _reserved.put("host", ""); 839 } 840 } 841 | Popular Tags |