/** * 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.ambari.server.proxy; import static javax.ws.rs.core.MediaType.APPLICATION_JSON; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.apache.ambari.server.controller.internal.URLStreamProvider; import org.apache.ambari.server.view.ImpersonatorSettingImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gson.Gson; @Path("/") public class ProxyService { public static final int URL_CONNECT_TIMEOUT = 20000; public static final int URL_READ_TIMEOUT = 15000; public static final int HTTP_ERROR_RANGE_START = Response.Status.BAD_REQUEST.getStatusCode(); private static final String REQUEST_TYPE_GET = "GET"; private static final String REQUEST_TYPE_POST = "POST"; private static final String REQUEST_TYPE_PUT = "PUT"; private static final String REQUEST_TYPE_DELETE = "DELETE"; private static final String QUERY_PARAMETER_URL = "url="; private static final String AMBARI_PROXY_PREFIX = "AmbariProxy-"; private static final String ERROR_PROCESSING_URL = "Error occurred during processing URL "; private static final String INVALID_PARAM_IN_URL = "Invalid query params found in URL "; private final static Logger LOG = LoggerFactory.getLogger(ProxyService.class); @GET public Response processGetRequestForwarding(@Context HttpHeaders headers, @Context UriInfo ui) { return handleRequest(REQUEST_TYPE_GET, ui, null, headers); } @POST @Consumes({MediaType.WILDCARD, MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON}) public Response processPostRequestForwarding(InputStream body, @Context HttpHeaders headers, @Context UriInfo ui) { return handleRequest(REQUEST_TYPE_POST, ui, body, headers); } @PUT @Consumes({MediaType.WILDCARD, MediaType.TEXT_PLAIN, MediaType.TEXT_XML, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON}) public Response processPutRequestForwarding(InputStream body, @Context HttpHeaders headers, @Context UriInfo ui) { return handleRequest(REQUEST_TYPE_PUT, ui, body, headers); } @DELETE public Response processDeleteRequestForwarding(@Context HttpHeaders headers, @Context UriInfo ui) { return handleRequest(REQUEST_TYPE_DELETE, ui, null, headers); } private Response handleRequest(String requestType, UriInfo ui, InputStream body, HttpHeaders headers) { URLStreamProvider urlStreamProvider = new URLStreamProvider(URL_CONNECT_TIMEOUT, URL_READ_TIMEOUT, null, null, null); String query = ui.getRequestUri().getQuery(); if (query != null && query.indexOf(QUERY_PARAMETER_URL) != -1) { String url = query.replaceFirst(QUERY_PARAMETER_URL, ""); MultivaluedMap<String, String> m = ui.getQueryParameters(); if (m.containsKey(ImpersonatorSettingImpl.DEFAULT_DO_AS_PARAM)) { // Case doesn't matter LOG.error(INVALID_PARAM_IN_URL + url); return Response.status(Response.Status.BAD_REQUEST.getStatusCode()).type(MediaType.TEXT_PLAIN). entity(INVALID_PARAM_IN_URL).build(); } try { HttpURLConnection connection = urlStreamProvider.processURL(url, requestType, body, getHeaderParamsToForward(headers)); int responseCode = connection.getResponseCode(); InputStream resultInputStream = null; if (responseCode >= HTTP_ERROR_RANGE_START) { resultInputStream = connection.getErrorStream(); } else { resultInputStream = connection.getInputStream(); } String contentType = connection.getContentType(); Response.ResponseBuilder rb = Response.status(responseCode); if (contentType.indexOf(APPLICATION_JSON) != -1) { rb.entity(new Gson().fromJson(new InputStreamReader(resultInputStream), Map.class)); } else { rb.entity(resultInputStream); } return rb.type(contentType).build(); } catch (IOException e) { LOG.error(ERROR_PROCESSING_URL + url, e); return Response.status(Response.Status.BAD_REQUEST.getStatusCode()).type(MediaType.TEXT_PLAIN). entity(e.getMessage()).build(); } } return null; } private Map<String, List<String>> getHeaderParamsToForward(HttpHeaders headers) { Map<String, List<String>> headerParamsToForward = new HashMap<>(); for (String paramName: headers.getRequestHeaders().keySet()) { if (paramName.startsWith(AMBARI_PROXY_PREFIX)) { headerParamsToForward.put(paramName.replaceAll(AMBARI_PROXY_PREFIX,""), headers.getRequestHeader(paramName)); } } return headerParamsToForward; } }