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
Example #2
0
    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
Example #3
0
def test_fulfill_self():
    p = Promise()
    assert_raises(TypeError, p.fulfill, p)
Example #4
0
def test_fake_promise():
    p = Promise()
    p.fulfill(FakePromise())
    assert p.isRejected
    assert_exception(p.reason, Exception, "FakePromise raises in 'then'")
Example #5
0
def df(value, dtime):
    p = Promise()
    t = DelayedFulfill(dtime, p, value)
    t.start()

    return p
Example #6
0
def dr(reason, dtime):
    p = Promise()
    t = DelayedRejection(dtime, p, reason)
    t.start()

    return p
Example #7
0
def test_wait_if():
    p1 = Promise()
    p1.fulfill(5)
    p1.wait()
    assert p1.isFulfilled
Example #8
0
def test_get_if():
    p1 = Promise()
    p1.fulfill(5)
    v = p1.get()
    assert p1.isFulfilled
    assert_equals(5, v)
Example #9
0
 def _handle_retrieve(self, resp):
     promise = Promise()
     promise.fulfill(
         self.serialize(RetrieveResponseConfirmation(resp.payload),
                        "retrieve"))
     return promise
Example #10
0
 def _handle_update(self, resp):
     promise = Promise()
     promise.fulfill(
         self.serialize(UpdateResponseConfirmation(self.path), "update"))
     return promise
Example #11
0
 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
Example #13
0
    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