private void sendUsingOutputStream(MessageContext msgContext) throws AxisFault { OMOutputFormat format = NhttpUtil.getOMOutputFormat(msgContext); MessageFormatter messageFormatter = MessageFormatterDecoratorFactory.createMessageFormatterDecorator(msgContext); OutputStream out = (OutputStream) msgContext.getProperty(MessageContext.TRANSPORT_OUT); if (msgContext.isServerSide()) { OutTransportInfo transportInfo = (OutTransportInfo) msgContext.getProperty(Constants.OUT_TRANSPORT_INFO); if (transportInfo != null) { transportInfo.setContentType( messageFormatter.getContentType(msgContext, format, msgContext.getSoapAction())); } else { throw new AxisFault(Constants.OUT_TRANSPORT_INFO + " has not been set"); } } try { messageFormatter.writeTo(msgContext, format, out, false); out.close(); } catch (IOException e) { handleException("IO Error sending response message", e); } }
/** * transport sender invocation from Axis2 core * * @param msgContext message to be sent * @return the invocation response (always InvocationResponse.CONTINUE) * @throws AxisFault on error */ public InvocationResponse invoke(MessageContext msgContext) throws AxisFault { // remove unwanted HTTP headers (if any from the current message) removeUnwantedHeaders(msgContext); if (AddressingHelper.isReplyRedirected(msgContext) && !msgContext.getReplyTo().hasNoneAddress()) { msgContext.setProperty(NhttpConstants.IGNORE_SC_ACCEPTED, Constants.VALUE_TRUE); } EndpointReference epr = NhttpUtil.getDestinationEPR(msgContext); if (epr != null) { if (!epr.hasNoneAddress()) { sendAsyncRequest(epr, msgContext); } else { handleException("Cannot send message to " + AddressingConstants.Final.WSA_NONE_URI); } } else { if (msgContext.getProperty(Constants.OUT_TRANSPORT_INFO) != null) { if (msgContext.getProperty(Constants.OUT_TRANSPORT_INFO) instanceof ServerWorker) { sendAsyncResponse(msgContext); } else { sendUsingOutputStream(msgContext); } } else { handleException("No valid destination EPR or OutputStream to send message"); } } if (msgContext.getOperationContext() != null) { msgContext .getOperationContext() .setProperty(Constants.RESPONSE_WRITTEN, Constants.VALUE_TRUE); } return InvocationResponse.CONTINUE; }
/** * Send the passed in response message, asynchronously * * @param msgContext the message context to be sent * @throws AxisFault on error */ private void sendAsyncResponse(MessageContext msgContext) throws AxisFault { int contentLength = extractContentLength(msgContext); // remove unwanted HTTP headers (if any from the current message) removeUnwantedHeaders(msgContext); Map transportHeaders = (Map) msgContext.getProperty(MessageContext.TRANSPORT_HEADERS); ServerWorker worker = (ServerWorker) msgContext.getProperty(Constants.OUT_TRANSPORT_INFO); HttpResponse response = worker.getResponse(); OMOutputFormat format = NhttpUtil.getOMOutputFormat(msgContext); MessageFormatter messageFormatter = MessageFormatterDecoratorFactory.createMessageFormatterDecorator(msgContext); Boolean noEntityBody = (Boolean) msgContext.getProperty(NhttpConstants.NO_ENTITY_BODY); if (noEntityBody == null || Boolean.FALSE == noEntityBody) { response.setHeader( HTTP.CONTENT_TYPE, messageFormatter.getContentType(msgContext, format, msgContext.getSoapAction())); } else if (Boolean.TRUE == noEntityBody) { ((BasicHttpEntity) response.getEntity()).setChunked(false); ((BasicHttpEntity) response.getEntity()).setContentLength(0); // Since HTTP HEAD request doesn't contain message body content length of the is set to be 0. // To handle // content length 0 while serving head method, content length of the backend response is set // as the content // as synapse cannot calculate content length without providing message body. if (transportHeaders.get(NhttpConstants.HTTP_REQUEST_METHOD) != null && NhttpConstants.HTTP_HEAD.equals( transportHeaders.get(NhttpConstants.HTTP_REQUEST_METHOD)) && transportHeaders.get(NhttpConstants.ORIGINAL_CONTENT_LEN) != null) { ((BasicHttpEntity) response.getEntity()) .setContentLength( Long.parseLong( String.valueOf(transportHeaders.get(NhttpConstants.ORIGINAL_CONTENT_LEN)))); transportHeaders.remove(NhttpConstants.ORIGINAL_CONTENT_LEN); transportHeaders.remove(NhttpConstants.HTTP_REQUEST_METHOD); } } response.setStatusCode(determineHttpStatusCode(msgContext, response)); // Override the Standard Reason Phrase if (msgContext.getProperty(NhttpConstants.HTTP_REASON_PHRASE) != null && !msgContext.getProperty(NhttpConstants.HTTP_REASON_PHRASE).equals("")) { response.setReasonPhrase( msgContext.getProperty(NhttpConstants.HTTP_REASON_PHRASE).toString()); } // set any transport headers if (transportHeaders != null && !transportHeaders.values().isEmpty()) { Iterator iter = transportHeaders.keySet().iterator(); while (iter.hasNext()) { Object header = iter.next(); Object value = transportHeaders.get(header); if (value != null && header instanceof String && value instanceof String) { response.addHeader((String) header, (String) value); String excessProp = NhttpConstants.EXCESS_TRANSPORT_HEADERS; Map map = (Map) msgContext.getProperty(excessProp); if (map != null && map.get(header) != null) { log.debug( "Number of excess values for " + header + " header is : " + ((Collection) (map.get(header))).size()); for (Iterator iterator = map.keySet().iterator(); iterator.hasNext(); ) { String key = (String) iterator.next(); for (String excessVal : (Collection<String>) map.get(key)) { if (header.equals(key)) { response.addHeader((String) header, (String) excessVal); } } } } } } } boolean forceContentLength = msgContext.isPropertyTrue(NhttpConstants.FORCE_HTTP_CONTENT_LENGTH); boolean forceContentLengthCopy = msgContext.isPropertyTrue(NhttpConstants.COPY_CONTENT_LENGTH_FROM_INCOMING); BasicHttpEntity entity = (BasicHttpEntity) response.getEntity(); MetricsCollector lstMetrics = worker.getServiceHandler().getMetrics(); try { if (forceContentLength) { entity.setChunked(false); if (forceContentLengthCopy && contentLength > 0) { entity.setContentLength(contentLength); } else { setStreamAsTempData(entity, messageFormatter, msgContext, format); } } worker.getServiceHandler().commitResponse(worker.getConn(), response); lstMetrics.reportResponseCode(response.getStatusLine().getStatusCode()); OutputStream out = worker.getOutputStream(); /* * if this is a dummy message to handle http 202 case with non-blocking IO * write an empty byte array as body */ if (msgContext.isPropertyTrue(NhttpConstants.SC_ACCEPTED) || Boolean.TRUE == noEntityBody) { out.write(new byte[0]); } else { if (forceContentLength) { if (forceContentLengthCopy && contentLength > 0) { messageFormatter.writeTo(msgContext, format, out, false); } else { writeMessageFromTempData(out, msgContext); } } else { messageFormatter.writeTo(msgContext, format, out, false); } } out.close(); if (lstMetrics != null) { lstMetrics.incrementMessagesSent(); } } catch (ProtocolException e) { log.error(e + " (Synapse may be trying to send an exact response more than once )"); } catch (HttpException e) { if (lstMetrics != null) { lstMetrics.incrementFaultsSending(); } handleException( "Unexpected HTTP protocol error sending response to : " + worker.getRemoteAddress(), e); } catch (ConnectionClosedException e) { if (lstMetrics != null) { lstMetrics.incrementFaultsSending(); } log.warn("Connection closed by client : " + worker.getRemoteAddress()); } catch (IllegalStateException e) { if (lstMetrics != null) { lstMetrics.incrementFaultsSending(); } log.warn("Connection closed by client : " + worker.getRemoteAddress()); } catch (IOException e) { if (lstMetrics != null) { lstMetrics.incrementFaultsSending(); } handleException("IO Error sending response message to : " + worker.getRemoteAddress(), e); } catch (Exception e) { if (lstMetrics != null) { lstMetrics.incrementFaultsSending(); } handleException( "General Error sending response message to : " + worker.getRemoteAddress(), e); } InputStream is = worker.getIs(); if (is != null) { try { is.close(); } catch (IOException ignore) { } } }
/** Process the incoming request */ @SuppressWarnings({"unchecked"}) public void run() { String method = request.getRequestLine().getMethod().toUpperCase(); msgContext.setProperty( Constants.Configuration.HTTP_METHOD, request.getRequestLine().getMethod()); if (NHttpConfiguration.getInstance().isHttpMethodDisabled(method)) { handleException("Unsupported method : " + method, null); } // String uri = request.getRequestLine().getUri(); String oriUri = request.getRequestLine().getUri(); String restUrlPostfix = NhttpUtil.getRestUrlPostfix(oriUri, cfgCtx.getServicePath()); msgContext.setProperty(NhttpConstants.REST_URL_POSTFIX, restUrlPostfix); String servicePrefix = oriUri.substring(0, oriUri.indexOf(restUrlPostfix)); if (servicePrefix.indexOf("://") == -1) { HttpInetConnection inetConn = (HttpInetConnection) conn; InetAddress localAddr = inetConn.getLocalAddress(); if (localAddr != null) { servicePrefix = schemeName + "://" + localAddr.getHostName() + ":" + inetConn.getLocalPort() + servicePrefix; } if (inetConn.getLocalPort() > 0) { msgContext.setProperty(NhttpConstants.SERVER_PORT, inetConn.getLocalPort()); } } msgContext.setProperty(NhttpConstants.SERVICE_PREFIX, servicePrefix); if ("GET".equals(method)) { httpGetRequestProcessor.process(request, response, msgContext, conn, os, isRestDispatching); } else if ("POST".equals(method)) { processEntityEnclosingMethod(); } else if ("PUT".equals(method)) { processEntityEnclosingMethod(); } else if ("HEAD".equals(method)) { processNonEntityEnclosingMethod(); } else if ("OPTIONS".equals(method)) { processNonEntityEnclosingMethod(); } else if ("DELETE".equals(method)) { processGetAndDelete("DELETE"); } else if ("TRACE".equals(method)) { processNonEntityEnclosingMethod(); } else { handleException("Unsupported method : " + method, null); } // here the RequestResponseTransport plays an important role when it comes to // dual channel invocation. This is becasue we need to ACK to the request once the request // is received to synapse. Otherwise we will not be able to support the single channel // invocation within the actual service and synapse for a dual channel request from the // client. if (isAckRequired()) { String respWritten = ""; if (msgContext.getOperationContext() != null) { respWritten = (String) msgContext.getOperationContext().getProperty(Constants.RESPONSE_WRITTEN); } boolean respWillFollow = !Constants.VALUE_TRUE.equals(respWritten) && !"SKIP".equals(respWritten); boolean acked = (((RequestResponseTransport) msgContext.getProperty(RequestResponseTransport.TRANSPORT_CONTROL)) .getStatus() == RequestResponseTransport.RequestResponseTransportStatus.ACKED); boolean forced = msgContext.isPropertyTrue(NhttpConstants.FORCE_SC_ACCEPTED); boolean nioAck = msgContext.isPropertyTrue("NIO-ACK-Requested", false); if (respWillFollow || acked || forced || nioAck) { if (!nioAck) { if (log.isDebugEnabled()) { log.debug( "Sending 202 Accepted response for MessageID : " + msgContext.getMessageID() + " response written : " + respWritten + " response will follow : " + respWillFollow + " acked : " + acked + " forced ack : " + forced); } response.setStatusCode(HttpStatus.SC_ACCEPTED); } else { if (log.isDebugEnabled()) { log.debug( "Sending ACK response with status " + msgContext.getProperty(NhttpConstants.HTTP_SC) + ", for MessageID : " + msgContext.getMessageID()); } response.setStatusCode( Integer.parseInt(msgContext.getProperty(NhttpConstants.HTTP_SC).toString())); Map<String, String> responseHeaders = (Map<String, String>) msgContext.getProperty(MessageContext.TRANSPORT_HEADERS); if (responseHeaders != null) { for (String headerName : responseHeaders.keySet()) { response.addHeader(headerName, responseHeaders.get(headerName)); String excessProp = NhttpConstants.EXCESS_TRANSPORT_HEADERS; Map map = (Map) msgContext.getProperty(excessProp); if (map != null) { log.debug( "Number of excess values for " + headerName + " header is : " + ((Collection) (map.get(headerName))).size()); for (Iterator iterator = map.keySet().iterator(); iterator.hasNext(); ) { String key = (String) iterator.next(); for (String excessVal : (Collection<String>) map.get(key)) { response.addHeader(headerName, (String) excessVal); } } } } } } if (metrics != null) { metrics.incrementMessagesSent(); } try { /* * Remove Content-Length and Transfer-Encoding headers, if already present. * */ response.removeHeaders(HTTP.TRANSFER_ENCODING); response.removeHeaders(HTTP.CONTENT_LEN); serverHandler.commitResponse(conn, response); } catch (HttpException e) { if (metrics != null) { metrics.incrementFaultsSending(); } handleException("Unexpected HTTP protocol error : " + e.getMessage(), e); } catch (ConnectionClosedException e) { if (metrics != null) { metrics.incrementFaultsSending(); } log.warn("Connection closed by client (Connection closed)"); } catch (IllegalStateException e) { if (metrics != null) { metrics.incrementFaultsSending(); } log.warn("Connection closed by client (Buffer closed)"); } catch (IOException e) { if (metrics != null) { metrics.incrementFaultsSending(); } handleException("IO Error sending response message", e); } catch (Exception e) { if (metrics != null) { metrics.incrementFaultsSending(); } handleException("General Error sending response message", e); } if (is != null) { try { is.close(); } catch (IOException ignore) { } } // make sure that the output stream is flushed and closed properly try { os.flush(); os.close(); } catch (IOException ignore) { } } } }
/** * Create an Axis2 message context for the given http request. The request may be in the process * of being streamed * * @param request the http request to be used to create the corresponding Axis2 message context * @return the Axis2 message context created */ private MessageContext createMessageContext(HttpRequest request) { MessageContext msgContext = new MessageContext(); msgContext.setMessageID(UIDGenerator.generateURNString()); // There is a discrepency in what I thought, Axis2 spawns a new threads to // send a message if this is TRUE - and I want it to be the other way msgContext.setProperty(MessageContext.CLIENT_API_NON_BLOCKING, Boolean.FALSE); msgContext.setConfigurationContext(cfgCtx); if ("https".equalsIgnoreCase(schemeName)) { msgContext.setTransportOut( cfgCtx.getAxisConfiguration().getTransportOut(Constants.TRANSPORT_HTTPS)); msgContext.setTransportIn( cfgCtx.getAxisConfiguration().getTransportIn(Constants.TRANSPORT_HTTPS)); msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTPS); SSLIOSession session = (SSLIOSession) (conn.getContext()).getAttribute("SSL_SESSION"); msgContext.setProperty( "ssl.client.auth.cert.X509", session.getAttribute("ssl.client.auth.cert.X509")); } else { msgContext.setTransportOut( cfgCtx.getAxisConfiguration().getTransportOut(Constants.TRANSPORT_HTTP)); msgContext.setTransportIn( cfgCtx.getAxisConfiguration().getTransportIn(Constants.TRANSPORT_HTTP)); msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP); } msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, this); // the following statement causes the soap session services to be failing - ruwan // msgContext.setServiceGroupContextId(UUIDGenerator.getUUID()); msgContext.setServerSide(true); msgContext.setProperty( Constants.Configuration.TRANSPORT_IN_URL, request.getRequestLine().getUri()); // http transport header names are case insensitive Map<String, String> headers = new TreeMap<String, String>( new Comparator<String>() { public int compare(String o1, String o2) { return o1.compareToIgnoreCase(o2); } }); for (Header header : request.getAllHeaders()) { String headerName = header.getName(); // if this header is already added if (headers.containsKey(headerName)) { /* this is a multi-value header */ // generate the key String key = NhttpConstants.EXCESS_TRANSPORT_HEADERS; // get the old value String oldValue = headers.get(headerName); // adds additional values to a list in a property of message context Map map; if (msgContext.getProperty(key) != null) { map = (Map) msgContext.getProperty(key); map.put(headerName, oldValue); } else { map = new MultiValueMap(); map.put(headerName, oldValue); // set as a property in message context msgContext.setProperty(key, map); } } headers.put(header.getName(), header.getValue()); } msgContext.setProperty(MessageContext.TRANSPORT_HEADERS, headers); // find the remote party IP address and set it to the message context if (conn instanceof HttpInetConnection) { HttpInetConnection inetConn = (HttpInetConnection) conn; InetAddress remoteAddr = inetConn.getRemoteAddress(); if (remoteAddr != null) { msgContext.setProperty(MessageContext.REMOTE_ADDR, remoteAddr.getHostAddress()); msgContext.setProperty(NhttpConstants.REMOTE_HOST, NhttpUtil.getHostName(remoteAddr)); remoteAddress = remoteAddr.getHostAddress(); } } msgContext.setProperty( RequestResponseTransport.TRANSPORT_CONTROL, new HttpCoreRequestResponseTransport(msgContext)); msgContext.setProperty( ServerHandler.SERVER_CONNECTION_DEBUG, conn.getContext().getAttribute(ServerHandler.SERVER_CONNECTION_DEBUG)); msgContext.setProperty(NhttpConstants.NHTTP_INPUT_STREAM, is); msgContext.setProperty(NhttpConstants.NHTTP_OUTPUT_STREAM, os); return msgContext; }