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