#! /usr/bin/env python from ZSI import * from ZSI import _copyright, resolvers, _child_elements, _textprotect import sys, time, cStringIO as StringIO from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from sclasses import Operation, WSDL_DEFINITION, TC_SOAPStruct class InteropRequestHandler(BaseHTTPRequestHandler): server_version = 'ZSI/1.2 OS390/VM5.4 ' + BaseHTTPRequestHandler.server_version def send_xml(self, text, code=200): '''Send some XML.''' self.send_response(code) self.send_header('Content-type', 'text/xml; charset="utf-8"') self.send_header('Content-Length', str(len(text))) self.end_headers() self.wfile.write(text) self.trace(text, 'SENT') self.wfile.flush() def send_fault(self, f): '''Send a fault.''' self.send_xml(f.AsSOAP(), 500) def trace(self, text, what): '''Log a debug/trace message.''' F = self.server.tracefile if not F: return print >>F, '=' * 60, '\n%s %s %s %s:' % \ (what, self.client_address, self.path, time.ctime(time.time())) print >>F, text print >>F, '=' * 60, '\n' F.flush() def do_QUIT(self): '''Quit.''' self.server.quitting = 1 self.log_message('Got QUIT command') sys.stderr.flush() raise SystemExit def do_GET(self): '''The GET command. Always returns the WSDL.''' self.send_xml(WSDL_DEFINITION.replace('>>>URL<<<', self.server.url)) def do_POST(self): '''The POST command.''' try: # SOAPAction header. action = self.headers.get('soapaction', None) if not action: self.send_fault(Fault(Fault.Client, 'SOAPAction HTTP header missing.')) return if action != Operation.SOAPAction: self.send_fault(Fault(Fault.Client, 'SOAPAction is "%s" not "%s"' % \ (action, Operation.SOAPAction))) return # Parse the message. ct = self.headers['content-type'] if ct.startswith('multipart/'): cid = resolvers.MIMEResolver(ct, self.rfile) xml = cid.GetSOAPPart() ps = ParsedSoap(xml, resolver=cid.Resolve) else: cl = int(self.headers['content-length']) IN = self.rfile.read(cl) self.trace(IN, 'RECEIVED') ps = ParsedSoap(IN) except ParseException, e: self.send_fault(FaultFromZSIException(e)) return except Exception, e: # Faulted while processing; assume it's in the header. self.send_fault(FaultFromException(e, 1, sys.exc_info()[2])) return try: # Actors? a = ps.WhatActorsArePresent() if len(a): self.send_fault(FaultFromActor(a[0])) return # Is the operation defined? root = ps.body_root if root.namespaceURI != Operation.ns: self.send_fault(Fault(Fault.Client, 'Incorrect namespace "%s"' % root.namespaceURI)) return n = root.localName op = Operation.dispatch.get(n, None) if not op: self.send_fault(Fault(Fault.Client, 'Undefined operation "%s"' % n)) return # Scan headers. First, see if we understand all headers with # mustUnderstand set. Then, get the ones intended for us (ignoring # others since step 1 insured they're not mustUnderstand). for mu in ps.WhatMustIUnderstand(): if mu not in op.headers: uri, localname = mu self.send_fault(FaultFromNotUnderstood(uri, localname)) return headers = [ e for e in ps.GetMyHeaderElements() if (e.namespaceURI, e.localName) in op.headers ] nsdict={ 'Z': Operation.ns } if headers: nsdict['E'] = Operation.hdr_ns self.process_headers(headers, ps) else: self.headertext = None try: results = op.TCin.parse(ps.body_root, ps) except ParseException, e: self.send_fault(FaultFromZSIException(e)) self.trace(str(results), 'PARSED') if op.convert: results = op.convert(results) if op.nsdict: nsdict.update(op.nsdict) reply = StringIO.StringIO() sw = SoapWriter(reply, nsdict=nsdict, header=self.headertext) sw.serialize(results, op.TCout, name = 'Z:' + n + 'Response', inline=1) sw.close() self.send_xml(reply.getvalue()) except Exception, e: # Fault while processing; now it's in the body. self.send_fault(FaultFromException(e, 0, sys.exc_info()[2])) return def process_headers(self, headers, ps): '''Process headers, set self.headertext to be what to output. ''' self.headertext = '' for h in headers: if h.localName == 'echoMeStringRequest': s = TC.String().parse(h, ps) self.headertext += \ '%s\n' % _textprotect(s) elif h.localName == 'echoMeStructRequest': tc = TC_SOAPStruct('echoMeStructRequest', inline=1) data = tc.parse(h, ps) s = StringIO.StringIO() sw = SoapWriter(s, envelope=0) tc.serialize(sw, data, name='E:echoMeStructResponse') sw.close() self.headertext += s.getvalue() else: raise TypeError('Unhandled header ' + h.nodeName) pass class InteropHTTPServer(HTTPServer): def __init__(self, me, url, **kw): HTTPServer.__init__(self, me, InteropRequestHandler) self.quitting = 0 self.tracefile = kw.get('tracefile', None) self.url = url def handle_error(self, req, client_address): if self.quitting: sys.exit(0) HTTPServer.handle_error(self, req, client_address) import getopt try: (opts, args) = getopt.getopt(sys.argv[1:], 'l:p:t:u:', ('log=', 'port=', 'tracefile=', 'url=') ) except getopt.GetoptError, e: print >>sys.stderr, sys.argv[0] + ': ' + str(e) sys.exit(1) if args: print >>sys.stderr, sys.argv[0] + ': Usage error.' sys.exit(1) portnum = 1122 tracefile = None url = None for opt, val in opts: if opt in [ '-l', '--logfile' ]: sys.stderr = open(val, 'a') elif opt in [ '-p', '--port' ]: portnum = int(val) elif opt in [ '-t', '--tracefile' ]: if val == '-': tracefile = sys.stdout else: tracefile = open(val, 'a') elif opt in [ '-u', '--url' ]: url = val ME = ( '', portnum ) if not url: import socket url = 'http://' + socket.getfqdn() if portnum != 80: url += ':%d' % portnum url += '/interop.wsdl' try: InteropHTTPServer(ME, url, tracefile=tracefile).serve_forever() except SystemExit: pass sys.exit(0)