1 /* 2 * Copyright (c) 1997, 2017, 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 ibmVmVendor = "http://www.ibm.com/"; 54 private static final boolean isIBMVM = ibmVmVendor.equals(vmVendor) ? true : false; 55 private static final String JAXM_URLENDPOINT="javax.xml.messaging.URLEndpoint"; 56 57 protected static final Logger log = 58 Logger.getLogger(LogDomainConstants.HTTP_CONN_DOMAIN, 59 "com.sun.xml.internal.messaging.saaj.client.p2p.LocalStrings"); 60 61 62 MessageFactory messageFactory = null; 63 64 boolean closed = false; 65 66 public HttpSOAPConnection() throws SOAPException { 67 68 try { 69 messageFactory = MessageFactory.newInstance(SOAPConstants.DYNAMIC_SOAP_PROTOCOL); 70 } catch (NoSuchMethodError ex) { 71 //fallback to default SOAP 1.1 in this case for backward compatibility 72 messageFactory = MessageFactory.newInstance(); 73 } catch (Exception ex) { 74 log.log(Level.SEVERE, "SAAJ0001.p2p.cannot.create.msg.factory", ex); 75 throw new SOAPExceptionImpl("Unable to create message factory", ex); 76 } 77 } 78 79 @Override 80 public void close() throws SOAPException { 81 if (closed) { 82 log.severe("SAAJ0002.p2p.close.already.closed.conn"); 83 throw new SOAPExceptionImpl("Connection already closed"); 84 } 85 86 messageFactory = null; 87 closed = true; 88 } 89 90 @Override 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 StringBuilder concat = new StringBuilder(); 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 @Override 354 public SOAPMessage get(Object endPoint) throws SOAPException { 355 if (closed) { 356 log.severe("SAAJ0011.p2p.get.already.closed.conn"); 357 throw new SOAPExceptionImpl("Connection is closed"); 358 } 359 Class<?> urlEndpointClass = null; 360 361 try { 362 urlEndpointClass = Class.forName("javax.xml.messaging.URLEndpoint"); 363 } catch (Exception ex) { 364 //Do nothing. URLEndpoint is available only when JAXM is there. 365 } 366 367 if (urlEndpointClass != null) { 368 if (urlEndpointClass.isInstance(endPoint)) { 369 String url = null; 370 371 try { 372 Method m = urlEndpointClass.getMethod("getURL", (Class[])null); 373 url = (String) m.invoke(endPoint, (Object[])null); 374 } catch (Exception ex) { 375 log.severe("SAAJ0004.p2p.internal.err"); 376 throw new SOAPExceptionImpl( 377 "Internal error: " + ex.getMessage()); 378 } 379 try { 380 endPoint = new URL(url); 381 } catch (MalformedURLException mex) { 382 log.severe("SAAJ0005.p2p."); 383 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 384 } 385 } 386 } 387 388 if (endPoint instanceof java.lang.String) { 389 try { 390 endPoint = new URL((String) endPoint); 391 } catch (MalformedURLException mex) { 392 log.severe("SAAJ0006.p2p.bad.URL"); 393 throw new SOAPExceptionImpl("Bad URL: " + mex.getMessage()); 394 } 395 } 396 397 if (endPoint instanceof URL) 398 try { 399 SOAPMessage response = doGet((URL)endPoint); 400 return response; 401 } catch (Exception ex) { 402 throw new SOAPExceptionImpl(ex); 403 } else 404 throw new SOAPExceptionImpl("Bad endPoint type " + endPoint); 405 } 406 407 SOAPMessage doGet(URL endPoint) throws SOAPException, IOException { 408 boolean isFailure = false; 409 410 URL url = null; 411 HttpURLConnection httpConnection = null; 412 413 int responseCode = 0; 414 try { 415 /// Is https GET allowed?? 416 if (endPoint.getProtocol().equals("https")) 417 initHttps(); 418 // Process the URL 419 URI uri = new URI(endPoint.toString()); 420 String userInfo = uri.getRawUserInfo(); 421 422 url = endPoint; 423 424 if (dL > 0) 425 d("uri: " + userInfo + " " + url + " " + uri); 426 427 // TBD 428 // Will deal with https later. 429 if (!url.getProtocol().equalsIgnoreCase("http") 430 && !url.getProtocol().equalsIgnoreCase("https")) { 431 log.severe("SAAJ0052.p2p.protocol.mustbe.http.or.https"); 432 throw new IllegalArgumentException( 433 "Protocol " 434 + url.getProtocol() 435 + " not supported in URL " 436 + url); 437 } 438 httpConnection = (HttpURLConnection) createConnection(url); 439 440 httpConnection.setRequestMethod("GET"); 441 442 httpConnection.setDoOutput(true); 443 httpConnection.setDoInput(true); 444 httpConnection.setUseCaches(false); 445 httpConnection.setInstanceFollowRedirects(true); 446 447 httpConnection.connect(); 448 449 try { 450 451 responseCode = httpConnection.getResponseCode(); 452 453 // let HTTP_INTERNAL_ERROR (500) through because it is used for SOAP faults 454 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 455 isFailure = true; 456 } else if ((responseCode / 100) != 2) { 457 log.log(Level.SEVERE, 458 "SAAJ0008.p2p.bad.response", 459 new String[] { httpConnection.getResponseMessage()}); 460 throw new SOAPExceptionImpl( 461 "Bad response: (" 462 + responseCode 463 + httpConnection.getResponseMessage()); 464 465 } 466 } catch (IOException e) { 467 // on JDK1.3.1_01, we end up here, but then getResponseCode() succeeds! 468 responseCode = httpConnection.getResponseCode(); 469 if (responseCode == HttpURLConnection.HTTP_INTERNAL_ERROR) { 470 isFailure = true; 471 } else { 472 throw e; 473 } 474 475 } 476 477 } catch (SOAPException ex) { 478 throw ex; 479 } catch (Exception ex) { 480 log.severe("SAAJ0012.p2p.get.failed"); 481 throw new SOAPExceptionImpl("Get failed", ex); 482 } 483 484 SOAPMessage response = null; 485 InputStream httpIn = null; 486 if (responseCode == HttpURLConnection.HTTP_OK || isFailure) { 487 try { 488 MimeHeaders headers = new MimeHeaders(); 489 490 String key, value; 491 492 // Header field 0 is the status line so we skip it. 493 494 int i = 1; 495 496 while (true) { 497 key = httpConnection.getHeaderFieldKey(i); 498 value = httpConnection.getHeaderField(i); 499 500 if (key == null && value == null) 501 break; 502 503 if (key != null) { 504 StringTokenizer values = 505 new StringTokenizer(value, ","); 506 while (values.hasMoreTokens()) 507 headers.addHeader(key, values.nextToken().trim()); 508 } 509 i++; 510 } 511 512 httpIn = 513 (isFailure 514 ? httpConnection.getErrorStream() 515 : httpConnection.getInputStream()); 516 // If no reply message is returned, 517 // content-Length header field value is expected to be zero. 518 // java SE 6 documentation says : 519 // available() : an estimate of the number of bytes that can be read 520 //(or skipped over) from this input stream without blocking 521 //or 0 when it reaches the end of the input stream. 522 if ((httpIn == null ) 523 || (httpConnection.getContentLength() == 0) 524 || (httpIn.available() == 0)) { 525 response = null; 526 log.warning("SAAJ0014.p2p.content.zero"); 527 } else { 528 response = messageFactory.createMessage(headers, httpIn); 529 } 530 531 } catch (SOAPException ex) { 532 throw ex; 533 } catch (Exception ex) { 534 log.log(Level.SEVERE, 535 "SAAJ0010.p2p.cannot.read.resp", 536 ex); 537 throw new SOAPExceptionImpl( 538 "Unable to read response: " + ex.getMessage()); 539 } finally { 540 if (httpIn != null) 541 httpIn.close(); 542 httpConnection.disconnect(); 543 } 544 } 545 return response; 546 } 547 548 private byte[] readFully(InputStream istream) throws IOException { 549 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 550 byte[] buf = new byte[1024]; 551 int num = 0; 552 553 while ((num = istream.read(buf)) != -1) { 554 bout.write(buf, 0, num); 555 } 556 557 byte[] ret = bout.toByteArray(); 558 559 return ret; 560 } 561 562 //private static String SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 563 //private static String SSL_PROVIDER = 564 // "com.sun.net.ssl.internal.ssl.Provider"; 565 private static final String SSL_PKG; 566 private static final String SSL_PROVIDER; 567 568 static { 569 if (isIBMVM) { 570 SSL_PKG ="com.ibm.net.ssl.internal.www.protocol"; 571 SSL_PROVIDER ="com.ibm.net.ssl.internal.ssl.Provider"; 572 } else { 573 //if not IBM VM default to Sun. 574 SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 575 SSL_PROVIDER ="com.sun.net.ssl.internal.ssl.Provider"; 576 } 577 } 578 579 private void initHttps() { 580 //if(!setHttps) { 581 String pkgs = SAAJUtil.getSystemProperty("java.protocol.handler.pkgs"); 582 if (log.isLoggable(Level.FINE)) 583 log.log(Level.FINE, "SAAJ0053.p2p.providers", new String[] { pkgs }); 584 585 if (pkgs == null || pkgs.indexOf(SSL_PKG) < 0) { 586 if (pkgs == null) 587 pkgs = SSL_PKG; 588 else 589 pkgs = pkgs + "|" + SSL_PKG; 590 System.setProperty("java.protocol.handler.pkgs", pkgs); 591 if (log.isLoggable(Level.FINE)) 592 log.log(Level.FINE, "SAAJ0054.p2p.set.providers", 593 new String[] { pkgs }); 594 try { 595 Class<?> c = Class.forName(SSL_PROVIDER); 596 Provider p = (Provider) c.newInstance(); 597 Security.addProvider(p); 598 if (log.isLoggable(Level.FINE)) 599 log.log(Level.FINE, "SAAJ0055.p2p.added.ssl.provider", 600 new String[] { SSL_PROVIDER }); 601 //System.out.println("Added SSL_PROVIDER " + SSL_PROVIDER); 602 //setHttps = true; 603 } catch (Exception ex) { 604 } 605 } 606 //} 607 } 608 609 private void initAuthUserInfo(HttpURLConnection conn, String userInfo) { 610 String user; 611 String password; 612 if (userInfo != null) { // get the user and password 613 //System.out.println("UserInfo= " + userInfo ); 614 int delimiter = userInfo.indexOf(':'); 615 if (delimiter == -1) { 616 user = ParseUtil.decode(userInfo); 617 password = null; 618 } else { 619 user = ParseUtil.decode(userInfo.substring(0, delimiter++)); 620 password = ParseUtil.decode(userInfo.substring(delimiter)); 621 } 622 623 String plain = user + ":"; 624 byte[] nameBytes = plain.getBytes(); 625 byte[] passwdBytes = (password == null ? new byte[0] : password 626 .getBytes()); 627 628 // concatenate user name and password bytes and encode them 629 byte[] concat = new byte[nameBytes.length + passwdBytes.length]; 630 631 System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length); 632 System.arraycopy( 633 passwdBytes, 634 0, 635 concat, 636 nameBytes.length, 637 passwdBytes.length); 638 String auth = "Basic " + new String(Base64.encode(concat)); 639 conn.setRequestProperty("Authorization", auth); 640 if (dL > 0) 641 d("Adding auth " + auth); 642 } 643 } 644 645 private static final int dL = 0; 646 private void d(String s) { 647 log.log(Level.SEVERE, 648 "SAAJ0013.p2p.HttpSOAPConnection", 649 new String[] { s }); 650 System.err.println("HttpSOAPConnection: " + s); 651 } 652 653 private java.net.HttpURLConnection createConnection(URL endpoint) 654 throws IOException { 655 return (HttpURLConnection) endpoint.openConnection(); 656 } 657 658 }