blob: 2cb1e2d423d1c0fd42a93cc9ee4e8d00c6806f80 [file] [log] [blame]
/*
* 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.synapse.transport.nhttp;
import org.apache.axis2.AxisFault;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisOperation;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.Parameter;
import org.apache.axis2.engine.AxisEngine;
import org.apache.axis2.transport.http.HTTPTransportReceiver;
import org.apache.axis2.util.JavaUtils;
import org.apache.axis2.util.MessageContextBuilder;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.http.protocol.HTTP;
import org.apache.synapse.transport.nhttp.util.RESTUtil;
import org.apache.ws.commons.schema.XmlSchema;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.*;
/**
* Default http Get processor implementation for Synapse.
*/
public class DefaultHttpGetProcessor implements HttpGetRequestProcessor {
private static final Log log = LogFactory.getLog(DefaultHttpGetProcessor.class);
private static final String LOCATION = "Location";
private static final String CONTENT_TYPE = "Content-Type";
private static final String TEXT_HTML = "text/html";
private static final String TEXT_XML = "text/xml";
protected ConfigurationContext cfgCtx;
protected ServerHandler serverHandler;
public void init(ConfigurationContext cfgCtx, ServerHandler serverHandler) throws AxisFault {
this.cfgCtx = cfgCtx;
this.serverHandler = serverHandler;
}
/**
* Process the HTTP GET request.
*
* @param request The HttpRequest
* @param response The HttpResponse
* @param msgContext The MessageContext
* @param conn The NHttpServerConnection
* @param os The OutputStream
*/
public void process(HttpRequest request,
HttpResponse response,
MessageContext msgContext,
NHttpServerConnection conn,
OutputStream os,
boolean isRestDispatching) {
String uri = request.getRequestLine().getUri();
String servicePath = cfgCtx.getServiceContextPath();
if (!servicePath.startsWith("/")) {
servicePath = "/" + servicePath;
}
String serviceName = getServiceName(request);
Map<String, String> parameters = new HashMap<String, String>();
int pos = uri.indexOf("?");
if (pos != -1) {
msgContext.setTo(new EndpointReference(uri.substring(0, pos)));
StringTokenizer st = new StringTokenizer(uri.substring(pos + 1), "&");
while (st.hasMoreTokens()) {
String param = st.nextToken();
pos = param.indexOf("=");
if (pos != -1) {
parameters.put(param.substring(0, pos), param.substring(pos + 1));
} else {
parameters.put(param, null);
}
}
} else {
msgContext.setTo(new EndpointReference(uri));
}
if (isServiceListBlocked(uri)) {
response.setStatusCode(HttpStatus.SC_FORBIDDEN);
serverHandler.commitResponseHideExceptions(conn, response);
} else if (uri.equals("/favicon.ico")) {
response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
response.addHeader(LOCATION, "http://ws.apache.org/favicon.ico");
serverHandler.commitResponseHideExceptions(conn, response);
} else if (serviceName != null && parameters.containsKey("wsdl")) {
generateWsdl(request, response, msgContext,
conn, os, serviceName, parameters, isRestDispatching);
return;
} else if (serviceName != null && parameters.containsKey("wsdl2")) {
generateWsdl2(request, response, msgContext,
conn, os, serviceName, isRestDispatching);
return;
} else if (serviceName != null && parameters.containsKey("xsd")) {
generateXsd(request, response, msgContext, conn, os, serviceName,
parameters, isRestDispatching);
return;
} else if (serviceName != null && parameters.containsKey("info")) {
generateServiceDetailsPage(response, conn, os, serviceName);
} else if (uri.startsWith(servicePath) &&
(serviceName == null || serviceName.length() == 0)) {
generateServicesList(response, conn, os, servicePath);
} else {
processGetAndDelete(request, response, msgContext,
conn, os, "GET", isRestDispatching);
return;
}
// make sure that the output stream is flushed and closed properly
closeOutputStream(os);
}
private void closeOutputStream(OutputStream os) {
try {
os.flush();
os.close();
} catch (IOException ignore) {
}
}
/**
* Is the incoming URI is requesting service list and http.block_service_list=true in
* nhttp.properties
* @param incomingURI incoming URI
* @return whether to proceed with incomingURI
*/
protected boolean isServiceListBlocked(String incomingURI) {
String isBlocked = NHttpConfiguration.getInstance().isServiceListBlocked();
return (("/services").equals(incomingURI) || ("/services" + "/").equals(incomingURI)) &&
Boolean.parseBoolean(isBlocked);
}
/**
* Returns the service name.
*
* @param request HttpRequest
* @return service name as a String
*/
protected String getServiceName(HttpRequest request) {
String uri = request.getRequestLine().getUri();
String servicePath = cfgCtx.getServiceContextPath();
if (!servicePath.startsWith("/")) {
servicePath = "/" + servicePath;
}
String serviceName = null;
if (uri.startsWith(servicePath)) {
serviceName = uri.substring(servicePath.length());
if (serviceName.startsWith("/")) {
serviceName = serviceName.substring(1);
}
if (serviceName.contains("?")) {
serviceName = serviceName.substring(0, serviceName.indexOf("?"));
}
} else {
// this may be a custom URI
String incomingURI = request.getRequestLine().getUri();
Map serviceURIMap = (Map) cfgCtx.getProperty(NhttpConstants.EPR_TO_SERVICE_NAME_MAP);
if (serviceURIMap != null) {
Set keySet = serviceURIMap.keySet();
for (Object key : keySet) {
if (incomingURI.toLowerCase().contains(((String) key).toLowerCase())) {
return (String) serviceURIMap.get(key);
}
}
}
}
if (serviceName != null) {
int opnStart = serviceName.indexOf("/");
if (opnStart != -1) {
serviceName = serviceName.substring(0, opnStart);
}
}
return serviceName;
}
/**
* Generates the services list.
*
* @param response HttpResponse
* @param conn NHttpServerConnection
* @param os OutputStream
* @param servicePath service path of the service
*/
protected void generateServicesList(HttpResponse response,
NHttpServerConnection conn,
OutputStream os, String servicePath) {
try {
byte[] bytes = getServicesHTML(
servicePath.endsWith("/") ? "" : servicePath + "/").getBytes();
response.addHeader(CONTENT_TYPE, TEXT_HTML);
serverHandler.commitResponseHideExceptions(conn, response);
os.write(bytes);
} catch (IOException e) {
handleBrowserException(response, conn, os,
"Error generating services list", e);
}
}
/**
* Generates service details page.
*
* @param response HttpResponse
* @param conn NHttpServerConnection
* @param os OutputStream
* @param serviceName service name
*/
protected void generateServiceDetailsPage(HttpResponse response,
NHttpServerConnection conn,
OutputStream os, String serviceName) {
AxisService service = cfgCtx.getAxisConfiguration().
getServices().get(serviceName);
if (service != null) {
String parameterValue = (String) service.getParameterValue("serviceType");
if ("proxy".equals(parameterValue) && !isWSDLProvidedForProxyService(service)) {
handleBrowserException(response, conn, os,
"No WSDL was provided for the Service " + serviceName +
". A WSDL cannot be generated.", null);
}
try {
byte[] bytes =
HTTPTransportReceiver.printServiceHTML(serviceName, cfgCtx).getBytes();
response.addHeader(CONTENT_TYPE, TEXT_HTML);
serverHandler.commitResponseHideExceptions(conn, response);
os.write(bytes);
} catch (IOException e) {
handleBrowserException(response, conn, os,
"Error generating service details page for : " + serviceName, e);
}
} else {
handleBrowserException(response, conn, os,
"Invalid service : " + serviceName, null);
}
}
/**
* Generates Schema.
*
* @param request HttpRequest
* @param response HttpResponse
* @param messageCtx Current MessageContext
* @param conn NHttpServerConnection
* @param os OutputStream
* @param serviceName service name
* @param parameters url parameters
* @param isRestDispatching Whether to handle this as REST
*/
protected void generateXsd(HttpRequest request, HttpResponse response,
MessageContext messageCtx, NHttpServerConnection conn,
OutputStream os, String serviceName,
Map<String, String> parameters, boolean isRestDispatching) {
if (parameters.get("xsd") == null || "".equals(parameters.get("xsd"))) {
AxisService service = cfgCtx.getAxisConfiguration()
.getServices().get(serviceName);
if (service != null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
service.printSchema(baos);
response.addHeader(CONTENT_TYPE, TEXT_XML);
serverHandler.commitResponseHideExceptions(conn, response);
os.write(baos.toByteArray());
closeOutputStream(os);
} catch (Exception e) {
handleBrowserException(response, conn, os,
"Error generating ?xsd output for service : " + serviceName, e);
}
} else {
processGetAndDelete(request, response, messageCtx, conn, os,
serviceName, isRestDispatching);
}
} else {
//cater for named xsds - check for the xsd name
String schemaName = parameters.get("xsd");
AxisService service = cfgCtx.getAxisConfiguration()
.getServices().get(serviceName);
if (service != null) {
//run the population logic just to be sure
service.populateSchemaMappings();
//write out the correct schema
Map schemaTable = service.getSchemaMappingTable();
XmlSchema schema = (XmlSchema) schemaTable.get(schemaName);
if (schema == null) {
int dotIndex = schemaName.indexOf('.');
if (dotIndex > 0) {
String schemaKey = schemaName.substring(0, dotIndex);
schema = (XmlSchema) schemaTable.get(schemaKey);
}
}
//schema found - write it to the stream
if (schema != null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
schema.write(baos);
response.addHeader(CONTENT_TYPE, TEXT_XML);
serverHandler.commitResponseHideExceptions(conn, response);
os.write(baos.toByteArray());
closeOutputStream(os);
} catch (Exception e) {
handleBrowserException(response, conn, os,
"Error generating named ?xsd output for service : " + serviceName, e);
}
} else {
// no schema available by that name - send 404
response.setStatusCode(HttpStatus.SC_NOT_FOUND);
closeOutputStream(os);
}
} else {
processGetAndDelete(request, response, messageCtx, conn, os,
serviceName, isRestDispatching);
}
}
}
/**
* Generate WSDL2.
*
* @param request HttpRequest
* @param response HttpResponse
* @param msgContext MessageContext
* @param conn NHttpServerConnection
* @param os OutputStream
* @param serviceName service name
* @param isRestDispatching weather nhttp should do rest dispatching
*/
protected void generateWsdl2(HttpRequest request, HttpResponse response,
MessageContext msgContext,
NHttpServerConnection conn,
OutputStream os, String serviceName, boolean isRestDispatching) {
AxisService service = cfgCtx.getAxisConfiguration().
getServices().get(serviceName);
if (service != null) {
String parameterValue = (String) service.getParameterValue("serviceType");
if ("proxy".equals(parameterValue) && !isWSDLProvidedForProxyService(service)) {
handleBrowserException(response, conn, os,
"No WSDL was provided for the Service " + serviceName +
". A WSDL cannot be generated.", null);
}
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
service.printWSDL2(baos, getIpAddress());
response.addHeader(CONTENT_TYPE, TEXT_XML);
serverHandler.commitResponseHideExceptions(conn, response);
os.write(baos.toByteArray());
closeOutputStream(os);
} catch (Exception e) {
handleBrowserException(response, conn, os,
"Error generating ?wsdl2 output for service : " + serviceName, e);
}
} else {
processGetAndDelete(request, response, msgContext,
conn, os, "GET", isRestDispatching);
}
}
/**
* Generate WSDL.
*
* @param request HttpRequest
* @param response HttpResponse
* @param msgContext MessageContext
* @param conn NHttpServerConnection
* @param os OutputStream
* @param serviceName service name
* @param parameters parameters
* @param isRestDispatching if restDispatching is on
*/
protected void generateWsdl(HttpRequest request, HttpResponse response,
MessageContext msgContext,
NHttpServerConnection conn,
OutputStream os, String serviceName,
Map<String, String> parameters, boolean isRestDispatching) {
AxisService service = cfgCtx.getAxisConfiguration().
getServices().get(serviceName);
if (service != null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
String parameterValue = parameters.get("wsdl");
if (parameterValue == null) {
service.printWSDL(baos, getIpAddress());
} else {
// here the parameter value should be the wsdl file name
service.printUserWSDL(baos, parameterValue);
}
response.addHeader(CONTENT_TYPE, TEXT_XML);
serverHandler.commitResponseHideExceptions(conn, response);
os.write(baos.toByteArray());
closeOutputStream(os);
} catch (Exception e) {
handleBrowserException(response, conn, os,
"Error generating ?wsdl output for service : " + serviceName, e);
}
} else {
processGetAndDelete(request, response, msgContext,
conn, os, "GET", isRestDispatching);
}
}
/**
* Calls the RESTUtil to process GET and DELETE Request
*
* @param request HttpRequest
* @param response HttpResponse
* @param msgContext MessageContext
* @param conn NHttpServerConnection
* @param os OutputStream
* @param method HTTP method, either GET or DELETE
* @param isRestDispatching weather transport should do rest dispatching
*/
protected void processGetAndDelete(HttpRequest request, HttpResponse response,
MessageContext msgContext,
NHttpServerConnection conn, OutputStream os,
String method, boolean isRestDispatching) {
try {
RESTUtil.processGetAndDeleteRequest(
msgContext, os, request.getRequestLine().getUri(),
request.getFirstHeader(HTTP.CONTENT_TYPE), method, isRestDispatching);
// do not let the output stream close (as by default below) since
// we are serving this GET/DELETE request through the Synapse engine
} catch (AxisFault axisFault) {
handleException(response, msgContext, conn, os,
"Error processing " + method + " request for: " +
request.getRequestLine().getUri(), axisFault);
}
}
/**
* Handles exception.
*
* @param response HttpResponse
* @param msgContext MessageContext
* @param conn NHttpServerConnection
* @param os OutputStream
* @param msg message
* @param e Exception
*/
protected void handleException(HttpResponse response, MessageContext msgContext,
NHttpServerConnection conn,
OutputStream os, String msg, Exception e) {
if (e == null) {
log.error(msg);
} else {
log.error(msg, e);
}
if (e == null) {
e = new Exception(msg);
}
try {
MessageContext faultContext = MessageContextBuilder.createFaultMessageContext(
msgContext, e);
AxisEngine.sendFault(faultContext);
} catch (Exception ex) {
response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
response.addHeader(CONTENT_TYPE, TEXT_XML);
serverHandler.commitResponseHideExceptions(conn, response);
try {
os.write(msg.getBytes());
if (ex != null) {
os.write(ex.getMessage().getBytes());
}
} catch (IOException ignore) {
}
if (conn != null) {
try {
conn.shutdown();
} catch (IOException ignore) {
}
}
}
}
/**
* Handles browser exception.
*
* @param response HttpResponse
* @param conn NHttpServerConnection
* @param os OutputStream
* @param msg message
* @param e Exception
*/
protected void handleBrowserException(HttpResponse response,
NHttpServerConnection conn, OutputStream os,
String msg, Exception e) {
if (e == null) {
log.error(msg);
} else {
log.error(msg, e);
}
if (!response.containsHeader(HTTP.TRANSFER_ENCODING)) {
response.setStatusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR);
response.setReasonPhrase(msg);
response.addHeader(CONTENT_TYPE, TEXT_HTML);
serverHandler.commitResponseHideExceptions(conn, response);
try {
os.write(msg.getBytes());
os.close();
} catch (IOException ignore) {
}
}
if (conn != null) {
try {
conn.shutdown();
} catch (IOException ignore) {
}
}
}
/**
* Checks whether a wsdl is provided for a proxy service.
*
* @param service AxisService
* @return whether the wsdl is provided or not
*/
protected boolean isWSDLProvidedForProxyService(AxisService service) {
boolean isWSDLProvided = false;
if (service.getParameterValue(WSDLConstants.WSDL_4_J_DEFINITION) != null ||
service.getParameterValue(WSDLConstants.WSDL_20_DESCRIPTION) != null) {
isWSDLProvided = true;
}
return isWSDLProvided;
}
/**
* Whatever this method returns as the IP is ignored by the actual http/s listener when
* its getServiceEPR is invoked. This was originally copied from axis2
*
* @return Returns String.
* @throws java.net.SocketException if the socket can not be accessed
*/
protected static String getIpAddress() throws SocketException {
Enumeration e = NetworkInterface.getNetworkInterfaces();
String address = "127.0.0.1";
while (e.hasMoreElements()) {
NetworkInterface netface = (NetworkInterface) e.nextElement();
Enumeration addresses = netface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress ip = (InetAddress) addresses.nextElement();
if (!ip.isLoopbackAddress() && isIP(ip.getHostAddress())) {
return ip.getHostAddress();
}
}
}
return address;
}
protected static boolean isIP(String hostAddress) {
return hostAddress.split("[.]").length == 4;
}
/**
* Returns the HTML text for the list of services deployed.
* This can be delegated to another Class as well
* where it will handle more options of GET messages.
*
* @param prefix to be used for the Service names
* @return the HTML to be displayed as a String
*/
protected String getServicesHTML(String prefix) {
Map services = cfgCtx.getAxisConfiguration().getServices();
Hashtable erroneousServices = cfgCtx.getAxisConfiguration().getFaultyServices();
boolean servicesFound = false;
StringBuilder resultBuf = new StringBuilder();
resultBuf.append("<html><head><title>Axis2: Services</title></head>" + "<body>");
if ((services != null) && !services.isEmpty()) {
servicesFound = true;
resultBuf.append("<h2>" + "Deployed services" + "</h2>");
for (Object service : services.values()) {
AxisService axisService = (AxisService) service;
Parameter parameter = axisService.getParameter(
NhttpConstants.HIDDEN_SERVICE_PARAM_NAME);
if (axisService.getName().startsWith("__") ||
(parameter != null && JavaUtils.isTrueExplicitly(parameter.getValue()))) {
continue; // skip private services
}
Iterator iterator = axisService.getOperations();
resultBuf.append("<h3><a href=\"").append(prefix).append(axisService.getName()).append(
"?wsdl\">").append(axisService.getName()).append("</a></h3>");
if (iterator.hasNext()) {
resultBuf.append("Available operations <ul>");
for (; iterator.hasNext();) {
AxisOperation axisOperation = (AxisOperation) iterator.next();
resultBuf.append("<li>").append(
axisOperation.getName().getLocalPart()).append("</li>");
}
resultBuf.append("</ul>");
} else {
resultBuf.append("No operations specified for this service");
}
}
}
if ((erroneousServices != null) && !erroneousServices.isEmpty()) {
servicesFound = true;
resultBuf.append("<hr><h2><font color=\"blue\">Faulty Services</font></h2>");
Enumeration faultyservices = erroneousServices.keys();
while (faultyservices.hasMoreElements()) {
String faultyserviceName = (String) faultyservices.nextElement();
resultBuf.append("<h3><font color=\"blue\">").append(
faultyserviceName).append("</font></h3>");
}
}
if (!servicesFound) {
resultBuf.append("<h2>There are no services deployed</h2>");
}
resultBuf.append("</body></html>");
return resultBuf.toString();
}
}