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 }
--- EOF ---