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 }