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

http-socket does not support Keep-Alive #1097

Closed
parasyte opened this Issue Nov 1, 2015 · 9 comments

Comments

Projects
None yet
7 participants
@parasyte
Contributor

parasyte commented Nov 1, 2015

The Native HTTP Support does not support HTTP Keep-Alive. It's easy to test with uwsgi --http-socket :8080 and using telnet to send a valid HTTP/1.1 request; uwsgi will close the socket after sending the response.

A well-behaved HTTP/1.1 server will keep the socket open unless the client requests Connection: Close.

@Darvame

This comment has been minimized.

Show comment
Hide comment
@Darvame

Darvame Nov 2, 2015

Contributor

uwsgi --http-socket :8080 --http-keepalive --add-header "Connection: keep-alive"

Contributor

Darvame commented Nov 2, 2015

uwsgi --http-socket :8080 --http-keepalive --add-header "Connection: keep-alive"

@parasyte

This comment has been minimized.

Show comment
Hide comment
@parasyte

parasyte Nov 2, 2015

Contributor

@Darvame Perhaps you could try this yourself, because it does not work:

# wsgi.py
import gevent.monkey
gevent.monkey.patch_all()

import bottle

app = bottle.Bottle()

@app.route('/ping')
def ping():
    return {
        'message' : "pong",
    }

if __name__ == '__main__':
    bottle.run(
        app,
        server=bottle.GeventServer,
    )

Run the server:

$ uwsgi --http-socket :8080 --add-header "Connection: Keep-Alive" --http-keepalive --so-keepalive --gevent 100 --virtualenv env --module wsgi:app

Test keepalive:

$ time telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json
Connection: Keep-Alive

{"message": "pong"}Connection closed by foreign host.

real    0m0.444s
user    0m0.014s
sys     0m0.009s

The following invocation works as expected:

$ uwsgi --http :8080 --http-keepalive --so-keepalive --gevent 100 --virtualenv env --module wsgi:app

Test keepalive:

$ time telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json

{"message": "pong"}Connection closed by foreign host.

real    1m0.223s
user    0m0.013s
sys     0m0.008s

Here are a few backtraces from lldb (plain ol' Python and gevent plugin) showing where the close is occurring unconditionally:

Python: https://github.com/unbit/uwsgi/blob/2.0.11.2/core/loop.c#L149

(lldb) b close
Breakpoint 1: 5 locations.
(lldb) c
Process 59371 resuming
Process 59371 stopped
* thread #1: tid = 0x3a67e2, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
    frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
libsystem_kernel.dylib`close:
->  0x7fff838e29d8 <+0>:  movl   $0x2000006, %eax
    0x7fff838e29dd <+5>:  movq   %rcx, %r10
    0x7fff838e29e0 <+8>:  syscall
    0x7fff838e29e2 <+10>: jae    0x7fff838e29ec            ; <+20>
(lldb) bt
* thread #1: tid = 0x3a67e2, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
  * frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
    frame #1: 0x0000000100002ee2 uwsgi`close_and_free_request(wsgi_req=0x00000001003fb078) + 34 at utils.c:1006
    frame #2: 0x00000001000031af uwsgi`uwsgi_close_request(wsgi_req=0x00000001003fb078) + 639 at utils.c:1105
    frame #3: 0x000000010004e886 uwsgi`simple_loop_run(arg1=<unavailable>) + 230 at loop.c:149
    frame #4: 0x0000000100055e0b uwsgi`uwsgi_ignition + 443 at uwsgi.c:3537
    frame #5: 0x0000000100055c03 uwsgi`uwsgi_worker_run + 883 at uwsgi.c:3465
    frame #6: 0x000000010005362e uwsgi`uwsgi_run + 478 at uwsgi.c:3375
    frame #7: 0x00000001000512ae uwsgi`main(argc=<unavailable>, argv=<unavailable>, envp=<unavailable>) + 14 at uwsgi.c:2002
    frame #8: 0x00007fff9314d5ad libdyld.dylib`start + 1
    frame #9: 0x00007fff9314d5ad libdyld.dylib`start + 1

gevent: https://github.com/unbit/uwsgi/blob/2.0.11.2/plugins/gevent/gevent.c#L325

(lldb) b close
Breakpoint 1: 5 locations.
(lldb) c
Process 59862 resuming
Process 59862 stopped
* thread #1: tid = 0x3a80a5, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
    frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
libsystem_kernel.dylib`close:
->  0x7fff838e29d8 <+0>:  movl   $0x2000006, %eax
    0x7fff838e29dd <+5>:  movq   %rcx, %r10
    0x7fff838e29e0 <+8>:  syscall
    0x7fff838e29e2 <+10>: jae    0x7fff838e29ec            ; <+20>
(lldb) bt
* thread #1: tid = 0x3a80a5, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
  * frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
    frame #1: 0x0000000100002ee2 uwsgi`close_and_free_request(wsgi_req=0x0000000101be1618) + 34 at utils.c:1006
    frame #2: 0x00000001000031af uwsgi`uwsgi_close_request(wsgi_req=0x0000000101be1618) + 639 at utils.c:1105
    frame #3: 0x00000001000743ed uwsgi`py_uwsgi_gevent_request(self=<unavailable>, args=<unavailable>) + 349 at gevent.c:325
    frame #4: 0x000000010022e2a5 Python`PyEval_EvalFrameEx + 12479
    frame #5: 0x000000010022afb4 Python`PyEval_EvalCodeEx + 1387
    frame #6: 0x00000001001cfbf5 Python`function_call + 352
    frame #7: 0x00000001001b1ad7 Python`PyObject_Call + 99
    frame #8: 0x00000001001bc962 Python`instancemethod_call + 174
    frame #9: 0x00000001001b1ad7 Python`PyObject_Call + 99
    frame #10: 0x0000000100230e2e Python`PyEval_CallObjectWithKeywords + 93
    frame #11: 0x0000000101ff1b2a greenlet.so`g_initialstub + 1114
    frame #12: 0x0000000101ff13ad greenlet.so`g_switch + 333
    frame #13: 0x00000001001bc8b4 Python`instancemethod_hash + 97
    frame #14: 0x000000010029da10 Python`instancemethod_getset + 80

These lines also execute with --http :8080 (i.e. in the working case), but I assume it's closing the socket on the backend?

Contributor

parasyte commented Nov 2, 2015

@Darvame Perhaps you could try this yourself, because it does not work:

# wsgi.py
import gevent.monkey
gevent.monkey.patch_all()

import bottle

app = bottle.Bottle()

@app.route('/ping')
def ping():
    return {
        'message' : "pong",
    }

if __name__ == '__main__':
    bottle.run(
        app,
        server=bottle.GeventServer,
    )

Run the server:

$ uwsgi --http-socket :8080 --add-header "Connection: Keep-Alive" --http-keepalive --so-keepalive --gevent 100 --virtualenv env --module wsgi:app

Test keepalive:

$ time telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json
Connection: Keep-Alive

{"message": "pong"}Connection closed by foreign host.

real    0m0.444s
user    0m0.014s
sys     0m0.009s

The following invocation works as expected:

$ uwsgi --http :8080 --http-keepalive --so-keepalive --gevent 100 --virtualenv env --module wsgi:app

Test keepalive:

$ time telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json

{"message": "pong"}Connection closed by foreign host.

real    1m0.223s
user    0m0.013s
sys     0m0.008s

Here are a few backtraces from lldb (plain ol' Python and gevent plugin) showing where the close is occurring unconditionally:

Python: https://github.com/unbit/uwsgi/blob/2.0.11.2/core/loop.c#L149

(lldb) b close
Breakpoint 1: 5 locations.
(lldb) c
Process 59371 resuming
Process 59371 stopped
* thread #1: tid = 0x3a67e2, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
    frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
libsystem_kernel.dylib`close:
->  0x7fff838e29d8 <+0>:  movl   $0x2000006, %eax
    0x7fff838e29dd <+5>:  movq   %rcx, %r10
    0x7fff838e29e0 <+8>:  syscall
    0x7fff838e29e2 <+10>: jae    0x7fff838e29ec            ; <+20>
(lldb) bt
* thread #1: tid = 0x3a67e2, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
  * frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
    frame #1: 0x0000000100002ee2 uwsgi`close_and_free_request(wsgi_req=0x00000001003fb078) + 34 at utils.c:1006
    frame #2: 0x00000001000031af uwsgi`uwsgi_close_request(wsgi_req=0x00000001003fb078) + 639 at utils.c:1105
    frame #3: 0x000000010004e886 uwsgi`simple_loop_run(arg1=<unavailable>) + 230 at loop.c:149
    frame #4: 0x0000000100055e0b uwsgi`uwsgi_ignition + 443 at uwsgi.c:3537
    frame #5: 0x0000000100055c03 uwsgi`uwsgi_worker_run + 883 at uwsgi.c:3465
    frame #6: 0x000000010005362e uwsgi`uwsgi_run + 478 at uwsgi.c:3375
    frame #7: 0x00000001000512ae uwsgi`main(argc=<unavailable>, argv=<unavailable>, envp=<unavailable>) + 14 at uwsgi.c:2002
    frame #8: 0x00007fff9314d5ad libdyld.dylib`start + 1
    frame #9: 0x00007fff9314d5ad libdyld.dylib`start + 1

gevent: https://github.com/unbit/uwsgi/blob/2.0.11.2/plugins/gevent/gevent.c#L325

(lldb) b close
Breakpoint 1: 5 locations.
(lldb) c
Process 59862 resuming
Process 59862 stopped
* thread #1: tid = 0x3a80a5, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
    frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
libsystem_kernel.dylib`close:
->  0x7fff838e29d8 <+0>:  movl   $0x2000006, %eax
    0x7fff838e29dd <+5>:  movq   %rcx, %r10
    0x7fff838e29e0 <+8>:  syscall
    0x7fff838e29e2 <+10>: jae    0x7fff838e29ec            ; <+20>
(lldb) bt
* thread #1: tid = 0x3a80a5, 0x00007fff838e29d8 libsystem_kernel.dylib`close, queue = 'com.apple.main-thread', stop reason = breakpoint 1.3
  * frame #0: 0x00007fff838e29d8 libsystem_kernel.dylib`close
    frame #1: 0x0000000100002ee2 uwsgi`close_and_free_request(wsgi_req=0x0000000101be1618) + 34 at utils.c:1006
    frame #2: 0x00000001000031af uwsgi`uwsgi_close_request(wsgi_req=0x0000000101be1618) + 639 at utils.c:1105
    frame #3: 0x00000001000743ed uwsgi`py_uwsgi_gevent_request(self=<unavailable>, args=<unavailable>) + 349 at gevent.c:325
    frame #4: 0x000000010022e2a5 Python`PyEval_EvalFrameEx + 12479
    frame #5: 0x000000010022afb4 Python`PyEval_EvalCodeEx + 1387
    frame #6: 0x00000001001cfbf5 Python`function_call + 352
    frame #7: 0x00000001001b1ad7 Python`PyObject_Call + 99
    frame #8: 0x00000001001bc962 Python`instancemethod_call + 174
    frame #9: 0x00000001001b1ad7 Python`PyObject_Call + 99
    frame #10: 0x0000000100230e2e Python`PyEval_CallObjectWithKeywords + 93
    frame #11: 0x0000000101ff1b2a greenlet.so`g_initialstub + 1114
    frame #12: 0x0000000101ff13ad greenlet.so`g_switch + 333
    frame #13: 0x00000001001bc8b4 Python`instancemethod_hash + 97
    frame #14: 0x000000010029da10 Python`instancemethod_getset + 80

These lines also execute with --http :8080 (i.e. in the working case), but I assume it's closing the socket on the backend?

@Darvame

This comment has been minimized.

Show comment
Hide comment
@Darvame

Darvame Nov 2, 2015

Contributor

Yes, does not work for me too :C

Contributor

Darvame commented Nov 2, 2015

Yes, does not work for me too :C

@unbit

This comment has been minimized.

Show comment
Hide comment
@unbit

unbit Nov 2, 2015

Owner

You need uWSGI 2.1 (master branch) and the --http11-socket option. Once set the server will try to maintain the connection opened if a bunch of rules are respected. This is not a smart http 1.1 parser (to avoid parsing the whole response) but assumes the developer is generating the right headers. It has been added to support RTSP protocol for video streaming.

Owner

unbit commented Nov 2, 2015

You need uWSGI 2.1 (master branch) and the --http11-socket option. Once set the server will try to maintain the connection opened if a bunch of rules are respected. This is not a smart http 1.1 parser (to avoid parsing the whole response) but assumes the developer is generating the right headers. It has been added to support RTSP protocol for video streaming.

@xrmx xrmx added the documentation label Nov 2, 2015

@depaolim

This comment has been minimized.

Show comment
Hide comment
@depaolim

depaolim Nov 6, 2015

Contributor

Does this new option replace completely the old ones?

I used

./uwsgi --http11-socket :8080 --gevent 100 --virtualenv env --module wsgi:app

and I obtained this:

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json

{"message": "pong"}

without any "Connection closed by foreign host."

So it seems to work as a "Keep-alive" without any need of the old options

--add-header "Connection: Keep-Alive" --http-keepalive --so-keepalive

Does it mean that they are no more necessary?

Contributor

depaolim commented Nov 6, 2015

Does this new option replace completely the old ones?

I used

./uwsgi --http11-socket :8080 --gevent 100 --virtualenv env --module wsgi:app

and I obtained this:

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /ping HTTP/1.1

HTTP/1.1 200 OK
Content-Length: 19
Content-Type: application/json

{"message": "pong"}

without any "Connection closed by foreign host."

So it seems to work as a "Keep-alive" without any need of the old options

--add-header "Connection: Keep-Alive" --http-keepalive --so-keepalive

Does it mean that they are no more necessary?

@xrmx

This comment has been minimized.

Show comment
Hide comment
@xrmx

xrmx Nov 13, 2015

Collaborator

@depaolim i think --http11-socket may replace the add-header and http-keepalive options but i don't see it touching tcp stuff as so-keepalive does.

Collaborator

xrmx commented Nov 13, 2015

@depaolim i think --http11-socket may replace the add-header and http-keepalive options but i don't see it touching tcp stuff as so-keepalive does.

@xrmx

This comment has been minimized.

Show comment
Hide comment
@xrmx

xrmx Nov 15, 2015

Collaborator

Documentation has been updated thanks to @depaolim, closing this.

Collaborator

xrmx commented Nov 15, 2015

Documentation has been updated thanks to @depaolim, closing this.

@xrmx xrmx closed this Nov 15, 2015

@marc1n

This comment has been minimized.

Show comment
Hide comment
@marc1n

marc1n Apr 1, 2017

I confirm (by testing it) that "http-keepalive" option works only with "http" (not "http-socket") in uWSGI 2.0.

Why it is "http-keepalive" not working with "http-socket"? Is it any reason behind this?

The "http11-socket" is a future option (in uWSGI 2.1 which for now is not released yet) so in reality I cannot use HTTP Keep-Alive in uWSGI not paying penalty of "http" option overhead.

Btw, "http-keepalive" option value is a number which specify timeout (in seconds) after which an inactive connection is closed by server. How can I configure this timeout when I will use "http11-socket"?

marc1n commented Apr 1, 2017

I confirm (by testing it) that "http-keepalive" option works only with "http" (not "http-socket") in uWSGI 2.0.

Why it is "http-keepalive" not working with "http-socket"? Is it any reason behind this?

The "http11-socket" is a future option (in uWSGI 2.1 which for now is not released yet) so in reality I cannot use HTTP Keep-Alive in uWSGI not paying penalty of "http" option overhead.

Btw, "http-keepalive" option value is a number which specify timeout (in seconds) after which an inactive connection is closed by server. How can I configure this timeout when I will use "http11-socket"?

@ushuz

This comment has been minimized.

Show comment
Hide comment
@ushuz

ushuz Apr 30, 2018

Seconding @marc1n, we observed the same problem. http-keepalive doesn't work with http-socket but work with http. There's also a stackoverflow question reporting the same.

http11-socket does enable keepalive, but it doesn't work well with gevent.

ushuz commented Apr 30, 2018

Seconding @marc1n, we observed the same problem. http-keepalive doesn't work with http-socket but work with http. There's also a stackoverflow question reporting the same.

http11-socket does enable keepalive, but it doesn't work well with gevent.

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