Search in sources :

Example 1 with ISignalWriteStream

use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.

the class ApiRequestExecutorImpl method parsePayload.

/**
 * Parse the inbound request's body into a payload object.  The object that is
 * produced will depend on the type and content-type of the API.  Options
 * include, but may not be limited to:
 * <ul>
 *   <li>REST+json</li>
 *   <li>REST+xml</li>
 *   <li>SOAP+xml</li>
 * </ul>
 * @param payloadResultHandler
 */
protected void parsePayload(IAsyncResultHandler<Object> payloadResultHandler) {
    // Strip out any content-length header from the request.  It will very likely
    // no longer be accurate.
    // $NON-NLS-1$
    request.getHeaders().remove("Content-Length");
    // Configure the api's max payload buffer size, if it's not already set.
    if (api.getMaxPayloadBufferSize() <= 0) {
        api.setMaxPayloadBufferSize(maxPayloadBufferSize);
    }
    // Now "handle" the inbound request stream, which will cause bytes to be streamed
    // to the writeStream we provide (which will store the bytes in a buffer for parsing)
    final ByteBuffer buffer = new ByteBuffer(2048);
    inboundStreamHandler.handle(new ISignalWriteStream() {

        private boolean done = false;

        @Override
        public void abort(Throwable t) {
            done = true;
            // $NON-NLS-1$
            payloadResultHandler.handle(AsyncResultImpl.create(new RuntimeException("Inbound request stream aborted.", t)));
        }

        @Override
        public boolean isFinished() {
            return done;
        }

        @Override
        public void write(IApimanBuffer chunk) {
            if (done) {
                return;
            }
            if (buffer.length() > api.getMaxPayloadBufferSize()) {
                // $NON-NLS-1$
                payloadResultHandler.handle(AsyncResultImpl.create(new Exception("Max request payload size exceeded.")));
                done = true;
                return;
            }
            buffer.append(chunk);
        }

        @Override
        public void end() {
            if (done) {
                return;
            }
            // When end() is called, the stream of bytes is done and we can parse them into
            // an appropriate payload object.
            done = true;
            if (buffer.length() == 0) {
                payloadResultHandler.handle(AsyncResultImpl.create(null));
            } else {
                payloadIO = null;
                if ("soap".equalsIgnoreCase(api.getEndpointType())) {
                    // $NON-NLS-1$
                    payloadIO = new SoapPayloadIO();
                } else if ("rest".equalsIgnoreCase(api.getEndpointType())) {
                    // $NON-NLS-1$
                    if ("xml".equalsIgnoreCase(api.getEndpointContentType())) {
                        // $NON-NLS-1$
                        payloadIO = new XmlPayloadIO();
                    } else if ("json".equalsIgnoreCase(api.getEndpointContentType())) {
                        // $NON-NLS-1$
                        payloadIO = new JsonPayloadIO();
                    }
                }
                if (payloadIO == null) {
                    payloadIO = new BytesPayloadIO();
                }
                try {
                    Object payload = payloadIO.unmarshall(buffer.getBytes());
                    payloadResultHandler.handle(AsyncResultImpl.create(payload));
                } catch (Exception e) {
                    // $NON-NLS-1$
                    payloadResultHandler.handle(AsyncResultImpl.create(new Exception("Failed to parse inbound request payload.", e)));
                }
            }
        }

        /**
         * Because of the way the parsePayload code was written, it's possible
         * with async for apiConnection to be +null+ when this is called.
         *
         * This is because parsePayload invokes inboundStreamHandler before
         * policiesLoadedHandler has had a chance to return (and assigned apiConnection).
         *
         * To work around this we check whether apiConnection is null for #drainHandler
         * and #isFull.
         */
        @Override
        public void drainHandler(IAsyncHandler<Void> drainHandler) {
            if (apiConnection != null)
                apiConnection.drainHandler(drainHandler);
        }

        @Override
        public boolean isFull() {
            if (apiConnection != null) {
                return apiConnection.isFull();
            } else {
                return false;
            }
        }
    });
}
Also used : IApimanBuffer(io.apiman.gateway.engine.io.IApimanBuffer) BytesPayloadIO(io.apiman.gateway.engine.io.BytesPayloadIO) ISignalWriteStream(io.apiman.gateway.engine.io.ISignalWriteStream) ByteBuffer(io.apiman.gateway.engine.io.ByteBuffer) JsonPayloadIO(io.apiman.gateway.engine.io.JsonPayloadIO) RequestAbortedException(io.apiman.gateway.engine.beans.exceptions.RequestAbortedException) InvalidApiException(io.apiman.gateway.engine.beans.exceptions.InvalidApiException) InvalidContractException(io.apiman.gateway.engine.beans.exceptions.InvalidContractException) ApiNotFoundException(io.apiman.gateway.engine.beans.exceptions.ApiNotFoundException) SoapPayloadIO(io.apiman.gateway.engine.io.SoapPayloadIO) XmlPayloadIO(io.apiman.gateway.engine.io.XmlPayloadIO)

Example 2 with ISignalWriteStream

use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.

the class DefaultEngineFactoryTest method testCreateEngine.

/**
 * Test method for {@link io.apiman.gateway.engine.impl.AbstractEngineFactory#createEngine()}.
 * @throws ExecutionException
 * @throws InterruptedException
 */
@Test
public void testCreateEngine() throws InterruptedException, ExecutionException {
    DefaultEngineFactory factory = new DefaultEngineFactory() {

        @Override
        protected IComponentRegistry createComponentRegistry(IPluginRegistry pluginRegistry) {
            return new DefaultComponentRegistry() {

                @Override
                protected void registerBufferFactoryComponent() {
                    addComponent(IBufferFactoryComponent.class, new ByteBufferFactoryComponent());
                }
            };
        }

        @Override
        protected IConnectorFactory createConnectorFactory(IPluginRegistry pluginRegistry) {
            return new IConnectorFactory() {

                @Override
                public IApiConnector createConnector(ApiRequest request, Api api, RequiredAuthType requiredAuthType, boolean hasDataPolicy, IConnectorConfig connectorConfig) {
                    Assert.assertEquals("test", api.getEndpointType());
                    Assert.assertEquals("test:endpoint", api.getEndpoint());
                    IApiConnector connector = new IApiConnector() {

                        /**
                         * @see io.apiman.gateway.engine.IApiConnector#connect(io.apiman.gateway.engine.beans.ApiRequest, io.apiman.gateway.engine.async.IAsyncResultHandler)
                         */
                        @Override
                        public IApiConnection connect(ApiRequest request, IAsyncResultHandler<IApiConnectionResponse> handler) throws ConnectorException {
                            final ApiResponse response = new ApiResponse();
                            response.setCode(200);
                            // $NON-NLS-1$
                            response.setMessage("OK");
                            mockApiConnectionResponse = new MockApiConnectionResponse() {

                                @Override
                                public void write(IApimanBuffer chunk) {
                                    handleBody(chunk);
                                }

                                @Override
                                protected void handleHead(ApiResponse head) {
                                    return;
                                }

                                @Override
                                public ApiResponse getHead() {
                                    return response;
                                }

                                @Override
                                public void end() {
                                    handleEnd();
                                }

                                @Override
                                public void transmit() {
                                    transmitHandler.handle((Void) null);
                                }

                                @Override
                                public void abort(Throwable t) {
                                }
                            };
                            IAsyncResult<IApiConnectionResponse> mockResponseResultHandler = mock(IAsyncResult.class);
                            given(mockResponseResultHandler.isSuccess()).willReturn(true);
                            given(mockResponseResultHandler.isError()).willReturn(false);
                            given(mockResponseResultHandler.getResult()).willReturn(mockApiConnectionResponse);
                            mockApiConnection = mock(MockApiConnection.class);
                            given(mockApiConnection.getHead()).willReturn(request);
                            // Handle head
                            handler.handle(mockResponseResultHandler);
                            return mockApiConnection;
                        }
                    };
                    return connector;
                }

                @Override
                public IConnectorConfig createConnectorConfig(ApiRequest request, Api api) {
                    return new TestConnectorConfigImpl();
                }
            };
        }

        @Override
        protected IDelegateFactory createLoggerFactory(IPluginRegistry pluginRegistry) {
            return null;
        }

        @Override
        protected IApiRequestPathParser createRequestPathParser(IPluginRegistry pluginRegistry) {
            return new DefaultRequestPathParser(null);
        }

        @Override
        protected void complete() {
        }
    };
    IEngine engine = factory.createEngine();
    Assert.assertNotNull(engine);
    // create a api
    Api api = new Api();
    api.setEndpointType("test");
    api.setEndpoint("test:endpoint");
    api.setOrganizationId("TestOrg");
    api.setApiId("TestApi");
    api.setVersion("1.0");
    // create a client
    Client app = new Client();
    app.setClientId("TestApp");
    app.setOrganizationId("TestOrg");
    app.setVersion("1.0");
    app.setApiKey("client-12345");
    Contract contract = new Contract();
    contract.setPlan("Gold");
    contract.setApiId("TestApi");
    contract.setApiOrgId("TestOrg");
    contract.setApiVersion("1.0");
    contract.setPolicies(policyList);
    app.addContract(contract);
    // simple api/app config
    engine.getRegistry().publishApi(api, new IAsyncResultHandler<Void>() {

        @Override
        public void handle(IAsyncResult<Void> result) {
        }
    });
    engine.getRegistry().registerClient(app, new IAsyncResultHandler<Void>() {

        @Override
        public void handle(IAsyncResult<Void> result) {
        }
    });
    ApiRequest request = new ApiRequest();
    request.setApiKey("client-12345");
    request.setApiId("TestApi");
    request.setApiOrgId("TestOrg");
    request.setApiVersion("1.0");
    request.setDestination("/");
    request.setUrl("http://localhost:9999/");
    request.setType("TEST");
    IApiRequestExecutor prExecutor = engine.executor(request, new IAsyncResultHandler<IEngineResult>() {

        // At this point, we are either saying *fail* or *response connection is ready*
        @Override
        public void handle(IAsyncResult<IEngineResult> result) {
            IEngineResult er = result.getResult();
            // No exception occurred
            Assert.assertTrue(result.isSuccess());
            // The chain evaluation succeeded
            Assert.assertNotNull(er);
            Assert.assertTrue(!er.isFailure());
            Assert.assertNotNull(er.getApiResponse());
            // $NON-NLS-1$
            Assert.assertEquals("OK", er.getApiResponse().getMessage());
            er.bodyHandler(mockBodyHandler);
            er.endHandler(mockEndHandler);
        }
    });
    prExecutor.streamHandler(new IAsyncHandler<ISignalWriteStream>() {

        @Override
        public void handle(ISignalWriteStream streamWriter) {
            streamWriter.write(mockBufferInbound);
            streamWriter.end();
        }
    });
    transmitHandler = new IAsyncHandler<Void>() {

        @Override
        public void handle(Void result) {
            // NB: This is cheating slightly for testing purposes, we don't have real async here.
            // Only now start writing stuff, so user has had opportunity to set handlers
            mockApiConnectionResponse.write(mockBufferOutbound);
            mockApiConnectionResponse.end();
        }
    };
    prExecutor.execute();
    // Request handler should receive the mock inbound buffer once only
    verify(mockApiConnection, times(1)).write(mockBufferInbound);
    // Ultimately user should receive the contrived response and end in order.
    InOrder order = inOrder(mockBodyHandler, mockEndHandler);
    order.verify(mockBodyHandler).handle(mockBufferOutbound);
    order.verify(mockEndHandler).handle((Void) null);
}
Also used : IApimanBuffer(io.apiman.gateway.engine.io.IApimanBuffer) IEngineResult(io.apiman.gateway.engine.IEngineResult) IEngine(io.apiman.gateway.engine.IEngine) IAsyncResultHandler(io.apiman.gateway.engine.async.IAsyncResultHandler) ApiRequest(io.apiman.gateway.engine.beans.ApiRequest) IApiConnectionResponse(io.apiman.gateway.engine.IApiConnectionResponse) ApiResponse(io.apiman.gateway.engine.beans.ApiResponse) RequiredAuthType(io.apiman.gateway.engine.auth.RequiredAuthType) IConnectorFactory(io.apiman.gateway.engine.IConnectorFactory) Client(io.apiman.gateway.engine.beans.Client) IApiRequestExecutor(io.apiman.gateway.engine.IApiRequestExecutor) IPluginRegistry(io.apiman.gateway.engine.IPluginRegistry) InOrder(org.mockito.InOrder) IConnectorConfig(io.apiman.gateway.engine.IConnectorConfig) ISignalWriteStream(io.apiman.gateway.engine.io.ISignalWriteStream) IApiConnector(io.apiman.gateway.engine.IApiConnector) Api(io.apiman.gateway.engine.beans.Api) Contract(io.apiman.gateway.engine.beans.Contract) Test(org.junit.Test)

Example 3 with ISignalWriteStream

use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.

the class InMemoryCacheStoreComponent method putBinary.

/**
 * @see io.apiman.gateway.engine.components.ICacheStoreComponent#putBinary(java.lang.String, java.lang.Object, long)
 */
@Override
public <T> ISignalWriteStream putBinary(final String cacheKey, final T jsonObject, final long timeToLive) throws IOException {
    final IApimanBuffer buffer = bufferFactory.createBuffer();
    synchronized (mapMutex) {
        expireOnMap.put(cacheKey, System.currentTimeMillis() + (timeToLive * 1000));
        objectCache.put(cacheKey, jsonObject);
        dataCache.put(cacheKey, buffer);
    }
    return new ISignalWriteStream() {

        private boolean finished = false;

        @Override
        public void abort(Throwable t) {
            finished = true;
        }

        @Override
        public boolean isFinished() {
            return finished;
        }

        @Override
        public void write(IApimanBuffer chunk) {
            buffer.append(chunk);
        }

        @Override
        public void end() {
            finished = true;
            synchronized (cacheSizeMutex) {
                cacheSize += buffer.length();
                if (cacheSize > maxCacheSize) {
                    synchronized (mapMutex) {
                        while (cacheSize > maxCacheSize) {
                            String cacheKey = objectCache.keySet().iterator().next();
                            objectCache.remove(cacheKey);
                            expireOnMap.remove(cacheKey);
                            IApimanBuffer removedBuffer = dataCache.remove(cacheKey);
                            if (removedBuffer != null) {
                                cacheSize -= removedBuffer.length();
                            }
                        }
                    }
                }
            }
        }
    };
}
Also used : IApimanBuffer(io.apiman.gateway.engine.io.IApimanBuffer) ISignalWriteStream(io.apiman.gateway.engine.io.ISignalWriteStream)

Example 4 with ISignalWriteStream

use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.

the class CachingPolicy method responseDataHandler.

/**
 * @see io.apiman.gateway.engine.policies.AbstractMappedDataPolicy#responseDataHandler(io.apiman.gateway.engine.beans.ApiResponse, io.apiman.gateway.engine.policy.IPolicyContext, java.lang.Object)
 */
@Deprecated
@Override
protected IReadWriteStream<ApiResponse> responseDataHandler(final ApiResponse response, IPolicyContext context, CachingConfig policyConfiguration) {
    // Possibly cache the response for future posterity.
    // Check the response code against list in config (empty/null list means cache all).
    final boolean shouldCache = (context.getAttribute(SHOULD_CACHE_ATTR, Boolean.FALSE) && ofNullable(policyConfiguration.getStatusCodes()).map(statusCodes -> statusCodes.isEmpty() || statusCodes.contains(String.valueOf(response.getCode()))).orElse(true));
    if (shouldCache) {
        try {
            String cacheId = context.getAttribute(CACHE_ID_ATTR, null);
            ICacheStoreComponent cache = context.getComponent(ICacheStoreComponent.class);
            final ISignalWriteStream writeStream = cache.putBinary(cacheId, response, policyConfiguration.getTtl());
            return new AbstractStream<ApiResponse>() {

                @Override
                public ApiResponse getHead() {
                    return response;
                }

                @Override
                protected void handleHead(ApiResponse head) {
                }

                @Override
                public void write(IApimanBuffer chunk) {
                    writeStream.write(chunk);
                    super.write(chunk);
                }

                @Override
                public void end() {
                    writeStream.end();
                    super.end();
                }
            };
        } catch (ComponentNotFoundException | IOException e) {
            // TODO log error
            return null;
        }
    } else {
        return null;
    }
}
Also used : IDataPolicy(io.apiman.gateway.engine.policy.IDataPolicy) ISignalReadStream(io.apiman.gateway.engine.io.ISignalReadStream) Optional.ofNullable(java.util.Optional.ofNullable) CacheConnectorInterceptor(io.apiman.gateway.engine.policies.caching.CacheConnectorInterceptor) IOException(java.io.IOException) ApiResponse(io.apiman.gateway.engine.beans.ApiResponse) CachingConfig(io.apiman.gateway.engine.policies.config.CachingConfig) IPolicyChain(io.apiman.gateway.engine.policy.IPolicyChain) ApiRequest(io.apiman.gateway.engine.beans.ApiRequest) IAsyncResult(io.apiman.gateway.engine.async.IAsyncResult) AbstractStream(io.apiman.gateway.engine.io.AbstractStream) IConnectorInterceptor(io.apiman.gateway.engine.policy.IConnectorInterceptor) IAsyncResultHandler(io.apiman.gateway.engine.async.IAsyncResultHandler) IApimanBuffer(io.apiman.gateway.engine.io.IApimanBuffer) ComponentNotFoundException(io.apiman.gateway.engine.beans.exceptions.ComponentNotFoundException) IReadWriteStream(io.apiman.gateway.engine.io.IReadWriteStream) IPolicyContext(io.apiman.gateway.engine.policy.IPolicyContext) ICacheStoreComponent(io.apiman.gateway.engine.components.ICacheStoreComponent) ISignalWriteStream(io.apiman.gateway.engine.io.ISignalWriteStream) CachedResponse(io.apiman.gateway.engine.impl.CachedResponse) IApimanBuffer(io.apiman.gateway.engine.io.IApimanBuffer) ComponentNotFoundException(io.apiman.gateway.engine.beans.exceptions.ComponentNotFoundException) AbstractStream(io.apiman.gateway.engine.io.AbstractStream) IOException(java.io.IOException) ISignalWriteStream(io.apiman.gateway.engine.io.ISignalWriteStream) ICacheStoreComponent(io.apiman.gateway.engine.components.ICacheStoreComponent) ApiResponse(io.apiman.gateway.engine.beans.ApiResponse)

Example 5 with ISignalWriteStream

use of io.apiman.gateway.engine.io.ISignalWriteStream in project apiman by apiman.

the class CachingResourcesPolicy method responseDataHandler.

/**
 * @see io.apiman.gateway.engine.policies.AbstractMappedDataPolicy#responseDataHandler(io.apiman.gateway.engine.beans.ApiResponse, io.apiman.gateway.engine.policy.IPolicyContext, java.lang.Object)
 */
@Override
protected IReadWriteStream<ApiResponse> responseDataHandler(final ApiResponse response, IPolicyContext context, CachingResourcesConfig policyConfiguration) {
    if (response == null) {
        // if the response is empty because of a policy failure before we end here and return null
        return null;
    }
    List<CachingResourcesSettingsEntry> possibleMatchingCachingEntries = context.getAttribute(CACHE_POSSIBLE_MATCHING_ENTRIES, new ArrayList<CachingResourcesSettingsEntry>());
    boolean isAMatch = false;
    for (CachingResourcesSettingsEntry entry : possibleMatchingCachingEntries) {
        isAMatch = isAMatch || matchesPolicyEntryVsActualValue(entry.getStatusCode(), String.valueOf(response.getCode()));
    }
    // Possibly cache the response for future posterity.
    final boolean shouldCache = context.getAttribute(SHOULD_CACHE_ATTR, Boolean.FALSE) && isAMatch;
    if (shouldCache) {
        try {
            String cacheId = context.getAttribute(CACHE_ID_ATTR, null);
            ICacheStoreComponent cache = context.getComponent(ICacheStoreComponent.class);
            final ISignalWriteStream writeStream = cache.putBinary(cacheId, response, policyConfiguration.getTtl());
            return new AbstractStream<ApiResponse>() {

                @Override
                public ApiResponse getHead() {
                    return response;
                }

                @Override
                protected void handleHead(ApiResponse head) {
                }

                @Override
                public void write(IApimanBuffer chunk) {
                    writeStream.write(chunk);
                    super.write(chunk);
                }

                @Override
                public void end() {
                    writeStream.end();
                    super.end();
                }
            };
        } catch (ComponentNotFoundException | IOException e) {
            throw new RuntimeException(e);
        }
    }
    return null;
}
Also used : CachingResourcesSettingsEntry(io.apiman.gateway.engine.policies.config.CachingResourcesSettingsEntry) IApimanBuffer(io.apiman.gateway.engine.io.IApimanBuffer) AbstractStream(io.apiman.gateway.engine.io.AbstractStream) IOException(java.io.IOException) ISignalWriteStream(io.apiman.gateway.engine.io.ISignalWriteStream) ApiResponse(io.apiman.gateway.engine.beans.ApiResponse) ComponentNotFoundException(io.apiman.gateway.engine.beans.exceptions.ComponentNotFoundException) ICacheStoreComponent(io.apiman.gateway.engine.components.ICacheStoreComponent)

Aggregations

ISignalWriteStream (io.apiman.gateway.engine.io.ISignalWriteStream)13 IApimanBuffer (io.apiman.gateway.engine.io.IApimanBuffer)11 ApiRequest (io.apiman.gateway.engine.beans.ApiRequest)5 ByteBuffer (io.apiman.gateway.engine.io.ByteBuffer)5 IEngineResult (io.apiman.gateway.engine.IEngineResult)4 ApiResponse (io.apiman.gateway.engine.beans.ApiResponse)4 IApiRequestExecutor (io.apiman.gateway.engine.IApiRequestExecutor)3 IAsyncResultHandler (io.apiman.gateway.engine.async.IAsyncResultHandler)3 IOException (java.io.IOException)3 Test (org.junit.Test)3 IApiConnector (io.apiman.gateway.engine.IApiConnector)2 IConnectorConfig (io.apiman.gateway.engine.IConnectorConfig)2 IEngine (io.apiman.gateway.engine.IEngine)2 IAsyncHandler (io.apiman.gateway.engine.async.IAsyncHandler)2 IAsyncResult (io.apiman.gateway.engine.async.IAsyncResult)2 ApiNotFoundException (io.apiman.gateway.engine.beans.exceptions.ApiNotFoundException)2 ComponentNotFoundException (io.apiman.gateway.engine.beans.exceptions.ComponentNotFoundException)2 InvalidApiException (io.apiman.gateway.engine.beans.exceptions.InvalidApiException)2 InvalidContractException (io.apiman.gateway.engine.beans.exceptions.InvalidContractException)2 RequestAbortedException (io.apiman.gateway.engine.beans.exceptions.RequestAbortedException)2