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