src/share/jaxws_classes/com/sun/xml/internal/ws/transport/http/client/HttpClientTransport.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 368
0989ad8c0860
parent 0
373ffda63c9a
permissions
-rw-r--r--

merge

     1 /*
     2  * Copyright (c) 1997, 2012, 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  */
    26 package com.sun.xml.internal.ws.transport.http.client;
    28 import com.sun.xml.internal.ws.api.EndpointAddress;
    29 import com.sun.xml.internal.ws.api.message.Packet;
    30 import com.sun.xml.internal.ws.client.BindingProviderProperties;
    31 import static com.sun.xml.internal.ws.client.BindingProviderProperties.*;
    32 import com.sun.xml.internal.ws.client.ClientTransportException;
    33 import com.sun.xml.internal.ws.resources.ClientMessages;
    34 import com.sun.xml.internal.ws.transport.Headers;
    35 import com.sun.xml.internal.ws.developer.JAXWSProperties;
    36 import com.sun.istack.internal.Nullable;
    37 import com.sun.istack.internal.NotNull;
    39 import javax.net.ssl.SSLSocketFactory;
    40 import javax.net.ssl.HttpsURLConnection;
    41 import javax.net.ssl.HostnameVerifier;
    42 import javax.net.ssl.SSLSession;
    43 import javax.xml.bind.JAXBContext;
    44 import javax.xml.bind.JAXBException;
    45 import javax.xml.ws.WebServiceException;
    46 import javax.xml.ws.handler.MessageContext;
    47 import java.io.FilterInputStream;
    48 import java.io.FilterOutputStream;
    49 import java.io.IOException;
    50 import java.io.InputStream;
    51 import java.io.OutputStream;
    52 import java.net.HttpURLConnection;
    53 import java.util.List;
    54 import java.util.Map;
    55 import java.util.zip.GZIPOutputStream;
    56 import java.util.zip.GZIPInputStream;
    58 /**
    59  *
    60  * @author WS Development Team
    61  */
    62 public class HttpClientTransport {
    64     private static final byte[] THROW_AWAY_BUFFER = new byte[8192];
    66     // Need to use JAXB first to register DatatypeConverter
    67     static {
    68         try {
    69             JAXBContext.newInstance().createUnmarshaller();
    70         } catch(JAXBException je) {
    71             // Nothing much can be done. Intentionally left empty
    72         }
    73     }
    75     /*package*/ int statusCode;
    76     /*package*/ String statusMessage;
    77     /*package*/ int contentLength;
    78     private final Map<String, List<String>> reqHeaders;
    79     private Map<String, List<String>> respHeaders = null;
    81     private OutputStream outputStream;
    82     private boolean https;
    83     private HttpURLConnection httpConnection = null;
    84     private final EndpointAddress endpoint;
    85     private final Packet context;
    86     private final Integer chunkSize;
    89     public HttpClientTransport(@NotNull Packet packet, @NotNull Map<String,List<String>> reqHeaders) {
    90         endpoint = packet.endpointAddress;
    91         context = packet;
    92         this.reqHeaders = reqHeaders;
    93         chunkSize = (Integer)context.invocationProperties.get(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE);
    94     }
    96     /*
    97      * Prepare the stream for HTTP request
    98      */
    99     OutputStream getOutput() {
   100         try {
   101             createHttpConnection();
   102             // for "GET" request no need to get outputStream
   103             if (requiresOutputStream()) {
   104                 outputStream = httpConnection.getOutputStream();
   105                 if (chunkSize != null) {
   106                     outputStream = new WSChunkedOuputStream(outputStream, chunkSize);
   107                 }
   108                 List<String> contentEncoding = reqHeaders.get("Content-Encoding");
   109                 // TODO need to find out correct encoding based on q value - RFC 2616
   110                 if (contentEncoding != null && contentEncoding.get(0).contains("gzip")) {
   111                     outputStream = new GZIPOutputStream(outputStream);
   112                 }
   113             }
   114             httpConnection.connect();
   115         } catch (Exception ex) {
   116             throw new ClientTransportException(
   117                 ClientMessages.localizableHTTP_CLIENT_FAILED(ex),ex);
   118         }
   120         return outputStream;
   121     }
   123     void closeOutput() throws IOException {
   124         if (outputStream != null) {
   125             outputStream.close();
   126             outputStream = null;
   127         }
   128     }
   130     /*
   131      * Get the response from HTTP connection and prepare the input stream for response
   132      */
   133     @Nullable InputStream getInput() {
   134         // response processing
   136         InputStream in;
   137         try {
   138             in = readResponse();
   139             if (in != null) {
   140                 String contentEncoding = httpConnection.getContentEncoding();
   141                 if (contentEncoding != null && contentEncoding.contains("gzip")) {
   142                     in = new GZIPInputStream(in);
   143                 }
   144             }
   145         } catch (IOException e) {
   146             throw new ClientTransportException(ClientMessages.localizableHTTP_STATUS_CODE(statusCode, statusMessage), e);
   147         }
   148         return in;
   149     }
   151     public Map<String, List<String>> getHeaders() {
   152         if (respHeaders != null) {
   153             return respHeaders;
   154         }
   155         respHeaders = new Headers();
   156         respHeaders.putAll(httpConnection.getHeaderFields());
   157         return respHeaders;
   158     }
   160     protected @Nullable InputStream readResponse() {
   161         InputStream is;
   162         try {
   163             is = httpConnection.getInputStream();
   164         } catch(IOException ioe) {
   165             is = httpConnection.getErrorStream();
   166         }
   167         if (is == null) {
   168             return is;
   169         }
   170         // Since StreamMessage doesn't read </s:Body></s:Envelope>, there
   171         // are some bytes left in the InputStream. This confuses JDK and may
   172         // not reuse underlying sockets. Hopefully JDK fixes it in its code !
   173         final InputStream temp = is;
   174         return new FilterInputStream(temp) {
   175             // Workaround for "SJSXP XMLStreamReader.next() closes stream".
   176             // So it doesn't read from the closed stream
   177             boolean closed;
   178             @Override
   179             public void close() throws IOException {
   180                 if (!closed) {
   181                     closed = true;
   182                     while(temp.read(THROW_AWAY_BUFFER) != -1);
   183                     super.close();
   184                 }
   185             }
   186         };
   187     }
   189     protected void readResponseCodeAndMessage() {
   190         try {
   191             statusCode = httpConnection.getResponseCode();
   192             statusMessage = httpConnection.getResponseMessage();
   193             contentLength = httpConnection.getContentLength();
   194         } catch(IOException ioe) {
   195             throw new WebServiceException(ioe);
   196         }
   197     }
   199     protected HttpURLConnection openConnection(Packet packet) {
   200         // default do nothing
   201         return null;
   202     }
   204     protected boolean checkHTTPS(HttpURLConnection connection) {
   205         if (connection instanceof HttpsURLConnection) {
   207             // TODO The above property needs to be removed in future version as the semantics of this property are not preoperly defined.
   208             // One should use JAXWSProperties.HOSTNAME_VERIFIER to control the behavior
   210             // does the client want client hostname verification by the service
   211             String verificationProperty =
   212                 (String) context.invocationProperties.get(HOSTNAME_VERIFICATION_PROPERTY);
   213             if (verificationProperty != null) {
   214                 if (verificationProperty.equalsIgnoreCase("true")) {
   215                     ((HttpsURLConnection) connection).setHostnameVerifier(new HttpClientVerifier());
   216                 }
   217             }
   219             // Set application's HostNameVerifier for this connection
   220             HostnameVerifier verifier =
   221                 (HostnameVerifier) context.invocationProperties.get(JAXWSProperties.HOSTNAME_VERIFIER);
   222             if (verifier != null) {
   223                 ((HttpsURLConnection) connection).setHostnameVerifier(verifier);
   224             }
   226             // Set application's SocketFactory for this connection
   227             SSLSocketFactory sslSocketFactory =
   228                 (SSLSocketFactory) context.invocationProperties.get(JAXWSProperties.SSL_SOCKET_FACTORY);
   229             if (sslSocketFactory != null) {
   230                 ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);
   231             }
   233             return true;
   234         }
   235         return false;
   236     }
   238     private void createHttpConnection() throws IOException {
   239         httpConnection = openConnection(context);
   241         if (httpConnection == null)
   242                 httpConnection = (HttpURLConnection) endpoint.openConnection();
   244         String scheme = endpoint.getURI().getScheme();
   245         if (scheme.equals("https")) {
   246             https = true;
   247         }
   248         if (checkHTTPS(httpConnection))
   249                 https = true;
   251         // allow interaction with the web page - user may have to supply
   252         // username, password id web page is accessed from web browser
   253         httpConnection.setAllowUserInteraction(true);
   255         // enable input, output streams
   256         httpConnection.setDoOutput(true);
   257         httpConnection.setDoInput(true);
   259         String requestMethod = (String) context.invocationProperties.get(MessageContext.HTTP_REQUEST_METHOD);
   260         String method = (requestMethod != null) ? requestMethod : "POST";
   261         httpConnection.setRequestMethod(method);
   263         //this code or something similiar needs t be moved elsewhere for error checking
   264         /*if (context.invocationProperties.get(BindingProviderProperties.BINDING_ID_PROPERTY).equals(HTTPBinding.HTTP_BINDING)){
   265             method = (requestMethod != null)?requestMethod:method;
   266         } else if
   267             (context.invocationProperties.get(BindingProviderProperties.BINDING_ID_PROPERTY).equals(SOAPBinding.SOAP12HTTP_BINDING) &&
   268             "GET".equalsIgnoreCase(requestMethod)) {
   269         }
   270        */
   272         Integer reqTimeout = (Integer)context.invocationProperties.get(BindingProviderProperties.REQUEST_TIMEOUT);
   273         if (reqTimeout != null) {
   274             httpConnection.setReadTimeout(reqTimeout);
   275         }
   277         Integer connectTimeout = (Integer)context.invocationProperties.get(JAXWSProperties.CONNECT_TIMEOUT);
   278         if (connectTimeout != null) {
   279             httpConnection.setConnectTimeout(connectTimeout);
   280         }
   282         Integer chunkSize = (Integer)context.invocationProperties.get(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE);
   283         if (chunkSize != null) {
   284             httpConnection.setChunkedStreamingMode(chunkSize);
   285         }
   287         // set the properties on HttpURLConnection
   288         for (Map.Entry<String, List<String>> entry : reqHeaders.entrySet()) {
   289             if ("Content-Length".equals(entry.getKey())) continue;
   290                 for(String value : entry.getValue()) {
   291                     httpConnection.addRequestProperty(entry.getKey(), value);
   292                 }
   293         }
   294     }
   296     boolean isSecure() {
   297         return https;
   298     }
   300     protected void setStatusCode(int statusCode) {
   301         this.statusCode = statusCode;
   302     }
   304     private boolean requiresOutputStream() {
   305         return !(httpConnection.getRequestMethod().equalsIgnoreCase("GET") ||
   306                 httpConnection.getRequestMethod().equalsIgnoreCase("HEAD") ||
   307                 httpConnection.getRequestMethod().equalsIgnoreCase("DELETE"));
   308     }
   310     @Nullable String getContentType() {
   311         return httpConnection.getContentType();
   312     }
   314     public int getContentLength() {
   315         return httpConnection.getContentLength();
   316     }
   318     // overide default SSL HttpClientVerifier to always return true
   319     // effectively overiding Hostname client verification when using SSL
   320     private static class HttpClientVerifier implements HostnameVerifier {
   321         public boolean verify(String s, SSLSession sslSession) {
   322             return true;
   323         }
   324     }
   326     private static class LocalhostHttpClientVerifier implements HostnameVerifier {
   327         public boolean verify(String s, SSLSession sslSession) {
   328             return "localhost".equalsIgnoreCase(s) || "127.0.0.1".equals(s);
   329         }
   330     }
   332     /**
   333      * HttpURLConnection.getOuputStream() returns sun.net.www.http.ChunkedOuputStream in chunked
   334      * streaming mode. If you call ChunkedOuputStream.write(byte[20MB], int, int), then the whole data
   335      * is kept in memory. This wraps the ChunkedOuputStream so that it writes only small
   336      * chunks.
   337      */
   338     private static final class WSChunkedOuputStream extends FilterOutputStream {
   339         final int chunkSize;
   341         WSChunkedOuputStream(OutputStream actual, int chunkSize) {
   342             super(actual);
   343             this.chunkSize = chunkSize;
   344         }
   346         @Override
   347         public void write(byte b[], int off, int len) throws IOException {
   348             while(len > 0) {
   349                 int sent = (len > chunkSize) ? chunkSize : len;
   350                 out.write(b, off, sent);        // don't use super.write() as it writes byte-by-byte
   351                 len -= sent;
   352                 off += sent;
   353             }
   354         }
   356     }
   358 }

mercurial