All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.documentdb.internal.GatewayProxy Maven / Gradle / Ivy

/*
 * Copyright (c) Microsoft Corporation.  All rights reserved.
 */

package com.microsoft.azure.documentdb.internal;

import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
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.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.microsoft.azure.documentdb.ConnectionPolicy;
import com.microsoft.azure.documentdb.ConsistencyLevel;
import com.microsoft.azure.documentdb.DocumentClientException;

/**
 * Used internally to provide functionality to communicate and process response from Gateway in the Azure Cosmos DB database service.
 */
public class GatewayProxy implements StoreModel {

    private final Logger logger = LoggerFactory.getLogger(GatewayProxy.class);
    private Map defaultHeaders;
    private ConnectionPolicy connectionPolicy;
    private HttpClient httpClient;
    private HttpClient mediaHttpClient;
    private QueryCompatibilityMode queryCompatibilityMode;
    private EndpointManager globalEndpointManager;
    private SessionContainer sessionContainer;

    public GatewayProxy(ConnectionPolicy connectionPolicy,
                        ConsistencyLevel consistencyLevel,
                        QueryCompatibilityMode queryCompatibilityMode,
                        String masterKey,
                        Map resourceTokens,
                        UserAgentContainer userAgentContainer,
                        EndpointManager globalEndpointManager,
                        HttpClient httpClient,
                        HttpClient mediaHttpClient,
                        SessionContainer sessionContainer) {
        this.defaultHeaders = new HashMap();
        this.defaultHeaders.put(HttpConstants.HttpHeaders.CACHE_CONTROL,
                                "no-cache");
        this.defaultHeaders.put(HttpConstants.HttpHeaders.VERSION,
                                HttpConstants.Versions.CURRENT_VERSION);

        if (userAgentContainer == null) {
            userAgentContainer = new UserAgentContainer();
        }

        this.defaultHeaders.put(HttpConstants.HttpHeaders.USER_AGENT, userAgentContainer.getUserAgent());

        if (consistencyLevel != null) {
            this.defaultHeaders.put(HttpConstants.HttpHeaders.CONSISTENCY_LEVEL,
                                    consistencyLevel.toString());
        }

        this.connectionPolicy = connectionPolicy;        
        this.globalEndpointManager = globalEndpointManager;
        this.queryCompatibilityMode = queryCompatibilityMode;

        this.httpClient = httpClient;
        this.mediaHttpClient = mediaHttpClient;
        this.sessionContainer = sessionContainer;
    }

    public DocumentServiceResponse doCreate(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performPostRequest(request);
    }
    
    public DocumentServiceResponse doUpsert(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performPostRequest(request);
    }

    public DocumentServiceResponse doRead(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performGetRequest(request);
    }
    
    public DocumentServiceResponse doReplace(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performPutRequest(request);
    }

    public DocumentServiceResponse doDelete(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performDeleteRequest(request);
    }

    public DocumentServiceResponse doExecute(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performPostRequest(request);
    }

    public DocumentServiceResponse doReadFeed(DocumentServiceRequest request)
        throws DocumentClientException {
        return this.performGetRequest(request);
    }

    public DocumentServiceResponse doQuery(DocumentServiceRequest request)
        throws DocumentClientException {
        request.getHeaders().put(HttpConstants.HttpHeaders.IS_QUERY, "true");

        switch (this.queryCompatibilityMode) {
            case SqlQuery:
                request.getHeaders().put(HttpConstants.HttpHeaders.CONTENT_TYPE,
                                         RuntimeConstants.MediaTypes.SQL);
                break;
            case Default:
            case Query:
            default:
                request.getHeaders().put(HttpConstants.HttpHeaders.CONTENT_TYPE,
                                         RuntimeConstants.MediaTypes.QUERY_JSON);
                break;
        }

        return this.performPostRequest(request);
    }

    private HttpClient getHttpClient(boolean isForMedia) {
        if (isForMedia) {
            return this.mediaHttpClient;
        } else {
            return this.httpClient;
        }
    }

    private void fillHttpRequestBaseWithHeaders(Map headers, HttpRequestBase httpBase) {
        // Add default headers.
        for (Map.Entry entry : this.defaultHeaders.entrySet()) {
            httpBase.setHeader(entry.getKey(), entry.getValue());
        }
        // Add override headers.
        if (headers != null) {
            for (Map.Entry entry : headers.entrySet()) {
                httpBase.setHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    private DocumentServiceResponse performDeleteRequest(DocumentServiceRequest request) 
            throws DocumentClientException {
        URI rootUri = request.getEndpointOverride();
        if (rootUri == null) {
            rootUri = this.globalEndpointManager.resolveServiceEndpoint(request);
        }
        
        URI uri;
        try {
            uri = new URI("https",
                          null,
                          rootUri.getHost(),
                          rootUri.getPort(),
                          request.getPath(),
                          null,  // Query string not used.
                          null);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Incorrect uri from request.", e);
        }
        
        HttpDelete httpDelete = new HttpDelete(uri);
        this.fillHttpRequestBaseWithHeaders(request.getHeaders(), httpDelete);
        this.addProxy(httpDelete);
        HttpResponse response = null;
        try {
            logger.trace("Sending DELETE request to {}", uri);
            response = this.getHttpClient(request.getIsMedia()).execute(httpDelete);
            ErrorUtils.maybeThrowException(uri.getPath(), response, true, this.logger);
        } catch (HttpHostConnectException e) {
            throw new IllegalStateException("Http client execution failed.", e);
        } catch (IOException e) {
            throw new DocumentClientException(HttpConstants.StatusCodes.FORBIDDEN, e);
        } catch (DocumentClientException e) {
            throw e;
        } catch (Exception e) {
            throw e;
        } finally {
            httpDelete.releaseConnection();
        }

        return new DocumentServiceResponse(response, request.getIsMedia(), request.getClientSideRequestStatistics());
    }

    private void addProxy(HttpRequestBase httpRequest) {
        if (this.connectionPolicy.getProxy() != null) {
            RequestConfig config = RequestConfig.custom().setProxy(this.connectionPolicy.getProxy()).build();
            httpRequest.setConfig(config);
        }
    }

    DocumentServiceResponse performGetRequest(DocumentServiceRequest request)
            throws DocumentClientException {
        URI rootUri = request.getEndpointOverride();
        if (rootUri == null) {
            if (request.getIsMedia()) {
                // For media read request, always use the write endpoint.
                rootUri = this.globalEndpointManager.getWriteEndpoint();
            } else {
                rootUri = this.globalEndpointManager.resolveServiceEndpoint(request);
            }
        }
        
        URI uri;
        try {
            uri = new URI("https",
                          null,
                          rootUri.getHost(),
                          rootUri.getPort(),
                          request.getPath(),
                          null,  // Query string not used.
                          null);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Incorrect uri from request.", e);
        }
        
        HttpGet httpGet = new HttpGet(uri);
        this.fillHttpRequestBaseWithHeaders(request.getHeaders(), httpGet);
        this.addProxy(httpGet);
        HttpResponse response = null;
        try {
            logger.trace("Sending GET request to {}", uri);
            response = this.getHttpClient(request.getIsMedia()).execute(httpGet);
            ErrorUtils.maybeThrowException(uri.getPath(), response, true, this.logger);
            return new DocumentServiceResponse(response, request.getIsMedia(), request.getClientSideRequestStatistics());
        } catch (ConnectTimeoutException|SocketTimeoutException e) {
            throw new DocumentClientException(HttpConstants.StatusCodes.TIMEOUT, e);
        } catch (HttpHostConnectException e) {
            throw new IllegalStateException("Http client execution failed.", e);
        } catch (IOException e) {
            throw new DocumentClientException(HttpConstants.StatusCodes.FORBIDDEN, e);
        } catch (DocumentClientException e) {
            throw e;
        } catch (Exception e) {
            throw e;
        } finally {
            httpGet.releaseConnection();
        }
    }

    DocumentServiceResponse performPostRequest(DocumentServiceRequest request)
            throws DocumentClientException {
        URI rootUri = request.getEndpointOverride();
        if (rootUri == null) {
            rootUri = this.globalEndpointManager.resolveServiceEndpoint(request);
        }
        
        URI uri;
        try {
            uri = new URI("https",
                          null,
                          rootUri.getHost(),
                          rootUri.getPort(),
                          request.getPath(),
                          null,  // Query string not used.
                          null);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Incorrect uri from request.", e);
        }

        HttpPost httpPost = new HttpPost(uri);
        this.fillHttpRequestBaseWithHeaders(request.getHeaders(), httpPost);
        httpPost.setEntity(request.getBody());
        this.addProxy(httpPost);
        HttpResponse response = null;
        try {
            logger.trace("Sending POST request to {}", uri);
            response = this.getHttpClient(request.getIsMedia()).execute(httpPost);
            ErrorUtils.maybeThrowException(uri.getPath(), response, true, this.logger);
            return new DocumentServiceResponse(response, request.getIsMedia(), request.getClientSideRequestStatistics());
        } catch (ConnectTimeoutException|SocketTimeoutException e) {
            throw new DocumentClientException(HttpConstants.StatusCodes.TIMEOUT, e);
        } catch (HttpHostConnectException e) {
            throw new IllegalStateException("Http client execution failed.", e);
        } catch (IOException e) {
            throw new DocumentClientException(HttpConstants.StatusCodes.FORBIDDEN, e);
        } catch (DocumentClientException e) {
            throw e;
        } catch (Exception e) {
            throw e;
        } finally {
            httpPost.releaseConnection();
        }
    }

    DocumentServiceResponse performPutRequest(DocumentServiceRequest request) 
            throws DocumentClientException {
        
        URI rootUri = request.getEndpointOverride();
        if (rootUri == null) {
            rootUri = this.globalEndpointManager.resolveServiceEndpoint(request);
        }
        
        URI uri;
        try {
            uri = new URI("https",
                          null,
                          rootUri.getHost(),
                          rootUri.getPort(),
                          request.getPath(),
                          null,  // Query string not used.
                          null);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Incorrect uri from request.", e);
        }
        
        HttpPut httpPut = new HttpPut(uri);
        this.fillHttpRequestBaseWithHeaders(request.getHeaders(), httpPut);
        httpPut.setEntity(request.getBody());
        this.addProxy(httpPut);
        HttpResponse response = null;
        try {
            logger.trace("Sending PUT request to {}", uri);
            response = this.getHttpClient(request.getIsMedia()).execute(httpPut);
            ErrorUtils.maybeThrowException(uri.getPath(), response, true, this.logger);
            return new DocumentServiceResponse(response, request.getIsMedia(), request.getClientSideRequestStatistics());
        } catch (HttpHostConnectException e) {
            throw new IllegalStateException("Http client execution failed.", e);
        } catch (IOException e) {
            throw new DocumentClientException(HttpConstants.StatusCodes.FORBIDDEN, e);
        } catch (DocumentClientException e) {
            throw e;
        } catch (Exception e) {
            throw e;
        } finally {
            httpPut.releaseConnection();
        }
    }

    @Override
    public DocumentServiceResponse processMessage(DocumentServiceRequest request) throws DocumentClientException {
        DocumentServiceResponse response;
        try {
            switch (request.getOperationType()) {
            case Create:
                response = this.doCreate(request);
                break;
            case Upsert:
                response = this.doUpsert(request);
                break;
            case Delete:
                response = this.doDelete(request);
                break;
            case ExecuteJavaScript:
                response = this.doExecute(request);
                break;
            case Read:
                response = this.doRead(request);
                break;
            case ReadFeed:
                response = this.doReadFeed(request);
                break;
            case Replace:
                response = this.doReplace(request);
                break;
            case SqlQuery:
            case Query:
                response = this.doQuery(request);
                break;
            default:
                throw new IllegalStateException("Unknown operation type " + request.getOperationType());
            }
        } catch (DocumentClientException dce) {
            if (request.getClientSideRequestStatistics() != null) {
                dce.setClientSideRequestStatistics(request.getClientSideRequestStatistics());
            }
            SessionTokenHelper.updateSession(this.sessionContainer, request, dce);
            throw dce;
        }

        return response;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy