/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.cxf.transport.http; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.ServerSocket; import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; import org.apache.cxf.Bus; import org.apache.cxf.attachment.AttachmentDataSource; import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.Base64Exception; import org.apache.cxf.common.util.Base64Utility; import org.apache.cxf.common.util.PropertyUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.configuration.Configurable; import org.apache.cxf.configuration.security.AuthorizationPolicy; import org.apache.cxf.continuations.ContinuationProvider; import org.apache.cxf.continuations.SuspendedInvocationException; import org.apache.cxf.helpers.HttpHeaderHelper; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.interceptor.Fault; import org.apache.cxf.interceptor.Interceptor; import org.apache.cxf.io.AbstractWrappedOutputStream; import org.apache.cxf.io.CopyingOutputStream; import org.apache.cxf.io.DelegatingInputStream; import org.apache.cxf.message.Attachment; import org.apache.cxf.message.Exchange; import org.apache.cxf.message.ExchangeImpl; import org.apache.cxf.message.Message; import org.apache.cxf.message.MessageImpl; import org.apache.cxf.message.MessageUtils; import org.apache.cxf.policy.PolicyDataEngine; import org.apache.cxf.security.SecurityContext; import org.apache.cxf.security.transport.TLSSessionInfo; import org.apache.cxf.service.model.EndpointInfo; import org.apache.cxf.transport.AbstractDestination; import org.apache.cxf.transport.AbstractMultiplexDestination; import org.apache.cxf.transport.Assertor; import org.apache.cxf.transport.Conduit; import org.apache.cxf.transport.http.policy.impl.ServerPolicyCalculator; import org.apache.cxf.transport.https.CertConstraints; import org.apache.cxf.transport.https.CertConstraintsInterceptor; import org.apache.cxf.transports.http.configuration.HTTPServerPolicy; import org.apache.cxf.ws.addressing.AddressingProperties; import org.apache.cxf.ws.addressing.ContextUtils; import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.apache.cxf.ws.addressing.EndpointReferenceUtils; /** * Common base for HTTP Destination implementations. */ public abstract class AbstractHTTPDestination extends AbstractMultiplexDestination implements Configurable, Assertor { public static final String HTTP_REQUEST = "HTTP.REQUEST"; public static final String HTTP_RESPONSE = "HTTP.RESPONSE"; public static final String HTTP_CONTEXT = "HTTP.CONTEXT"; public static final String HTTP_CONFIG = "HTTP.CONFIG"; public static final String HTTP_CONTEXT_MATCH_STRATEGY = "HTTP_CONTEXT_MATCH_STRATEGY"; public static final String RESPONSE_HEADERS_COPIED = "http.headers.copied"; public static final String RESPONSE_COMMITED = "http.response.done"; public static final String REQUEST_REDIRECTED = "http.request.redirected"; public static final String CXF_CONTINUATION_MESSAGE = "cxf.continuation.message"; public static final String CXF_ASYNC_CONTEXT = "cxf.async.context"; public static final String SERVICE_REDIRECTION = "http.service.redirection"; private static final String HTTP_BASE_PATH = "http.base.path"; private static final String SSL_CIPHER_SUITE_ATTRIBUTE = "javax.servlet.request.cipher_suite"; private static final String SSL_PEER_CERT_CHAIN_ATTRIBUTE = "javax.servlet.request.X509Certificate"; private static final String DECODE_BASIC_AUTH_WITH_ISO8859 = "decode.basicauth.with.iso8859"; private static final Logger LOG = LogUtils.getL7dLogger(AbstractHTTPDestination.class); protected final Bus bus; protected DestinationRegistry registry; protected final String path; // Configuration values protected volatile HTTPServerPolicy serverPolicy; protected String contextMatchStrategy = "stem"; protected boolean fixedParameterOrder; protected boolean multiplexWithAddress; protected CertConstraints certConstraints; protected boolean isServlet3; protected boolean decodeBasicAuthWithIso8859; protected ContinuationProviderFactory cproviderFactory; protected boolean enableWebSocket; private volatile boolean serverPolicyCalced; /** * Constructor * * @param b the associated Bus * @param ci the associated conduit initiator * @param ei the endpoint info of the destination * @param dp true for adding the default port if it is missing * @throws IOException */ public AbstractHTTPDestination(Bus b, DestinationRegistry registry, EndpointInfo ei, String path, boolean dp) throws IOException { super(b, getTargetReference(getAddressValue(ei, dp), b), ei); this.bus = b; this.registry = registry; this.path = path; try { ServletRequest.class.getMethod("isAsyncSupported"); isServlet3 = true; } catch (Throwable t) { //servlet 2.5 or earlier, no async support } decodeBasicAuthWithIso8859 = PropertyUtils.isTrue(bus.getProperty(DECODE_BASIC_AUTH_WITH_ISO8859)); initConfig(); } public Bus getBus() { return bus; } private AuthorizationPolicy getAuthorizationPolicyFromMessage(String credentials, SecurityContext sc) { if (credentials == null || StringUtils.isEmpty(credentials.trim())) { return null; } List<String> creds = StringUtils.getParts(credentials, " "); String authType = creds.get(0); if ("Basic".equals(authType) && creds.size() == 2) { String authEncoded = creds.get(1); try { byte[] authBytes = Base64Utility.decode(authEncoded); String authDecoded = decodeBasicAuthWithIso8859 ? new String(authBytes, StandardCharsets.ISO_8859_1) : new String(authBytes); int idx = authDecoded.indexOf(':'); String username = null; String password = null; if (idx == -1) { username = authDecoded; } else { username = authDecoded.substring(0, idx); if (idx < (authDecoded.length() - 1)) { password = authDecoded.substring(idx + 1); } } AuthorizationPolicy policy = sc.getUserPrincipal() == null ? new AuthorizationPolicy() : new PrincipalAuthorizationPolicy(sc); policy.setUserName(username); policy.setPassword(password); policy.setAuthorizationType(authType); return policy; } catch (Base64Exception ex) { // Invalid authentication => treat as not authenticated or use the Principal } } if (sc.getUserPrincipal() != null) { AuthorizationPolicy policy = new PrincipalAuthorizationPolicy(sc); policy.setAuthorization(credentials); policy.setAuthorizationType(authType); return policy; } return null; } public static final class PrincipalAuthorizationPolicy extends AuthorizationPolicy { final SecurityContext sc; public PrincipalAuthorizationPolicy(SecurityContext sc) { this.sc = sc; } public Principal getPrincipal() { return sc.getUserPrincipal(); } @Override public String getUserName() { String name = super.getUserName(); if (name != null) { return name; } Principal pp = getPrincipal(); return pp != null ? pp.getName() : null; } } /** * @param message the message under consideration * @return true iff the message has been marked as oneway */ protected final boolean isOneWay(Message message) { Exchange ex = message.getExchange(); return ex == null ? false : ex.isOneWay(); } public void invoke(final ServletConfig config, final ServletContext context, final HttpServletRequest req, final HttpServletResponse resp) throws IOException { Message inMessage = retrieveFromContinuation(req); if (inMessage == null) { LOG.fine("Create a new message for processing"); inMessage = new MessageImpl(); ExchangeImpl exchange = new ExchangeImpl(); exchange.setInMessage(inMessage); setupMessage(inMessage, config, context, req, resp); exchange.setSession(new HTTPSession(req)); ((MessageImpl)inMessage).setDestination(this); } else { LOG.fine("Get the message from the request for processing"); } copyKnownRequestAttributes(req, inMessage); try { incomingObserver.onMessage(inMessage); invokeComplete(context, req, resp, inMessage); } catch (SuspendedInvocationException ex) { if (ex.getRuntimeException() != null) { throw ex.getRuntimeException(); } //else nothing to do, just finishing the processing } catch (Fault ex) { Throwable cause = ex.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException)cause; } else { throw ex; } } catch (RuntimeException ex) { throw ex; } finally { if (LOG.isLoggable(Level.FINE)) { LOG.fine("Finished servicing http request on thread: " + Thread.currentThread()); } } } protected void invokeComplete(final ServletContext context, final HttpServletRequest req, final HttpServletResponse resp, Message m) throws IOException { ContinuationProvider p = m.get(ContinuationProvider.class); if (p != null) { p.complete(); } } private void copyKnownRequestAttributes(HttpServletRequest request, Message message) { message.put(SERVICE_REDIRECTION, request.getAttribute(SERVICE_REDIRECTION)); } protected void setupMessage(final Message inMessage, final ServletConfig config, final ServletContext context, final HttpServletRequest req, final HttpServletResponse resp) throws IOException { setupContinuation(inMessage, req, resp); final Exchange exchange = inMessage.getExchange(); DelegatingInputStream in = new DelegatingInputStream(req.getInputStream()) { public void cacheInput() { if (!cached && (exchange.isOneWay() || isWSAddressingReplyToSpecified(exchange))) { //For one-ways and WS-Addressing invocations with ReplyTo address, //we need to cache the values of the HttpServletRequest //so they can be queried later for things like paths and schemes //and such like that. //Please note, exchange used to always get the "current" message exchange.getInMessage().put(HTTP_REQUEST, new HttpServletRequestSnapshot(req)); } super.cacheInput(); } private boolean isWSAddressingReplyToSpecified(Exchange ex) { AddressingProperties map = ContextUtils.retrieveMAPs(ex.getInMessage(), false, false, false); return map != null && !ContextUtils.isGenericAddress(map.getReplyTo()); } }; inMessage.setContent(DelegatingInputStream.class, in); inMessage.setContent(InputStream.class, in); inMessage.put(HTTP_REQUEST, req); inMessage.put(HTTP_RESPONSE, resp); inMessage.put(HTTP_CONTEXT, context); inMessage.put(HTTP_CONFIG, config); inMessage.put(HTTP_CONTEXT_MATCH_STRATEGY, contextMatchStrategy); inMessage.put(Message.HTTP_REQUEST_METHOD, req.getMethod()); String requestURI = req.getRequestURI(); inMessage.put(Message.REQUEST_URI, requestURI); String requestURL = req.getRequestURL().toString(); inMessage.put(Message.REQUEST_URL, requestURL); String contextPath = req.getContextPath(); if (contextPath == null) { contextPath = ""; } String servletPath = req.getServletPath(); if (servletPath == null) { servletPath = ""; } String contextServletPath = contextPath + servletPath; String pathInfo = req.getPathInfo(); if (pathInfo != null) { inMessage.put(Message.PATH_INFO, contextServletPath + pathInfo); } else { inMessage.put(Message.PATH_INFO, requestURI); } if (!StringUtils.isEmpty(requestURI)) { int index = requestURL.indexOf(requestURI); if (index > 0) { // Can be useful for referencing resources with URIs not covered by CXFServlet. // For example, if we a have web application name 'app' and CXFServlet listening // on "/services/*" then having HTTP_BASE_PATH pointing to say // http://localhost:8080/app will make it easy to refer to non CXF resources String schemaInfo = requestURL.substring(0, index); String basePathWithContextOnly = schemaInfo + contextPath; inMessage.put(HTTP_BASE_PATH, basePathWithContextOnly); } } else if (!StringUtils.isEmpty(servletPath) && requestURL.endsWith(servletPath)) { int index = requestURL.lastIndexOf(servletPath); if (index > 0) { inMessage.put(HTTP_BASE_PATH, requestURL.substring(0, index)); } } String contentType = req.getContentType(); inMessage.put(Message.CONTENT_TYPE, contentType); setEncoding(inMessage, req, contentType); inMessage.put(Message.QUERY_STRING, req.getQueryString()); inMessage.put(Message.ACCEPT_CONTENT_TYPE, req.getHeader("Accept")); String basePath = getBasePath(contextServletPath); if (!StringUtils.isEmpty(basePath)) { inMessage.put(Message.BASE_PATH, basePath); } inMessage.put(Message.FIXED_PARAMETER_ORDER, isFixedParameterOrder()); inMessage.put(Message.ASYNC_POST_RESPONSE_DISPATCH, Boolean.TRUE); SecurityContext httpSecurityContext = new SecurityContext() { public Principal getUserPrincipal() { return req.getUserPrincipal(); } public boolean isUserInRole(String role) { return req.isUserInRole(role); } }; inMessage.put(SecurityContext.class, httpSecurityContext); Headers headers = new Headers(inMessage); headers.copyFromRequest(req); String credentials = headers.getAuthorization(); AuthorizationPolicy authPolicy = getAuthorizationPolicyFromMessage(credentials, httpSecurityContext); inMessage.put(AuthorizationPolicy.class, authPolicy); propogateSecureSession(req, inMessage); inMessage.put(CertConstraints.class.getName(), certConstraints); inMessage.put(Message.IN_INTERCEPTORS, Arrays.asList(new Interceptor[] {CertConstraintsInterceptor.INSTANCE})); } /** * Propogate in the message a TLSSessionInfo instance representative * of the TLS-specific information in the HTTP request. * * @param request the Jetty request * @param message the Message */ private static void propogateSecureSession(HttpServletRequest request, Message message) { final String cipherSuite = (String) request.getAttribute(SSL_CIPHER_SUITE_ATTRIBUTE); if (cipherSuite != null) { final java.security.cert.Certificate[] certs = (java.security.cert.Certificate[]) request.getAttribute(SSL_PEER_CERT_CHAIN_ATTRIBUTE); message.put(TLSSessionInfo.class, new TLSSessionInfo(cipherSuite, null, certs)); } } private String setEncoding(final Message inMessage, final HttpServletRequest req, final String contentType) throws IOException { String enc = HttpHeaderHelper.findCharset(contentType); if (enc == null) { enc = req.getCharacterEncoding(); } // work around a bug with Jetty which results in the character // encoding not being trimmed correctly. if (enc != null && enc.endsWith("\"")) { enc = enc.substring(0, enc.length() - 1); } if (enc != null || "POST".equals(req.getMethod()) || "PUT".equals(req.getMethod())) { //allow gets/deletes/options to not specify an encoding String normalizedEncoding = HttpHeaderHelper.mapCharset(enc); if (normalizedEncoding == null) { String m = new org.apache.cxf.common.i18n.Message("INVALID_ENCODING_MSG", LOG, enc).toString(); LOG.log(Level.WARNING, m); throw new IOException(m); } inMessage.put(Message.ENCODING, normalizedEncoding); } return contentType; } protected Message retrieveFromContinuation(HttpServletRequest req) { if (!isServlet3) { if (cproviderFactory != null) { return cproviderFactory.retrieveFromContinuation(req); } return null; } return retrieveFromServlet3Async(req); } protected Message retrieveFromServlet3Async(HttpServletRequest req) { try { return (Message)req.getAttribute(CXF_CONTINUATION_MESSAGE); } catch (Throwable ex) { // the request may not implement the Servlet3 API } return null; } protected void setupContinuation(Message inMessage, final HttpServletRequest req, final HttpServletResponse resp) { try { if (isServlet3 && req.isAsyncSupported()) { inMessage.put(ContinuationProvider.class.getName(), new Servlet3ContinuationProvider(req, resp, inMessage)); } else if (cproviderFactory != null) { ContinuationProvider p = cproviderFactory.createContinuationProvider(inMessage, req, resp); if (p != null) { inMessage.put(ContinuationProvider.class.getName(), p); } } } catch (Throwable ex) { // the request may not implement the Servlet3 API } } protected String getBasePath(String contextPath) throws IOException { if (StringUtils.isEmpty(endpointInfo.getAddress())) { return ""; } return new URL(endpointInfo.getAddress()).getPath(); } protected static EndpointInfo getAddressValue(EndpointInfo ei) { return getAddressValue(ei, true); } protected static EndpointInfo getAddressValue(EndpointInfo ei, boolean dp) { if (dp) { String eiAddress = ei.getAddress(); if (eiAddress == null) { try { ServerSocket s = new ServerSocket(0); ei.setAddress("http://localhost:" + s.getLocalPort()); s.close(); return ei; } catch (IOException ex) { // problem allocating a random port, go to the default one ei.setAddress("http://localhost"); } } String addr = StringUtils.addDefaultPortIfMissing(ei.getAddress()); if (addr != null) { ei.setAddress(addr); } } return ei; } /** * @param inMessage the incoming message * @return the inbuilt backchannel */ protected Conduit getInbuiltBackChannel(Message inMessage) { HttpServletResponse response = (HttpServletResponse)inMessage.get(HTTP_RESPONSE); return new BackChannelConduit(response); } private void initConfig() { cproviderFactory = bus.getExtension(ContinuationProviderFactory.class); } private synchronized HTTPServerPolicy calcServerPolicyInternal(Message m) { HTTPServerPolicy sp = serverPolicy; if (!serverPolicyCalced) { PolicyDataEngine pde = bus.getExtension(PolicyDataEngine.class); if (pde != null) { sp = pde.getServerEndpointPolicy(m, endpointInfo, this, new ServerPolicyCalculator()); } if (null == sp) { sp = endpointInfo.getTraversedExtensor( new HTTPServerPolicy(), HTTPServerPolicy.class); } serverPolicy = sp; serverPolicyCalced = true; } return sp; } private HTTPServerPolicy calcServerPolicy(Message m) { HTTPServerPolicy sp = serverPolicy; if (!serverPolicyCalced) { sp = calcServerPolicyInternal(m); } return sp; } /** * On first write, we need to make sure any attachments and such that are still on the incoming stream * are read in. Otherwise we can get into a deadlock where the client is still trying to send the * request, but the server is trying to send the response. Neither side is reading and both blocked * on full buffers. Not a good situation. * @param outMessage */ private void cacheInput(Message outMessage) { if (outMessage.getExchange() == null) { return; } Message inMessage = outMessage.getExchange().getInMessage(); if (inMessage == null) { return; } Object o = inMessage.get("cxf.io.cacheinput"); DelegatingInputStream in = inMessage.getContent(DelegatingInputStream.class); if (MessageUtils.isTrue(o)) { Collection<Attachment> atts = inMessage.getAttachments(); if (atts != null) { for (Attachment a : atts) { if (a.getDataHandler().getDataSource() instanceof AttachmentDataSource) { try { ((AttachmentDataSource)a.getDataHandler().getDataSource()).cache(inMessage); } catch (IOException e) { throw new Fault(e); } } } } if (in != null) { in.cacheInput(); } } else if (in != null) { //We don't need to cache it, but we may need to consume it in order for the client // to be able to receive a response. (could be blocked sending) //However, also don't want to consume indefinitely. We'll limit to 16M. try { IOUtils.consume(in, 16 * 1024 * 1024); } catch (Exception ioe) { //ignore } } } protected OutputStream flushHeaders(Message outMessage) throws IOException { return flushHeaders(outMessage, true); } protected OutputStream flushHeaders(Message outMessage, boolean getStream) throws IOException { if (isResponseRedirected(outMessage)) { return null; } cacheInput(outMessage); HTTPServerPolicy sp = calcServerPolicy(outMessage); if (sp != null) { new Headers(outMessage).setFromServerPolicy(sp); } OutputStream responseStream = null; boolean oneWay = isOneWay(outMessage); HttpServletResponse response = getHttpResponseFromMessage(outMessage); int responseCode = getReponseCodeFromMessage(outMessage); if (responseCode >= 300) { String ec = (String)outMessage.get(Message.ERROR_MESSAGE); if (!StringUtils.isEmpty(ec)) { response.sendError(responseCode, ec); return null; } } response.setStatus(responseCode); new Headers(outMessage).copyToResponse(response); outMessage.put(RESPONSE_HEADERS_COPIED, "true"); if (hasNoResponseContent(outMessage)) { response.setContentLength(0); response.flushBuffer(); response.getOutputStream().close(); } else if (!getStream) { response.getOutputStream().close(); } else { responseStream = response.getOutputStream(); } if (oneWay) { outMessage.remove(HTTP_RESPONSE); } return responseStream; } private int getReponseCodeFromMessage(Message message) { Integer i = (Integer)message.get(Message.RESPONSE_CODE); if (i != null) { return i.intValue(); } else { int code = hasNoResponseContent(message) ? HttpURLConnection.HTTP_ACCEPTED : HttpURLConnection.HTTP_OK; // put the code in the message so that others can get it message.put(Message.RESPONSE_CODE, code); return code; } } /** * Determines if the current message has no response content. * The message has no response content if either: * - the request is oneway and the current message is no partial * response or an empty partial response. * - the request is not oneway but the current message is an empty partial * response. * @param message * @return */ private boolean hasNoResponseContent(Message message) { final boolean ow = isOneWay(message); final boolean pr = MessageUtils.isPartialResponse(message); final boolean epr = MessageUtils.isEmptyPartialResponse(message); //REVISIT may need to provide an option to choose other behavior? // old behavior not suppressing any responses => ow && !pr // suppress empty responses for oneway calls => ow && (!pr || epr) // suppress additionally empty responses for decoupled twoway calls => return (ow && !pr) || epr; } private HttpServletResponse getHttpResponseFromMessage(Message message) throws IOException { Object responseObj = message.get(HTTP_RESPONSE); if (responseObj instanceof HttpServletResponse) { return (HttpServletResponse)responseObj; } else if (null != responseObj) { String m = (new org.apache.cxf.common.i18n.Message("UNEXPECTED_RESPONSE_TYPE_MSG", LOG, responseObj.getClass())).toString(); LOG.log(Level.WARNING, m); throw new IOException(m); } else { String m = (new org.apache.cxf.common.i18n.Message("NULL_RESPONSE_MSG", LOG)).toString(); LOG.log(Level.WARNING, m); throw new IOException(m); } } private boolean isResponseRedirected(Message outMessage) { Exchange exchange = outMessage.getExchange(); return exchange != null && Boolean.TRUE.equals(exchange.get(REQUEST_REDIRECTED)); } /** * Backchannel conduit. */ public class BackChannelConduit extends AbstractDestination.AbstractBackChannelConduit { protected HttpServletResponse response; BackChannelConduit(HttpServletResponse resp) { response = resp; } /** * Send an outbound message, assumed to contain all the name-value * mappings of the corresponding input message (if any). * * @param message the message to be sent. */ public void prepare(Message message) throws IOException { message.put(HTTP_RESPONSE, response); OutputStream os = message.getContent(OutputStream.class); if (os == null) { message.setContent(OutputStream.class, new WrappedOutputStream(message)); } } @Override public void close(Message msg) throws IOException { super.close(msg); if (msg.getExchange() == null) { return; } Message m = msg.getExchange().getInMessage(); if (m == null) { return; } InputStream is = m.getContent(InputStream.class); if (is != null) { try { is.close(); m.removeContent(InputStream.class); } catch (IOException ioex) { //ignore } } } } /** * Wrapper stream responsible for flushing headers and committing outgoing * HTTP-level response. */ private class WrappedOutputStream extends AbstractWrappedOutputStream implements CopyingOutputStream { private Message outMessage; WrappedOutputStream(Message m) { super(); this.outMessage = m; } @Override public int copyFrom(InputStream in) throws IOException { if (!written) { onFirstWrite(); written = true; } if (wrappedStream != null) { return IOUtils.copy(in, wrappedStream); } return IOUtils.copy(in, this, IOUtils.DEFAULT_BUFFER_SIZE); } /** * Perform any actions required on stream flush (freeze headers, * reset output stream ... etc.) */ protected void onFirstWrite() throws IOException { OutputStream responseStream = flushHeaders(outMessage); if (null != responseStream) { wrappedStream = responseStream; } } /** * Perform any actions required on stream closure (handle response etc.) */ public void close() throws IOException { if (!written && wrappedStream == null) { OutputStream responseStream = flushHeaders(outMessage, false); if (null != responseStream) { wrappedStream = responseStream; } } if (wrappedStream != null) { // closing the stream should indirectly call the servlet response's flushBuffer wrappedStream.close(); } /* try { //make sure the input stream is also closed in this //case so that any resources it may have is cleaned up Message m = outMessage.getExchange().getInMessage(); if (m != null) { InputStream ins = m.getContent(InputStream.class); if (ins != null) { ins.close(); } } } catch (IOException ex) { //ignore } */ } } protected boolean contextMatchOnExact() { return "exact".equals(contextMatchStrategy); } public void finalizeConfig() { } public String getBeanName() { String beanName = null; if (endpointInfo.getName() != null) { beanName = endpointInfo.getName().toString() + ".http-destination"; } return beanName; } /* * Implement multiplex via the address URL to avoid the need for ws-a. * Requires contextMatchStrategy of stem. * * @see org.apache.cxf.transport.AbstractMultiplexDestination#getAddressWithId(java.lang.String) */ public EndpointReferenceType getAddressWithId(String id) { EndpointReferenceType ref = null; if (isMultiplexWithAddress()) { String address = EndpointReferenceUtils.getAddress(reference); ref = EndpointReferenceUtils.duplicate(reference); if (address.endsWith("/")) { EndpointReferenceUtils.setAddress(ref, address + id); } else { EndpointReferenceUtils.setAddress(ref, address + "/" + id); } } else { ref = super.getAddressWithId(id); } return ref; } /* * (non-Javadoc) * * @see org.apache.cxf.transport.AbstractMultiplexDestination#getId(java.util.Map) */ @Override public String getId(Map<String, Object> context) { String id = null; if (isMultiplexWithAddress()) { String address = (String)context.get(Message.PATH_INFO); if (null != address) { int afterLastSlashIndex = address.lastIndexOf("/") + 1; if (afterLastSlashIndex > 0 && afterLastSlashIndex < address.length()) { id = address.substring(afterLastSlashIndex); } } else { getLogger().log(Level.WARNING, new org.apache.cxf.common.i18n.Message( "MISSING_PATH_INFO", LOG).toString()); } } else { return super.getId(context); } return id; } public String getContextMatchStrategy() { return contextMatchStrategy; } public void setContextMatchStrategy(String contextMatchStrategy) { this.contextMatchStrategy = contextMatchStrategy; } public boolean isFixedParameterOrder() { return fixedParameterOrder; } public void setFixedParameterOrder(boolean fixedParameterOrder) { this.fixedParameterOrder = fixedParameterOrder; } public boolean isMultiplexWithAddress() { return multiplexWithAddress; } public void setMultiplexWithAddress(boolean multiplexWithAddress) { this.multiplexWithAddress = multiplexWithAddress; } public HTTPServerPolicy getServer() { return calcServerPolicy(null); } public void setServer(HTTPServerPolicy server) { this.serverPolicy = server; if (server != null) { serverPolicyCalced = true; } } public void assertMessage(Message message) { PolicyDataEngine pde = bus.getExtension(PolicyDataEngine.class); pde.assertMessage(message, calcServerPolicy(message), new ServerPolicyCalculator()); } public boolean canAssert(QName type) { return new ServerPolicyCalculator().getDataClassName().equals(type); } public void releaseRegistry() { registry = null; } public String getPath() { return path; } @Override protected void activate() { synchronized (this) { if (registry != null) { registry.addDestination(this); } } } @Override protected void deactivate() { synchronized (this) { if (registry != null) { registry.removeDestination(path); } } } @Override public void shutdown() { synchronized (this) { if (registry != null) { registry.removeDestination(path); } } super.shutdown(); } }