def on_message(self, message): # called in tornado's thread if self._writeable is None: ipv4_ip = self.request.remote_ip if ipv4_ip == "::1": # Special case IPV6 loopback ipv4_ip = "127.0.0.1" remoteaddr = struct.unpack("!I", socket.inet_aton(ipv4_ip))[0] if self._validators: # Work out if the remote ip is within the netmask of any of our # interfaces. If not, Put and Post are forbidden self._writeable = max(v(remoteaddr) for v in self._validators) else: self._writeable = True log.info( "Puts and Posts are %s from %s", "allowed" if self._writeable else "forbidden", self.request.remote_ip, ) msg_id = -1 try: d = json_decode(message) try: msg_id = d["id"] except KeyError: raise FieldError("id field not present in JSON message") request = deserialize_object(d, Request) request.set_callback(self.on_response) if isinstance(request, Subscribe): assert msg_id not in self._id_to_mri, ( "Duplicate subscription ID %d" % msg_id ) self._id_to_mri[msg_id] = request.path[0] if isinstance(request, Unsubscribe): mri = self._id_to_mri[msg_id] else: mri = request.path[0] if isinstance(request, (Put, Post)) and not self._writeable: raise ValueError( "Put/Post is forbidden from %s" % self.request.remote_ip ) self._registrar.report(builtin.infos.RequestInfo(request, mri)) except Exception as e: log.exception("Error handling message:\n%s", message) error = Error(msg_id, e) error_message = error.to_dict() self.write_message(json_encode(error_message))
def callback(value=None): # TODO: ordering is not maintained here... # TODO: should we strip_tuples here? d = value.toDict(True) if d.get("typeid", "") == Error.typeid: response = Error(request.id, d["message"]) self._monitors.pop(request.generate_key()) channel.unsubscribe("") else: # TODO: support Deltas properly if request.delta: response = Delta(request.id, [[[], d]]) else: response = Update(request.id, d) request.callback(response)
def _report_fault(self): # Called in cothread thread with self._lock: if self.state.value != self.state_set.DISABLING: self.transition(self.state_set.FAULT, "Server disconnected") self._connected_queue.put(None) for id in list(self._request_lookup): request = self._request_lookup.pop(id) response = Error(id=request.id, message=ResponseError("Server disconnected")) try: request.callback(response) except Exception: # Most things will error here, not really a problem self.log.debug("Callback %s raised", request.callback)
def recv_loop(self): url = "ws://%(hostname)s:%(port)d/ws" % self.params self._conn = yield websocket_connect( url, self.loop, connect_timeout=self.params.connectTimeout - 0.5) self._connected_queue.put(True) for request in self._subscription_keys.values(): self._send_request(request) while True: message = yield self._conn.read_message() if message is None: for request, old_id in self._request_lookup.values(): if not isinstance(request, Subscribe): # Respond with an error response = Error(old_id, message="Server disconnected") request.callback(response) self.spawn(self._report_fault) return self.on_message(message)
def _pv_error_structure(self, exception): """Make an error structure in lieu of actually being able to raise""" error = Error(message=str(exception)).to_dict() error.pop("id") return dict_to_pv_object(error)
def _response_from_dict(self, request, d): if d.get("typeid", "") == Error.typeid: response = Error(request.id, d["message"]) else: response = Return(request.id, d) return response