aaf55d25c6
It turns out that some miners have been staying with old versions of Bitcoin Core because their software behaves poorly with persistent connections and the Bitcoin Core thread and connection limits. What happens is that underlying HTTP libraries leave connections open invisibly to their users and then the user runs into the default four thread limit. This looks like Bitcoin Core is unresponsive to RPC. There are many things that should be improved in Bitcoin Core's behavior here, e.g. supporting more concurrent connections, not tying up threads for idle connections, disconnecting kept-alive connections when limits are reached, etc. All are fairly big, risky changes. Disabling keep-alive is a simple workaround. It's often not easy to turn off the keep-alive support in the client where it may be buried in some platform library. If you are one of the few who really needs persistent connections you probably know that you want them and can find a switch; while if you don't and the misbehavior is hitting you it is hard to discover the source of your problems is keepalive related. Given that it is best to default to off until they're handled better. Github-Merge: #5655 Rebased-From:16a5c18cea
56c1093dae
1dd8ee72af
103 lines
4.6 KiB
Python
Executable File
103 lines
4.6 KiB
Python
Executable File
#!/usr/bin/env python2
|
|
# Copyright (c) 2014 The Bitcoin Core developers
|
|
# Distributed under the MIT software license, see the accompanying
|
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#
|
|
# Test REST interface
|
|
#
|
|
|
|
from test_framework import BitcoinTestFramework
|
|
from util import *
|
|
import base64
|
|
|
|
try:
|
|
import http.client as httplib
|
|
except ImportError:
|
|
import httplib
|
|
try:
|
|
import urllib.parse as urlparse
|
|
except ImportError:
|
|
import urlparse
|
|
|
|
class HTTPBasicsTest (BitcoinTestFramework):
|
|
def setup_nodes(self):
|
|
return start_nodes(4, self.options.tmpdir, extra_args=[['-rpckeepalive=1'], ['-rpckeepalive=0'], [], []])
|
|
|
|
def run_test(self):
|
|
|
|
#################################################
|
|
# lowlevel check for http persistent connection #
|
|
#################################################
|
|
url = urlparse.urlparse(self.nodes[0].url)
|
|
authpair = url.username + ':' + url.password
|
|
headers = {"Authorization": "Basic " + base64.b64encode(authpair)}
|
|
|
|
conn = httplib.HTTPConnection(url.hostname, url.port)
|
|
conn.connect()
|
|
conn.request('GET', '/', '{"method": "getbestblockhash"}', headers)
|
|
out1 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True)
|
|
assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open!
|
|
|
|
#send 2nd request without closing connection
|
|
conn.request('GET', '/', '{"method": "getchaintips"}', headers)
|
|
out2 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True) #must also response with a correct json-rpc message
|
|
assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open!
|
|
conn.close()
|
|
|
|
#same should be if we add keep-alive because this should be the std. behaviour
|
|
headers = {"Authorization": "Basic " + base64.b64encode(authpair), "Connection": "keep-alive"}
|
|
|
|
conn = httplib.HTTPConnection(url.hostname, url.port)
|
|
conn.connect()
|
|
conn.request('GET', '/', '{"method": "getbestblockhash"}', headers)
|
|
out1 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True)
|
|
assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open!
|
|
|
|
#send 2nd request without closing connection
|
|
conn.request('GET', '/', '{"method": "getchaintips"}', headers)
|
|
out2 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True) #must also response with a correct json-rpc message
|
|
assert_equal(conn.sock!=None, True) #according to http/1.1 connection must still be open!
|
|
conn.close()
|
|
|
|
#now do the same with "Connection: close"
|
|
headers = {"Authorization": "Basic " + base64.b64encode(authpair), "Connection":"close"}
|
|
|
|
conn = httplib.HTTPConnection(url.hostname, url.port)
|
|
conn.connect()
|
|
conn.request('GET', '/', '{"method": "getbestblockhash"}', headers)
|
|
out1 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True)
|
|
assert_equal(conn.sock!=None, False) #now the connection must be closed after the response
|
|
|
|
#node1 (2nd node) is running with disabled keep-alive option
|
|
urlNode1 = urlparse.urlparse(self.nodes[1].url)
|
|
authpair = urlNode1.username + ':' + urlNode1.password
|
|
headers = {"Authorization": "Basic " + base64.b64encode(authpair)}
|
|
|
|
conn = httplib.HTTPConnection(urlNode1.hostname, urlNode1.port)
|
|
conn.connect()
|
|
conn.request('GET', '/', '{"method": "getbestblockhash"}', headers)
|
|
out1 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True)
|
|
assert_equal(conn.sock!=None, False) #connection must be closed because keep-alive was set to false
|
|
|
|
#node2 (third node) is running with standard keep-alive parameters which means keep-alive is off
|
|
urlNode2 = urlparse.urlparse(self.nodes[2].url)
|
|
authpair = urlNode2.username + ':' + urlNode2.password
|
|
headers = {"Authorization": "Basic " + base64.b64encode(authpair)}
|
|
|
|
conn = httplib.HTTPConnection(urlNode2.hostname, urlNode2.port)
|
|
conn.connect()
|
|
conn.request('GET', '/', '{"method": "getbestblockhash"}', headers)
|
|
out1 = conn.getresponse().read();
|
|
assert_equal('"error":null' in out1, True)
|
|
assert_equal(conn.sock!=None, False) #connection must be closed because bitcoind should use keep-alive by default
|
|
|
|
if __name__ == '__main__':
|
|
HTTPBasicsTest ().main ()
|