1 31 32 package org.apache.commons.httpclient; 33 34 import java.io.BufferedOutputStream ; 35 import java.io.IOException ; 36 import java.io.InputStream ; 37 import java.io.InterruptedIOException ; 38 import java.io.OutputStream ; 39 import java.io.PushbackInputStream ; 40 import java.lang.reflect.Method ; 41 import java.net.InetAddress ; 42 import java.net.Socket ; 43 import java.net.SocketException ; 44 45 import org.apache.commons.httpclient.protocol.DefaultProtocolSocketFactory; 46 import org.apache.commons.httpclient.protocol.Protocol; 47 import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; 48 import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; 49 import org.apache.commons.httpclient.util.TimeoutController; 50 import org.apache.commons.logging.Log; 51 import org.apache.commons.logging.LogFactory; 52 53 85 public class HttpConnection { 86 87 89 95 public HttpConnection(String host, int port) { 96 this(null, -1, host, port, false); 97 } 98 99 112 public HttpConnection(String host, int port, boolean secure) { 113 this(null, -1, host, port, secure); 114 } 115 116 124 public HttpConnection(String host, int port, Protocol protocol) { 125 this(null, -1, host, null, port, protocol); 126 } 127 128 137 public HttpConnection(String host, String virtualHost, int port, Protocol protocol) { 138 this(null, -1, host, virtualHost, port, protocol); 139 } 140 141 150 public HttpConnection( 151 String proxyHost, 152 int proxyPort, 153 String host, 154 int port) { 155 this(proxyHost, proxyPort, host, port, false); 156 } 157 158 174 public HttpConnection( 175 String proxyHost, 176 int proxyPort, 177 String host, 178 int port, 179 boolean secure) { 180 this(proxyHost, proxyPort, host, null, port, 181 Protocol.getProtocol(secure ? "https" : "http")); 182 } 183 184 189 public HttpConnection(HostConfiguration hostConfiguration) { 190 this(hostConfiguration.getProxyHost(), 191 hostConfiguration.getProxyPort(), 192 hostConfiguration.getHost(), 193 hostConfiguration.getVirtualHost(), 194 hostConfiguration.getPort(), 195 hostConfiguration.getProtocol()); 196 this.localAddress = hostConfiguration.getLocalAddress(); 197 } 198 199 211 public HttpConnection( 212 String proxyHost, 213 int proxyPort, 214 String host, 215 String virtualHost, 216 int port, 217 Protocol protocol) { 218 219 if (host == null) { 220 throw new IllegalArgumentException ("host parameter is null"); 221 } 222 if (protocol == null) { 223 throw new IllegalArgumentException ("protocol is null"); 224 } 225 226 proxyHostName = proxyHost; 227 proxyPortNumber = proxyPort; 228 hostName = host; 229 virtualName = virtualHost; 230 portNumber = protocol.resolvePort(port); 231 protocolInUse = protocol; 232 } 233 234 236 241 public String getHost() { 242 return hostName; 243 } 244 245 251 public void setHost(String host) throws IllegalStateException { 252 if (host == null) { 253 throw new IllegalArgumentException ("host parameter is null"); 254 } 255 assertNotOpen(); 256 hostName = host; 257 } 258 259 264 public String getVirtualHost() { 265 return virtualName; 266 } 267 268 278 public void setVirtualHost(String host) throws IllegalStateException { 279 assertNotOpen(); 280 virtualName = host; 281 } 282 283 291 public int getPort() { 292 if (portNumber < 0) { 293 return isSecure() ? 443 : 80; 294 } else { 295 return portNumber; 296 } 297 } 298 299 306 public void setPort(int port) throws IllegalStateException { 307 assertNotOpen(); 308 portNumber = port; 309 } 310 311 316 public String getProxyHost() { 317 return proxyHostName; 318 } 319 320 327 public void setProxyHost(String host) throws IllegalStateException { 328 assertNotOpen(); 329 proxyHostName = host; 330 } 331 332 337 public int getProxyPort() { 338 return proxyPortNumber; 339 } 340 341 348 public void setProxyPort(int port) throws IllegalStateException { 349 assertNotOpen(); 350 proxyPortNumber = port; 351 } 352 353 359 public boolean isSecure() { 360 return protocolInUse.isSecure(); 361 } 362 363 367 public Protocol getProtocol() { 368 return protocolInUse; 369 } 370 371 383 public void setSecure(boolean secure) throws IllegalStateException { 384 assertNotOpen(); 385 protocolInUse = secure 386 ? Protocol.getProtocol("https") 387 : Protocol.getProtocol("http"); 388 } 389 390 397 public void setProtocol(Protocol protocol) { 398 assertNotOpen(); 399 400 if (protocol == null) { 401 throw new IllegalArgumentException ("protocol is null"); 402 } 403 404 protocolInUse = protocol; 405 406 } 407 408 414 public InetAddress getLocalAddress() { 415 return this.localAddress; 416 } 417 418 424 public void setLocalAddress(InetAddress localAddress) { 425 assertNotOpen(); 426 this.localAddress = localAddress; 427 } 428 429 435 public boolean isOpen() { 436 if (used && isOpen && isStaleCheckingEnabled() && isStale()) { 437 LOG.debug("Connection is stale, closing..."); 438 close(); 439 } 440 return isOpen; 441 } 442 443 450 public boolean isStaleCheckingEnabled() { 451 return staleCheckingEnabled; 452 } 453 454 467 public void setStaleCheckingEnabled(boolean staleCheckEnabled) { 468 this.staleCheckingEnabled = staleCheckEnabled; 469 } 470 471 491 protected boolean isStale() { 492 boolean isStale = true; 493 if (isOpen) { 494 isStale = false; 497 try { 498 if (inputStream.available() == 0) { 499 try { 500 socket.setSoTimeout(1); 501 int byteRead = inputStream.read(); 502 if (byteRead == -1) { 503 isStale = true; 506 } else { 507 inputStream.unread(byteRead); 508 } 509 } finally { 510 socket.setSoTimeout(soTimeout); 511 } 512 } 513 } catch (InterruptedIOException e) { 514 } catch (IOException e) { 516 LOG.debug( 518 "An error occurred while reading from the socket, is appears to be stale", 519 e 520 ); 521 isStale = true; 522 } 523 } 524 525 return isStale; 526 } 527 528 535 public boolean isProxied() { 536 return (!(null == proxyHostName || 0 >= proxyPortNumber)); 537 } 538 539 549 public void setLastResponseInputStream(InputStream inStream) { 550 lastResponseInputStream = inStream; 551 } 552 553 566 public InputStream getLastResponseInputStream() { 567 return lastResponseInputStream; 568 } 569 570 572 583 public void setSoTimeout(int timeout) 584 throws SocketException , IllegalStateException { 585 LOG.debug("HttpConnection.setSoTimeout(" + timeout + ")"); 586 soTimeout = timeout; 587 if (socket != null) { 588 socket.setSoTimeout(timeout); 589 } 590 } 591 592 601 public int getSoTimeout() throws SocketException { 602 LOG.debug("HttpConnection.getSoTimeout()"); 603 if (this.socket != null) { 604 return this.socket.getSoTimeout(); 605 } else { 606 return this.soTimeout; 607 } 608 } 609 610 616 public void setConnectionTimeout(int timeout) { 617 this.connectTimeout = timeout; 618 } 619 620 628 public void open() throws IOException { 629 LOG.trace("enter HttpConnection.open()"); 630 631 assertNotOpen(); 632 try { 633 if (null == socket) { 634 635 final String host = (null == proxyHostName) ? hostName : proxyHostName; 636 final int port = (null == proxyHostName) ? portNumber : proxyPortNumber; 637 638 usingSecureSocket = isSecure() && !isProxied(); 639 640 final ProtocolSocketFactory socketFactory = 643 (isSecure() && isProxied() 644 ? new DefaultProtocolSocketFactory() 645 : protocolInUse.getSocketFactory()); 646 647 if (connectTimeout == 0) { 648 if (localAddress != null) { 649 socket = socketFactory.createSocket(host, port, localAddress, 0); 650 } else { 651 socket = socketFactory.createSocket(host, port); 652 } 653 } else { 654 SocketTask task = new SocketTask() { 655 public void doit() throws IOException { 656 if (localAddress != null) { 657 setSocket(socketFactory.createSocket(host, port, localAddress, 0)); 658 } else { 659 setSocket(socketFactory.createSocket(host, port)); 660 } 661 } 662 }; 663 TimeoutController.execute(task, connectTimeout); 664 socket = task.getSocket(); 665 if (task.exception != null) { 666 throw task.exception; 667 } 668 } 669 670 } 671 672 679 680 socket.setTcpNoDelay(soNodelay); 681 socket.setSoTimeout(soTimeout); 682 if (sendBufferSize != -1) { 683 socket.setSendBufferSize(sendBufferSize); 684 } 685 int outbuffersize = socket.getSendBufferSize(); 686 if (outbuffersize > 2048) { 687 outbuffersize = 2048; 688 } 689 inputStream = new PushbackInputStream (socket.getInputStream()); 690 outputStream = new BufferedOutputStream ( 691 new WrappedOutputStream(socket.getOutputStream()), 692 outbuffersize 693 ); 694 isOpen = true; 695 used = false; 696 } catch (IOException e) { 697 closeSocketAndStreams(); 700 throw e; 701 } catch (TimeoutController.TimeoutException e) { 702 if (LOG.isWarnEnabled()) { 703 LOG.warn("The host " + hostName + ":" + portNumber 704 + " (or proxy " + proxyHostName + ":" + proxyPortNumber 705 + ") did not accept the connection within timeout of " 706 + connectTimeout + " milliseconds"); 707 } 708 throw new ConnectionTimeoutException(); 709 } 710 } 711 712 722 public void tunnelCreated() throws IllegalStateException , IOException { 723 LOG.trace("enter HttpConnection.tunnelCreated()"); 724 725 if (!isSecure() || !isProxied()) { 726 throw new IllegalStateException ( 727 "Connection must be secure " 728 + "and proxied to use this feature"); 729 } 730 731 if (usingSecureSocket) { 732 throw new IllegalStateException ("Already using a secure socket"); 733 } 734 735 SecureProtocolSocketFactory socketFactory = 736 (SecureProtocolSocketFactory) protocolInUse.getSocketFactory(); 737 738 socket = socketFactory.createSocket(socket, hostName, portNumber, true); 739 if (sendBufferSize != -1) { 740 socket.setSendBufferSize(sendBufferSize); 741 } 742 int outbuffersize = socket.getSendBufferSize(); 743 if (outbuffersize > 2048) { 744 outbuffersize = 2048; 745 } 746 inputStream = new PushbackInputStream (socket.getInputStream()); 747 outputStream = new BufferedOutputStream ( 748 new WrappedOutputStream(socket.getOutputStream()), 749 outbuffersize 750 ); 751 usingSecureSocket = true; 752 tunnelEstablished = true; 753 LOG.debug("Secure tunnel created"); 754 } 755 756 762 public boolean isTransparent() { 763 return !isProxied() || tunnelEstablished; 764 } 765 766 772 public void flushRequestOutputStream() throws IOException { 773 LOG.trace("enter HttpConnection.flushRequestOutputStream()"); 774 assertOpen(); 775 outputStream.flush(); 776 } 777 778 785 public OutputStream getRequestOutputStream() 786 throws IOException , IllegalStateException { 787 LOG.trace("enter HttpConnection.getRequestOutputStream()"); 788 assertOpen(); 789 OutputStream out = this.outputStream; 790 if (Wire.CONTENT_WIRE.enabled()) { 791 out = new WireLogOutputStream(out, Wire.CONTENT_WIRE); 792 } 793 return out; 794 } 795 796 806 public OutputStream getRequestOutputStream(boolean useChunking) 807 throws IOException , IllegalStateException { 808 LOG.trace("enter HttpConnection.getRequestOutputStream(boolean)"); 809 810 OutputStream out = getRequestOutputStream(); 811 if (useChunking) { 812 out = new ChunkedOutputStream(out); 813 } 814 return out; 815 } 816 817 831 public InputStream getResponseInputStream(HttpMethod method) 832 throws IOException , IllegalStateException { 833 LOG.trace("enter HttpConnection.getResponseInputStream(HttpMethod)"); 834 return getResponseInputStream(); 835 } 836 837 843 public InputStream getResponseInputStream() 844 throws IOException , IllegalStateException { 845 LOG.trace("enter HttpConnection.getResponseInputStream()"); 846 assertOpen(); 847 return inputStream; 848 } 849 850 860 public boolean isResponseAvailable() 861 throws IOException { 862 LOG.trace("enter HttpConnection.isResponseAvailable()"); 863 assertOpen(); 864 return this.inputStream.available() > 0; 865 } 866 867 877 public boolean isResponseAvailable(int timeout) 878 throws IOException { 879 LOG.trace("enter HttpConnection.isResponseAvailable(int)"); 880 assertOpen(); 881 boolean result = false; 882 if (this.inputStream.available() > 0) { 883 result = true; 884 } else { 885 try { 886 this.socket.setSoTimeout(timeout); 887 int byteRead = inputStream.read(); 888 if (byteRead != -1) { 889 inputStream.unread(byteRead); 890 LOG.debug("Input data available"); 891 result = true; 892 } else { 893 LOG.debug("Input data not available"); 894 } 895 } catch (InterruptedIOException e) { 896 if (LOG.isDebugEnabled()) { 897 LOG.debug("Input data not available after " + timeout + " ms"); 898 } 899 } finally { 900 try { 901 socket.setSoTimeout(soTimeout); 902 } catch (IOException ioe) { 903 LOG.debug("An error ocurred while resetting soTimeout, we will assume that" 904 + " no response is available.", 905 ioe); 906 result = false; 907 } 908 } 909 } 910 return result; 911 } 912 913 922 public void write(byte[] data) 923 throws IOException , IllegalStateException , HttpRecoverableException { 924 LOG.trace("enter HttpConnection.write(byte[])"); 925 this.write(data, 0, data.length); 926 } 927 928 944 public void write(byte[] data, int offset, int length) 945 throws IOException , IllegalStateException , HttpRecoverableException { 946 LOG.trace("enter HttpConnection.write(byte[], int, int)"); 947 948 if (offset + length > data.length) { 949 throw new HttpRecoverableException("Unable to write:" 950 + " offset=" + offset + " length=" + length 951 + " data.length=" + data.length); 952 } else if (data.length <= 0) { 953 throw new HttpRecoverableException( 954 "Unable to write:" + " data.length=" + data.length); 955 } 956 957 assertOpen(); 958 959 try { 960 outputStream.write(data, offset, length); 961 } catch (HttpRecoverableException hre) { 962 throw hre; 963 } catch (SocketException se) { 964 LOG.debug( 965 "HttpConnection: Socket exception while writing data", 966 se); 967 throw new HttpRecoverableException(se.toString()); 968 } catch (IOException ioe) { 969 LOG.debug("HttpConnection: Exception while writing data", ioe); 970 throw ioe; 971 } 972 } 973 974 983 public void writeLine(byte[] data) 984 throws IOException , IllegalStateException , HttpRecoverableException { 985 LOG.trace("enter HttpConnection.writeLine(byte[])"); 986 write(data); 987 writeLine(); 988 } 989 990 998 public void writeLine() 999 throws IOException , IllegalStateException , HttpRecoverableException { 1000 LOG.trace("enter HttpConnection.writeLine()"); 1001 write(CRLF); 1002 } 1003 1004 1013 public void print(String data) 1014 throws IOException , IllegalStateException , HttpRecoverableException { 1015 LOG.trace("enter HttpConnection.print(String)"); 1016 write(HttpConstants.getBytes(data)); 1017 } 1018 1019 1029 public void printLine(String data) 1030 throws IOException , IllegalStateException , HttpRecoverableException { 1031 LOG.trace("enter HttpConnection.printLine(String)"); 1032 writeLine(HttpConstants.getBytes(data)); 1033 } 1034 1035 1043 public void printLine() 1044 throws IOException , IllegalStateException , HttpRecoverableException { 1045 LOG.trace("enter HttpConnection.printLine()"); 1046 writeLine(); 1047 } 1048 1049 1058 public String readLine() throws IOException , IllegalStateException { 1059 LOG.trace("enter HttpConnection.readLine()"); 1060 1061 assertOpen(); 1062 return HttpParser.readLine(inputStream); 1063 } 1064 1065 1069 public void shutdownOutput() { 1070 LOG.trace("enter HttpConnection.shutdownOutput()"); 1071 1072 try { 1073 Class [] paramsClasses = new Class [0]; 1077 Method shutdownOutput = 1078 socket.getClass().getMethod("shutdownOutput", paramsClasses); 1079 Object [] params = new Object [0]; 1080 shutdownOutput.invoke(socket, params); 1081 } catch (Exception ex) { 1082 LOG.debug("Unexpected Exception caught", ex); 1083 } 1085 } 1087 1088 1091 public void close() { 1092 LOG.trace("enter HttpConnection.close()"); 1093 closeSocketAndStreams(); 1094 } 1095 1096 1100 public HttpConnectionManager getHttpConnectionManager() { 1101 return httpConnectionManager; 1102 } 1103 1104 1108 public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) { 1109 this.httpConnectionManager = httpConnectionManager; 1110 } 1111 1112 1115 public void releaseConnection() { 1116 LOG.trace("enter HttpConnection.releaseConnection()"); 1117 1118 used = true; 1120 if (httpConnectionManager != null) { 1121 httpConnectionManager.releaseConnection(this); 1122 } 1123 } 1124 1125 1127 1130 protected void closeSocketAndStreams() { 1131 LOG.trace("enter HttpConnection.closeSockedAndStreams()"); 1132 1133 lastResponseInputStream = null; 1135 1136 if (null != outputStream) { 1137 OutputStream temp = outputStream; 1138 outputStream = null; 1139 try { 1140 temp.close(); 1141 } catch (Exception ex) { 1142 LOG.debug("Exception caught when closing output", ex); 1143 } 1145 } 1146 1147 if (null != inputStream) { 1148 InputStream temp = inputStream; 1149 inputStream = null; 1150 try { 1151 temp.close(); 1152 } catch (Exception ex) { 1153 LOG.debug("Exception caught when closing input", ex); 1154 } 1156 } 1157 1158 if (null != socket) { 1159 Socket temp = socket; 1160 socket = null; 1161 try { 1162 temp.close(); 1163 } catch (Exception ex) { 1164 LOG.debug("Exception caught when closing socket", ex); 1165 } 1167 } 1168 isOpen = false; 1169 used = false; 1170 tunnelEstablished = false; 1171 usingSecureSocket = false; 1172 } 1173 1174 1179 protected void assertNotOpen() throws IllegalStateException { 1180 if (isOpen) { 1181 throw new IllegalStateException ("Connection is open"); 1182 } 1183 } 1184 1185 1190 protected void assertOpen() throws IllegalStateException { 1191 if (!isOpen) { 1192 throw new IllegalStateException ("Connection is not open"); 1193 } 1194 } 1195 1196 1206 public int getSendBufferSize() throws SocketException { 1207 if (socket == null) { 1208 return -1; 1209 } else { 1210 return socket.getSendBufferSize(); 1211 } 1212 } 1213 1214 1223 public void setSendBufferSize(int sendBufferSize) throws SocketException { 1224 this.sendBufferSize = sendBufferSize; 1225 if (socket != null) { 1226 socket.setSendBufferSize(sendBufferSize); 1227 } 1228 } 1229 1230 1234 public static class ConnectionTimeoutException extends IOException { 1235 1236 public ConnectionTimeoutException() { 1237 } 1238 } 1239 1240 1241 1244 private abstract class SocketTask implements Runnable { 1245 1246 1247 private Socket socket; 1248 1249 private IOException exception; 1250 1251 1255 protected void setSocket(final Socket newSocket) { 1256 socket = newSocket; 1257 } 1258 1259 1263 protected Socket getSocket() { 1264 return socket; 1265 } 1266 1270 public abstract void doit() throws IOException ; 1271 1272 1273 public void run() { 1274 try { 1275 doit(); 1276 } catch (IOException e) { 1277 exception = e; 1278 } 1279 } 1280 } 1281 1282 1286 private class WrappedOutputStream extends OutputStream { 1287 1288 1289 private OutputStream out; 1290 1291 1294 public WrappedOutputStream(OutputStream out) { 1295 this.out = out; 1296 } 1297 1298 1303 private IOException handleException(IOException ioe) { 1304 boolean tempUsed = used; 1306 HttpConnection.this.close(); 1307 if (tempUsed) { 1308 LOG.debug( 1309 "Output exception occurred on a used connection. Will treat as recoverable.", 1310 ioe 1311 ); 1312 return new HttpRecoverableException(ioe.toString()); 1313 } else { 1314 return ioe; 1315 } 1316 } 1317 1318 public void write(int b) throws IOException { 1319 try { 1320 out.write(b); 1321 } catch (IOException ioe) { 1322 throw handleException(ioe); 1323 } 1324 } 1325 1326 public void flush() throws IOException { 1327 try { 1328 out.flush(); 1329 } catch (IOException ioe) { 1330 throw handleException(ioe); 1331 } 1332 } 1333 1334 public void close() throws IOException { 1335 try { 1336 out.close(); 1337 } catch (IOException ioe) { 1338 throw handleException(ioe); 1339 } 1340 } 1341 1342 public void write(byte[] b, int off, int len) throws IOException { 1343 try { 1344 out.write(b, off, len); 1345 } catch (IOException ioe) { 1346 throw handleException(ioe); 1347 } 1348 } 1349 1350 public void write(byte[] b) throws IOException { 1351 try { 1352 out.write(b); 1353 } catch (IOException ioe) { 1354 throw handleException(ioe); 1355 } 1356 } 1357 1358 } 1359 1360 1362 1363 private static final byte[] CRLF = new byte[] {(byte) 13, (byte) 10}; 1364 1365 1366 private static final Log LOG = LogFactory.getLog(HttpConnection.class); 1367 1368 1370 1371 private boolean used = false; 1372 1373 1374 private String hostName = null; 1375 1376 1377 private String virtualName = null; 1378 1379 1380 private int portNumber = -1; 1381 1382 1383 private String proxyHostName = null; 1384 1385 1386 private int proxyPortNumber = -1; 1387 1388 1389 private Socket socket = null; 1390 1391 1392 private PushbackInputStream inputStream = null; 1393 1394 1395 private OutputStream outputStream = null; 1396 1397 1398 private int sendBufferSize = -1; 1399 1400 1401 private InputStream lastResponseInputStream = null; 1402 1403 1404 protected boolean isOpen = false; 1405 1406 1407 private Protocol protocolInUse; 1408 1409 1410 private int soTimeout = 0; 1411 1412 1413 private boolean soNodelay = true; 1414 1415 1416 private boolean usingSecureSocket = false; 1417 1418 1419 private boolean tunnelEstablished = false; 1420 1421 1422 private boolean staleCheckingEnabled = true; 1423 1424 1425 private int connectTimeout = 0; 1426 1427 1428 private HttpConnectionManager httpConnectionManager; 1429 1430 1431 private InetAddress localAddress; 1432} 1433 | Popular Tags |