1 /* 2 * Copyright (c) 1997, 2014, 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 if (log.isLoggable(Level.FINEST)) 109 log.finest("SAAJ0090.p2p.endpoint.available.only.for.JAXM"); 110 } 111 112 if (urlEndpointClass != null) { 113 if (urlEndpointClass.isInstance(endPoint)) { 114 String url = null; 115 116 try { 117 Method m = urlEndpointClass.getMethod("getURL", (Class[])null); 118 url = (String) m.invoke(endPoint, (Object[])null); 119 } catch (Exception ex) { 120 // TBD -- exception chaining 121 log.log(Level.SEVERE,"SAAJ0004.p2p.internal.err",ex); 122 throw new SOAPExceptionImpl( 123 "Internal error: " + ex.getMessage()); 124 } 125 try { 126 endPoint = new URL(url); 127 } catch (MalformedURLException mex) { 128 log.log(Level.SEVERE,"SAAJ0005.p2p.", mex); 129 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 130 } 131 } 132 } 133 134 if (endPoint instanceof java.lang.String) { 135 try { 136 endPoint = new URL((String) endPoint); 137 } catch (MalformedURLException mex) { 138 log.log(Level.SEVERE, "SAAJ0006.p2p.bad.URL", mex); 139 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 140 } 141 } 142 143 if (endPoint instanceof URL) 144 try { 145 SOAPMessage response = post(message, (URL)endPoint); 146 return response; 147 } catch (Exception ex) { 148 // TBD -- chaining? 149 throw new SOAPExceptionImpl(ex); 150 } else { 151 log.severe("SAAJ0007.p2p.bad.endPoint.type"); 152 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint); 153 } 154 } 155 156 SOAPMessage post(SOAPMessage message, URL endPoint) throws SOAPException, IOException { 157 boolean isFailure = false; 158 159 URL url = null; 160 HttpURLConnection httpConnection = null; 161 162 int responseCode = 0; 163 try { 164 if (endPoint.getProtocol().equals("https")) 165 //if(!setHttps) 166 initHttps(); 167 // Process the URL 168 URI uri = new URI(endPoint.toString()); 169 String userInfo = uri.getRawUserInfo(); 170 171 url = endPoint; 172 173 if (dL > 0) 174 d("uri: " + userInfo + " " + url + " " + uri); 175 176 // TBD 177 // Will deal with https later. 178 if (!url.getProtocol().equalsIgnoreCase("http") 179 && !url.getProtocol().equalsIgnoreCase("https")) { 180 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https"); 181 throw new IllegalArgumentException( 182 "Protocol " 183 + url.getProtocol() 184 + " not supported in URL " 185 + url); 186 } 187 httpConnection = (HttpURLConnection) createConnection(url); 188 189 httpConnection.setRequestMethod("POST"); 190 191 httpConnection.setDoOutput(true); 192 httpConnection.setDoInput(true); 193 httpConnection.setUseCaches(false); 194 httpConnection.setInstanceFollowRedirects(true); 195 196 if (message.saveRequired()) 197 message.saveChanges(); 198 199 MimeHeaders headers = message.getMimeHeaders(); 200 201 Iterator it = headers.getAllHeaders(); 202 boolean hasAuth = false; // true if we find explicit Auth header 203 while (it.hasNext()) { 204 MimeHeader header = (MimeHeader) it.next(); 205 206 String[] values = headers.getHeader(header.getName()); 207 if (values.length == 1) 208 httpConnection.setRequestProperty( 209 header.getName(), 210 header.getValue()); 211 else { 212 StringBuffer concat = new StringBuffer(); 213 int i = 0; 214 while (i < values.length) { 215 if (i != 0) 216 concat.append(','); 217 concat.append(values[i]); 218 i++; 219 } 220 221 httpConnection.setRequestProperty( 222 header.getName(), 223 concat.toString()); 224 } 225 226 if ("Authorization".equals(header.getName())) { 227 hasAuth = true; 228 if (log.isLoggable(Level.FINE)) 229 log.fine("SAAJ0091.p2p.https.auth.in.POST.true"); 230 } 231 } 232 233 if (!hasAuth && userInfo != null) { 234 initAuthUserInfo(httpConnection, userInfo); 235 } 236 237 OutputStream out = httpConnection.getOutputStream(); 238 try { 239 message.writeTo(out); 240 out.flush(); 241 } finally { 242 out.close(); 243 } 244 245 httpConnection.connect(); 246 247 try { 248 249 responseCode = httpConnection.getResponseCode(); 250 251 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults 252 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 253 isFailure = true; 254 } 255 //else if (responseCode != HttpURLConnection.HTTP_OK) 256 //else if (!(responseCode >= HttpURLConnection.HTTP_OK && responseCode < 207)) 257 else if ((responseCode / 100) != 2) { 258 log.log(Level.SEVERE, 259 "SAAJ0008.p2p.bad.response", 260 new String[] {httpConnection.getResponseMessage()}); 261 throw new SOAPExceptionImpl( 262 "Bad response: (" 263 + responseCode 264 + httpConnection.getResponseMessage()); 265 266 } 267 } catch (IOException e) { 268 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds! 269 responseCode = httpConnection.getResponseCode(); 270 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 271 isFailure = true; 272 } else { 273 throw e; 274 } 275 276 } 277 278 } catch (SOAPException ex) { 279 throw ex; 280 } catch (Exception ex) { 281 log.severe("SAAJ0009.p2p.msg.send.failed"); 282 throw new SOAPExceptionImpl("Message send failed", ex); 283 } 284 285 SOAPMessage response = null; 286 InputStream httpIn = null; 287 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) { 288 try { 289 MimeHeaders headers = new MimeHeaders(); 290 291 String key, value; 292 293 // Header field 0 is the status line so we skip it. 294 295 int i = 1; 296 297 while (true) { 298 key = httpConnection.getHeaderFieldKey(i); 299 value = httpConnection.getHeaderField(i); 300 301 if (key == null && value == null) 302 break; 303 304 if (key != null) { 305 StringTokenizer values = 306 new StringTokenizer(value, ","); 307 while (values.hasMoreTokens()) 308 headers.addHeader(key, values.nextToken().trim()); 309 } 310 i++; 311 } 312 313 httpIn = 314 (isFailure 315 ? httpConnection.getErrorStream() 316 : httpConnection.getInputStream()); 317 318 byte[] bytes = readFully(httpIn); 319 320 int length = 321 httpConnection.getContentLength() == -1 322 ? bytes.length 323 : httpConnection.getContentLength(); 324 325 // If no reply message is returned, 326 // content-Length header field value is expected to be zero. 327 if (length == 0) { 328 response = null; 329 log.warning("SAAJ0014.p2p.content.zero"); 330 } else { 331 ByteInputStream in = new ByteInputStream(bytes, length); 332 response = messageFactory.createMessage(headers, in); 333 } 334 335 } catch (SOAPException ex) { 336 throw ex; 337 } catch (Exception ex) { 338 log.log(Level.SEVERE,"SAAJ0010.p2p.cannot.read.resp", ex); 339 throw new SOAPExceptionImpl( 340 "Unable to read response: " + ex.getMessage()); 341 } finally { 342 if (httpIn != null) 343 httpIn.close(); 344 httpConnection.disconnect(); 345 } 346 } 347 return response; 348 } 349 350 // Object identifies where the request should be sent. 351 // It is required to support objects of type String and java.net.URL. 352 353 public SOAPMessage get(Object endPoint) throws SOAPException { 354 if (closed) { 355 log.severe("SAAJ0011.p2p.get.already.closed.conn"); 356 throw new SOAPExceptionImpl("Connection is closed"); 357 } 358 Class urlEndpointClass = null; 359 360 try { 361 urlEndpointClass = Class.forName("javax.xml.messaging.URLEndpoint"); 362 } catch (Exception ex) { 363 //Do nothing. URLEndpoint is available only when JAXM is there. 364 } 365 366 if (urlEndpointClass != null) { 367 if (urlEndpointClass.isInstance(endPoint)) { 368 String url = null; 369 370 try { 371 Method m = urlEndpointClass.getMethod("getURL", (Class[])null); 372 url = (String) m.invoke(endPoint, (Object[])null); 373 } catch (Exception ex) { 374 log.severe("SAAJ0004.p2p.internal.err"); 375 throw new SOAPExceptionImpl( 376 "Internal error: " + ex.getMessage()); 377 } 378 try { 379 endPoint = new URL(url); 380 } catch (MalformedURLException mex) { 381 log.severe("SAAJ0005.p2p."); 382 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 383 } 384 } 385 } 386 387 if (endPoint instanceof java.lang.String) { 388 try { 389 endPoint = new URL((String) endPoint); 390 } catch (MalformedURLException mex) { 391 log.severe("SAAJ0006.p2p.bad.URL"); 392 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 393 } 394 } 395 396 if (endPoint instanceof URL) 397 try { 398 SOAPMessage response = doGet((URL)endPoint); 399 return response; 400 } catch (Exception ex) { 401 throw new SOAPExceptionImpl(ex); 402 } else 403 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint); 404 } 405 406 SOAPMessage doGet(URL endPoint) throws SOAPException, IOException { 407 boolean isFailure = false; 408 409 URL url = null; 410 HttpURLConnection httpConnection = null; 411 412 int responseCode = 0; 413 try { 414 /// Is https GET allowed?? 415 if (endPoint.getProtocol().equals("https")) 416 initHttps(); 417 // Process the URL 418 URI uri = new URI(endPoint.toString()); 419 String userInfo = uri.getRawUserInfo(); 420 421 url = endPoint; 422 423 if (dL > 0) 424 d("uri: " + userInfo + " " + url + " " + uri); 425 426 // TBD 427 // Will deal with https later. 428 if (!url.getProtocol().equalsIgnoreCase("http") 429 && !url.getProtocol().equalsIgnoreCase("https")) { 430 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https"); 431 throw new IllegalArgumentException( 432 "Protocol " 433 + url.getProtocol() 434 + " not supported in URL " 435 + url); 436 } 437 httpConnection = (HttpURLConnection) createConnection(url); 438 439 httpConnection.setRequestMethod("GET"); 440 441 httpConnection.setDoOutput(true); 442 httpConnection.setDoInput(true); 443 httpConnection.setUseCaches(false); 444 httpConnection.setFollowRedirects(true); 445 446 httpConnection.connect(); 447 448 try { 449 450 responseCode = httpConnection.getResponseCode(); 451 452 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults 453 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 454 isFailure = true; 455 } else if ((responseCode / 100) != 2) { 456 log.log(Level.SEVERE, 457 "SAAJ0008.p2p.bad.response", 458 new String[] { httpConnection.getResponseMessage()}); 459 throw new SOAPExceptionImpl( 460 "Bad response: (" 461 + responseCode 462 + httpConnection.getResponseMessage()); 463 464 } 465 } catch (IOException e) { 466 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds! 467 responseCode = httpConnection.getResponseCode(); 468 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 469 isFailure = true; 470 } else { 471 throw e; 472 } 473 474 } 475 476 } catch (SOAPException ex) { 477 throw ex; 478 } catch (Exception ex) { 479 log.severe("SAAJ0012.p2p.get.failed"); 480 throw new SOAPExceptionImpl("Get failed", ex); 481 } 482 483 SOAPMessage response = null; 484 InputStream httpIn = null; 485 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) { 486 try { 487 MimeHeaders headers = new MimeHeaders(); 488 489 String key, value; 490 491 // Header field 0 is the status line so we skip it. 492 493 int i = 1; 494 495 while (true) { 496 key = httpConnection.getHeaderFieldKey(i); 497 value = httpConnection.getHeaderField(i); 498 499 if (key == null && value == null) 500 break; 501 502 if (key != null) { 503 StringTokenizer values = 504 new StringTokenizer(value, ","); 505 while (values.hasMoreTokens()) 506 headers.addHeader(key, values.nextToken().trim()); 507 } 508 i++; 509 } 510 511 httpIn = 512 (isFailure 513 ? httpConnection.getErrorStream() 514 : httpConnection.getInputStream()); 515 // If no reply message is returned, 516 // content-Length header field value is expected to be zero. 517 // java SE 6 documentation says : 518 // available() : an estimate of the number of bytes that can be read 519 //(or skipped over) from this input stream without blocking 520 //or 0 when it reaches the end of the input stream. 521 if ((httpIn == null ) 522 || (httpConnection.getContentLength() == 0) 523 || (httpIn.available() == 0)) { 524 response = null; 525 log.warning("SAAJ0014.p2p.content.zero"); 526 } else { 527 response = messageFactory.createMessage(headers, httpIn); 528 } 529 530 } catch (SOAPException ex) { 531 throw ex; 532 } catch (Exception ex) { 533 log.log(Level.SEVERE, 534 "SAAJ0010.p2p.cannot.read.resp", 535 ex); 536 throw new SOAPExceptionImpl( 537 "Unable to read response: " + ex.getMessage()); 538 } finally { 539 if (httpIn != null) 540 httpIn.close(); 541 httpConnection.disconnect(); 542 } 543 } 544 return response; 545 } 546 547 private byte[] readFully(InputStream istream) throws IOException { 548 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 549 byte[] buf = new byte[1024]; 550 int num = 0; 551 552 while ((num = istream.read(buf)) != -1) { 553 bout.write(buf, 0, num); 554 } 555 556 byte[] ret = bout.toByteArray(); 557 558 return ret; 559 } 560 561 //private static String SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 562 //private static String SSL_PROVIDER = 563 // "com.sun.net.ssl.internal.ssl.Provider"; 564 private static final String SSL_PKG; 565 private static final String SSL_PROVIDER; 566 567 static { 568 if (isIBMVM) { 569 SSL_PKG ="com.ibm.net.ssl.internal.www.protocol"; 570 SSL_PROVIDER ="com.ibm.net.ssl.internal.ssl.Provider"; 571 } else { 572 //if not IBM VM default to Sun. 573 SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 574 SSL_PROVIDER ="com.sun.net.ssl.internal.ssl.Provider"; 575 } 576 } 577 578 private void initHttps() { 579 //if(!setHttps) { 580 String pkgs = SAAJUtil.getSystemProperty("java.protocol.handler.pkgs"); 581 if (log.isLoggable(Level.FINE)) 582 log.log(Level.FINE, "SAAJ0053.p2p.providers", new String[] { pkgs }); 583 584 if (pkgs == null || pkgs.indexOf(SSL_PKG) < 0) { 585 if (pkgs == null) 586 pkgs = SSL_PKG; 587 else 588 pkgs = pkgs + "|" + SSL_PKG; 589 System.setProperty("java.protocol.handler.pkgs", pkgs); 590 if (log.isLoggable(Level.FINE)) 591 log.log(Level.FINE, "SAAJ0054.p2p.set.providers", 592 new String[] { pkgs }); 593 try { 594 Class c = Class.forName(SSL_PROVIDER); 595 Provider p = (Provider) c.newInstance(); 596 Security.addProvider(p); 597 if (log.isLoggable(Level.FINE)) 598 log.log(Level.FINE, "SAAJ0055.p2p.added.ssl.provider", 599 new String[] { SSL_PROVIDER }); 600 //System.out.println("Added SSL_PROVIDER " + SSL_PROVIDER); 601 //setHttps = true; 602 } catch (Exception ex) { 603 } 604 } 605 //} 606 } 607 608 private void initAuthUserInfo(HttpURLConnection conn, String userInfo) { 609 String user; 610 String password; 611 if (userInfo != null) { // get the user and password 612 //System.out.println("UserInfo= " + userInfo ); 613 int delimiter = userInfo.indexOf(':'); 614 if (delimiter == -1) { 615 user = ParseUtil.decode(userInfo); 616 password = null; 617 } else { 618 user = ParseUtil.decode(userInfo.substring(0, delimiter++)); 619 password = ParseUtil.decode(userInfo.substring(delimiter)); 620 } 621 622 String plain = user + ":"; 623 byte[] nameBytes = plain.getBytes(); 624 byte[] passwdBytes = (password == null ? new byte[0] : password 625 .getBytes()); 626 627 // concatenate user name and password bytes and encode them 628 byte[] concat = new byte[nameBytes.length + passwdBytes.length]; 629 630 System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length); 631 System.arraycopy( 632 passwdBytes, 633 0, 634 concat, 635 nameBytes.length, 636 passwdBytes.length); 637 String auth = "Basic " + new String(Base64.encode(concat)); 638 conn.setRequestProperty("Authorization", auth); 639 if (dL > 0) 640 d("Adding auth " + auth); 641 } 642 } 643 644 private static final int dL = 0; 645 private void d(String s) { 646 log.log(Level.SEVERE, 647 "SAAJ0013.p2p.HttpSOAPConnection", 648 new String[] { s }); 649 System.err.println("HttpSOAPConnection: " + s); 650 } 651 652 private java.net.HttpURLConnection createConnection(URL endpoint) 653 throws IOException { 654 return (HttpURLConnection) endpoint.openConnection(); 655 } 656 657 }