openfaas / of-watchdog

Reverse proxy for STDIO and HTTP microservices
MIT License
259 stars 115 forks source link

Chunked response from function is blocked #49

Open ryskiwt opened 5 years ago

ryskiwt commented 5 years ago

Dear maintainers,

I wrote my template that is similar to https://github.com/openfaas-incubator/python-flask-template/tree/master/template/python3-flask.

The different from the original is that my template uses stream response (or, chunked response) as follows.

@app.route("/", defaults={"path": ""}, methods=["POST", "GET"])
@app.route("/<path:path>", methods=["POST", "GET"])
def main_route(path):
    ...
    def gen():
        yield "1"
        time.sleep(1)
        yield "2"
        time.sleep(1)
        yield "3"

    return Response(gen())

When I invoke a function from the template, the function does not return response chunk by chunk. It blocks 3 seconds, and then, returns whole the response (1, 2 and 3) all at once.

Expected Behaviour

Function returns response chunk by chunk.

Current Behaviour

Function blocks 3 seconds, and then, returns whole the response all at once.

Possible Solution

The following code blocks until whole the response returned.

https://github.com/openfaas-incubator/of-watchdog/blob/bae373954932a07d89ab926457d237f03f6c60dc/executor/http_runner.go#L173

And openfaas/faas's following code also blocks, maybe.

https://github.com/openfaas/faas/blob/04d240aac26acdaf49863dbab768b4f5cb311d84/gateway/handlers/forwarding_proxy.go#L130

Steps to Reproduce (for bugs)

  1. fetch this template https://github.com/openfaas-incubator/python-flask-template/tree/master/template/python3-flask
  2. edit the template as described above
  3. create new function from the template
  4. build, push, deploy and invoke the function

Your Environment

Docker version 18.09.1, build 4c52b90 Docker swarm Linux (vagrant, vm.box = "bento/centos-7.4")

Thank you.

ryskiwt commented 5 years ago

I found a hint to solve this issue in httputil package's ReverseProxy.copyBuffer method.

https://golang.org/src/net/http/httputil/reverseproxy.go#L335

copyBuffer method reads response little by little and writes to maxLatencyWriter

https://golang.org/src/net/http/httputil/reverseproxy.go#L366

and maxLatencyWriter flushes periodically.

https://golang.org/src/net/http/httputil/reverseproxy.go#L418