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))
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))
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)
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)