/*
* Copyright 2015 WSO2, Inc. (http://wso2.com)
*
* Licensed 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.wso2.carbon.mediation.transport.handlers;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.xpath.AXIOMXPath;
import org.apache.axiom.util.blob.OverflowBlob;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.util.XMLUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpInetConnection;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.nio.NHttpServerConnection;
import org.apache.synapse.transport.nhttp.DefaultHttpGetProcessor;
import org.apache.synapse.transport.nhttp.ServerHandler;
import org.jaxen.SimpleNamespaceContext;
import org.jaxen.XPath;
import org.wso2.carbon.base.ServerConfiguration;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.multitenancy.utils.TenantAxisUtils;
import org.wso2.carbon.core.transports.CarbonHttpRequest;
import org.wso2.carbon.core.transports.CarbonHttpResponse;
import org.wso2.carbon.core.transports.HttpGetRequestProcessor;
import org.wso2.carbon.utils.ServerConstants;
import javax.servlet.ServletException;
import javax.xml.namespace.QName;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Get Processor implementation for NHttp Transport.
*/
public class NHttpGetProcessor extends DefaultHttpGetProcessor {
private Map<String, org.wso2.carbon.core.transports.HttpGetRequestProcessor> getRequestProcessors =
new LinkedHashMap<String, org.wso2.carbon.core.transports.HttpGetRequestProcessor>();
private static final QName ITEM_QN = new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Item");
private static final QName CLASS_QN = new QName(ServerConstants.CARBON_SERVER_XML_NAMESPACE, "Class");
private static final Log log = LogFactory.getLog(NHttpGetProcessor.class);
private void populateGetRequestProcessors() throws AxisFault {
try {
OMElement docEle = XMLUtils.toOM(ServerConfiguration.getInstance().getDocumentElement());
if (docEle != null) {
SimpleNamespaceContext nsCtx = new SimpleNamespaceContext();
nsCtx.addNamespace("wsas", ServerConstants.CARBON_SERVER_XML_NAMESPACE);
XPath xp = new AXIOMXPath("//wsas:HttpGetRequestProcessors/wsas:Processor");
xp.setNamespaceContext(nsCtx);
List nodeList = xp.selectNodes(docEle);
for (Object aNodeList : nodeList) {
OMElement processorEle = (OMElement) aNodeList;
OMElement itemEle = processorEle.getFirstChildWithName(ITEM_QN);
if (itemEle == null) {
throw new ServletException("Required element, 'Item' not found!");
}
OMElement classEle = processorEle.getFirstChildWithName(CLASS_QN);
org.wso2.carbon.core.transports.HttpGetRequestProcessor processor;
if (classEle == null) {
throw new ServletException("Required element, 'Class' not found!");
} else {
processor =
(org.wso2.carbon.core.transports.HttpGetRequestProcessor)
Class.forName(classEle.getText().trim()).newInstance();
}
getRequestProcessors.put(itemEle.getText().trim(), processor);
}
}
} catch (Exception e) {
handleException("Error populating GetRequestProcessors", e);
}
}
private void processWithGetProcessor(HttpRequest request,
HttpResponse response,
String requestUri,
String requestUrl,
String queryString,
String item,
OutputStream outputStream,
NHttpServerConnection conn) throws Exception {
OverflowBlob temporaryData = new OverflowBlob(256, 4048, "_nhttp", ".dat");
try {
CarbonHttpRequest carbonHttpRequest = new CarbonHttpRequest(
"GET", requestUri, requestUrl);
String uri = request.getRequestLine().getUri();
// setting the parameters for nhttp transport
int pos = uri.indexOf("?");
if (pos != -1) {
StringTokenizer st = new StringTokenizer(uri.substring(pos + 1), "&");
while (st.hasMoreTokens()) {
String param = st.nextToken();
pos = param.indexOf("=");
if (pos != -1) {
carbonHttpRequest.setParameter(
param.substring(0, pos), param.substring(pos + 1));
} else {
carbonHttpRequest.setParameter(param, null);
}
}
}
carbonHttpRequest.setContextPath(cfgCtx.getServiceContextPath());
carbonHttpRequest.setQueryString(queryString);
CarbonHttpResponse carbonHttpResponse = new CarbonHttpResponse(
temporaryData.getOutputStream());
try {
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(TenantAxisUtils.
getTenantDomain(requestUrl), true);
(getRequestProcessors.get(item)).process(carbonHttpRequest,
carbonHttpResponse, cfgCtx);
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
// adding headers
Map<String, String> responseHeaderMap = carbonHttpResponse.getHeaders();
for (Object key : responseHeaderMap.keySet()) {
Object value = responseHeaderMap.get(key);
response.addHeader(key.toString(), value.toString());
}
// setting status code
response.setStatusCode(carbonHttpResponse.getStatusCode());
// setting error codes
if (carbonHttpResponse.isError()) {
if (carbonHttpResponse.getStatusMessage() != null) {
response.setStatusLine(response.getProtocolVersion(),
carbonHttpResponse.getStatusCode(),
carbonHttpResponse.getStatusMessage());
} else {
response.setStatusLine(response.getProtocolVersion(),
carbonHttpResponse.getStatusCode());
}
}
if (carbonHttpResponse.isRedirect()) {
response.addHeader("Location", carbonHttpResponse.getRedirect());
response.setStatusLine(response.getProtocolVersion(), 302);
}
serverHandler.commitResponseHideExceptions(conn, response);
temporaryData.writeTo(outputStream);
try {
outputStream.flush();
outputStream.close();
} catch (Exception ignored) {}
} finally {
temporaryData.release();
}
}
public void init(ConfigurationContext configurationContext, ServerHandler serverHandler)
throws AxisFault {
super.init(configurationContext, serverHandler);
if (cfgCtx.getProperty("GETRequestProcessorMap") != null) {
getRequestProcessors = (Map<String, HttpGetRequestProcessor>)
cfgCtx.getProperty("GETRequestProcessorMap");
} else {
populateGetRequestProcessors();
}
}
public void process(HttpRequest request, HttpResponse response,
MessageContext messageContext,
NHttpServerConnection conn,
OutputStream outputStream, boolean b) {
boolean isRequestHandled = false;
String uri = request.getRequestLine().getUri();
String servicePath = cfgCtx.getServiceContextPath();
if (!servicePath.startsWith("/")) {
servicePath = "/" + servicePath;
}
String serviceName = getServiceName(request);
boolean loadBalancer = Boolean.parseBoolean(System.getProperty("wso2.loadbalancer", "false"));
if (uri.equals("/favicon.ico")) {
response.setStatusCode(HttpStatus.SC_MOVED_PERMANENTLY);
response.addHeader("Location", "http://wso2.org/favicon.ico");
serverHandler.commitResponseHideExceptions(conn, response);
isRequestHandled = true;
} else if(uri.startsWith(servicePath) &&
(serviceName == null || serviceName.length() == 0)){
//check if service listing request is blocked
if (isServiceListBlocked(uri)) {
response.setStatusCode(HttpStatus.SC_FORBIDDEN);
serverHandler.commitResponseHideExceptions(conn, response);
} else{
generateServicesList(response, conn, outputStream, servicePath);
}
try {
outputStream.flush();
outputStream.close();
} catch (IOException ignore) {
}
isRequestHandled = true ;
} else {
int pos = uri.indexOf('?');
if (pos != -1) {
String queryString = uri.substring(pos + 1);
String requestUri = uri.substring(0, pos);
String requestUrl = uri;
if (requestUri.indexOf("://") == -1) {
HttpInetConnection inetConn = (HttpInetConnection) conn;
String hostName = "localhost";
ServerConfiguration serverConfig = ServerConfiguration.getInstance();
if (serverConfig.getFirstProperty("HostName") != null) {
hostName = serverConfig.getFirstProperty("HostName");
}
requestUrl = "http://" +
hostName + ":" + inetConn.getLocalPort() + requestUri;
}
String contextPath = cfgCtx.getServiceContextPath();
int beginIndex = -1;
if (requestUri.indexOf(contextPath) != -1) {
beginIndex = requestUri.indexOf(contextPath) + contextPath.length() + 1;
}
/**
* This reverseProxyMode was introduce to avoid LB exposing it's own services when invoked through rest call.
* For a soap call this works well. But for a rest call this does not work as intended. in LB it has to set system property "reverseProxyMode"
*
*/
boolean reverseProxyMode = Boolean.parseBoolean(System.getProperty("reverseProxyMode"));
AxisService axisService = null;
if (!reverseProxyMode) {
if (!(beginIndex < 0 || beginIndex > requestUri.length())) {
serviceName = requestUri.substring(beginIndex);
axisService = cfgCtx.getAxisConfiguration().getServiceForActivation(serviceName);
}
if (axisService == null && !loadBalancer && serviceName != null) {
// Try to see whether the service is available in a tenant
try {
String tenantDomain = TenantAxisUtils.getTenantDomain(uri);
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true);
axisService = TenantAxisUtils.getAxisService(serviceName, cfgCtx);
} catch (AxisFault axisFault) {
axisFault.printStackTrace();
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
}
}
if (queryString != null) {
for (String item : getRequestProcessors.keySet()) {
if (queryString.indexOf(item) == 0 &&
(queryString.equals(item) ||
queryString.indexOf("&") == item.length() ||
queryString.indexOf("=") == item.length())) {
if (axisService == null) {
continue;
}
try {
processWithGetProcessor(request, response, requestUri,
requestUrl, queryString,
item, outputStream, conn);
} catch (Exception e) {
handleBrowserException(response, conn, outputStream,
"Error processing request", e);
}
isRequestHandled = true;
break;
}
}
}
}
}
if (!isRequestHandled) {
processGetAndDelete(request, response, messageContext,
conn, outputStream, "GET", b);
}
}
public void handleException(String msg, Exception e) throws AxisFault {
log.error(msg, e);
throw new AxisFault(msg, e);
}
}