def __call__(self, request):
        if request.method == 'OPTIONS':
            sys.stderr.write("###OPTIONS: \n");
            for h in self.CORS_HEADERS:
                sys.stderr.write("   %s: %s\n" % h);
            # Trace to see if this is actually setting the headers...
            return json_response('',
                headerlist = copy.deepcopy(self.CORS_HEADERS));
        request.config = self.config
        client_id = request.headers.get('X-KeyExchange-Id')
        method = request.method
        url = request.path_info

        # the root does a health check on memcached, then
        # redirects to services.mozilla.com
        if url == '/':
            if method != 'GET':
                raise HTTPMethodNotAllowed()
            self._health_check()
            raise HTTPMovedPermanently(location=self.root)

        match = _URL.match(url)
        if match is None:
            raise HTTPNotFound()

        url = match.group(1)
        if url == 'new_channel':
            # creation of a channel
            if method != 'GET':
                raise HTTPMethodNotAllowed()
            if not self._valid_client_id(client_id):
                # The X-KeyExchange-Id is valid
                try:
                    log = 'Invalid X-KeyExchange-Id'
                    log_cef(log, 5, request.environ, self.config,
                            msg=_cid2str(client_id))
                finally:
                    raise HTTPBadRequest()
            cid = self._get_new_cid(client_id)
            headers = [('X-KeyExchange-Channel', cid),
                       ('Content-Type', 'application/json')]
            headers.extend(self.CORS_HEADERS)
            return json_response(cid, headerlist=headers)

        elif url == 'report':
            if method != 'POST':
                raise HTTPMethodNotAllowed()
            return self.report(request, client_id)

        # validating the client id - or registering id #2
        channel_content = self._check_client_id(url, client_id, request)

        # actions are dispatched in this class
        sys.stderr.write('calling ' + method + "\n");
        method = getattr(self, '%s_channel' % method.lower(), None)
        if method is None:
            sys.stderr.write("not found\n");
            raise HTTPNotFound()

        return method(request, url, channel_content)
    def report(self, request, client_id):
        """Reports a log and delete the channel if relevant"""
        # logging the report
        log = []
        header_log = request.headers.get('X-KeyExchange-Log')
        if header_log is not None:
            log.append(header_log)

        body_log = request.body[:2000].strip()
        if body_log != '':
            log.append(body_log)

        # logging only if the log is not empty
        if len(log) > 0:
            log = '\n'.join(log)
            log_cef('Report', 5, request.environ, self.config, msg=log)

        # removing the channel if present
        channel_id = request.headers.get('X-KeyExchange-Cid')
        if client_id is not None and channel_id is not None:
            content = self.cache.get(channel_id)
            if content is not None:
                # the channel is still existing
                ttl, ids, data, etag = content

                # if the client_ids is in ids, we allow the deletion
                # of the channel
                if not self._delete_channel(channel_id):
                    log_cef('Could not delete the channel', 5,
                            request.environ, self.config,
                            msg=_cid2str(channel_id))
        return json_response('', 
                headers=copy.deepcopy(self.CORS_HEADERS))
    def put_channel(self, request, channel_id, existing_content):
        sys.stderr.write("###Rcv'd PUT \n'" );
        """Append data into channel."""
        ttl, ids, old_data, old_etag = existing_content

        data = request.body
        sys.stderr.write("   body len: %s \n" % len(data));
        etag = self._etag(data)

        # check the If-Match header
        if 'If-Match' in request.headers:
            if str(request.if_match) != '*':
                # if If-Match is provided, it must be the value of
                # the etag before the update is applied
                if not self._etag_match(old_etag, request.if_match):
                    raise HTTPPreconditionFailed(etag=etag)
        elif 'If-None-Match' in request.headers:
            if str(request.if_none_match) == '*':
                # we will put data in the channel only if it's
                # empty (== first PUT)
                if old_data != _EMPTY:
                    raise HTTPPreconditionFailed(etag=etag,
                            headers=copy.deepcopy(self.CORS_HEADERS))

        if not self.cache.set(channel_id, (ttl, ids, request.body, etag),
                              time=ttl):
            raise HTTPServiceUnavailable(headers=
                    copy.deepcopy(self.CORS_HEADERS))

        sys.stderr.write("### Return success \n");

        return json_response('', etag=etag, 
                headers=copy.deepcopy(self.CORS_HEADERS))
Beispiel #4
0
    def get_channel(self, request, channel_id, existing_content):
        """Grabs data from channel if available."""
        ttl, ids, data, etag = existing_content

        # check the If-None-Match header
        if request.if_none_match is not None:
            if self._etag_match(etag, request.if_none_match):
                raise HTTPNotModified()

        # keep the GET counter up-to-date
        # the counter is a separate key
        deletion = False
        ckey = 'GET:%s' % channel_id
        count = self.cache.get(ckey)
        if count is None:
            self.cache.set(ckey, '1')
        else:
            if int(count) + 1 == self.max_gets:
                # we reached the last authorized call, the channel is remove
                # after that
                deletion = True
            else:
                self.cache.incr(ckey)

        try:
            return json_response(data, dump=False, etag=etag)
        finally:
            # deleting the channel in case we did all GETs
            if deletion:
                if not self._delete_channel(channel_id):
                    log_cef('Could not delete the channel', 5,
                            request.environ, self.config,
                            msg=_cid2str(channel_id))
Beispiel #5
0
    def put_channel(self, request, channel_id, existing_content):
        """Append data into channel."""
        ttl, ids, old_data, old_etag = existing_content

        data = request.body
        etag = self._etag(data)

        # check the If-Match header
        if 'If-Match' in request.headers:
            if str(request.if_match) != '*':
                # if If-Match is provided, it must be the value of
                # the etag before the update is applied
                if not self._etag_match(old_etag, request.if_match):
                    raise HTTPPreconditionFailed(etag=etag)
        elif 'If-None-Match' in request.headers:
            if str(request.if_none_match) == '*':
                # we will put data in the channel only if it's
                # empty (== first PUT)
                if old_data != _EMPTY:
                    raise HTTPPreconditionFailed(etag=etag)

        if not self.cache.set(channel_id, (ttl, ids, request.body, etag),
                              time=ttl):
            raise HTTPServiceUnavailable()

        return json_response('', etag=etag)
Beispiel #6
0
    def __call__(self, request):
        request.config = self.config
        client_id = request.headers.get('X-KeyExchange-Id')
        method = request.method
        url = request.path_info

        # the root does a health check on memcached, then
        # redirects to services.mozilla.com
        if url == '/':
            if method != 'GET':
                raise HTTPMethodNotAllowed()
            self._health_check()
            raise HTTPMovedPermanently(location=self.root)

        match = _URL.match(url)
        if match is None:
            raise HTTPNotFound()

        url = match.group(1)
        if url == 'new_channel':
            # creation of a channel
            if method != 'GET':
                raise HTTPMethodNotAllowed()
            if not self._valid_client_id(client_id):
                # The X-KeyExchange-Id is valid
                try:
                    log = 'Invalid X-KeyExchange-Id'
                    log_cef(log, 5, request.environ, self.config,
                            msg=_cid2str(client_id))
                finally:
                    raise HTTPBadRequest()
            cid = self._get_new_cid(client_id)
            headers = [('X-KeyExchange-Channel', cid),
                       ('Content-Type', 'application/json')]
            return json_response(cid, headerlist=headers)

        elif url == 'report':
            if method != 'POST':
                raise HTTPMethodNotAllowed()
            return self.report(request, client_id)

        # validating the client id - or registering id #2
        channel_content = self._check_client_id(url, client_id, request)

        # actions are dispatched in this class
        method = getattr(self, '%s_channel' % method.lower(), None)
        if method is None:
            raise HTTPNotFound()

        return method(request, url, channel_content)