Skip to content
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

Why am I getting Error #01: Invalid status code? #102

Closed
adriendomoison opened this issue Mar 15, 2018 · 13 comments
Closed

Why am I getting Error #01: Invalid status code? #102

adriendomoison opened this issue Mar 15, 2018 · 13 comments

Comments

@adriendomoison
Copy link

Hi, it me again, still new to Krakend ! 💃

When I do a request throug the gateway, I am getting a status code 0 (?) in my response when using the "gin example" of the repository.

api_gateway_1  | [GIN] 2018/03/15 - 07:49:49 |   0 |   85.720918ms |      172.20.0.1 | POST     /api/v1/users?create_profile=true
api_gateway_1  | Error #01: Invalid status code

(I still get the response body I expected)

Then doing the request again:

api_gateway_1  | [GIN] 2018/03/15 - 07:49:49 |   200 |   85.720918ms |      172.20.0.1 | POST     /api/v1/users?create_profile=true

The request is returning 200 while I am waiting for a 409, nothing is called and nothing is returned.

I then tried to use the example directly in the README.md (since it is an even simpler version using gin too) and I get better results:

api_user_1     | [GIN] 2018/03/15 - 07:57:21 | 201 |   86.655951ms |      172.20.0.1 | POST     /api/v1/users?create_profile=true
api_gateway_1  | [GIN] 2018/03/15 - 07:57:21 | 200 |   89.901626ms |      172.20.0.1 | POST     /api/v1/users?create_profile=true

But I get a 200 while I was expecting a 201 (?)
Then I tried to do the request again, it fails as expected but I get a 500 instead of the expected 409 with the message again:

api_user_1     | [GIN] 2018/03/15 - 07:57:28 | 409 |   51.846764ms |      172.20.0.1 | POST     /api/v1/users?create_profile=true
api_gateway_1  | [GIN] 2018/03/15 - 07:57:28 | 500 |   52.963005ms |      172.20.0.1 | POST     /api/v1/users?create_profile=true
api_gateway_1  | Error #01: Invalid status code

go code:

func main() {
	serviceConfig, err := config.NewParser().Parse("/etc/krakend/configuration.json")
	if err != nil {
		log.Fatal("ERROR:", err.Error())
	}
	serviceConfig.Debug = true
	serviceConfig.Port = 4200

	logger, err := logging.NewLogger("ERROR", os.Stdout, "[KRAKEND]")
	if err != nil {
		log.Fatal("ERROR:", err.Error())
	}

	routerFactory := gin.DefaultFactory(proxy.DefaultFactory(logger), logger)

	routerFactory.New().Run(serviceConfig)
}

Why are the response I am getting inconsistent? How is it possible and what am I doing wrong here?

Thank you, I really value your time and I appreciate your help very much! 👍

@kpacha
Copy link
Member

kpacha commented Mar 15, 2018

The status 0 is a known 'feature' of the cache package included in the gin example (https://github.com/devopsfaith/krakend/blob/master/examples/gin/main.go#L68)

Regarding the bigger than 200 status: by default, the gateway will always send a HTTP status 200 if the backend returns a 200 or a 201, and a 500 for any status above 400

https://github.com/devopsfaith/krakend/blob/master/proxy/http_status.go

You can inject your own HTTPStatusHandler implementation if you need a different behaviour.

cheers!

@adriendomoison
Copy link
Author

adriendomoison commented Mar 16, 2018

@kpacha do you have an example or a documentation for that? I read the code and it did not look very trivial to inject my own HTTPStatusHandler.

I would need to use NewHTTPProxyDetailed(remote *config.Backend, requestExecutor HTTPRequestExecutor, ch HTTPStatusHandler, rp HTTPResponseParser) Proxy right? Then what is the correct way to generate the config.Backend, the HTTPRequestExecutor and the HTTPResponseParser?

And then will I be able to just get the generated proxy and do routerFactory := gin.DefaultFactory(proxy.DefaultFactory(logger), logger)

Thanks!

@kpacha
Copy link
Member

kpacha commented Mar 16, 2018

check out the tests and the krakend-ce repo for examples of how to build the proxy stack.

if you are not using the default factories, you must go with the 'explicit' constructors... something like this:

customStatusHandler := ...

backendFactory := func(backendCfg *config.Backend) proxy.Proxy {
	// the default request executor
	re := proxy.DefaultHTTPRequestExecutor(proxy. NewHTTPClient)

	// default entity formatter for the given backend
	ef := proxy.NewEntityFormatter(backendCfg.Target, backendCfg.Whitelist, backendCfg.Blacklist, backendCfg.Group, backendCfg.Mapping)

	// the default response parser with the required config
	rp := proxy.DefaultHTTPResponseParserFactory(proxy.HTTPResponseParserConfig{backendCfg.Decoder, ef})

	// build and return the new backend proxy
	return proxy.NewHTTPProxyDetailed(backend, re, customStatusHandler, rp)
}

// build the pipes on top of the custom backend factory
proxyFactory := proxy.NewDefaultFactory(backendFactory, logger)

// create the router factory with the custom proxy factory
routerFactory := gin.DefaultFactory(proxyFactory, logger)

The backendCfg (of type *config.Backend) argument is automatically generated from the config file and injected into the factories when calling routerFactory.NewWithContext(ctx).Run(cfg)

@adriendomoison
Copy link
Author

adriendomoison commented Mar 16, 2018

Yuhu! Getting there! One last question @kpacha : is there a way to have access to the original headers of the responses (generated by my services) when injecting a HandlerFactory: func(configuration *config.EndpointConfig, proxy proxy.Proxy) gin.HandlerFunc ?

I was able to figure out that this was the main handler I had to work on and inject to get the result I need thanks to your help 👍

My goal here is still to be able to decide what kind of status I want to return to the client (e.g. to be able to return a 201 when something was created as much as a 409 if there were a conflict). So far, without access to the list of headers from the requests, the only thing I could do is to modify all the responses from the services and let them return some additional information about what they did and what happened, then decide what status to set and after blacklist this additional data.

I am curious, how did you imagined and designed Krakend to be used for this kind of scenario? I would really like to follow the idea and the design :)

@bartoszgolek
Copy link

bartoszgolek commented Mar 16, 2018

Checkout github.com/devopsfaith/krakend/proxy/merging.go:33 and github.com/devopsfaith/krakend/proxy/merging.go:80, here is the logic of merging responses. I would sugest to extract combineData to interface and provide it trough parameter, then You would be able to provide StatusHandler not throwing error and here provide "merger" which merge responses and statuses.

If You would like to wait till monday, I would like to prepare pull request for this.

@kpacha
Copy link
Member

kpacha commented Mar 16, 2018

I have some minor remarks here:

1- the proxy.HTTPStatusHandler is responsible of dealing with the status code of the backend response. And it has full access to the http response and its headers for extra verifications.

2- as @bartoszgolek commented, the proxy.Responses from all the backends in a pipe are merged into a single one. If your endpoint is dealing with multiple backends, the headers will get lost using the default response combiner/merger. But if your endpoint consumes just a single backend, the headers will be passed through the proxy pipe to your router.EndpointHandler. There is a issue for adding a 'combiner register and factory' so they can be defined per endpoint in the config file (#78 )

3- even when your backend headers arrive to the router.EndpointHandler, the default one won't use them to build the final response. You must create a custom one and inject it in your stack.

As a final note, we created the KrakenD to be used as a pure, state-less API gateway (http://www.krakend.io/blog/what-is-an-api-gateway/). So I'm not sure if your use case fits in that classification. But this doesn't mean you shouldn't do it.

@bartoszgolek
Copy link

I have created POC for this: bartoszgolek@630996f

It's not complete, just showing the way of creating ability to manage merging

@kpacha
Copy link
Member

kpacha commented Mar 19, 2018

here you have the complete PoC I was working on two months ago: https://github.com/devopsfaith/krakend/blob/raw_encoding/proxy/merging.go

@kpacha
Copy link
Member

kpacha commented Mar 20, 2018

@adriendomoison , @bartoszgolek, please check this PR: #103

@vahid4mm
Copy link

vahid4mm commented Apr 5, 2020

Hi, so long story short, if I want to send the response the service is providing to the user through the krakend gateway, I need to inject code?

@vladkras
Copy link

vladkras commented Aug 13, 2020

you can use return_error_details (https://www.krakend.io/docs/backends/detailed-errors/) to get status code and message in additional object, but krakend still sends only 200 or 500

or you can use no-op feature

@riibeirogabriel
Copy link

@kpacha I can use the no-op only to backend responses? reading the docs about (https://www.krakend.io/docs/endpoints/no-op/) sounds that no-op needs be setted in both ways (in requests and responses), i set the no-op in backend thinking work only in responses but this not work, there is a way to use only in backend response, not in request?

@github-actions
Copy link

github-actions bot commented Apr 7, 2022

This issue was marked as resolved a long time ago and now has been automatically locked as there has not been any recent activity after it. You can still open a new issue and reference this link.

@github-actions github-actions bot added the locked label Apr 7, 2022
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 7, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants