1#!/usr/bin/env python3
2# Copyright (c) 2014-2019 The Bitcoin Core developers
3# Distributed under the MIT software license, see the accompanying
4# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5"""Test the RPC HTTP basics."""
6
7from test_framework.test_framework import BitcoinTestFramework
8from test_framework.util import assert_equal, str_to_b64str
9
10import http.client
11import urllib.parse
12
13class HTTPBasicsTest (BitcoinTestFramework):
14    def set_test_params(self):
15        self.num_nodes = 3
16        self.supports_cli = False
17
18    def setup_network(self):
19        self.setup_nodes()
20
21    def run_test(self):
22
23        #################################################
24        # lowlevel check for http persistent connection #
25        #################################################
26        url = urllib.parse.urlparse(self.nodes[0].url)
27        authpair = url.username + ':' + url.password
28        headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
29
30        conn = http.client.HTTPConnection(url.hostname, url.port)
31        conn.connect()
32        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
33        out1 = conn.getresponse().read()
34        assert b'"error":null' in out1
35        assert conn.sock is not None  #according to http/1.1 connection must still be open!
36
37        #send 2nd request without closing connection
38        conn.request('POST', '/', '{"method": "getchaintips"}', headers)
39        out1 = conn.getresponse().read()
40        assert b'"error":null' in out1  #must also response with a correct json-rpc message
41        assert conn.sock is not None  #according to http/1.1 connection must still be open!
42        conn.close()
43
44        #same should be if we add keep-alive because this should be the std. behaviour
45        headers = {"Authorization": "Basic " + str_to_b64str(authpair), "Connection": "keep-alive"}
46
47        conn = http.client.HTTPConnection(url.hostname, url.port)
48        conn.connect()
49        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
50        out1 = conn.getresponse().read()
51        assert b'"error":null' in out1
52        assert conn.sock is not None  #according to http/1.1 connection must still be open!
53
54        #send 2nd request without closing connection
55        conn.request('POST', '/', '{"method": "getchaintips"}', headers)
56        out1 = conn.getresponse().read()
57        assert b'"error":null' in out1  #must also response with a correct json-rpc message
58        assert conn.sock is not None  #according to http/1.1 connection must still be open!
59        conn.close()
60
61        #now do the same with "Connection: close"
62        headers = {"Authorization": "Basic " + str_to_b64str(authpair), "Connection":"close"}
63
64        conn = http.client.HTTPConnection(url.hostname, url.port)
65        conn.connect()
66        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
67        out1 = conn.getresponse().read()
68        assert b'"error":null' in out1
69        assert conn.sock is None  #now the connection must be closed after the response
70
71        #node1 (2nd node) is running with disabled keep-alive option
72        urlNode1 = urllib.parse.urlparse(self.nodes[1].url)
73        authpair = urlNode1.username + ':' + urlNode1.password
74        headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
75
76        conn = http.client.HTTPConnection(urlNode1.hostname, urlNode1.port)
77        conn.connect()
78        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
79        out1 = conn.getresponse().read()
80        assert b'"error":null' in out1
81
82        #node2 (third node) is running with standard keep-alive parameters which means keep-alive is on
83        urlNode2 = urllib.parse.urlparse(self.nodes[2].url)
84        authpair = urlNode2.username + ':' + urlNode2.password
85        headers = {"Authorization": "Basic " + str_to_b64str(authpair)}
86
87        conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
88        conn.connect()
89        conn.request('POST', '/', '{"method": "getbestblockhash"}', headers)
90        out1 = conn.getresponse().read()
91        assert b'"error":null' in out1
92        assert conn.sock is not None  #connection must be closed because bitcoind should use keep-alive by default
93
94        # Check excessive request size
95        conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
96        conn.connect()
97        conn.request('GET', '/' + ('x'*1000), '', headers)
98        out1 = conn.getresponse()
99        assert_equal(out1.status, http.client.NOT_FOUND)
100
101        conn = http.client.HTTPConnection(urlNode2.hostname, urlNode2.port)
102        conn.connect()
103        conn.request('GET', '/' + ('x'*10000), '', headers)
104        out1 = conn.getresponse()
105        assert_equal(out1.status, http.client.BAD_REQUEST)
106
107
108if __name__ == '__main__':
109    HTTPBasicsTest ().main ()
110