| /** |
| * 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; |
| } |
| |
| } |
| |
| |
| } |