1 /* 2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.messaging.saaj.client.p2p; 27 28 import java.io.*; 29 import java.lang.reflect.Method; 30 import java.net.*; 31 import java.security.*; 32 import java.util.Iterator; 33 import java.util.StringTokenizer; 34 import java.util.logging.Level; 35 import java.util.logging.Logger; 36 37 import javax.xml.soap.*; 38 39 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl; 40 import com.sun.xml.internal.messaging.saaj.util.*; 41 42 /** 43 * This represents a "connection" to the simple HTTP-based provider. 44 * 45 * @author Anil Vijendran (akv@eng.sun.com) 46 * @author Rajiv Mordani (rajiv.mordani@sun.com) 47 * @author Manveen Kaur (manveen.kaur@sun.com) 48 * 49 */ 50 class HttpSOAPConnection extends SOAPConnection { 51 52 public static final String vmVendor = SAAJUtil.getSystemProperty("java.vendor.url"); 53 private static final String sunVmVendor = "http://java.sun.com/"; 54 private static final String ibmVmVendor = "http://www.ibm.com/"; 55 private static final boolean isSunVM = sunVmVendor.equals(vmVendor) ? true: false; 56 private static final boolean isIBMVM = ibmVmVendor.equals(vmVendor) ? true : false; 57 private static final String JAXM_URLENDPOINT="javax.xml.messaging.URLEndpoint"; 58 59 protected static final Logger log = 60 Logger.getLogger(LogDomainConstants.HTTP_CONN_DOMAIN, 61 "com.sun.xml.internal.messaging.saaj.client.p2p.LocalStrings"); 62 63 64 MessageFactory messageFactory = null; 65 66 boolean closed = false; 67 68 public HttpSOAPConnection() throws SOAPException { 69 70 try { 71 messageFactory = MessageFactory.newInstance(SOAPConstants.DYNAMIC_SOAP_PROTOCOL); 72 } catch (NoSuchMethodError ex) { 73 //fallback to default SOAP 1.1 in this case for backward compatibility 74 messageFactory = MessageFactory.newInstance(); 75 } catch (Exception ex) { 76 log.log(Level.SEVERE, "SAAJ0001.p2p.cannot.create.msg.factory", ex); 77 throw new SOAPExceptionImpl("Unable to create message factory", ex); 78 } 79 } 80 81 public void close() throws SOAPException { 82 if (closed) { 83 log.severe("SAAJ0002.p2p.close.already.closed.conn"); 84 throw new SOAPExceptionImpl("Connection already closed"); 85 } 86 87 messageFactory = null; 88 closed = true; 89 } 90 91 public SOAPMessage call(SOAPMessage message, Object endPoint) 92 throws SOAPException { 93 if (closed) { 94 log.severe("SAAJ0003.p2p.call.already.closed.conn"); 95 throw new SOAPExceptionImpl("Connection is closed"); 96 } 97 98 Class urlEndpointClass = null; 99 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 100 try { 101 if (loader != null) { 102 urlEndpointClass = loader.loadClass(JAXM_URLENDPOINT); 103 } else { 104 urlEndpointClass = Class.forName(JAXM_URLENDPOINT); 105 } 106 } catch (ClassNotFoundException ex) { 107 //Do nothing. URLEndpoint is available only when JAXM is there. 108 log.finest("SAAJ0090.p2p.endpoint.available.only.for.JAXM"); 109 } 110 111 if (urlEndpointClass != null) { 112 if (urlEndpointClass.isInstance(endPoint)) { 113 String url = null; 114 115 try { 116 Method m = urlEndpointClass.getMethod("getURL", (Class[])null); 117 url = (String) m.invoke(endPoint, (Object[])null); 118 } catch (Exception ex) { 119 // TBD -- exception chaining 120 log.log(Level.SEVERE,"SAAJ0004.p2p.internal.err",ex); 121 throw new SOAPExceptionImpl( 122 "Internal error: " + ex.getMessage()); 123 } 124 try { 125 endPoint = new URL(url); 126 } catch (MalformedURLException mex) { 127 log.log(Level.SEVERE,"SAAJ0005.p2p.", mex); 128 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 129 } 130 } 131 } 132 133 if (endPoint instanceof java.lang.String) { 134 try { 135 endPoint = new URL((String) endPoint); 136 } catch (MalformedURLException mex) { 137 log.log(Level.SEVERE, "SAAJ0006.p2p.bad.URL", mex); 138 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 139 } 140 } 141 142 if (endPoint instanceof URL) 143 try { 144 SOAPMessage response = post(message, (URL)endPoint); 145 return response; 146 } catch (Exception ex) { 147 // TBD -- chaining? 148 throw new SOAPExceptionImpl(ex); 149 } else { 150 log.severe("SAAJ0007.p2p.bad.endPoint.type"); 151 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint); 152 } 153 } 154 155 SOAPMessage post(SOAPMessage message, URL endPoint) throws SOAPException { 156 boolean isFailure = false; 157 158 URL url = null; 159 HttpURLConnection httpConnection = null; 160 161 int responseCode = 0; 162 try { 163 if (endPoint.getProtocol().equals("https")) 164 //if(!setHttps) 165 initHttps(); 166 // Process the URL 167 JaxmURI uri = new JaxmURI(endPoint.toString()); 168 String userInfo = uri.getUserinfo(); 169 170 url = endPoint; 171 172 if (dL > 0) 173 d("uri: " + userInfo + " " + url + " " + uri); 174 175 // TBD 176 // Will deal with https later. 177 if (!url.getProtocol().equalsIgnoreCase("http") 178 && !url.getProtocol().equalsIgnoreCase("https")) { 179 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https"); 180 throw new IllegalArgumentException( 181 "Protocol " 182 + url.getProtocol() 183 + " not supported in URL " 184 + url); 185 } 186 httpConnection = (HttpURLConnection) createConnection(url); 187 188 httpConnection.setRequestMethod("POST"); 189 190 httpConnection.setDoOutput(true); 191 httpConnection.setDoInput(true); 192 httpConnection.setUseCaches(false); 193 httpConnection.setInstanceFollowRedirects(true); 194 195 if (message.saveRequired()) 196 message.saveChanges(); 197 198 MimeHeaders headers = message.getMimeHeaders(); 199 200 Iterator it = headers.getAllHeaders(); 201 boolean hasAuth = false; // true if we find explicit Auth header 202 while (it.hasNext()) { 203 MimeHeader header = (MimeHeader) it.next(); 204 205 String[] values = headers.getHeader(header.getName()); 206 if (values.length == 1) 207 httpConnection.setRequestProperty( 208 header.getName(), 209 header.getValue()); 210 else { 211 StringBuffer concat = new StringBuffer(); 212 int i = 0; 213 while (i < values.length) { 214 if (i != 0) 215 concat.append(','); 216 concat.append(values[i]); 217 i++; 218 } 219 220 httpConnection.setRequestProperty( 221 header.getName(), 222 concat.toString()); 223 } 224 225 if ("Authorization".equals(header.getName())) { 226 hasAuth = true; 227 log.fine("SAAJ0091.p2p.https.auth.in.POST.true"); 228 } 229 } 230 231 if (!hasAuth && userInfo != null) { 232 initAuthUserInfo(httpConnection, userInfo); 233 } 234 235 OutputStream out = httpConnection.getOutputStream(); 236 message.writeTo(out); 237 238 out.flush(); 239 out.close(); 240 241 httpConnection.connect(); 242 243 try { 244 245 responseCode = httpConnection.getResponseCode(); 246 247 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults 248 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 249 isFailure = true; 250 } 251 //else if (responseCode != HttpURLConnection.HTTP_OK) 252 //else if (!(responseCode >= HttpURLConnection.HTTP_OK && responseCode < 207)) 253 else if ((responseCode / 100) != 2) { 254 log.log(Level.SEVERE, 255 "SAAJ0008.p2p.bad.response", 256 new String[] {httpConnection.getResponseMessage()}); 257 throw new SOAPExceptionImpl( 258 "Bad response: (" 259 + responseCode 260 + httpConnection.getResponseMessage()); 261 262 } 263 } catch (IOException e) { 264 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds! 265 responseCode = httpConnection.getResponseCode(); 266 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 267 isFailure = true; 268 } else { 269 throw e; 270 } 271 272 } 273 274 } catch (SOAPException ex) { 275 throw ex; 276 } catch (Exception ex) { 277 log.severe("SAAJ0009.p2p.msg.send.failed"); 278 throw new SOAPExceptionImpl("Message send failed", ex); 279 } 280 281 SOAPMessage response = null; 282 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) { 283 try { 284 MimeHeaders headers = new MimeHeaders(); 285 286 String key, value; 287 288 // Header field 0 is the status line so we skip it. 289 290 int i = 1; 291 292 while (true) { 293 key = httpConnection.getHeaderFieldKey(i); 294 value = httpConnection.getHeaderField(i); 295 296 if (key == null && value == null) 297 break; 298 299 if (key != null) { 300 StringTokenizer values = 301 new StringTokenizer(value, ","); 302 while (values.hasMoreTokens()) 303 headers.addHeader(key, values.nextToken().trim()); 304 } 305 i++; 306 } 307 308 InputStream httpIn = 309 (isFailure 310 ? httpConnection.getErrorStream() 311 : httpConnection.getInputStream()); 312 313 byte[] bytes = readFully(httpIn); 314 315 int length = 316 httpConnection.getContentLength() == -1 317 ? bytes.length 318 : httpConnection.getContentLength(); 319 320 // If no reply message is returned, 321 // content-Length header field value is expected to be zero. 322 if (length == 0) { 323 response = null; 324 log.warning("SAAJ0014.p2p.content.zero"); 325 } else { 326 ByteInputStream in = new ByteInputStream(bytes, length); 327 response = messageFactory.createMessage(headers, in); 328 } 329 330 httpIn.close(); 331 httpConnection.disconnect(); 332 333 } catch (SOAPException ex) { 334 throw ex; 335 } catch (Exception ex) { 336 log.log(Level.SEVERE,"SAAJ0010.p2p.cannot.read.resp", ex); 337 throw new SOAPExceptionImpl( 338 "Unable to read response: " + ex.getMessage()); 339 } 340 } 341 return response; 342 } 343 344 // Object identifies where the request should be sent. 345 // It is required to support objects of type String and java.net.URL. 346 347 public SOAPMessage get(Object endPoint) throws SOAPException { 348 if (closed) { 349 log.severe("SAAJ0011.p2p.get.already.closed.conn"); 350 throw new SOAPExceptionImpl("Connection is closed"); 351 } 352 Class urlEndpointClass = null; 353 354 try { 355 urlEndpointClass = Class.forName("javax.xml.messaging.URLEndpoint"); 356 } catch (Exception ex) { 357 //Do nothing. URLEndpoint is available only when JAXM is there. 358 } 359 360 if (urlEndpointClass != null) { 361 if (urlEndpointClass.isInstance(endPoint)) { 362 String url = null; 363 364 try { 365 Method m = urlEndpointClass.getMethod("getURL", (Class[])null); 366 url = (String) m.invoke(endPoint, (Object[])null); 367 } catch (Exception ex) { 368 log.severe("SAAJ0004.p2p.internal.err"); 369 throw new SOAPExceptionImpl( 370 "Internal error: " + ex.getMessage()); 371 } 372 try { 373 endPoint = new URL(url); 374 } catch (MalformedURLException mex) { 375 log.severe("SAAJ0005.p2p."); 376 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 377 } 378 } 379 } 380 381 if (endPoint instanceof java.lang.String) { 382 try { 383 endPoint = new URL((String) endPoint); 384 } catch (MalformedURLException mex) { 385 log.severe("SAAJ0006.p2p.bad.URL"); 386 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 387 } 388 } 389 390 if (endPoint instanceof URL) 391 try { 392 SOAPMessage response = doGet((URL)endPoint); 393 return response; 394 } catch (Exception ex) { 395 throw new SOAPExceptionImpl(ex); 396 } else 397 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint); 398 } 399 400 SOAPMessage doGet(URL endPoint) throws SOAPException { 401 boolean isFailure = false; 402 403 URL url = null; 404 HttpURLConnection httpConnection = null; 405 406 int responseCode = 0; 407 try { 408 /// Is https GET allowed?? 409 if (endPoint.getProtocol().equals("https")) 410 initHttps(); 411 // Process the URL 412 JaxmURI uri = new JaxmURI(endPoint.toString()); 413 String userInfo = uri.getUserinfo(); 414 415 url = endPoint; 416 417 if (dL > 0) 418 d("uri: " + userInfo + " " + url + " " + uri); 419 420 // TBD 421 // Will deal with https later. 422 if (!url.getProtocol().equalsIgnoreCase("http") 423 && !url.getProtocol().equalsIgnoreCase("https")) { 424 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https"); 425 throw new IllegalArgumentException( 426 "Protocol " 427 + url.getProtocol() 428 + " not supported in URL " 429 + url); 430 } 431 httpConnection = (HttpURLConnection) createConnection(url); 432 433 httpConnection.setRequestMethod("GET"); 434 435 httpConnection.setDoOutput(true); 436 httpConnection.setDoInput(true); 437 httpConnection.setUseCaches(false); 438 httpConnection.setFollowRedirects(true); 439 440 httpConnection.connect(); 441 442 try { 443 444 responseCode = httpConnection.getResponseCode(); 445 446 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults 447 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 448 isFailure = true; 449 } else if ((responseCode / 100) != 2) { 450 log.log(Level.SEVERE, 451 "SAAJ0008.p2p.bad.response", 452 new String[] { httpConnection.getResponseMessage()}); 453 throw new SOAPExceptionImpl( 454 "Bad response: (" 455 + responseCode 456 + httpConnection.getResponseMessage()); 457 458 } 459 } catch (IOException e) { 460 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds! 461 responseCode = httpConnection.getResponseCode(); 462 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 463 isFailure = true; 464 } else { 465 throw e; 466 } 467 468 } 469 470 } catch (SOAPException ex) { 471 throw ex; 472 } catch (Exception ex) { 473 log.severe("SAAJ0012.p2p.get.failed"); 474 throw new SOAPExceptionImpl("Get failed", ex); 475 } 476 477 SOAPMessage response = null; 478 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) { 479 try { 480 MimeHeaders headers = new MimeHeaders(); 481 482 String key, value; 483 484 // Header field 0 is the status line so we skip it. 485 486 int i = 1; 487 488 while (true) { 489 key = httpConnection.getHeaderFieldKey(i); 490 value = httpConnection.getHeaderField(i); 491 492 if (key == null && value == null) 493 break; 494 495 if (key != null) { 496 StringTokenizer values = 497 new StringTokenizer(value, ","); 498 while (values.hasMoreTokens()) 499 headers.addHeader(key, values.nextToken().trim()); 500 } 501 i++; 502 } 503 504 InputStream httpIn = 505 (isFailure 506 ? httpConnection.getErrorStream() 507 : httpConnection.getInputStream()); 508 // If no reply message is returned, 509 // content-Length header field value is expected to be zero. 510 // java SE 6 documentation says : 511 // available() : an estimate of the number of bytes that can be read 512 //(or skipped over) from this input stream without blocking 513 //or 0 when it reaches the end of the input stream. 514 if ((httpIn == null ) 515 || (httpConnection.getContentLength() == 0) 516 || (httpIn.available() == 0)) { 517 response = null; 518 log.warning("SAAJ0014.p2p.content.zero"); 519 } else { 520 response = messageFactory.createMessage(headers, httpIn); 521 } 522 523 httpIn.close(); 524 httpConnection.disconnect(); 525 526 } catch (SOAPException ex) { 527 throw ex; 528 } catch (Exception ex) { 529 log.log(Level.SEVERE, 530 "SAAJ0010.p2p.cannot.read.resp", 531 ex); 532 throw new SOAPExceptionImpl( 533 "Unable to read response: " + ex.getMessage()); 534 } 535 } 536 return response; 537 } 538 539 private byte[] readFully(InputStream istream) throws IOException { 540 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 541 byte[] buf = new byte[1024]; 542 int num = 0; 543 544 while ((num = istream.read(buf)) != -1) { 545 bout.write(buf, 0, num); 546 } 547 548 byte[] ret = bout.toByteArray(); 549 550 return ret; 551 } 552 553 //private static String SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 554 //private static String SSL_PROVIDER = 555 // "com.sun.net.ssl.internal.ssl.Provider"; 556 private static final String SSL_PKG; 557 private static final String SSL_PROVIDER; 558 559 static { 560 if (isIBMVM) { 561 SSL_PKG ="com.ibm.net.ssl.internal.www.protocol"; 562 SSL_PROVIDER ="com.ibm.net.ssl.internal.ssl.Provider"; 563 } else { 564 //if not IBM VM default to Sun. 565 SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 566 SSL_PROVIDER ="com.sun.net.ssl.internal.ssl.Provider"; 567 } 568 } 569 570 private void initHttps() { 571 //if(!setHttps) { 572 String pkgs = SAAJUtil.getSystemProperty("java.protocol.handler.pkgs"); 573 log.log(Level.FINE, 574 "SAAJ0053.p2p.providers", 575 new String[] { pkgs }); 576 577 if (pkgs == null || pkgs.indexOf(SSL_PKG) < 0) { 578 if (pkgs == null) 579 pkgs = SSL_PKG; 580 else 581 pkgs = pkgs + "|" + SSL_PKG; 582 System.setProperty("java.protocol.handler.pkgs", pkgs); 583 log.log(Level.FINE, 584 "SAAJ0054.p2p.set.providers", 585 new String[] { pkgs }); 586 try { 587 Class c = Class.forName(SSL_PROVIDER); 588 Provider p = (Provider) c.newInstance(); 589 Security.addProvider(p); 590 log.log(Level.FINE, 591 "SAAJ0055.p2p.added.ssl.provider", 592 new String[] { SSL_PROVIDER }); 593 //System.out.println("Added SSL_PROVIDER " + SSL_PROVIDER); 594 //setHttps = true; 595 } catch (Exception ex) { 596 } 597 } 598 //} 599 } 600 601 private void initAuthUserInfo(HttpURLConnection conn, String userInfo) { 602 String user; 603 String password; 604 if (userInfo != null) { // get the user and password 605 //System.out.println("UserInfo= " + userInfo ); 606 int delimiter = userInfo.indexOf(':'); 607 if (delimiter == -1) { 608 user = ParseUtil.decode(userInfo); 609 password = null; 610 } else { 611 user = ParseUtil.decode(userInfo.substring(0, delimiter++)); 612 password = ParseUtil.decode(userInfo.substring(delimiter)); 613 } 614 615 String plain = user + ":"; 616 byte[] nameBytes = plain.getBytes(); 617 byte[] passwdBytes = password.getBytes(); 618 619 // concatenate user name and password bytes and encode them 620 byte[] concat = new byte[nameBytes.length + passwdBytes.length]; 621 622 System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length); 623 System.arraycopy( 624 passwdBytes, 625 0, 626 concat, 627 nameBytes.length, 628 passwdBytes.length); 629 String auth = "Basic " + new String(Base64.encode(concat)); 630 conn.setRequestProperty("Authorization", auth); 631 if (dL > 0) 632 d("Adding auth " + auth); 633 } 634 } 635 636 private static final int dL = 0; 637 private void d(String s) { 638 log.log(Level.SEVERE, 639 "SAAJ0013.p2p.HttpSOAPConnection", 640 new String[] { s }); 641 System.err.println("HttpSOAPConnection: " + s); 642 } 643 644 private java.net.HttpURLConnection createConnection(URL endpoint) 645 throws IOException { 646 return (HttpURLConnection) endpoint.openConnection(); 647 } 648 649 }