New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[gateway] Query parameters without key not encoded (IllegalArgumentException) #665

Closed
SirBigoo opened this Issue Jun 30, 2017 · 2 comments

Comments

Projects
None yet
3 participants
@SirBigoo

SirBigoo commented Jun 30, 2017

Hi,
I have to call a backend which consume a query parameter (json) without key. I registered my API in gravitee. But when I try to call it by passing query parameter without key, I got an HTTP 500 response (java.lang.IllegalArgumentException: Illegal character in query in stacktrace). Indeed, it's Json, so there are some special chars like double quote. If I pass a simple query parameter without key, it works.

However, if I prefix my query parameter with a key, it works (no 500 response and no error).

This parameter without key seems to be valid in RFC.

Example (I only pass gravitee API key header) :

  • Error case :
    GET http://xxxx/api/test/myEndpoint?{"key": "value"}
{
    "message": "io.gravitee.gateway.policy.PolicyChainException: io.gravitee.gateway.policy.PolicyException: java.lang.reflect.InvocationTargetException
	at io.gravitee.gateway.policy.impl.RequestPolicyChain.execute(RequestPolicyChain.java:46)
	at io.gravitee.gateway.policy.impl.PolicyChain.doNext(PolicyChain.java:66)
	at io.gravitee.gateway.policy.impl.StreamablePolicyChain.doNext(StreamablePolicyChain.java:51)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.execute(RequestPolicyChainProcessor.java:61)
	at io.gravitee.gateway.handlers.api.ApiReactorHandler.doHandle(ApiReactorHandler.java:174)
	at io.gravitee.gateway.reactor.handler.AbstractReactorHandler.handle(AbstractReactorHandler.java:93)
	at io.gravitee.gateway.reactor.impl.DefaultReactor.lambda$route$0(DefaultReactor.java:87)
	at io.gravitee.gateway.reactor.handler.transaction.TransactionHandler.handle(TransactionHandler.java:58)
	at io.gravitee.gateway.reactor.handler.transaction.TransactionHandler.handle(TransactionHandler.java:29)
	at io.gravitee.gateway.reactor.impl.DefaultReactor.route(DefaultReactor.java:98)
	at io.gravitee.gateway.standalone.vertx.VertxReactorHandler.handleRequest(VertxReactorHandler.java:39)
	at io.gravitee.gateway.standalone.vertx.VertxReactorHandler.handle(VertxReactorHandler.java:35)
	at io.gravitee.gateway.standalone.vertx.VertxReactorHandler.handle(VertxReactorHandler.java:25)
	at io.vertx.core.http.impl.ServerConnection.handleRequest(ServerConnection.java:285)
	at io.vertx.core.http.impl.ServerConnection.processMessage(ServerConnection.java:429)
	at io.vertx.core.http.impl.ServerConnection.handleMessage(ServerConnection.java:131)
	at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.lambda$createConnAndHandle$1(HttpServerImpl.java:763)
	at io.vertx.core.impl.ContextImpl.lambda$wrapTask$2(ContextImpl.java:335)
	at io.vertx.core.impl.ContextImpl.executeFromIO(ContextImpl.java:193)
	at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.createConnAndHandle(HttpServerImpl.java:757)
	at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:676)
	at io.vertx.core.http.impl.HttpServerImpl$ServerHandler.doMessageReceived(HttpServerImpl.java:573)
	at io.vertx.core.http.impl.VertxHttpHandler.channelRead(VertxHttpHandler.java:76)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:122)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
	at io.vertx.core.http.impl.HttpServerImpl$Http1xOrHttp2Handler.http1(HttpServerImpl.java:1064)
	at io.vertx.core.http.impl.HttpServerImpl$Http1xOrHttp2Handler.channelRead(HttpServerImpl.java:1035)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:341)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:363)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:349)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:642)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:565)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:479)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:441)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
	at java.lang.Thread.run(Thread.java:745)
Caused by: io.gravitee.gateway.policy.PolicyException: java.lang.reflect.InvocationTargetException
	at io.gravitee.gateway.policy.impl.PolicyImpl.invoke(PolicyImpl.java:91)
	at io.gravitee.gateway.policy.impl.PolicyImpl.onRequest(PolicyImpl.java:44)
	at io.gravitee.gateway.policy.impl.RequestPolicyChain.execute(RequestPolicyChain.java:44)
	... 47 more
Caused by: java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at io.gravitee.gateway.policy.impl.PolicyImpl.invoke(PolicyImpl.java:89)
	... 49 more
Caused by: java.lang.IllegalArgumentException: Illegal character in query at index 80: http://xxxx/api/test/myEndpoint?{\"key\":\"value\"}
	at java.net.URI.create(URI.java:852)
	at io.gravitee.gateway.http.core.invoker.DefaultHttpInvoker.encodeQueryParameters(DefaultHttpInvoker.java:173)
	at io.gravitee.gateway.http.core.invoker.DefaultHttpInvoker.invoke(DefaultHttpInvoker.java:105)
	at io.gravitee.gateway.handlers.api.ApiReactorHandler.lambda$doHandle$9(ApiReactorHandler.java:109)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.execute(RequestPolicyChainProcessor.java:63)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.lambda$execute$0(RequestPolicyChainProcessor.java:56)
	at io.gravitee.gateway.policy.NoOpPolicyChain.doNext(NoOpPolicyChain.java:41)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.execute(RequestPolicyChainProcessor.java:61)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.lambda$execute$0(RequestPolicyChainProcessor.java:56)
	at io.gravitee.gateway.policy.NoOpPolicyChain.doNext(NoOpPolicyChain.java:41)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.execute(RequestPolicyChainProcessor.java:61)
	at io.gravitee.gateway.policy.impl.RequestPolicyChainProcessor.lambda$execute$0(RequestPolicyChainProcessor.java:56)
	at io.gravitee.gateway.policy.impl.PolicyChain.doNext(PolicyChain.java:75)
	at io.gravitee.gateway.policy.impl.StreamablePolicyChain.doNext(StreamablePolicyChain.java:51)
	at io.gravitee.policy.apikey.ApiKeyPolicy.onRequest(ApiKeyPolicy.java:94)
	... 54 more
Caused by: java.net.URISyntaxException: Illegal character in query at index 80: http://xxxx/api/test/myEndpoint?{\"key\":\"value\"}
	at java.net.URI$Parser.fail(URI.java:2848)
	at java.net.URI$Parser.checkChars(URI.java:3021)
	at java.net.URI$Parser.parseHierarchical(URI.java:3111)
	at java.net.URI$Parser.parse(URI.java:3053)
	at java.net.URI.<init>(URI.java:588)
	at java.net.URI.create(URI.java:850)
	... 68 more
",
    "http_status_code": 500
}
  • Valid case
    GET http://xxxx/api/test/myEndpoint?myQuery={"key": "value"}
{
    "response": "value"
}

Expected Behavior

Query parameters without key had to be encoded.

Current Behavior

Query parameters is not recognized as a parameter and is not encoded (base request is not encoded).

Possible Solution

With stacktrace, I search in gravitee source code where URL is encoded and I guess I found what causes that bug.
In class io.gravitee.gateway.http.core.invoker.DefaultHttpInvoker, line 220 (function encodeQueryParameters) :

    private URI encodeQueryParameters(Request request, String endpointUri) {
        final StringBuilder requestURI =
                new StringBuilder(endpointUri);

        if (request.parameters() != null && !request.parameters().isEmpty()) {
            // blabla...
        }

        return URI.create(requestURI.toString());
    }

Query parameter without key appears in String endpointUri and not in Request request parameters. Indeed, key is not present so no loop passing. Here, requestURI is not encoded which causes this error. I guess that code fixes this bug :

    private URI encodeQueryParameters(Request request, String endpointUri) {
        final StringBuilder requestURI =
                new StringBuilder(URLEncoder.encode(endpointUri));

        if (request.parameters() != null && !request.parameters().isEmpty()) {
            // blabla...
        }

        return URI.create(requestURI.toString());
    }

Steps to Reproduce

  1. Register an API
  2. Call it with a query parameter without key

Your Environment

  • Version used: 1.5.1
@brasseld

This comment has been minimized.

Member

brasseld commented Jun 30, 2017

Thanks for sharing @SirBigoo

Anyway I have a question for you: if your param is passed without key, how did you process and get it at server-side ?

@SirBigoo

This comment has been minimized.

SirBigoo commented Jun 30, 2017

I do not know what target API does and how it process my query, but if I name my parameter or if I remove it, target API respond with a failure.

By the way, in this case, I have a dynamic call to process : one for REST request, one for SOAP request.
So I tried two methods to verify that my policy was not the problem :

  • Single API with a dynamic routing policy
    Depends my URI, I redirect to REST API or SOAP API as follow :
    image

  • Two API, one for REST and one for SOAP without any policies

Behaviour is the same for both method : error if my parameter has no key, fine if I specify one.

brasseld added a commit to gravitee-io/gravitee-gateway that referenced this issue Jul 3, 2017

brasseld added a commit to gravitee-io/gravitee-gateway that referenced this issue Jul 3, 2017

@brasseld brasseld added the type: bug label Jul 3, 2017

@brasseld brasseld self-assigned this Jul 3, 2017

@brasseld brasseld added this to the 1.8.0 milestone Jul 3, 2017

tcompiegne added a commit to gravitee-io/gravitee-gateway that referenced this issue Jul 6, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment