def handle_aggregation(self, request_indication): """Handles aggregation for request_indications where path starts with aggregate:///""" p = Promise() # check if really notify if request_indication.method != "notify": return p.reject(SCLMethodNotAllowed(request_indication.method)) path = urlparse(request_indication.path).path[1:] self.logger.debug("aggregating for path: %s", path) # missing content_type signals me unparsed resource, so parse it ct = request_indication.content_type if ct: serializer = get_serializer(ct) typename, resource = serializer.decode(request_indication.resource) self.logger.debug("decoded resource: %s - %s", resource, typename) data = resource data["requestingEntity"] = request_indication.requestingEntity else: data = request_indication.resource data["requestingEntity"] = request_indication.requestingEntity self.logger.debug("Set requestingEntity to %s", request_indication.requestingEntity) # TODO: use dict.get() # check if there's a running aggregation for the path already # if so, just add the notify and promise to the maps if path in aggregate_data.ncolmap and len( aggregate_data.ncolmap[path]) > 0: aggregate_data.ncolmap[path].append(data) aggregate_data.ncolpromises[path].append(p) self.logger.debug("added notification to database, size: %s", len(aggregate_data.ncolmap[path])) # otherwise, start a timer and create ney keys in the maps else: self.logger.debug("no pending notifies for %s", path) aggregate_data.ncolmap[path] = [data] aggregate_data.ncolpromises[path] = [p] try: dtol = aggregate_data.ncoldelaytolerance[path] except KeyError: # FIXME (ren) put default delay tolerance somewhere dtol = 10 # to be fired after delaytolerance timer is over def dtol_handler(): self.logger.debug("dtol_handler FIRING") payload = list(aggregate_data.ncolmap[path]) promises = list(aggregate_data.ncolpromises[path]) # remove path from maps aggregate_data.ncolmap.pop(path, None) aggregate_data.ncolpromises.pop(path, None) # FIXME (ren): from log: "Specifying typename is deprecated" notify_req_ind = NotifyRequestIndication( path, {"notify": payload}, typename="notifyCollection") self.logger.debug("dtol_handler sends collection: %s", payload) def finished(response): # fulfill each pending promise of the original notifies result = NotifyResponseConfirmation() for pending_p in promises: pending_p.fulfill(result) self.logger.debug("confirmed %s notifications for: %s", len(promises), path) return self.api.send_request_indication(notify_req_ind).then( finished) self.logger.debug("starting dtol_handler, countdown: %s seconds", dtol) self.api.set_timer(dtol, dtol_handler) return p
def send_request(self, request): with Promise() as p: # TODO: caching of clients and answers # TODO: set accept # TODO: set auth # TODO: other headers? # TODO: params fullpath = request.path parsed = urlparse(fullpath) client = self._get_client(parsed) method = self.__methodmap[request.method] payload = request.payload if payload: headers = {"Content-Type": request.content_type} # TODO: do we need to set Content-Length? else: headers = {} t = time() if request.originator: headers["From"] = request.originator if request.metadata: for k, v in request.metadata.items(): headers[k] = v path = parsed.path self.logger.debug("Request params: %s", request.params) if request.params: path += "?" param_str = [ k + "=" + str(v) for k, v in request.params.items() ] path += "&".join(param_str) self.logger.debug("%s %s (%s)\n%r", method, path, headers, request.payload) try: # FIXME: rst: geventhttpclient host header is not ipv6 safe # it is missing the brackets around the IP # our notification server is ignoring it but other servers could # handle this as an error and it is not correct wrt the standard headers['Host'] = parsed.netloc payload = request.payload # this was a try/catch block before, but broke the code # TODO: if openmtc can have streams here, too, those also have # TODO: to be dealt with if type(payload) is Input: payload = payload.read() response = client.request(method, path, payload, headers) except (SocketError, gaierror) as exc: self._handle_network_error(exc, p, method, parsed, path, t, ConnectionFailed) except Exception as exc: self.logger.exception("Error in HTTP request") self._handle_network_error(exc, p, method, parsed, path, t) else: try: status_code = response.get_code() content_type = response.get("Content-Type") self.logger.debug("%s %s result: %s (%.4fs)", method, fullpath, status_code, time() - t) self.logger.debug("Response headers: %s", response.items()) if status_code >= 400: data = self._get_error_message(response, content_type) if content_type is 'text/plain': msg = "Error during execution: %s - %s" \ "Request was: %s %s." % (status_code, data, method, request.path) p.reject( ErrorResponse(status_code, msg, "text/plain")) else: p.reject( ErrorResponse(status_code, data, content_type, metadata=dict(response.headers))) elif status_code < 200 or status_code >= 300: raise OpenMTCNetworkError(status_code) else: p.fulfill( Response(status_code, response.read() or None, content_type, location=response.get("Location") or response.get("Content-Location"), metadata=dict(response.headers))) finally: response.release() return p
def test_fulfill_self(): p = Promise() assert_raises(TypeError, p.fulfill, p)
def test_fake_promise(): p = Promise() p.fulfill(FakePromise()) assert p.isRejected assert_exception(p.reason, Exception, "FakePromise raises in 'then'")
def df(value, dtime): p = Promise() t = DelayedFulfill(dtime, p, value) t.start() return p
def dr(reason, dtime): p = Promise() t = DelayedRejection(dtime, p, reason) t.start() return p
def test_wait_if(): p1 = Promise() p1.fulfill(5) p1.wait() assert p1.isFulfilled
def test_get_if(): p1 = Promise() p1.fulfill(5) v = p1.get() assert p1.isFulfilled assert_equals(5, v)
def _handle_retrieve(self, resp): promise = Promise() promise.fulfill( self.serialize(RetrieveResponseConfirmation(resp.payload), "retrieve")) return promise
def _handle_update(self, resp): promise = Promise() promise.fulfill( self.serialize(UpdateResponseConfirmation(self.path), "update")) return promise
def _handle_notify(self, resp): promise = Promise() return promise
def move_to_gui_thread(self, value): promise = Promise() print "the other thread should fulfil the result to this promise, we are in thread", threading.currentThread() self.signal_promise.emit(promise, value) return promise
def request(self, method, path, data=None, headers={}, args=None): if isinstance(data, unicode): data = data.encode("utf-8") fullpath = self.__base + path request_headers = self.__headers.copy() if args: fullpath += ("?" in fullpath and "&" or "?") + compose_qs(args) if headers: request_headers.update(headers) if method == "GET": timeout = self.get_timeout try: etag, modified, cached = self.__cache[fullpath] if etag: request_headers["If-None-Match"] = etag request_headers["If-Modified-Since"] = modified except KeyError: request_headers.pop("If-None-Match", None) request_headers.pop("If-Modified-Since", None) else: timeout = self.timeout request_headers.setdefault("Content-Type", self.__content_type) log_headers = request_headers if self.logger.isEnabledFor( DEBUG) and "Authorization" in request_headers: log_headers = request_headers.copy() log_headers["Authorization"] = "<purged>" if method == "GET": self.logger.debug("%s: %s (%s)", method, fullpath, log_headers) else: self.logger.debug("%s: %s (%s)\n%s", method, fullpath, log_headers, repr(data)) #t = time() promise = Promise() try: #response = self.__connection_manager.request(method, fullpath, data, request_headers, timeout) def response_handler(resp): if resp.status_code == 304: try: promise.fulfill(closing(StringIO(cached))) except NameError: promise.reject( NetworkError( "Error: The %s returned 304 though no cached version is available. Request was: %s %s" % (self.component_name, method, fullpath))) if resp.status_code < 200 or resp.status_code >= 300: try: promise.reject( HTTPError(msg=resp.status_message, status=resp.status_code)) except: promise.reject( HTTPError(msg="Http error", status=response.status)) else: promise.fulfill(resp) req = self.client.request(method, fullpath, response_handler) for head, value in request_headers.items(): req.put_header(head, value) if data: req.chunked = True req.write_str(data) req.end() except Exception as e: print("Exception triggered: %s" % e) promise.reject(e) return promise