<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the LICENSE 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 com.sap.cloudlabs.connectivity.proxy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.DeflateDecompressingEntity;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.core.connectivity.api.DestinationException;
import com.sap.core.connectivity.api.DestinationFactory;
import com.sap.core.connectivity.api.http.HttpDestination;

/**
 * This servlet is used as connectivity proxy between a consuming agent, like a Web browser
 * application, and a backend service and can be seen as an add-on of the SAP HANA Cloud 
 * connectivity service. The backend service can either be an on-premise application  
 * which is accessed via SAP HANA Cloud connector, or an Internet-accessible service. For 
 * both cases, the servlet tries to access the remote service via a configured destination. 
 * The name of the destination has to be passed by the calling client of this servlet 
 * as part of the URL, following this pattern: 
 * <pre>
 *   /<context-path>/<servlet-path>/<destination>/<relative-path-to-backend-service>
 * </pre> 
 * <p>
 * Main purpose of the proxy servlet is to assure the same-origin-policy (SOP)
 * for JavaScript applications running in Web browsers.
 * 
 * @version 0.1
 */
public class ProxyServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/** headers which will be blocked from forwarding in backend request */
	private static String[] BLOCKED_REQUEST_HEADERS = { "host", "content-length" };
		
	/** buffer size for piping the content */
	private static final int IO_BUFFER_SIZE = 4 * 1024;

	private static final Logger LOGGER = LoggerFactory.getLogger(ProxyServlet.class);
		
	private static DestinationFactory destinationFactory;
	
	/*
	 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
	 */
	public void init(ServletConfig servletConfig) throws ServletException {
		super.init(servletConfig);
	}

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
			IOException {

		LOGGER.debug(">>>>>>>>>>>> start request");
		
		int contextPathLength = request.getContextPath().length();
		int servletPathLength = request.getServletPath().length();
		
		// read destination and relative service path from URL
		if (request.getRequestURI().indexOf(request.getContextPath() + request.getServletPath() + "/") == -1) {
			throw new ServletException(writeMessage("no destination specified"));
		}
		
		String pathInfo = request.getRequestURI().substring(
				contextPathLength + servletPathLength + 1);
		String queryString = request.getQueryString();
		String destinationName = getDestinationFromUrl(pathInfo);
		String urlToService = getRelativePathFromUrl(pathInfo, queryString);
	
		// get the http client for the destination
		HttpDestination dest = getDestination(destinationName);	
		HttpClient httpClient;
		try {
			httpClient = dest.createHttpClient();
		} catch (DestinationException e) {
			throw new ServletException(e);
		}
		
		// create request to targeted backend service
		HttpRequestBase backendRequest = getBackendRequest(request, urlToService);

		// execute the backend request
		HttpResponse backendResponse = httpClient.execute(backendRequest);

		String rewriteUrl = getDestinationUrl(dest);  
		String proxyUrl = getProxyUrl(request, destinationName);
					
		// process response from backend request and pipe it to origin response of client
		processBackendResponse(request, response, backendResponse, proxyUrl, rewriteUrl);
		
		LOGGER.debug(">>>>>>>>>>>> end request");
	}

	
	/**
	 * Returns the URL specified in the given destination. 
	 */
	private String getDestinationUrl(HttpDestination destination) throws ServletException {
		try {
			String rewriteUrl = destination.getURI().toString();
			if (rewriteUrl.endsWith("/")) {
				rewriteUrl = rewriteUrl.substring(0, rewriteUrl.length()-1);
			}
			return rewriteUrl; 
		} catch (URISyntaxException e) {
			throw new ServletException(e);
		}
	}

	/**
	 * Returns the URL to the proxy servlet and used destination. <commit_msg>Bug fix: connection was not closed; make sure connection to backend is closed at end of request<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the LICENSE 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 com.sap.cloudlabs.connectivity.proxy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.DeflateDecompressingEntity;
import org.apache.http.client.entity.GzipDecompressingEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sap.core.connectivity.api.DestinationException;
import com.sap.core.connectivity.api.DestinationFactory;
import com.sap.core.connectivity.api.http.HttpDestination;

/**
 * This servlet is used as connectivity proxy between a consuming agent, like a Web browser
 * application, and a backend service and can be seen as an add-on of the SAP HANA Cloud 
 * connectivity service. The backend service can either be an on-premise application  
 * which is accessed via SAP HANA Cloud connector, or an Internet-accessible service. For 
 * both cases, the servlet tries to access the remote service via a configured destination. 
 * The name of the destination has to be passed by the calling client of this servlet 
 * as part of the URL, following this pattern: 
 * <pre>
 *   /<context-path>/<servlet-path>/<destination>/<relative-path-to-backend-service>
 * </pre> 
 * <p>
 * Main purpose of the proxy servlet is to assure the same-origin-policy (SOP)
 * for JavaScript applications running in Web browsers.
 * 
 * @version 0.1
 */
public class ProxyServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/** headers which will be blocked from forwarding in backend request */
	private static String[] BLOCKED_REQUEST_HEADERS = { "host", "content-length" };
		
	/** buffer size for piping the content */
	private static final int IO_BUFFER_SIZE = 4 * 1024;

	private static final Logger LOGGER = LoggerFactory.getLogger(ProxyServlet.class);
		
	private static DestinationFactory destinationFactory;
	
	/*
	 * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
	 */
	public void init(ServletConfig servletConfig) throws ServletException {
		super.init(servletConfig);
	}

	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
			IOException {

		LOGGER.debug(">>>>>>>>>>>> start request");
		
		int contextPathLength = request.getContextPath().length();
		int servletPathLength = request.getServletPath().length();
		
		// read destination and relative service path from URL
		if (request.getRequestURI().indexOf(request.getContextPath() + request.getServletPath() + "/") == -1) {
			throw new ServletException(writeMessage("no destination specified"));
		}
		
		String pathInfo = request.getRequestURI().substring(
				contextPathLength + servletPathLength + 1);
		String queryString = request.getQueryString();
		String destinationName = getDestinationFromUrl(pathInfo);
		String urlToService = getRelativePathFromUrl(pathInfo, queryString);
	
		// get the http client for the destination
		HttpDestination dest = getDestination(destinationName);	
		HttpClient httpClient = null;
		try {
			httpClient = dest.createHttpClient();
		
			// create request to targeted backend service
			HttpRequestBase backendRequest = getBackendRequest(request, urlToService);
	
			// execute the backend request
			HttpResponse backendResponse = httpClient.execute(backendRequest);
	
			String rewriteUrl = getDestinationUrl(dest);  
			String proxyUrl = getProxyUrl(request, destinationName);
						
			// process response from backend request and pipe it to origin response of client
			processBackendResponse(request, response, backendResponse, proxyUrl, rewriteUrl);
		} catch (DestinationException e) {
			throw new ServletException(e);
		} finally {
			if (httpClient != null) {
				httpClient.getConnectionManager().shutdown();
			}
			
			LOGGER.debug(">>>>>>>>>>>> end request");			
		}		
	}

	
	/**
	 * Returns the URL specified in the given destination. 
	 */
	private String getDestinationUrl(HttpDestination destination) throws ServletException {
		try {
			String rewriteUrl = destination.getURI().toString();
			if (rewriteUrl.endsWith("/")) {
				rewriteUrl = rewriteUrl.substring(0, rewriteUrl.length()-1);
			}
			return rewriteUrl; 
		} catch (URISyntaxException e) {
			throw new ServletException(e);
		}
	}

	/**
	 * Returns the URL to the proxy servlet and used destination. 
{'fraction_non_alphanumeric': 0.09093123325485102, 'fraction_numerical': 0.002922789640334497, 'mean_word_length': 4.658245291685806, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5034690', 'n_tokens_mistral': 3517, 'n_tokens_neox': 3283, 'n_words': 1072}