xref: /spdk/scripts/rpc_http_proxy.py (revision 2f5c602574a98ede645991abe279a96e19c50196)
1#!/usr/bin/env python3
2
3import argparse
4import base64
5import errno
6import json
7import socket
8import ssl
9import sys
10try:
11    from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
12except ImportError:
13    from http.server import HTTPServer
14    from http.server import BaseHTTPRequestHandler
15from rpc.client import print_json
16
17rpc_sock = None
18
19parser = argparse.ArgumentParser(description='http(s) proxy for SPDK RPC calls')
20parser.add_argument('host', help='Host name / IP representing proxy server')
21parser.add_argument('port', help='Port number', type=int)
22parser.add_argument('user', help='User name used for authentication')
23parser.add_argument('password', help='Password used for authentication')
24parser.add_argument('-s', dest='sock', help='RPC domain socket path', default='/var/tmp/spdk.sock')
25parser.add_argument('-c', dest='cert', help='SSL certificate')
26
27
28def print_usage_and_exit(status):
29    print('Usage: rpc_http_proxy.py <server IP> <server port> <user name>' +
30          ' <password> <SPDK RPC socket (optional, default: /var/tmp/spdk.sock)>')
31    sys.exit(status)
32
33
34def rpc_call(req):
35    global rpc_sock
36
37    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
38    sock.connect(rpc_sock)
39    sock.sendall(req)
40
41    if 'id' not in json.loads(req.decode('ascii')):
42        sock.close()
43        return None
44
45    buf = ''
46    closed = False
47    response = None
48
49    print_json(req.decode('ascii'))
50
51    while not closed:
52        newdata = sock.recv(1024)
53        if (newdata == b''):
54            closed = True
55        buf += newdata.decode('ascii')
56        try:
57            response = json.loads(buf)
58        except ValueError:
59            continue  # incomplete response; keep buffering
60        break
61
62    sock.close()
63
64    if not response and len(buf) > 0:
65        raise
66
67    print_json(buf)
68
69    return buf
70
71
72class ServerHandler(BaseHTTPRequestHandler):
73
74    key = ""
75
76    def do_HEAD(self):
77        self.send_response(200)
78        self.send_header('Content-type', 'text/html')
79        self.end_headers()
80
81    def do_AUTHHEAD(self):
82        self.send_response(401)
83        self.send_header('WWW-Authenticate', 'text/html')
84        self.send_header('Content-type', 'text/html')
85        self.end_headers()
86
87    def do_INTERNALERROR(self):
88        self.send_response(500)
89        self.send_header('Content-type', 'text/html')
90        self.end_headers()
91
92    def do_POST(self):
93        if self.headers['Authorization'] != 'Basic ' + self.key:
94            self.do_AUTHHEAD()
95        else:
96            data_string = self.rfile.read(int(self.headers['Content-Length']))
97
98            try:
99                response = rpc_call(data_string)
100                if response is not None:
101                    self.do_HEAD()
102                    self.wfile.write(bytes(response.encode(encoding='ascii')))
103            except ValueError:
104                self.do_INTERNALERROR()
105
106
107def main():
108    global rpc_sock
109
110    args = parser.parse_args()
111    rpc_sock = args.sock
112
113    # encoding user name and password
114    key = base64.b64encode((args.user+':'+args.password).encode(encoding='ascii')).decode('ascii')
115
116    try:
117        ServerHandler.key = key
118        httpd = HTTPServer((args.host, args.port), ServerHandler)
119        if args.cert is not None:
120            httpd.socket = ssl.wrap_socket(httpd.socket, certfile=args.cert, server_side=True)
121        print('Started RPC http proxy server')
122        httpd.serve_forever()
123    except KeyboardInterrupt:
124        print('Shutting down server')
125        httpd.socket.close()
126
127
128if __name__ == '__main__':
129    main()
130