/** * 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.hadoop.gateway.service.test; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.net.ssl.SSLContext; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import org.apache.hadoop.gateway.config.GatewayConfig; import org.apache.hadoop.gateway.services.GatewayServices; import org.apache.hadoop.gateway.services.topology.TopologyService; import org.apache.hadoop.gateway.topology.Service; import org.apache.hadoop.gateway.topology.Topology; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.glassfish.jersey.internal.util.Base64; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import static javax.ws.rs.core.MediaType.APPLICATION_XML; @Path( "/service-test" ) public class ServiceTestResource { @Context private HttpServletRequest request; @GET @Produces({APPLICATION_XML, APPLICATION_JSON}) public ServiceTestWrapper serviceTest(@QueryParam("username") String username, @QueryParam("password") String password) { List<ServiceTest> tests = new ArrayList<>(); List<String> messages = new ArrayList<>(); String authString; GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); SSLContext ctx = null; CloseableHttpClient client; String id = getTopologyName(); Topology topology = getTopology(id); // Create Authorization String if( username != null && password != null) { authString = "Basic " + Base64.encodeAsString((username + ":" + password).getBytes()); } else if (request.getHeader("Authorization") != null) { authString = request.getHeader("Authorization"); } else { authString = null; } // Attempt to build SSL context for HTTP client. try { ctx = SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(); } catch (Exception e) { messages.add(e.getMessage()); } // Initialize the HTTP client if(ctx == null) { client = HttpClients.createDefault(); } else { client = HttpClients.custom().setSslcontext(ctx).build(); } if(topology != null) { for (Service s : topology.getServices()) { List<String> urls = getServiceTestURLs(config, s.getRole(), topology); // Make sure we handle a case where no URLs are found. if(urls.size() <= 0) { ServiceTest test = new ServiceTest(s); test.setMessage("This service did not contain any test URLs"); } for (String url : urls) { HttpGet req = new HttpGet(); ServiceTest test = new ServiceTest(s, url); if(authString != null) { req.setHeader("Authorization", authString); } else { messages.add("No credentials provided. Expect HTTP 401 responses."); } try { req.setURI(new URIBuilder(url).build()); CloseableHttpResponse res = client.execute(req); String contentLength = "Content-Length:" + res.getEntity().getContentLength(); String contentType = (res.getEntity().getContentType() != null) ? res.getEntity().getContentType().toString() : "No-contenttype"; test.setResponseContent( contentLength + "," + contentType ); test.setHttpCode(res.getStatusLine().getStatusCode()); res.close(); } catch (IOException e) { messages.add("Exception: " + e.getMessage()); test.setMessage(e.getMessage()); } catch (URISyntaxException e) { test.setMessage(e.getMessage()); } catch (Exception e) { messages.add(e.getMessage()); test.setMessage(e.getMessage()); } finally { req.releaseConnection(); tests.add(test); } } } } else { messages.add("Topology " + id + " not found"); } try { client.close(); } catch (IOException e) { e.printStackTrace(); } ServiceTestWrapper stw = new ServiceTestWrapper(); stw.setTests(tests); stw.setMessages(messages); return stw; } private String getTopologyName() { String ctxPath = request.getContextPath(); GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); String path = config.getGatewayPath(); String topologyName = ctxPath.replace(path, "").replace("/", ""); return topologyName; } public Topology getTopology(@PathParam("id") String id) { GatewayServices services = (GatewayServices) request.getServletContext() .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); GatewayConfig config = (GatewayConfig) request.getServletContext().getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE); TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE); for (Topology t : ts.getTopologies()) { if(t.getName().equals(id)) { try { t.setUri(new URI( buildURI(t, config, request) )); } catch (URISyntaxException se) { t.setUri(null); } return t; } } return null; } private List<String> getServiceTestURLs(GatewayConfig conf, String role, Topology topology) { GatewayServices services = (GatewayServices) request.getServletContext() .getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE); List<String> fullURLs = new ArrayList<>(); if(services != null) { TopologyService ts = services.getService(GatewayServices.TOPOLOGY_SERVICE); Map<String, List<String>> urls = ts.getServiceTestURLs(topology, conf); List<String> urlPaths = urls.get(role); if(urlPaths != null) { String base = buildURI(topology, conf, request); for (String u : urlPaths) { fullURLs.add(base + u); } } } return fullURLs; } private String buildXForwardBaseURL(HttpServletRequest req){ final String X_Forwarded = "X-Forwarded-"; final String X_Forwarded_Context = X_Forwarded + "Context"; final String X_Forwarded_Proto = X_Forwarded + "Proto"; final String X_Forwarded_Host = X_Forwarded + "Host"; final String X_Forwarded_Port = X_Forwarded + "Port"; final String X_Forwarded_Server = X_Forwarded + "Server"; String baseURL = ""; // Get Protocol if(req.getHeader(X_Forwarded_Proto) != null){ baseURL += req.getHeader(X_Forwarded_Proto) + "://"; } else { baseURL += req.getProtocol() + "://"; } // Handle Server/Host and Port Here if (req.getHeader(X_Forwarded_Host) != null && req.getHeader(X_Forwarded_Port) != null){ // Double check to see if host has port if(req.getHeader(X_Forwarded_Host).contains(req.getHeader(X_Forwarded_Port))){ baseURL += req.getHeader(X_Forwarded_Host); } else { // If there's no port, add the host and port together; baseURL += req.getHeader(X_Forwarded_Host) + ":" + req.getHeader(X_Forwarded_Port); } } else if(req.getHeader(X_Forwarded_Server) != null && req.getHeader(X_Forwarded_Port) != null){ // Tack on the server and port if they're available. Try host if server not available baseURL += req.getHeader(X_Forwarded_Server) + ":" + req.getHeader(X_Forwarded_Port); } else if(req.getHeader(X_Forwarded_Port) != null) { // if we at least have a port, we can use it. baseURL += req.getServerName() + ":" + req.getHeader(X_Forwarded_Port); } else { // Resort to request members baseURL += req.getServerName() + ":" + req.getLocalPort(); } // Handle Server context if( req.getHeader(X_Forwarded_Context) != null ) { baseURL += req.getHeader( X_Forwarded_Context ); } else { baseURL += req.getContextPath(); } return baseURL; } String buildURI(Topology topology, GatewayConfig config, HttpServletRequest req){ String uri = buildXForwardBaseURL(req); // Strip extra context uri = uri.replace(req.getContextPath(), ""); // Add the gateway path String gatewayPath; if(config.getGatewayPath() != null){ gatewayPath = config.getGatewayPath(); }else{ gatewayPath = "gateway"; } uri += "/" + gatewayPath; uri += "/" + topology.getName(); return uri; } @XmlAccessorType(XmlAccessType.NONE) public static class ServiceTest { @XmlElement private String serviceName; @XmlElement private String requestURL; @XmlElement private String responseContent; @XmlElement private int httpCode = -1; @XmlElement String message; public ServiceTest() { } public ServiceTest(Service s) { this.serviceName = s.getRole(); } public ServiceTest( Service s, String requestURL) { this.serviceName = s.getRole(); this.requestURL = requestURL; } public String getServiceName() { return serviceName; } public void setServiceName(String n) { serviceName = n; } public String getRequestURL() { return requestURL; } public void setRequestURL(String requestURL) { this.requestURL = requestURL; } public String getResponseContent() { return responseContent; } public void setResponseContent(String responseContent) { this.responseContent = responseContent; } public int getHttpCode() { return httpCode; } public void setHttpCode(int httpCode) { this.httpCode = httpCode; setMessage(); } public void setMessage() { message = buildMessage(httpCode); } public void setMessage(String msg) { if(httpCode != -1) { message = buildMessage(httpCode); } else { message = msg; } } public String getMessage(){ return message; } static String buildMessage(int code) { String message = ""; switch (code) { case 200: message += "Request sucessful."; break; case 400: message += "Could not properly intepret HTTP request."; break; case 401: message += "User was not authorized. Try using credentials with access to all services. " + "Ensure LDAP server is running."; break; case 403: message += "Access to this resource is forbidden. It seems we might have made a bad request."; break; case 404: message += "The page could not be found. Are the URLs for the topology services correct?"; break; case 500: message += "The server encountered an error. Are all of the cluster's services running? \n" + "Can a connection be established without Knox?"; break; } return message; } } @XmlAccessorType(XmlAccessType.FIELD) @XmlRootElement public static class ServiceTestWrapper{ @XmlElement(name="ServiceTest") @XmlElementWrapper(name="Tests") private List<ServiceTest> tests = new ArrayList<ServiceTest>(); @XmlElement(name="message") @XmlElementWrapper(name="messages") private List<String> messages = new ArrayList<>(); public List<ServiceTest> getTests(){ return tests; } public void setTests(List<ServiceTest> st){ this.tests = st; } public List<String> getMessages() { return messages; } public void setMessages(List<String> messages){ this.messages = messages; } } }