<commit_before>package com.atlassian.jira.plugins.dvcs.spi.bitbucket.clientlibrary.request;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.atlassian.jira.plugins.dvcs.spi.bitbucket.clientlibrary.client.ClientUtils;

/**
 * BaseRemoteRequestor
 * 
 * 
 * <br />
 * <br />
 * Created on 13.7.2012, 10:25:24 <br />
 * <br />
 * 
 * @author <EMAIL>
 * 
 */
public class BaseRemoteRequestor implements RemoteRequestor
{
    private static final short HTTP_STATUS_CODE_UNAUTHORIZED = 401;
    private static final short HTTP_STATUS_CODE_NOT_FOUND    = 404;
    

	protected final String apiUrl;

	public BaseRemoteRequestor(String apiUrl)
	{
		super();
		this.apiUrl = apiUrl;
	}

	@Override
	public RemoteResponse get(String uri, Map<String, String> parameters)
	{
		return requestWithoutPayload(HttpMethod.GET, uri, parameters);
	}
	
	@Override
	public RemoteResponse delete(String uri)
	{
		return requestWithoutPayload(HttpMethod.DELETE, uri, new HashMap<String, String>());
	}

	@Override
	public RemoteResponse post(String uri, Map<String, String> parameters)
	{
		return requestWithPayload(HttpMethod.POST, uri, parameters, false);
	}
	
	@Override
	public RemoteResponse post(String uri, Object payload)
	{
		return requestWithPayload(HttpMethod.POST, uri, payload, true);
	}

	@Override
	public RemoteResponse put(String uri, Map<String, String> parameters)
	{
		return requestWithPayload(HttpMethod.PUT, uri, parameters, false);
	}
	
	@Override
	public RemoteResponse put(String uri, Object payload)
	{
		return requestWithPayload(HttpMethod.PUT, uri, payload, true);
	}
	
	//--------------------------------------------------------------------------------------------------
	// extension hooks
	//--------------------------------------------------------------------------------------------------
	/**
	 * E.g. append basic auth headers ... 
	 */
	protected void onConnectionCreated(HttpURLConnection connection, HttpMethod method) throws IOException
	{

	}
	
	/**
	 * E.g. append oauth params ...
	 */
	protected String afterFinalUriConstructed(HttpMethod forMethod, String finalUri)
	{
		return finalUri;
	}

	//--------------------------------------------------------------------------------------------------
	// Helpers
	//--------------------------------------------------------------------------------------------------
	
	private RemoteResponse requestWithPayload(HttpMethod postOrPut, String uri, Object params, boolean isJson)
	{
		HttpURLConnection connection = null;
		try
		{
			connection = createConnection(postOrPut, uri);
			if (isJson) {
				setPayloadOrParams(connection, params, true);
			} else {
				setPayloadOrParams(connection, params, false);
			}
			return checkAndCreateRemoteResponse(connection);
			
		} catch (BitbucketRequestException e)
        {
            throw e; // Unauthorized or NotFound exceptions will be rethrown
        }catch (Exception e)
		{
			// TODO log + message
			throw new BitbucketRequestException("Failed to execute request " + connection, e);
		}
	}

	private RemoteResponse requestWithoutPayload(HttpMethod getOrDelete, String uri, Map<String, String> parameters)
	{
		HttpURLConnection connection = null;
		try
		{
			connection = createConnection(getOrDelete, uri + paramsToString(parameters, uri.contains("?")));
			return checkAndCreateRemoteResponse(connection);

		} catch (Exception e)
		{
			// TODO log + message
			throw new BitbucketRequestException("Failed to execute request " + connection, e);
		}
	}
	
	private RemoteResponse checkAndCreateRemoteResponse(HttpURLConnection connection) throws IOException
	{
		RemoteResponse response = new RemoteResponse();
		
        if (connection.getResponseCode() >= 400)
        {
            switch (connection.getResponseCode())
            {
                case HTTP_STATUS_CODE_UNAUTHORIZED:
                    throw new BitbucketRequestException.Unauthorized();

                case HTTP_STATUS_CODE_NOT_FOUND:
                    throw new BitbucketRequestException.NotFound();

                default:
                    throw new BitbucketRequestException("Error response code during the request : " + connection.getResponseCode());
            }
        }
		
		response.setHttpStatusCode(connection.getResponseCode());
		response.setResponse(connection.getInputStream());
		return response;
	}

	protected String paramsToString(Map<String, String> parameters, boolean urlAlreadyHasParams)
	{
		StringBuilder queryStringBuilder = new StringBuilder();

		if (parameters != null && !parameters.isEmpty())
		{
			if (!urlAlreadyHasParams)
			{
				queryStringBuilder.append("?");
			} else {
				queryStringBuilder.append("&");
			}
			for (Iterator<Map.Entry<String, String>> iterator = parameters.entrySet().iterator(); iterator.hasNext();)
			{
				Map.Entry<String, String> entry = iterator.next();
				queryStringBuilder.append(encode(entry.getKey()));
				queryStringBuilder.append("=");
				queryStringBuilder.append(encode(entry.getValue()));
				if (iterator.hasNext())<commit_msg>BBC-217 - BaseRemoteRequestor now handles HTTP code 403 correctly<commit_after>package com.atlassian.jira.plugins.dvcs.spi.bitbucket.clientlibrary.request;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.atlassian.jira.plugins.dvcs.spi.bitbucket.clientlibrary.client.ClientUtils;

/**
 * BaseRemoteRequestor
 * 
 * 
 * <br />
 * <br />
 * Created on 13.7.2012, 10:25:24 <br />
 * <br />
 * 
 * @author <EMAIL>
 * 
 */
public class BaseRemoteRequestor implements RemoteRequestor
{
    private static final short HTTP_STATUS_CODE_UNAUTHORIZED = 401;
    private static final short HTTP_STATUS_CODE_FORBIDDEN    = 403;
    private static final short HTTP_STATUS_CODE_NOT_FOUND    = 404;
    

	protected final String apiUrl;

	public BaseRemoteRequestor(String apiUrl)
	{
		super();
		this.apiUrl = apiUrl;
	}

	@Override
	public RemoteResponse get(String uri, Map<String, String> parameters)
	{
		return requestWithoutPayload(HttpMethod.GET, uri, parameters);
	}
	
	@Override
	public RemoteResponse delete(String uri)
	{
		return requestWithoutPayload(HttpMethod.DELETE, uri, new HashMap<String, String>());
	}

	@Override
	public RemoteResponse post(String uri, Map<String, String> parameters)
	{
		return requestWithPayload(HttpMethod.POST, uri, parameters, false);
	}
	
	@Override
	public RemoteResponse post(String uri, Object payload)
	{
		return requestWithPayload(HttpMethod.POST, uri, payload, true);
	}

	@Override
	public RemoteResponse put(String uri, Map<String, String> parameters)
	{
		return requestWithPayload(HttpMethod.PUT, uri, parameters, false);
	}
	
	@Override
	public RemoteResponse put(String uri, Object payload)
	{
		return requestWithPayload(HttpMethod.PUT, uri, payload, true);
	}
	
	//--------------------------------------------------------------------------------------------------
	// extension hooks
	//--------------------------------------------------------------------------------------------------
	/**
	 * E.g. append basic auth headers ... 
	 */
	protected void onConnectionCreated(HttpURLConnection connection, HttpMethod method) throws IOException
	{

	}
	
	/**
	 * E.g. append oauth params ...
	 */
	protected String afterFinalUriConstructed(HttpMethod forMethod, String finalUri)
	{
		return finalUri;
	}

	//--------------------------------------------------------------------------------------------------
	// Helpers
	//--------------------------------------------------------------------------------------------------
	
	private RemoteResponse requestWithPayload(HttpMethod postOrPut, String uri, Object params, boolean isJson)
	{
		HttpURLConnection connection = null;
		try
		{
			connection = createConnection(postOrPut, uri);
			if (isJson) {
				setPayloadOrParams(connection, params, true);
			} else {
				setPayloadOrParams(connection, params, false);
			}
			return checkAndCreateRemoteResponse(connection);
			
		} catch (BitbucketRequestException e)
        {
            throw e; // Unauthorized or NotFound exceptions will be rethrown
        }catch (Exception e)
		{
			// TODO log + message
			throw new BitbucketRequestException("Failed to execute request " + connection, e);
		}
	}

	private RemoteResponse requestWithoutPayload(HttpMethod getOrDelete, String uri, Map<String, String> parameters)
	{
		HttpURLConnection connection = null;
		try
		{
			connection = createConnection(getOrDelete, uri + paramsToString(parameters, uri.contains("?")));
			return checkAndCreateRemoteResponse(connection);

		} catch (Exception e)
		{
			// TODO log + message
			throw new BitbucketRequestException("Failed to execute request " + connection, e);
		}
	}
	
	private RemoteResponse checkAndCreateRemoteResponse(HttpURLConnection connection) throws IOException
	{
		RemoteResponse response = new RemoteResponse();
		
        if (connection.getResponseCode() >= 400)
        {
            switch (connection.getResponseCode())
            {
                case HTTP_STATUS_CODE_UNAUTHORIZED:
                    throw new BitbucketRequestException.Unauthorized_401();

                case HTTP_STATUS_CODE_FORBIDDEN:
                    throw new BitbucketRequestException.Forbidden_403();
                    
                case HTTP_STATUS_CODE_NOT_FOUND:
                    throw new BitbucketRequestException.NotFound_404();

                default:
                    throw new BitbucketRequestException("Error response code during the request : " + connection.getResponseCode());
            }
        }
		
		response.setHttpStatusCode(connection.getResponseCode());
		response.setResponse(connection.getInputStream());
		return response;
	}

	protected String paramsToString(Map<String, String> parameters, boolean urlAlreadyHasParams)
	{
		StringBuilder queryStringBuilder = new StringBuilder();

		if (parameters != null && !parameters.isEmpty())
		{
			if (!urlAlreadyHasParams)
			{
				queryStringBuilder.append("?");
			} else {
				queryStringBuilder.append("&");
			}
			for (Iterator<Map.Entry<String, String>> iterator = parameters.entrySet().iterator(); iterator.hasNext();)
			{
				Map.Entry<String, String> entry = iterator.next();
				queryStringBuilder.append(encode(entry.getKey()));
				queryStringBuilder.append("=");
				queryStringBuilder.append(encode(entry.getValue()));
				if (iterator.hasNext())
{'fraction_non_alphanumeric': 0.16618510578805107, 'fraction_numerical': 0.005778730543387082, 'mean_word_length': 2.728283530229326, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '7065733', 'n_tokens_mistral': 3086, 'n_tokens_neox': 2760, 'n_words': 659}