Links: Table of Contents | Single HTML | Single PDF

Chapter 5. HTTP

Table of Contents

5.1. HTTP headers
5.1.1. Sending HTTP headers on request
5.1.2. Accessing HTTP headers of the response
5.2. HTTP compression
5.3. HTTP cookies
5.3.1. Enabling cookie support
5.3.2. Accessing HTTP cookies in the response
5.3.3. Accessing HTTP cookies on the server
5.4. HTTP client streaming support
5.5. Access HTTP headers in a Handler
5.5.1. From Client side handler
5.5.2. From Server side handler
5.6. HTTP Timeouts
5.7. HTTP Persistent Connections (keep-alive)
5.8. HTTPS HostnameVerifier
5.9. HTTPS SSLSocketFactory
5.10. HTTP address in soap:address and import locations

5.1. HTTP headers

5.1.1. Sending HTTP headers on request

Client can set additional HTTP headers for making a requests by using MessageContext.HTTP_REQUEST_HEADERS. See the following code for an example:

Example 5.1. Sending HTTP headers

import java.util.Collections;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext; 

HelloPort port =...;
((BindingProvider) port).getRequestContext().put(MessageContext
        .HTTP_REQUEST_HEADERS, Collections.singletonMap
        ("X-Client-Version", Collections.singletonList("1.0-RC")));

// the header will be sent to all successive invocations
port.sayHelloTo("duke");
port.sayHelloTo("duke");

Note that the property takes Map<String,List<String>> as the type.

5.1.2. Accessing HTTP headers of the response

Clients can access the HTTP headers of the response by using MessageContext.HTTP_RESPONSE_HEADERS. See the following code for example:

Example 5.2. Accessing HTTP headers

HelloPort port = ...;
port.sayHelloTo("duke");

headers = (Map<String, List<String>>) ((BindingProvider) port)
        .getResponseContext().get(MessageContext.HTTP_RESPONSE_HEADERS);

5.2. HTTP compression

HTTP supports compression of data at the transport level, which can substantially reduce the size of the data (at the expense of an additional CPU load.)

When sending a request to a server, a client can inform the server that it can receive compressed response like this:

Example 5.3. Request HTTP Compression

Map<String, List<String> httpHeaders = new HashMap<String, List<String>>();
httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
Map<String, Object> reqContext = ((bindingProvider)proxy).getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);

This works even if the server isn't capable of doing compression; it will simply respond with uncompressed response and it will work transparently.

If a client knows that the server is capable of receiving a compressed request, it can send a compressed request by adding one more HTTP header as follows:

Example 5.4. Sending Compressed Request

Map<String, List<String> httpHeaders = new HashMap<String, List<String>>();
httpHeaders.put("Content-Encoding", Collections.singletonList("gzip"));
httpHeaders.put("Accept-Encoding", Collections.singletonList("gzip"));
Map<String, Object> reqContext = ((bindingProvider) proxy)
        .getRequestContext();
requestContext.put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders);

Note that this will fail if the server is incapable of dealing with compressed HTTP traffic. Most modern HTTP servers understand it, but this is not guaranteed.

5.3. HTTP cookies

5.3.1. Enabling cookie support

To enable cookie support, you need to enable the session property. This causes requests sent via the port to use the same cookie jar, so if the server responds with a cookie, the next request will go out with those cookies. This allows the server to use the normal session tracking mechanism like HttpSession.

Example 5.5. Sending HTTP headers

HelloPort port = ...;
port.getRequestContext().put(BindingProvider.SESSION_MAINTAIN_PROPERTY,true);

5.3.2. Accessing HTTP cookies in the response

TODO

5.3.3. Accessing HTTP cookies on the server

When the web service is using servlets as the transport mechanism, you can use servlet's native support for cookies. See the following code to how to access javax.servlet.http.HttpServletRequest from your service.

Example 5.6. Accessing cookies

class MyService {
    @Resource
    WebServiceContext context;

    public void foo() {
        HttpServletRequest rq = (HttpServletRequest) context
                .getMessageContext().get(MessageContext.SERVLET_REQUEST);
        HttpServletResponse rs = (HttpServletResponse) context
                .getMessageContext().get(MessageContext.SERVLET_RESPONSE);
    }
}

5.4. HTTP client streaming support

JAX-WS uses Java SE's HttpURLConnection to send requests to web service. By default, HttpURLConnection buffers the entire request before sending it on the wire. To enable HTTP streaming support, one needs to enable setChunkedStreamingMode() on the connection. The same thing can be achieved by doing the following in JAX-WS clients.

Example 5.7. HTTP client streaming support

int chunkSize = ...;
Map<String, Object> ctxt = ((BindingProvider)proxy).getRequestContext();
ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, chunkSize);

5.5. Access HTTP headers in a Handler

HTTP headers can be accessed in a Handler. Here is how you can access the Content-Type HTTP header in a Handler:

5.5.1. From Client side handler

HTTP headers can be accessed in a client side Handler in an incoming response. Here is Handler code that demonstrates how to do this:

Example 5.8. ClientHandler.java

public class ClientHandler implements SOAPHandler<SOAPMessageContext> {
    public boolean handleMessage(SOAPMessageContext context) {
        if (!(Boolean) context.get(MessageContext
                .MESSAGE_OUTBOUND_PROPERTY)) {
            
            Map<String, List<String>> map = (Map<String, 
                    List<String>>) context.get(MessageContext
                    .HTTP_RESPONSE_HEADERS);
            
            List<String> contentType = getHTTPHeader(map, "Content-Type");
            if (contentType != null) {
                StringBuffer strBuf = new StringBuffer();
                for (String type : contentType) {
                    strBuf.append(type);
                }
                System.out.println("Content-Type:" + strBuf.toString());
            }
        }
        return true;
    }


    private
    @Nullable
    List<String> getHTTPHeader(Map<String, List<String>> headers, 
                               String header) {
        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
            String name = entry.getKey();
            if (name.equalsIgnoreCase(header))
                return entry.getValue();
        }
        return null;
    }
}

5.5.2. From Server side handler

HTTP headers can be accessed in a server side Handler on an incoming request. Here is Handler code that demonstrates how to do this:

Example 5.9. ServerHandler.java

public class ServerHandler implements SOAPHandler<SOAPMessageContext> {
    public boolean handleMessage(SOAPMessageContext context) {

        if (!(Boolean) context.get(MessageContext
                .MESSAGE_OUTBOUND_PROPERTY)) {
            
            Map<String, List<String>> map = (Map<String, 
                    List<String>>) context.get(MessageContext
                    .HTTP_REQUEST_HEADERS);
            
            List<String> contentType = getHTTPHeader(map, "Content-Type");
            if (contentType != null) {
                StringBuffer strBuf = new StringBuffer();
                for (String type : contentType) {
                    strBuf.append(type);
                }
                System.out.println("Content-Type:" + strBuf.toString());
            }
        }

        return true;
    }

    private
    @Nullable
    List<String> getHTTPHeader(@NotNull Map<String, 
            List<String>> headers, @NotNull String header) {
        
        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
            String name = entry.getKey();
            if (name.equalsIgnoreCase(header))
                return entry.getValue();
        }
        return null;
    }
}

5.6. HTTP Timeouts

JAX-WS uses Java SE's HttpURLConnection to send requests to web services. URLConnection offers setConnectTimeout() and setReadTimeout() methods so that clients can control connection timeouts. The same things can be achieved by doing the following in JAX-WS clients:

Example 5.10. HTTP client timeouts

// setConnectTimeout()
int timeout = ...;
Map<String, Object> ctxt = ((BindingProvider)proxy).getRequestContext();
ctxt.put(JAXWSProperties.CONNECT_TIMEOUT, timeout);

// setReadTimeout()
int timeout = ...;
Map<String, Object> ctxt = ((BindingProvider)proxy).getRequestContext();
ctxt.put("com.sun.xml.ws.request.timeout", timeout);

5.7. HTTP Persistent Connections (keep-alive)

Persistent connections improve performance by allowing the underlying socket connection to be reused for multiple http requests. JAX-WS uses Java SE's HttpURLConnection to send requests to web services. HTTP keep-alive behavior can be controlled by the http.keepAlive (default: true) and http.maxConnections (default: 5) system properties. For more information, see Networking Properties

5.8. HTTPS HostnameVerifier

JAX-WS uses Java SE's HttpsURLConnection to send requests to web services that use the HTTPS transport. HttpsURLConnection offers a setHostnameVerifier() method so that the client's verification callback can be called to determine whether a connection is allowed. The same thing can be achieved by doing the following in JAX-WS clients:

Example 5.11. SSL Hostname Verification

HostNameVerifier hostNameVerifier = ...;
int timeout = ...;
Map<String, Object> ctxt = ((BindingProvider)proxy).getRequestContext();
ctxt.put(JAXWSProperties.HOSTNAME_VERIFIER, hostNameVerifier);

5.9. HTTPS SSLSocketFactory

JAX-WS uses Java SE's HttpsURLConnection to send requests to web services that use HTTPS transport. HttpsURLConnection offers setSSLSocketFactory() method and that factory is used when creating sockets for secure https URL connections. The same thing can be achieved by doing the following in JAX-WS clients:

Example 5.12. HTTPS SSLSocketFactory

SSLSocketFactory sslSocketFactory = ...;
Map<String, Object> ctxt = ((BindingProvider)proxy).getRequestContext();
ctxt.put(JAXWSProperties.SSL_SOCKET_FACTORY, sslSocketFactory);

5.10. HTTP address in soap:address and import locations

A service may be hosted in a servlet container that is behind firewall/load balancer. Then a published WSDL's soap:address, wsdl:import, xsd:import locations may point to the internal address(not to the external firewall/loadbalancer address). Metro uses the HttpServletRequest's getServerName() and getServerPort() to figure out the server's address and port respectively. This works in many cases, but you may need to configure the servlet container's server address in some cases.

  • This is supported in GlassFish, by configuring "server-name" attribute of <http-listener> in domain.xml. For example, set it to "http://firewall-host:firewall-port"

  • This is supported in Tomcat, by using the "proxyName" and "proxyPort" attributes on <Connector>. See tomcat configuration