async def check_rtsp_availability(rtsp_link, timeout): """ This function needed because some rtsp links requires to authenticate and pyav lib may freeze in such cases. If some rtsp link require authentication contact NVR admins to replace it with working link. """ url = urlparse(rtsp_link) ip = url.hostname port = url.port if url.port else 554 reader = None writer = None try: reader, writer = await asyncio.wait_for(asyncio.open_connection( ip, port), timeout=timeout) is_available = bool(reader) and bool(writer) except asyncio.TimeoutError: raise web.HTTPBadGateway( text='Can not establish connection with rtsp media source') except OSError: raise web.HTTPBadGateway( text='Can not establish connection with rtsp media source') finally: if writer: writer.close() await writer.wait_closed() if not is_available: raise web.HTTPBadGateway( text='Can not establish connection with rtsp media source')
async def handle(self, request: web.Request, camera: Camera) -> web.StreamResponse: """Serve camera stream, possibly with interval.""" if (interval_str := request.query.get("interval")) is None: stream = await camera.handle_async_mjpeg_stream(request) if stream is None: raise web.HTTPBadGateway() return stream
async def receive_and_echo(request): """Accept and validate an SMS delivery.""" secret = request.app["secret"] if request.content_type == "application/json": payload = await request.json() else: payload = await request.post() msg = SMSMessage.from_payload(payload) logging.info("Received message: %s", msg) sig = request.headers.get("X-Telnyx-Signature") raw_payload = await request.read() epoch = get_epoch_from_header(sig) expected_sig = webhook_sig_hs256(secret, raw_payload, epoch) if sig != expected_sig: logging.error("Invalid signature: %s (expected %s)", sig, expected_sig) return web.HTTPBadRequest(text="Invalid signature") try: echo_msg = msg.echo_message() await async_send(echo_msg, secret) except SMSSendError as e: logging.error("Echo failed: %s", e) return web.HTTPBadGateway(text="Echo failed") logging.info("Echoed message: %s", echo_msg) return web.Response(text="Echo OK")
async def receive_and_echo(request): """Accept and validate an SMS delivery.""" secret = request.app["secret"] if request.content_type == "application/json": payload = await request.json() else: payload = await request.post() msg = SMSMessage.from_payload(payload) logging.info("Received message: %s", msg) sig = request.headers.get(SIGNATURE_HEADER_KEY) raw_payload = await request.read() timestamp, _ = parse_signature(sig) expected_sig = generate_signature(secret=secret, payload=raw_payload, timestamp=timestamp) if sig != expected_sig: logging.error("Invalid signature: %s (expected %s)", sig, expected_sig) return web.HTTPBadRequest(text="Invalid signature") try: echo_msg = msg.echo_message() await async_send(echo_msg, secret) except SMSSendError as e: logging.error("Echo failed: %s", e) return web.HTTPBadGateway(text="Echo failed") logging.info("Echoed message: %s", echo_msg) return web.Response(text="Echo OK")
async def _put_file_to_object_store(uuid: str, content_type: str, data, filename: T.Optional[str]=None): _logger.debug("url: %s\n", _BASE_URL + uuid) _logger.debug("Authorization: %s", _AUTHORIZATION) headers = { 'Authorization': _AUTHORIZATION, 'Content-Type': content_type } if filename is not None: headers['Content-Disposition'] = helpers.content_disposition_header( 'attachment', filename=filename ) async with aiohttp.ClientSession() as session: async with session.put( _BASE_URL + uuid, data=data, headers=headers, chunked=True, expect100=True ) as response: if response.status >= 400: _logger.error( "Couldn't store file in object store.\n" "%s %s:\n%s\n%r", response.status, response.reason, await response.text(), response ) raise web.HTTPBadGateway()
async def handle_request(client_request): headers = filter_headers(client_request, OMIT_REQ_HEADERS) headers["Host"] = hostname request = { "method": client_request.method, "url": "{}{}".format(remote_address, client_request.rel_url), "headers": headers, "allow_redirects": False, "data": await client_request.read(), } async with ClientSession() as session: try: async with session.request(**request) as response: logger.debug( "%s : %s -> %d, %s", request["method"], request["url"], response.status, response.content_type, ) return await build_response(response) except ClientConnectionError as exc: msg = "{} : {} -> {}".format( request["method"], request["url"], str(exc) ) logger.error(msg) return web.HTTPBadGateway(reason=msg)
async def handle_rpc(request): body = await request.json() if ('method' not in body or not isinstance(body['method'], str)): logger.warn('bad request for for body %s', body) return web.HTTPBadRequest() m = parse_method(body['method']) if not m: logger.warn('parse method error %s', body) return web.HTTPBadRequest() if _whitelist is not None: if body['method'] not in _whitelist: logger.warn('method not in whitelist') return web.HTTPForbidden() # TODO: srv::method white list srv, method = m.group('srv'), m.group('method') params = body['params'] try: timeout = float(request.headers.get('X-Bbox-Proxy-Timeout', '8')) except ValueError: timeout = 8 try: r = await pool.request(srv, method, *params, req_id=body.get('id'), timeout=timeout) except asyncio.TimeoutError: logging.warn('timeout error on request srv %s, method %s', srv, method, exc_info=True) return web.HTTPBadGateway() except ConnectionError: logger.warn('connect error on request srv %s, method %s', srv, method, exc_info=True) return web.HTTPBadGateway() return web.json_response(r)
async def handle(self, request: web.Request, camera: Camera) -> web.StreamResponse: """Serve camera stream, possibly with interval.""" if (interval_str := request.query.get("interval")) is None: try: stream = await camera.handle_async_mjpeg_stream(request) except ConnectionResetError: stream = None _LOGGER.debug("Error while writing MJPEG stream to transport") if stream is None: raise web.HTTPBadGateway() return stream
async def __call__(self, request): kw = None if self._has_var_kw_arg or self._has_named_kw_args or self._required_kw_args: if request.method == 'POST': if not request.content_type: return web.HTTPBadRequest(body='Missing content-type') ct = request.content_type.lower() if ct.startswith('application/json'): params = await request.json() if not isinstance(params, dict): return web.HTTPBadRequest(body='JSON body must be object.') kw = params elif ct.startswith('application/x-www-form-urlencoded') or ct.startswith('multipart/form-data'): params = await request.post() kw = dict(**params) else: return web.HTTPBadGateway(body=('Unsupported content-type: %s' % request.content_type)) if request.method == 'GET': qs = request.query_string if qs: kw = dict() for k, v in parse.parse_qs(qs, True).items(): kw[k] = v if kw is None: kw = dict(**request.match_info) else: if not self._has_var_kw_arg and self._named_kw_args: # remove all unamed kw: copy = dict() for name in self._named_kw_args: if name in kw: copy[name] = kw[name] kw = copy # check named arg: for k, v in request.match_info.items(): if k in kw: logging.warning('Duplicate arg name in named arg and kw args: %s' , k) kw[k] = v if self._has_request_arg: kw['request'] = request # check required kw: if self._required_kw_args: for name in self._required_kw_args: if not name in kw: return web.HTTPBadRequest(body=('Missing argument: %s' % name)) logging.info('call with args: %s', str(kw)) try: r = await self._func(**kw) return r except APIError as e: return dict(error=e.error, data=e.data, message=e.message)
async def offer(request): request_url = request.match_info['stream'] streams = await get_streams(args.nvr_token) if request_url not in streams: raise web.HTTPNotFound(text='No rtsp source related to this url') play_from = streams[request_url] if not play_from: raise web.HTTPBadGateway( text= 'NVR response with cam rtsp link is empty. Contact NVR admins to fix it' ) url = urlparse(play_from) if url.scheme == 'rtsp': await check_rtsp_availability(play_from, timeout=10) params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() pcs.add(pc) @pc.on("iceconnectionstatechange") async def on_iceconnectionstatechange(): print("ICE connection state is %s" % pc.iceConnectionState) if pc.iceConnectionState == "failed": await pc.close() pcs.discard(pc) player = MediaPlayer(play_from) await pc.setRemoteDescription(offer) for t in pc.getTransceivers(): if t.kind == "audio" and player.audio: pc.addTrack(player.audio) elif t.kind == "video" and player.video: track = VideoTransformTrack(player.video, request_url) pc.addTrack(track) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return web.Response(content_type="application/json", headers=cors_headers, text=json.dumps({ "sdp": pc.localDescription.sdp, "type": pc.localDescription.type }))
async def get_link(request): request_url = request.match_info['stream'] streams = await get_streams(args.nvr_token) if request_url not in streams: raise web.HTTPNotFound(text='No rtsp source related to this url') play_from = streams[request_url] if not play_from: raise web.HTTPBadGateway( text= 'NVR response with cam rtsp link is empty. Contact NVR admins to fix it' ) # play_from = 'rtsp://*****:*****@' + play_from.lstrip('rtsp://') return web.Response(headers=cors_headers, text=play_from)
async def proxy(request: web.Request) -> web.Response: if not config.get_permissions(request["token"].user_id).admin: raise Error.no_access_docker path = request.match_info["path"] query = request.query.copy() headers = request.headers.copy() del headers["Host"] del headers["Authorization"] try: timeout = aiohttp.ClientTimeout(total=None, connect=5, sock_connect=5, sock_read=None) resp = await http.request(request.method, host / path, headers=headers, params=query, data=request.content, timeout=timeout) except aiohttp.ClientError: raise web.HTTPBadGateway(text="Failed to contact Docker daemon") return web.Response(status=resp.status, headers=resp.headers, body=resp.content)
async def client_handler(r : web.Request): """Handler for websocket connections and messages""" if not NODES: return web.HTTPBadGateway() ip = UTIL.get_request_ip(r) if (sessions_count(ip) >= 2): return web.HTTPForbidden() ws = web.WebSocketResponse(heartbeat=30) try: await ws.prepare(r) # Connection Opened except: log.server_logger.error('Failed to prepare websocket: %s', UTIL.get_request_ip(r)) return ws ws.id = str(uuid.uuid4()) global CLIENTS CLIENTS[ws.id] = {'ws':ws, 'events':set(), 'ip':ip} try: log.server_logger.info('new client connection;%s;%s;User-Agent:%s', ip, ws.id, str( r.headers.get('User-Agent'))) async for msg in ws: if msg.type == WSMsgType.TEXT: if msg.data == 'close': await ws.close() else: res = await handle_client_messages(r, msg.data, ws=ws) if res: res = json.dumps(res) log.server_logger.debug('Sending response %s to %s', res, ip) await ws.send_str(res) elif msg.type == WSMsgType.CLOSE: log.server_logger.info('Client Connection closed normally') break elif msg.type == WSMsgType.ERROR: log.server_logger.info('Client Connection closed with error %s', ws.exception()) break log.server_logger.info('Client connection closed normally') except Exception: log.server_logger.exception('Client Closed with exception') finally: del CLIENTS[ws.id] return ws
async def handle_req(request): if not default_backend: return web.HTTPNotFound() webreq = { 'method': request.method, 'path': request.path, 'qs': request.query_string, 'headers': dict(request.headers.items()), 'body': None } if request.method in ('POST', 'PUT'): ctype = request.headers.get('Content-Type', 'application/octet-stream') if ctype in ('application/x-www-form-urlencoded', 'application/json'): # ONLY parse known body body = await request.post() webreq['body'] = dict(body.items()) srv, method = default_backend.split('::') try: r = await srv_pool.request(srv, method, webreq) except ConnectionError: return web.HTTPBadGateway() except NoServiceFound as e: return web.HTTPNotFound(str(e)) if r['result']: res = r['result'] if isinstance(res, str): return web.Response(body=res) elif 'body' in res: headers = res.get('headers', {}) body = res['body'] if isinstance(body, str): return web.Response(body=body, headers=headers) else: headers.pop('Content-Type', None) return web.json_response(body, headers=headers) else: return web.json_response(res) else: code = r['error'].get('code', '500') if code.isdigit(): code = int(code) else: code = 500 return web.Response(status=code, body=r['error'].get('message', ''))
async def authenticate(self, request): token = self.get_token(request) if token is None: raise unauthorized("jupyterhub") url = "{}/authorizations/token/{}".format( self.jupyterhub_api_url, quote(token, safe=""), ) kwargs = { "headers": { "Authorization": "token %s" % self.jupyterhub_api_token }, "ssl": self.ssl_context, } resp = await self.session.get(url, **kwargs) if resp.status < 400: data = await resp.json() # "groups" attribute doesn't exists in case of a service return User( data["name"], groups=data.get("groups", []), admin=data.get("admin", False), ) elif resp.status == 404: self.log.debug("Token for non-existent user requested") raise unauthorized("jupyterhub") else: if resp.status == 403: err = web.HTTPInternalServerError( reason= "Permission failure verifying user's JupyterHub API token") elif resp.status >= 500: err = web.HTTPBadGateway( reason= "Upstream failure verifying user's JupyterHub API token") else: err = web.HTTPInternalServerError( reason="Failure verifying user's JupyterHub API token") self.log.error("%s - code: %s, reason: %s", err.reason, resp.status, resp.reason) raise err
async def handle(self, request: web.Request, camera: Camera) -> web.StreamResponse: """Serve camera stream, possibly with interval.""" interval_str = request.query.get("interval") if interval_str is None: stream = await camera.handle_async_mjpeg_stream(request) if stream is None: raise web.HTTPBadGateway() return stream try: # Compose camera stream from stills interval = float(interval_str) if interval < MIN_STREAM_INTERVAL: raise ValueError(f"Stream interval must be be > {MIN_STREAM_INTERVAL}") return await camera.handle_async_still_stream(request, interval) except ValueError as err: raise web.HTTPBadRequest() from err
async def get_articles(self, request): request_params = request.query query_str = request_params.get('query_str') if not query_str: raise web.HTTPBadGateway() current_ts = time.time() key = f'{query_str}-{current_ts}' await self.kafka_producer.send('request_topic', value=bytes(query_str, 'utf-8'), key=bytes(key, 'utf-8')) logging.info(f'{query_str} was sent to kafka') event = asyncio.Event() self.synchronization_events[key] = event await event.wait() articles = self.articles_storage.pop(key) return web.json_response(articles)
async def client_handler(r: web.Request): """Handler for websocket connections and messages""" if not r.app['nodes']: return web.HTTPBadGateway() ws = web.WebSocketResponse(heartbeat=30) try: await ws.prepare(r) # Connection Opened except: log.server_logger.error('Failed to prepare websocket: %s', UTIL.get_request_ip(r)) return ws ws.id = str(uuid.uuid4()) ip = UTIL.get_request_ip(r) r.app['clients'][ws.id] = {'ws': ws, 'accounts': set()} log.server_logger.info('new client connection;%s;%s;User-Agent:%s', ip, ws.id, str(r.headers.get('User-Agent'))) try: async for msg in ws: if msg.type == WSMsgType.TEXT: if msg.data == 'close': await ws.close() else: reply = await handle_client_messages(r, msg.data, ws=ws) if reply: log.server_logger.debug('Sending response %s to %s', reply, ip) await ws.send_str(reply) elif msg.type == WSMsgType.CLOSE: log.server_logger.info('Client Connection closed normally') break elif msg.type == WSMsgType.ERROR: log.server_logger.info( 'Client Connection closed with error %s', ws.exception()) break log.server_logger.info('Client connection closed normally') except Exception: log.server_logger.exception('Client Closed with exception') finally: await destory_client(r, ws.id) return ws
async def handle_rpc(request: web.Request) -> web.Response: # type: ignore body = await request.json() try: req = Request(body) except DataError as e: logger.warn('json parse error %s', body) return web.HTTPBadRequest('json parse error') if _whitelist is not None: if not req.allowed(_whitelist): logger.warn('method not allowed') return web.HTTPForbidden() try: timeout = float(request.headers.get('X-Bbox-Proxy-Timeout', '20')) except ValueError: timeout = 20 try: r = await srv_pool.request_obj(req, timeout=timeout) return web.json_response(r) except asyncio.TimeoutError: logging.warn('timeout error on request %s', req.full_method, exc_info=True) except ClientConnectorError: logger.warn('client connector error, method %s', req.method, exc_info=True) await get_cluster().get_boxes() except ConnectionError: logger.warn('connect error on request %s', req.full_method, exc_info=True) await get_cluster().get_boxes() except NoServiceFound: logger.warn('no service found for %s', req.full_method) await get_cluster().get_boxes() return web.HTTPNotFound() return web.HTTPBadGateway()
async def discover(host: str, port: int, path: str, verb: str, endpoint: str) -> dict[str, Any]: """Call discovery service and get microservice connection data. :param host: Discovery host name. :param port: Discovery port. :param path: Discovery path. :param verb: Endpoint Verb. :param endpoint: Endpoint url. :return: The response of the discovery. """ url = URL.build(scheme="http", host=host, port=port, path=path, query={ "verb": verb, "path": endpoint }) try: async with ClientSession() as session: async with session.get(url=url) as response: if not response.ok: if response.status == 404: raise web.HTTPNotFound( text= f"The {endpoint!r} path is not available for {verb!r} method." ) raise web.HTTPBadGateway( text="The Discovery Service response is wrong.") data = await response.json() except ClientConnectorError: raise web.HTTPGatewayTimeout( text="The Discovery Service is not available.") data["port"] = int(data["port"]) return data
async def proxy(url: URL, secret: str, request: web.Request, path_prefix: str) -> web.Response: if not secret: raise Error.bridge_disabled query = request.query.copy() query["user_id"] = request["token"].user_id headers = request.headers.copy() headers["Authorization"] = f"Bearer {secret}" if path_prefix: url /= path_prefix path = request.match_info.get("path", None) if path: url /= path try: resp = await http.request(request.method, url, headers=headers, params=query, data=request.content) except aiohttp.ClientError: log.debug("Failed to proxy request", exc_info=True) raise web.HTTPBadGateway(text="Failed to contact bridge") except CancelledError: log.debug( f"Proxying request to {url} was cancelled before it responded") raise except Exception: log.warning(f"Proxying request to {url} threw unhandled exception", exc_info=True) raise return web.Response(status=resp.status, headers=resp.headers, body=resp.content)
async def dev_deploy_branch(request, userdata): app = request.app try: params = await request.json() except Exception: message = 'could not read body as JSON' log.info('dev deploy failed: ' + message, exc_info=True) raise web.HTTPBadRequest(text=message) try: branch = FQBranch.from_short_str(params['branch']) steps = params['steps'] except Exception: message = f'parameters are wrong; check the branch and steps syntax.\n\n{params}' log.info('dev deploy failed: ' + message, exc_info=True) raise web.HTTPBadRequest(text=message) gh = app['github_client'] request_string = f'/repos/{branch.repo.owner}/{branch.repo.name}/git/refs/heads/{branch.name}' try: branch_gh_json = await gh.getitem(request_string) sha = branch_gh_json['object']['sha'] except Exception: message = f'error finding {branch} at GitHub' log.info('dev deploy failed: ' + message, exc_info=True) raise web.HTTPBadRequest(text=message) unwatched_branch = UnwatchedBranch(branch, sha, userdata) batch_client = app['batch_client'] try: batch_id = await unwatched_branch.deploy(batch_client, steps) except Exception as e: # pylint: disable=broad-except raise web.HTTPBadGateway(text=f'starting the deploy failed due to {e}') return web.json_response({'sha': sha, 'batch_id': batch_id})
async def _identify_to_kisee(self, data: LoginCredentials): """Async request to identify to kisee""" create_token_endpoint = await self.get_endpoint("jwt") async with aiohttp.ClientSession() as session: async with session.post( create_token_endpoint, headers={ "Content-Type": "application/json", "Accept": "application/vnd.coreapi+json", }, json=data, ) as response: if response.status == 403: raise web.HTTPForbidden(reason="Can not authenticate on kisee") if response.status != 201: raise web.HTTPBadGateway( reason="Something went wrong with identity provider" ) kisee_response = await response.text() kisee_response = json.loads(kisee_response) return kisee_response
async def submit_job(request): """ Function to submit a query. :param request: used to get the params to connect to the db :return: json with job_id Example: curl -H "Content-Type:application/json" -d "{\"databases\": [\"miRBase\"], \"query\": \"CUAUACAAUCUACUGUCUUUC\"}" localhost:8002/api/submit-job --- tags: - Jobs summary: Accepts a job for execution consumes: - application/json parameters: - in: body name: Sequence and database description: Query sequence and database to be used in the search required: true schema: properties: query: type: string description: Query sequence example: "CUAUACAAUCUACUGUCUUUC" databases: type: array description: Database to search the query sequence against. Empty list uses the RNAcentral database example: ['mirbase'] required: - query - databases responses: 201: description: Created 400: description: Bad request (either query is not a nucleotide sequence, or the database is not in RNAcentral) 500: description: Internal server error """ data = await request.json() # converts all uppercase characters to lowercase. if data['databases']: data['databases'] = [db.lower() for db in data['databases']] try: data = serialize(request, data) except (KeyError, TypeError, ValueError) as e: raise web.HTTPBadRequest(text=str(e)) from e # database that the user wants to use to perform the search databases = producer_to_consumers_databases(data['databases']) # check if this query has already been searched job_list = await sequence_exists(request.app['engine'], data['query']) job_id = None if job_list: # check the database used in each job_id. # set job_id if the database used is the same as in "databases" for job in job_list: if await database_used_in_search(request.app['engine'], job, databases): job_id = job break # do the search if the data is not in the database if not job_id: # check for unfinished jobs unfinished_job = await find_highest_priority_jobs(request.app['engine'] ) # get URL - for statistical purposes try: url = data['url'] except KeyError: url = '' # get priority try: priority = data['priority'] except KeyError: priority = 'low' expert_dbs = [ "rnacentral.org", "rfam.xfam.org", "rfam.org", "mirbase.org", "scottgroup.med.usherbrooke.ca", "gtrnadb.ucsc.edu" ] search_from_expert_db = [item for item in expert_dbs if item in url] if not search_from_expert_db: priority = 'low' # save metadata about this job to the database job_id = await save_job(request.app['engine'], data['query'], data['description'], url, priority) # save metadata about job_chunks to the database # TODO: what if Job was saved and JobChunk was not? Need transactions? for database in databases: # save job_chunk with "created" status. This prevents the check_chunks_and_consumers function, # which runs every 5 seconds, from executing the same job_chunk again. await save_job_chunk(request.app['engine'], job_id, database) # save metadata about infernal_job to the database # TODO: what if Job was saved and InfernalJob was not? Need transactions? await save_infernal_job(request.app['engine'], job_id, priority) # if there are unfinished jobs, change the status of each new job_chunk to pending; # otherwise try starting the job if unfinished_job: for database in databases: try: await set_job_chunk_status( request.app['engine'], job_id, database, status=JOB_CHUNK_STATUS_CHOICES.pending) except Exception as e: return web.HTTPBadGateway(text=str(e)) else: # check for available consumers consumers = await find_available_consumers(request.app['engine']) # if consumers are available, delegate to infernal_job first if consumers: consumer = consumers.pop(0) try: await delegate_infernal_job_to_consumer( engine=request.app['engine'], consumer_ip=consumer.ip, consumer_port=consumer.port, job_id=job_id, query=data['query']) except Exception as e: return web.HTTPBadGateway(text=str(e)) # after infernal_job, delegate consumers to job_chunks for index in range(min(len(consumers), len(databases))): try: await delegate_job_chunk_to_consumer( engine=request.app['engine'], consumer_ip=consumers[index].ip, consumer_port=consumers[index].port, job_id=job_id, database=databases[index], query=data['query']) except Exception as e: return web.HTTPBadGateway(text=str(e)) # if no consumer is available, change the status of the remaining job_chunks to pending for index in range(len(consumers), len(databases)): try: await set_job_chunk_status( request.app['engine'], job_id, databases[index], status=JOB_CHUNK_STATUS_CHOICES.pending) except Exception as e: return web.HTTPBadGateway(text=str(e)) return web.json_response({"job_id": job_id}, status=201)
async def handler(request: web.Request) -> web.Response: raise web.HTTPBadGateway()