def authorized_sfk(appid, token): """ Request Userbase by API """ import base64 import requests # authorize scp in userbase b64_uid = base64.standard_b64encode(CONFIG['scp_userbase']['uid']) b64_token = base64.standard_b64encode(CONFIG['scp_userbase']['token']) headers = { 'Authorization': 'Nptv %s:%s' % (b64_uid, b64_token), 'Content-Type': 'application/json' } userbase_host = CONFIG['scp_userbase']['host'] if CONFIG['scp_userbase'][ 'host'] else 'userbase.staging.nptv.home' url = 'http://%s/v2/applications/%s/auth.json' % (userbase_host, appid) data = json.dumps({'token': token}) try: resp = requests.post(url, data=data, headers=headers) except Exception as e: LOGGER.error('Authorization request fails: %s; appid %s; token %s' % (str(e), appid, token)) return False if int(resp.status_code) == 204: LOGGER.info('Authorization passed') return True else: LOGGER.info('Authorization rejected: appid %s; token %s' % (appid, token)) return False
def start_client(self, sock=None, guid=None, addr=None, first_packet=None, death_event=None): if self.alive == False: return sockwrap = ClientSockWrap(sock=sock, addr=addr, guid=guid) docker = ClientDocker(client=sockwrap, sfk=self.sfk.sockwrap, guid=guid, addr=addr) sender = eventlet.spawn(sockwrap.sender, callback=lambda: self.drop_client(guid)) recver = eventlet.spawn(sockwrap.recver, callback=lambda: self.drop_client(guid)) docker_thread = eventlet.spawn(docker.mainloop, callback=lambda: self.drop_client(guid)) self.clients[guid] = ClientSubsystem(sockwrap, docker, death_event, threads=(sender, recver, docker_thread)) self.sfk.docker.add_session(guid, sockwrap) if first_packet: try: docker.send_first(first_packet) except Exception as e: LOGGER.error("First packet sending fails: " + str(e))
def close_socket(self): try: self.sock.shutdown(socket.SHUT_RDWR) self.sock.close() LOGGER.info("{0} sockwrapper close socket".format(str(self))) except Exception as e: LOGGER.error("Fails socket close: %s" % str(e))
def recver(self, callback=lambda: None): """ recieve packets from sock, check packet's type, put packet to recv queue """ f = self.sock.makefile() try: while True: try: packet_class = determine_packet_type(f) except Disconnection as e: raise Disconnection if packet_class == self.NATIVE_PACKET: packet = packet_class() packet.read_fields(f) self.queue_recv.put(packet) else: LOGGER.error("{0} recver: unexpected magic".format( str(self))) raise UnexpectedProtocol except Disconnection as e: LOGGER.info("Disconnection: {0}".format(str(self))) except Exception as e: LOGGER.error("recver error: {0} {1}".format(str(self), str(e))) LOGGER.info(str(self) + " recver terminate") eventlet.spawn_n(callback)
def recved_packets_processor(self): try: while True: packet = self.queue_recv.get() LOGGER.info("recved: %s" % packet.bindata) except Exception as e: LOGGER.error("recved_packets_processor: %s" % str(e))
def authorized_sfk(appid, token): """ Request Userbase by API """ import base64 import requests # authorize scp in userbase b64_uid = base64.standard_b64encode(CONFIG['scp_userbase']['uid']) b64_token = base64.standard_b64encode(CONFIG['scp_userbase']['token']) headers = { 'Authorization' : 'Nptv %s:%s' % (b64_uid, b64_token), 'Content-Type' : 'application/json' } userbase_host = CONFIG['scp_userbase']['host'] if CONFIG['scp_userbase']['host'] else 'userbase.staging.nptv.home' url = 'http://%s/v2/applications/%s/auth.json' % (userbase_host, appid) data = json.dumps({'token' : token}) try: resp = requests.post(url, data=data, headers=headers) except Exception as e: LOGGER.error('Authorization request fails: %s; appid %s; token %s' % (str(e), appid, token)) return False if int(resp.status_code) == 204: LOGGER.info('Authorization passed') return True else: LOGGER.info('Authorization rejected: appid %s; token %s' % (appid, token)) return False
def recver(self, callback=lambda: None): """ recieve packets from sock, check packet's type, put packet to recv queue """ f = self.sock.makefile() try: while True: try: packet_class = determine_packet_type(f) except Disconnection as e: raise Disconnection if packet_class == self.NATIVE_PACKET: packet = packet_class() packet.read_fields(f) self.queue_recv.put(packet) else: LOGGER.error( "{0} recver: unexpected magic".format(str(self))) raise UnexpectedProtocol except Disconnection as e: LOGGER.info("Disconnection: {0}".format(str(self))) except Exception as e: LOGGER.error("recver error: {0} {1}".format(str(self), str(e))) LOGGER.info(str(self) + " recver terminate") eventlet.spawn_n(callback)
def send_packet(self, packet): data = packet.assemble() try: self.sock.sendall(data) except Exception as e: LOGGER.error("Sending auth packet fails: " + str(e)) else: LOGGER.info("Auth packet sent successfully.")
def mainloop(self, callback=lambda: None): try: while True: packet = self.client_sockwrap.get_packet() new_packet = self._pack_spif2_to_scp(packet) self.sfk_sockwrap.put_packet(new_packet) except Exception as e: LOGGER.error("ClientDocker {0} {1}".format(str_uuid(self.guid), str(e))) callback()
def _read_int(fileobj): try: data = fileobj.read(4) if not data: LOGGER.info("disconnection") # хотелось бы таки знать, что именно отвалилось return None return struct.unpack('!I', data)[0] except Exception as e: LOGGER.error( "cannot read int field from socket: {0}".format(str(e))) return None
def get_appid(self): # from client try: pb = sfk_pb2.Msg() pb.ParseFromString(self.protobuf) sfk_url = json.loads(pb.session.params)['sfk-url'] parsed_url = urlparse(sfk_url) appid = parsed_url.path[1:] # drop '/' symbol return appid except Exception as e: LOGGER.error('in get_appid: ' + str(e)) raise FieldExtractionError("cannot extract appid")
def get_auth_info(self): # from sfk in very first packet """ extract appid, token from very first (auth) packet from sfk. return (None, None) if any errors happened """ pb = scp_pb2.Msg() try: pb.ParseFromString(self.protobuf) except Exception as e: LOGGER.error(str(e)) return None, None else: return pb.auth.appid, pb.auth.token
def _read_int(fileobj): try: data = fileobj.read(4) if not data: LOGGER.info("disconnection" ) # хотелось бы таки знать, что именно отвалилось return None return struct.unpack('!I', data)[0] except Exception as e: LOGGER.error("cannot read int field from socket: {0}".format( str(e))) return None
def _make_reply_packet(self, payload='', guid=None): if guid not in self.guids: LOGGER.error("No session with guid %s" % str_uuid(guid)) pb = sfk_pb2.Msg() pb.mtype = pb.DATA pb.data.content_type = "simple" pb.data.payload = "test" protobuf = pb.SerializeToString() data = "%s %s %s" % (self.appid, payload, str_uuid(guid)) spif = prot.Spif2Packet(protobuf=protobuf, data=data) return prot.ScpPacket(guid=guid, data=spif.assemble())
def sender(self, callback=lambda: None): """get packet from sending queue, send it via sock. By convention, packet type checking performed before putting in queue """ try: while True: packet = self.queue_send.get() data = packet.assemble() self.sock.sendall(data) # TODO if DEBUG try: if packet.get_msg_type() == 'pong': LOGGER.debug('pong sent %s' % self) except AttributeError: pass except Exception: LOGGER.error(str(self) + " sender error") eventlet.spawn_n(callback)
def get_msg_type(self): pb = scp_pb2.Msg() try: pb.ParseFromString(self.protobuf) except Exception as e: LOGGER.error("Cannot parse protobuf") return None msg_type = { pb.AUTH: 'auth', pb.PING: 'ping', pb.MESSAGE: 'msg', pb.PONG: 'pong', pb.SFKCONTENT: 'sfkcontent', pb.SESSION_DROPPED: 'session_dropped', }.get(pb.mtype, None) # if msg_type is None: # raise ProtocolError("bad mtype") # else: # no one catch this exception!!! return msg_type
def register_rengine(self, sock, addr): packet = self._recv_packet(Spif2Packet, sock) if packet == None: sock.close() return try: appid = packet.get_appid() except ProtocolError: LOGGER.error( "cannot extract appid needed for just connected client") sock.close() return if not self._is_sfk_connectable(appid): LOGGER.error( "attempt connecting to non-connected sfk with appid " \ + str(appid)) sock.close() return sfk, sfk_id = self._get_sfk(appid) # after _is_connectable check # always correct guid = self._generate_guid() self.info.add_client(appid, sfk_id, guid, addr) LOGGER.info( 'Register: Client connected to appid {0} with guid {1}'.format( appid, str_uuid(guid))) # waiting for death_event prevents eventlet.serve from closing socket death_event = eventlet.event.Event() sfk.start_client(sock=sock, guid=guid, addr=addr, first_packet=packet, death_event=death_event) death_event.wait() LOGGER.info('Register: death_event from rengine guid {0}'.format( str_uuid(guid)))
def read_fields(self, fileobj): timeout = Timeout(SCP_TIMEOUT, TimeoutError('Timeout while recv scp packet')) try: s = fileobj self.flags = s.read(1) self.guid = s.read(16) pb_len = self._read_int(s) data_len = self._read_int(s) if pb_len == None or data_len == None: raise ProtocolError('Protobuf or data length not supplied') # protobuf len issues if pb_len > SCP_MAX_PROTOBUF_LEN: raise ProtocolError('Protobuf lenght too large') elif pb_len > 0: self.protobuf = s.read(pb_len) else: raise ProtocolError('Protobuf lenght 0') # data len issues if data_len != 0: self.data = s.read(data_len) else: self.data = '' except TimeoutError as e: LOGGER.error("Timeout fired while recv field of packet") raise ProtocolError(str(e)) finally: timeout.cancel() # clean alternative representation of packet self.set_raw_packet(None)
def recved_packets_processor(self): try: while True: packet = self.queue_recv.get() self.guids.add(packet.guid) mtype = packet.get_msg_type() payload = None if mtype == 'msg': buf = StringIO.StringIO(packet.data) pack_class = prot.determine_packet_type(buf) client_pack = pack_class() client_pack.read_fields(buf) payload = client_pack.bindata elif mtype == 'sfkcontent': SfkModel.sfkcontent_handler(packet) continue elif mtype == 'session_dropped': self.guids.remove(packet.guid) LOGGER.info('session dropped guid {0}'.format( str_uuid(packet.guid))) continue elif mtype == 'pong': LOGGER.info('Pong recieved') else: LOGGER.error("Unknown message type: {0}".format(mtype)) LOGGER.info('recved packet guid {0}; mtype {1}'.format( str_uuid(packet.guid), mtype)) if payload: LOGGER.info('message: %s' % payload) reply = self._make_reply_packet(payload=payload, guid=packet.guid) self.queue_send.put(reply) LOGGER.info('reply is sent') except Exception as e: LOGGER.exception("recved_packets_processor: " + str(e))
def register_rengine(self, sock, addr): packet = self._recv_packet(Spif2Packet, sock) if packet == None: sock.close() return try: appid = packet.get_appid() except ProtocolError: LOGGER.error( "cannot extract appid needed for just connected client") sock.close() return if not self._is_sfk_connectable(appid): LOGGER.error( "attempt connecting to non-connected sfk with appid " \ + str(appid)) sock.close() return sfk, sfk_id = self._get_sfk(appid) # after _is_connectable check # always correct guid = self._generate_guid() self.info.add_client(appid, sfk_id, guid, addr) LOGGER.info( 'Register: Client connected to appid {0} with guid {1}'.format( appid, str_uuid(guid))) # waiting for death_event prevents eventlet.serve from closing socket death_event = eventlet.event.Event() sfk.start_client(sock=sock, guid=guid, addr=addr, first_packet=packet, death_event=death_event) death_event.wait() LOGGER.info( 'Register: death_event from rengine guid {0}'.format( str_uuid(guid)))
def sfkcontent_handler(packet): LOGGER.info("Got SfkContent protobuf") uri, request_id, headers = packet.get_sfkcontent() LOGGER.info("uri: %s" % uri) LOGGER.info("request_id: %s" % request_id) url = '/'.join([STATIC_SERV_URL, request_id, uri]) LOGGER.info("URL: %s" % url) # time.sleep(2) if uri in URI_PATH: data=open(URI_PATH[uri], 'rb').read() headers={'Content-Type': 'image/jpg', 'X-SCP-Status': '200 OK'} else: LOGGER.error("Chpoken uri: %s" % uri) data='No way!' headers={'Content-Type': 'text/plain', 'X-SCP-Status': '404 Not Found'} r = requests.post(url, data=data, headers=headers) LOGGER.info("Uri %s sent, with response code %s" % (uri, r.status_code)) if int(r.status_code) != 200: LOGGER.error(r.text) LOGGER.info("request headers") for header in headers: LOGGER.info(header) LOGGER.info("response headers") for header, val in r.headers.items(): LOGGER.info("%s: %s" % (header, val)) return None
def sfkcontent_handler(packet): LOGGER.info("Got SfkContent protobuf") uri, request_id, headers = packet.get_sfkcontent() LOGGER.info("uri: %s" % uri) LOGGER.info("request_id: %s" % request_id) url = '/'.join([STATIC_SERV_URL, request_id, uri]) LOGGER.info("URL: %s" % url) # time.sleep(2) if uri in URI_PATH: data = open(URI_PATH[uri], 'rb').read() headers = {'Content-Type': 'image/jpg', 'X-SCP-Status': '200 OK'} else: LOGGER.error("Chpoken uri: %s" % uri) data = 'No way!' headers = { 'Content-Type': 'text/plain', 'X-SCP-Status': '404 Not Found' } r = requests.post(url, data=data, headers=headers) LOGGER.info("Uri %s sent, with response code %s" % (uri, r.status_code)) if int(r.status_code) != 200: LOGGER.error(r.text) LOGGER.info("request headers") for header in headers: LOGGER.info(header) LOGGER.info("response headers") for header, val in r.headers.items(): LOGGER.info("%s: %s" % (header, val)) return None
def sfk_side(self, request_id, uri): """ Handle POST requests from sfk """ if not self.sfk_authorization_ok(request_id): LOGGER.info('Sfk POST bad request_id') abort(401, "Bad request_id") body = request.body headers = request.headers # cut custom headers for scp if 'X-SCP-Status' in headers: # headers case-insensitive status = headers['X-SCP-Status'] passed_headers = dict((k, v) for (k, v) in headers.items() if not k.startswith(_hkey('X-SCP'))) status_supplied = True else: status = '500 Internal Server Error' body = '' passed_headers = {} status_supplied = False LOGGER.error("X-SCP-Status was not supplied") resp = HTTPResponse(body, status, **passed_headers) evt = self.request_id_events.get(request_id, None) if evt is None: # если нет такого ожидаемого события, то уже прошел таймаут abort(408, '') # Request Timeout else: evt.send(resp) del self.request_id_events[request_id] if status_supplied: return '' # пустой ответ с кодом 200 else: abort(400, 'X-Scp-Status not supplied')
def register_sfk(self, sfk_sock, sfk_addr): auth_packet = self._recv_packet(ScpPacket, sfk_sock) # basic auth packet check basic_test_passed = False # just init value error_msg = "" if auth_packet == None: error_msg = "Error when auth packed recved" elif auth_packet.get_msg_type() != 'auth': error_msg = "Unexpected message type while auth attempt" elif len(auth_packet.data) != 0: error_msg = "Auth packet contains unexpected data" else: basic_test_passed = True if not basic_test_passed: LOGGER.error(error_msg) sfk_sock.close() return appid, token = auth_packet.get_auth_info() # check authorization auth_check_passed = False if not appid or not token: LOGGER.error("Appid or Token not supplied") elif NEED_AUTH and not authorized_sfk(appid, token): LOGGER.error( "Bad appid/token pair from just connected sfk: %s %s" % (appid, token)) else: auth_check_passed = True if not auth_check_passed: sfk_sock.close() return sfk_id = str(uuid.uuid4()) LOGGER.info("Sfk with appid %s, sfk_id %s connected" % (str_uuid(appid), sfk_id)) # waiting for death_event prevents eventlet.serve from closing socket death_event = eventlet.event.Event() sfk_supervizor = SfkSupervizor(sfk_sock=sfk_sock, registrar=self, appid=appid, sfk_id=sfk_id, sfk_addr=sfk_addr, death_event=death_event) self.remember_sfk(sfk_supervizor, appid, sfk_id) self.info.add_sfk_instance(appid, sfk_id, sfk_addr) death_event.wait() LOGGER.info( 'Register: death_event from sfk appid {0}; sfk_id {1}'.format( appid, sfk_id))
def register_sfk(self, sfk_sock, sfk_addr): auth_packet = self._recv_packet(ScpPacket, sfk_sock) # basic auth packet check basic_test_passed = False # just init value error_msg = "" if auth_packet == None: error_msg = "Error when auth packed recved" elif auth_packet.get_msg_type() != 'auth': error_msg = "Unexpected message type while auth attempt" elif len(auth_packet.data) != 0: error_msg = "Auth packet contains unexpected data" else: basic_test_passed = True if not basic_test_passed: LOGGER.error(error_msg) sfk_sock.close() return appid, token = auth_packet.get_auth_info() # check authorization auth_check_passed = False if not appid or not token: LOGGER.error("Appid or Token not supplied") elif NEED_AUTH and not authorized_sfk(appid, token): LOGGER.error("Bad appid/token pair from just connected sfk: %s %s" % (appid, token)) else: auth_check_passed = True if not auth_check_passed: sfk_sock.close() return sfk_id = str(uuid.uuid4()) LOGGER.info("Sfk with appid %s, sfk_id %s connected" % (str_uuid(appid), sfk_id)) # waiting for death_event prevents eventlet.serve from closing socket death_event = eventlet.event.Event() sfk_supervizor = SfkSupervizor(sfk_sock=sfk_sock, registrar=self, appid=appid, sfk_id=sfk_id, sfk_addr=sfk_addr, death_event=death_event) self.remember_sfk(sfk_supervizor, appid, sfk_id) self.info.add_sfk_instance(appid, sfk_id, sfk_addr) death_event.wait() LOGGER.info('Register: death_event from sfk appid {0}; sfk_id {1}'.format(appid, sfk_id))
def _recv_packet(self, expected_packet_class, sock): f = sock.makefile() timeout = Timeout(CONFIG['scp_protocol']['scp_timeout']) try: packet_class = determine_packet_type(f) except Timeout as t: if t is not timeout: LOGGER.error("Accidently catch wrong timeout!") else: LOGGER.error("Timeout fired when try to recieve magic") return None except (UnexpectedProtocol, Disconnection) as e: LOGGER.error(e) return None finally: timeout.cancel() if packet_class != expected_packet_class: LOGGER.error("Authorization fails: unexpected packet type") return None packet = packet_class() # TODO timeout try: packet.read_fields(f) except socket.error as e: LOGGER.error("Cannot recieve packet correctly, %s" % e) return None except ProtocolError as e: LOGGER.error("Cannot recieve packet correctly, %s" % e) return None except Exception as e: LOGGER.error("Cannot recieve packet correctly, %s" % e) return None return packet