def _transaction(self, bert_request): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) if self.service.timeout is not None: sock.settimeout(self.service.timeout) sock.connect((self.service.host, self.service.port)) if self.request.options is not None: if self.request.options.get('cache', None) is not None: if self.request.options['cache'][0] == 'validation': token = self.request.options['cache'][1] info_bert = Encoder().encode( (bert.Atom('info'), bert.Atom('cache'), [bert.Atom('validation'), bert.Atom(token)])) info_header = struct.pack(">l", len(info_bert)) sock.sendall(info_header) sock.sendall(info_bert) header = struct.pack(">l", len(bert_request)) sock.sendall(header) sock.sendall(bert_request) lenheader = sock.recv(4) if lenheader is None: raise error.ProtocolError(error.ProtocolError.NO_HEADER) length = struct.unpack(">l", lenheader)[0] bert_response = sock.recv(length) if bert_response is None or len(bert_response) == 0: raise error.ProtocolError(error.ProtocolError.NO_DATA) sock.close() return bert_response except socket.timeout, e: raise error.ReadTimeoutError( 'No response from %s:%s in %ss' % (self.service.host, self.service.port, self.service.timeout))
def decode(self, bert_response): python_response = bert.decode(bert_response) if python_response[0] == bert.Atom('reply'): return python_response[1] elif python_response[0] == bert.Atom('noreply'): return None elif python_response[0] == bert.Atom('error'): return self._error(python_response[1]) else: raise error.BERTRPCError('invalid response received from server')
def _error(self, err): level, code, klass, message, backtrace = err exception_map = { bert.Atom('protocol'): error.ProtocolError, bert.Atom('server'): error.ServerError, bert.Atom('user'): error.UserError, bert.Atom('proxy'): error.ProxyError } exception = exception_map.get(level, None) if level is not None: raise exception([code, message], klass, backtrace) else: raise error.BERTRPCError('invalid error code received from server')
def request(self, kind, options=None): if kind in ['call', 'cast']: self._verify_options(options) return Request(self, bert.Atom(kind), options) else: raise error.InvalidRequest('unsupported request of kind: "%s"' % kind)
def start(self): Ernie.log("Starting") # On windows nouse_stdio is ignored by Erlang at the port creation, # so we cannot use file descriptor 3 and 4 for communication. # We must use file descriptors 0 and 1. fdi, fdo = (0, 1) if os.name is 'nt' else (3, 4) input = os.fdopen(fdi) output = os.fdopen(fdo, "w") while (True): ipy = self.read_berp(input) if ipy == None: print 'Could not read BERP length header. Ernie server may have gone away. Exiting now.' exit() if len(ipy) == 4 and ipy[0] == bert.Atom('call'): mod, fun, args = ipy[1:4] self.log("-> " + ipy.__str__()) try: res = self.dispatch(mod, fun, args) opy = (bert.Atom('reply'), res) self.log("<- " + opy.__str__()) self.write_berp(output, opy) except ServerError, e: opy = (bert.Atom('error'), (bert.Atom('server'), 0, str(type(e)), str(e), '')) self.log("<- " + opy.__str__()) self.write_berp(output, opy) except Exception, e: opy = (bert.Atom('error'), (bert.Atom('user'), 0, str(type(e)), str(e), '')) self.log("<- " + opy.__str__()) self.write_berp(output, opy)
def generate_error(etype, value, backtrace): error_type = bert.Atom(getattr(etype, 'error_type', 'user')) error_code = getattr(etype, 'error_code', 100) return ( error_atom, ( error_type, error_code, etype.__name__, str(value.args[0] ), # We can only assume the first exception argument # is the message traceback.format_tb(backtrace), ))
import eventlet import struct import bert import logging import sys import traceback import zlib from types import ModuleType from bertlet.exceptions import (InvalidFunction, ProtocolError, InvalidModule, InvalidFunction, InvalidInfo, CloseSession) bert_decode = bert.BERTDecoder().decode bert_encode = bert.BERTEncoder().encode call_atom = bert.Atom('call') cast_atom = bert.Atom('cast') reply_atom = bert.Atom('reply') noreply_atom = bert.Atom('noreply') error_atom = bert.Atom('error') info_atom = bert.Atom('info') encode_atom = bert.Atom('encoding') gzip_atom = bert.Atom('gzip') accept_encoding_atom = bert.Atom('accept_encoding') GZIP_INFO_BERT = (info_atom, encode_atom, [(gzip_atom, )]) GZIP_ACCEPT_BERT = (info_atom, accept_encoding_atom, [(gzip_atom, )]) def create_berp(bert): return '%s%s' % (struct.pack('>I', len(bert)), bert)
class Ernie(object): mods = {} logger = None @classmethod def mod(cls, name): if cls.mods.has_key(name) == False: cls.mods[name] = Mod(name) return cls.mods[name] @classmethod def logfile(cls, file): logging.basicConfig(filename=file, level=logging.DEBUG) cls.logger = logging.getLogger('ernie') @classmethod def log(cls, text): if cls.logger != None: cls.logger.debug(text) def dispatch(self, mod, fun, args): if Ernie.mods.has_key(mod) == False: raise ServerError("No such module '" + mod + "'") if Ernie.mods[mod].funs.has_key(fun) == False: raise ServerError("No such function '" + mod + ":" + fun + "'") return Ernie.mods[mod].funs[fun](*args) def read_4(self, input): raw = input.read(4) if len(raw) == 0: return None return struct.unpack('!L', raw)[0] def read_berp(self, input): packet_size = self.read_4(input) if packet_size == None: return None ber = input.read(packet_size) return bert.decode(ber) def write_berp(self, output, obj): data = bert.encode(obj) output.write(struct.pack("!L", len(data))) output.write(data) output.flush() def start(self): Ernie.log("Starting") # On windows nouse_stdio is ignored by Erlang at the port creation, # so we cannot use file descriptor 3 and 4 for communication. # We must use file descriptors 0 and 1. fdi, fdo = (0, 1) if os.name is 'nt' else (3, 4) input = os.fdopen(fdi) output = os.fdopen(fdo, "w") while (True): ipy = self.read_berp(input) if ipy == None: print 'Could not read BERP length header. Ernie server may have gone away. Exiting now.' exit() if len(ipy) == 4 and ipy[0] == bert.Atom('call'): mod, fun, args = ipy[1:4] self.log("-> " + ipy.__str__()) try: res = self.dispatch(mod, fun, args) opy = (bert.Atom('reply'), res) self.log("<- " + opy.__str__()) self.write_berp(output, opy) except ServerError, e: opy = (bert.Atom('error'), (bert.Atom('server'), 0, str(type(e)), str(e), '')) self.log("<- " + opy.__str__()) self.write_berp(output, opy) except Exception, e: opy = (bert.Atom('error'), (bert.Atom('user'), 0, str(type(e)), str(e), '')) self.log("<- " + opy.__str__()) self.write_berp(output, opy) elif len(ipy) == 4 and ipy[0] == bert.Atom('cast'): mod, fun, args = ipy[1:4] self.log("-> " + ipy.__str__()) try: res = self.dispatch(mod, fun, args) except: pass self.write_berp(output, (bert.Atom('noreply')))
except Exception, e: opy = (bert.Atom('error'), (bert.Atom('user'), 0, str(type(e)), str(e), '')) self.log("<- " + opy.__str__()) self.write_berp(output, opy) elif len(ipy) == 4 and ipy[0] == bert.Atom('cast'): mod, fun, args = ipy[1:4] self.log("-> " + ipy.__str__()) try: res = self.dispatch(mod, fun, args) except: pass self.write_berp(output, (bert.Atom('noreply'))) else: self.log("-> " + ipy.__str__()) opy = (bert.Atom('error'), (bert.Atom('server'), 0, "Invalid request: " + ipy.__str__())) self.log("<- " + opy.__str__()) self.write_berp(output, opy) class ServerError(Exception): def __init__(self, value): self.value = value def __str__(self): return repr(self.value) class Mod:
def method_missing(self, *args, **kwargs): return Action(self.service, self.request, self.module, bert.Atom(args[0]), list(args[1:])).execute()
def __getattr__(self, attr): return Module(self.service, self, bert.Atom(attr))
def testRequestEncoder(self): bert_encoded = "\203h\004d\000\004calld\000\005mymodd\000\005myfunl\000\000\000\003a\001a\002a\003j" request = (bert.Atom('call'), bert.Atom('mymod'), bert.Atom('myfun'), [1, 2, 3]) self.assertEqual(bert_encoded, bertrpc.client.Encoder().encode(request))