1 /* 2 * Copyright (c) 1997, 2016, 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 // InputStream#available() can't be used here - it just says no data *YET*! 516 if ((httpIn == null ) 517 || (httpConnection.getContentLength() == 0)) { 518 response = null; 519 log.warning("SAAJ0014.p2p.content.zero"); 520 } else { 521 response = messageFactory.createMessage(headers, httpIn); 522 } 523 524 } catch (SOAPException ex) { 525 throw ex; 526 } catch (Exception ex) { 527 log.log(Level.SEVERE, 528 "SAAJ0010.p2p.cannot.read.resp", 529 ex); 530 throw new SOAPExceptionImpl( 531 "Unable to read response: " + ex.getMessage()); 532 } finally { 533 if (httpIn != null) 534 httpIn.close(); 535 httpConnection.disconnect(); 536 } 537 } 538 return response; 539 } 540 541 private byte[] readFully(InputStream istream) throws IOException { 542 ByteArrayOutputStream bout = new ByteArrayOutputStream(); 543 byte[] buf = new byte[1024]; 544 int num = 0; 545 546 while ((num = istream.read(buf)) != -1) { 547 bout.write(buf, 0, num); 548 } 549 550 byte[] ret = bout.toByteArray(); 551 552 return ret; 553 } 554 555 //private static String SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 556 //private static String SSL_PROVIDER = 557 // "com.sun.net.ssl.internal.ssl.Provider"; 558 private static final String SSL_PKG; 559 private static final String SSL_PROVIDER; 560 561 static { 562 if (isIBMVM) { 563 SSL_PKG ="com.ibm.net.ssl.internal.www.protocol"; 564 SSL_PROVIDER ="com.ibm.net.ssl.internal.ssl.Provider"; 565 } else { 566 //if not IBM VM default to Sun. 567 SSL_PKG = "com.sun.net.ssl.internal.www.protocol"; 568 SSL_PROVIDER ="com.sun.net.ssl.internal.ssl.Provider"; 569 } 570 } 571 572 private void initHttps() { 573 //if(!setHttps) { 574 String pkgs = SAAJUtil.getSystemProperty("java.protocol.handler.pkgs"); 575 if (log.isLoggable(Level.FINE)) 576 log.log(Level.FINE, "SAAJ0053.p2p.providers", new String[] { pkgs }); 577 578 if (pkgs == null || pkgs.indexOf(SSL_PKG) < 0) { 579 if (pkgs == null) 580 pkgs = SSL_PKG; 581 else 582 pkgs = pkgs + "|" + SSL_PKG; 583 System.setProperty("java.protocol.handler.pkgs", pkgs); 584 if (log.isLoggable(Level.FINE)) 585 log.log(Level.FINE, "SAAJ0054.p2p.set.providers", 586 new String[] { pkgs }); 587 try { 588 Class<?> c = Class.forName(SSL_PROVIDER); 589 Provider p = (Provider) c.newInstance(); 590 Security.addProvider(p); 591 if (log.isLoggable(Level.FINE)) 592 log.log(Level.FINE, "SAAJ0055.p2p.added.ssl.provider", 593 new String[] { SSL_PROVIDER }); 594 //System.out.println("Added SSL_PROVIDER " + SSL_PROVIDER); 595 //setHttps = true; 596 } catch (Exception ex) { 597 } 598 } 599 //} 600 } 601 602 private void initAuthUserInfo(HttpURLConnection conn, String userInfo) { 603 String user; 604 String password; 605 if (userInfo != null) { // get the user and password 606 //System.out.println("UserInfo= " + userInfo ); 607 int delimiter = userInfo.indexOf(':'); 608 if (delimiter == -1) { 609 user = ParseUtil.decode(userInfo); 610 password = null; 611 } else { 612 user = ParseUtil.decode(userInfo.substring(0, delimiter++)); 613 password = ParseUtil.decode(userInfo.substring(delimiter)); 614 } 615 616 String plain = user + ":"; 617 byte[] nameBytes = plain.getBytes(); 618 byte[] passwdBytes = (password == null ? new byte[0] : password 619 .getBytes()); 620 621 // concatenate user name and password bytes and encode them 622 byte[] concat = new byte[nameBytes.length + passwdBytes.length]; 623 624 System.arraycopy(nameBytes, 0, concat, 0, nameBytes.length); 625 System.arraycopy( 626 passwdBytes, 627 0, 628 concat, 629 nameBytes.length, 630 passwdBytes.length); 631 String auth = "Basic " + new String(Base64.encode(concat)); 632 conn.setRequestProperty("Authorization", auth); 633 if (dL > 0) 634 d("Adding auth " + auth); 635 } 636 } 637 638 private static final int dL = 0; 639 private void d(String s) { 640 log.log(Level.SEVERE, 641 "SAAJ0013.p2p.HttpSOAPConnection", 642 new String[] { s }); 643 System.err.println("HttpSOAPConnection: " + s); 644 } 645 646 private java.net.HttpURLConnection createConnection(URL endpoint) 647 throws IOException { 648 return (HttpURLConnection) endpoint.openConnection(); 649 } 650 651 }