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