Ejemplo n.º 1
0
async def handle_user_message(r: web.Request,
                              message: str,
                              ws: web.WebSocketResponse = None):
    """Process data sent by client"""
    address = util.get_request_ip(r)
    uid = ws.id if ws is not None else '0'
    now = int(round(time.time() * 1000))
    if address in r.app['last_msg']:
        if (now - r.app['last_msg'][address]['last']) < 25:
            if r.app['last_msg'][address]['count'] > 3:
                log.server_logger.error(
                    'client messaging too quickly: %s ms; %s; %s; User-Agent: %s',
                    str(now - r.app['last_msg'][address]['last']), address,
                    uid, str(r.headers.get('User-Agent')))
                return None
            else:
                r.app['last_msg'][address]['count'] += 1
        else:
            r.app['last_msg'][address]['count'] = 0
    else:
        r.app['last_msg'][address] = {}
        r.app['last_msg'][address]['count'] = 0
    r.app['last_msg'][address]['last'] = now
    log.server_logger.info('request; %s, %s, %s', message, address, uid)
    if message not in r.app['active_messages']:
        r.app['active_messages'].add(message)
    else:
        log.server_logger.error('request already active; %s; %s; %s', message,
                                address, uid)
        return None
    ret = None
    try:
        request_json = json.loads(message)
        if request_json['action'] in allowed_rpc_actions:
            if 'request_id' in request_json:
                requestid = request_json['request_id']
            else:
                requestid = None

            # adjust counts so nobody can block the node with a huge request
            if 'count' in request_json:
                if (request_json['count'] < 0) or (request_json['count'] >
                                                   3500):
                    request_json['count'] = 3500

            # rpc: account_subscribe (only applies to websocket connections)
            if request_json['action'] == "account_subscribe" and ws is not None:
                # If account doesnt match the uuid self-heal
                resubscribe = True
                if 'uuid' in request_json:
                    # Perform multi-account upgrade if not already done
                    account = await r.app['rdata'].hget(
                        request_json['uuid'], "account")
                    # No account for this uuid, first subscribe
                    if account is None:
                        resubscribe = False
                    else:
                        # If account isn't stored in list-format, modify it so it is
                        # If it already is, add this account to the list
                        try:
                            account_list = json.loads(account)
                            if 'account' in request_json and request_json[
                                    'account'].lower() not in account_list:
                                account_list.append(
                                    request_json['account'].lower())
                                await r.app['rdata'].hset(
                                    request_json['uuid'], "account",
                                    json.dumps(account_list))
                        except Exception:
                            if 'account' in request_json and request_json[
                                    'account'].lower() != account.lower():
                                resubscribe = False
                            else:
                                # Perform upgrade to list style
                                account_list = []
                                account_list.append(account.lower())
                                await r.app['rdata'].hset(
                                    request_json['uuid'], "account",
                                    json.dumps(account_list))
                # already subscribed, reconnect (websocket connections)
                if 'uuid' in request_json and resubscribe:
                    if uid in r.app['clients']:
                        del r.app['clients'][uid]
                    uid = request_json['uuid']
                    ws.id = uid
                    r.app['clients'][uid] = ws
                    log.server_logger.info('reconnection request;' + address +
                                           ';' + uid)
                    try:
                        if 'currency' in request_json and request_json[
                                'currency'] in currency_list:
                            currency = request_json['currency']
                            r.app['cur_prefs'][uid] = currency
                            await r.app['rdata'].hset(uid, "currency",
                                                      currency)
                        else:
                            setting = await r.app['rdata'].hget(
                                uid, "currency")
                            if setting is not None:
                                r.app['cur_prefs'][uid] = setting
                            else:
                                r.app['cur_prefs'][uid] = 'usd'
                                await r.app['rdata'].hset(
                                    uid, "currency", 'usd')

                        # Get relevant account
                        account_list = json.loads(await r.app['rdata'].hget(
                            uid, "account"))
                        if 'account' in request_json:
                            account = request_json['account']
                        else:
                            # Legacy connections
                            account = account_list[0]
                        if account.replace("nano_", "xrb_") in account_list:
                            account_list.remove(
                                account.replace("nano_", "xrb_"))
                            account = account.replace('xrb_', 'nano_')
                            account_list.append(account)
                            await r.app['rdata'].hset(uid, "account",
                                                      json.dumps(account_list))
                        await rpc.rpc_reconnect(ws, r, account)
                        # Store FCM token for this account, for push notifications
                        if 'fcm_token' in request_json:
                            await update_fcm_token_for_account(
                                account, request_json['fcm_token'], r)
                        elif 'fcm_token_v2' in request_json and 'notification_enabled' in request_json:
                            if request_json['notification_enabled']:
                                await update_fcm_token_for_account(
                                    account,
                                    request_json['fcm_token_v2'],
                                    r,
                                    v2=True)
                            else:
                                await delete_fcm_token_for_account(
                                    account, request_json['fcm_token_v2'], r)
                    except Exception as e:
                        log.server_logger.error('reconnect error; %s; %s; %s',
                                                str(e), address, uid)
                        reply = {'error': 'reconnect error', 'detail': str(e)}
                        if requestid is not None:
                            reply['request_id'] = requestid
                        ret = json.dumps(reply)
                # new user, setup uuid(or use existing if available) and account info
                else:
                    log.server_logger.info('subscribe request; %s; %s',
                                           util.get_request_ip(r), uid)
                    try:
                        if 'currency' in request_json and request_json[
                                'currency'] in currency_list:
                            currency = request_json['currency']
                        else:
                            currency = 'usd'
                        await rpc.rpc_subscribe(
                            ws, r,
                            request_json['account'].replace("nano_",
                                                            "xrb_"), currency)
                        # Store FCM token if available, for push notifications
                        if 'fcm_token' in request_json:
                            await update_fcm_token_for_account(
                                request_json['account'],
                                request_json['fcm_token'], r)
                        elif 'fcm_token_v2' in request_json and 'notification_enabled' in request_json:
                            if request_json['notification_enabled']:
                                await update_fcm_token_for_account(
                                    request_json['account'],
                                    request_json['fcm_token_v2'],
                                    r,
                                    v2=True)
                            else:
                                await delete_fcm_token_for_account(
                                    request_json['account'],
                                    request_json['fcm_token_v2'], r)
                    except Exception as e:
                        log.server_logger.error('subscribe error;%s;%s;%s',
                                                str(e), address, uid)
                        reply = {'error': 'subscribe error', 'detail': str(e)}
                        if requestid is not None:
                            reply['request_id'] = requestid
                        ret = json.dumps(reply)
            elif request_json['action'] == "fcm_update":
                # Updating FCM token
                if 'fcm_token_v2' in request_json and 'account' in request_json and 'enabled' in request_json:
                    if request_json['enabled']:
                        await update_fcm_token_for_account(
                            request_json['account'],
                            request_json['fcm_token_v2'],
                            r,
                            v2=True)
                    else:
                        await delete_fcm_token_for_account(
                            request_json['account'],
                            request_json['fcm_token_v2'], r)
            # rpc: price_data
            elif request_json['action'] == "price_data":
                log.server_logger.info('price data request;%s;%s',
                                       util.get_request_ip(r), uid)
                try:
                    if request_json['currency'].upper() in currency_list:
                        try:
                            price = await r.app['rdata'].hget(
                                "prices", f"{price_prefix}-" +
                                request_json['currency'].lower())
                            reply = json.dumps({
                                'currency':
                                request_json['currency'].lower(),
                                'price':
                                str(price)
                            })
                            ret = reply
                        except Exception:
                            log.server_logger.error(
                                'price data error, unable to get price;%s;%s',
                                util.get_request_ip(r), uid)
                            ret = json.dumps({
                                'error':
                                'price data error - unable to get price'
                            })
                    else:
                        log.server_logger.error(
                            'price data error, unknown currency;%s;%s',
                            util.get_request_ip(r), uid)
                        ret = json.dumps({'error': 'unknown currency'})
                except Exception as e:
                    log.server_logger.error('price data error;%s;%s;%s',
                                            str(e), util.get_request_ip(r),
                                            uid)
                    ret = json.dumps({
                        'error': 'price data error',
                        'details': str(e)
                    })
            # rpc: account_check
            elif request_json['action'] == "account_check":
                log.server_logger.info('account check request;%s;%s',
                                       util.get_request_ip(r), uid)
                try:
                    response = await rpc.rpc_accountcheck(
                        r, uid, request_json['account'])
                    ret = json.dumps(response)
                except Exception as e:
                    log.server_logger.error('account check error;%s;%s;%s',
                                            str(e), util.get_request_ip(r),
                                            uid)
                    ret = json.dumps({
                        'error': 'account check error',
                        'detail': str(e)
                    })
            # rpc: process
            elif request_json['action'] == "process":
                try:
                    do_work = False
                    if 'do_work' in request_json and request_json[
                            'do_work'] == True:
                        do_work = True
                    subtype = None
                    if 'subtype' in request_json:
                        subtype = request_json['subtype']
                    if 'json_block' in request_json and request_json[
                            'json_block']:
                        sblock = request_json['block']
                    else:
                        sblock = json.loads(request_json['block'])
                    reply = await rpc.process_defer(r,
                                                    uid,
                                                    sblock,
                                                    do_work,
                                                    subtype=subtype)
                    if reply is None:
                        raise Exception
                    ret = json.dumps(reply)
                except Exception as e:
                    log.server_logger.error(
                        'process rpc error;%s;%s;%s;User-Agent:%s', str(e),
                        util.get_request_ip(r), uid,
                        str(r.headers.get('User-Agent')))
                    ret = json.dumps({
                        'error': 'process rpc error',
                        'detail': str(e)
                    })
            # rpc: pending
            elif request_json['action'] == "pending":
                try:
                    reply = await rpc.pending_defer(r, uid, request_json)
                    if reply is None:
                        raise Exception
                    ret = json.dumps(reply)
                except Exception as e:
                    log.server_logger.error(
                        'pending rpc error;%s;%s;%s;User-Agent:%s', str(e),
                        util.get_request_ip(r), uid,
                        str(r.headers.get('User-Agent')))
                    ret = json.dumps({
                        'error': 'pending rpc error',
                        'detail': str(e)
                    })
            elif request_json['action'] == 'account_history':
                if await r.app['rdata'].hget(uid, "account") is None:
                    await r.app['rdata'].hset(
                        uid, "account", json.dumps([request_json['account']]))
                try:
                    response = await rpc.json_post(request_json)
                    if response is None:
                        raise Exception
                    ret = json.dumps(response)
                except Exception as e:
                    log.server_logger.error('rpc error;%s;%s;%s', str(e),
                                            util.get_request_ip(r), uid)
                    ret = json.dumps({
                        'error': 'account_history rpc error',
                        'detail': str(e)
                    })
            # rpc: fallthrough and error catch
            else:
                try:
                    response = await rpc.json_post(request_json)
                    if response is None:
                        raise Exception
                    ret = json.dumps(response)
                except Exception as e:
                    log.server_logger.error('rpc error;%s;%s;%s', str(e),
                                            util.get_request_ip(r), uid)
                    ret = json.dumps({'error': 'rpc error', 'detail': str(e)})
    except Exception as e:
        log.server_logger.exception('uncaught error;%s;%s',
                                    util.get_request_ip(r), uid)
        ret = json.dumps({
            'error': 'general error',
            'detail': str(sys.exc_info())
        })
    finally:
        r.app['active_messages'].remove(message)
        return ret
Ejemplo n.º 2
0
async def handle_user_message(r: web.Request,
                              message: str,
                              ws: web.WebSocketResponse = None):
    """Process data sent by client"""
    address = util.get_request_ip(r)
    uid = ws.id if ws is not None else '0'
    now = int(round(time.time() * 1000))
    if address in r.app['last_msg']:
        if (now - r.app['last_msg'][address]['last']) < 25:
            if r.app['last_msg'][address]['count'] > 3:
                log.server_logger.error(
                    'client messaging too quickly: %s ms; %s; %s; User-Agent: %s',
                    str(now - r.app['last_msg'][address]['last']), address,
                    uid, str(r.headers.get('User-Agent')))
                return None
            else:
                r.app['last_msg'][address]['count'] += 1
        else:
            r.app['last_msg'][address]['count'] = 0
    else:
        r.app['last_msg'][address] = {}
        r.app['last_msg'][address]['count'] = 0
    r.app['last_msg'][address]['last'] = now
    log.server_logger.info('request; %s, %s, %s', message, address, uid)
    if message not in r.app['active_messages']:
        r.app['active_messages'].add(message)
    else:
        log.server_logger.error('request already active; %s; %s; %s', message,
                                address, uid)
        return None
    ret = None
    try:
        request_json = json.loads(message)
        if request_json['action'] in ws_whitelist:
            # rpc: account_subscribe (only applies to websocket connections)
            if request_json['action'] == "account_subscribe" and ws is not None:
                # already subscribed, reconnect (websocket connections)
                if 'uuid' in request_json:
                    try:
                        del r.app['clients'][uid]
                    except Exception:
                        pass
                    uid = request_json['uuid']
                    ws.id = uid
                    r.app['clients'][uid] = ws
                    log.server_logger.info('reconnection request;' + address +
                                           ';' + uid)
                    try:
                        if 'currency' in request_json and request_json[
                                'currency'] in currency_list:
                            currency = request_json['currency']
                            r.app['cur_prefs'][uid] = currency
                            await r.app['rdata'].hset(uid, "currency",
                                                      currency)
                        else:
                            setting = await r.app['rdata'].hget(
                                uid, "currency")
                            if setting is not None:
                                r.app['cur_prefs'][uid] = setting
                            else:
                                r.app['cur_prefs'][uid] = 'usd'
                                await r.app['rdata'].hset(
                                    uid, "currency", 'usd')

                        # Get relevant account and public key
                        account = request_json[
                            'account'] if 'account' in request_json else None
                        b58_pubkey = request_json[
                            'b58_pubkey'] if 'b58_pubkey' in request_json else None

                        await ws_reconnect(ws, r, account, b58_pubkey)

                        if 'fcm_token' in request_json and account is not None:
                            if request_json['notification_enabled']:
                                await update_fcm_token_for_account(
                                    account, request_json['fcm_token'], r,
                                    b58_pubkey)
                            else:
                                await delete_fcm_token(
                                    account, request_json['fcm_token'], r)
                    except Exception as e:
                        log.server_logger.error('reconnect error; %s; %s; %s',
                                                str(e), address, uid)
                        reply = {'error': 'reconnect error', 'detail': str(e)}
                        ret = json.dumps(reply)
                # new user, setup uuid(or use existing if available) and account info
                else:
                    log.server_logger.info('subscribe request; %s; %s',
                                           util.get_request_ip(r), uid)
                    try:
                        if 'currency' in request_json and request_json[
                                'currency'] in currency_list:
                            currency = request_json['currency']
                        else:
                            currency = 'usd'
                        account = request_json[
                            'account'] if 'account' in request_json else None
                        b58_pubkey = request_json[
                            'b58_pubkey'] if 'b58_pubkey' in request_json else None
                        await ws_connect(ws, r, account, currency, b58_pubkey)

                        if 'fcm_token' in request_json and account is not None:
                            if request_json['notification_enabled']:
                                await update_fcm_token_for_account(
                                    account, request_json['fcm_token'], r,
                                    b58_pubkey)
                            else:
                                await delete_fcm_token(
                                    account, request_json['fcm_token'], r)
                    except Exception as e:
                        log.server_logger.error('subscribe error;%s;%s;%s',
                                                str(e), address, uid)
                        reply = {'error': 'subscribe error', 'detail': str(e)}
                        ret = json.dumps(reply)
            elif request_json['action'] == "fcm_update":
                # Updating FCM token
                if 'fcm_token' in request_json and 'account' in request_json and 'enabled' in request_json:
                    b58_pubkey = request_json[
                        'b58_pubkey'] if 'b58_pubkey' in request_json else None
                    if request_json['enabled']:
                        await update_fcm_token_for_account(
                            request_json['account'], request_json['fcm_token'],
                            r, b58_pubkey)
                    else:
                        await delete_fcm_token(request_json['account'],
                                               request_json['fcm_token'], r)
            elif request_json['action'] == 'fcm_update_bulk':
                if 'fcm_token' in request_json and 'accounts' in request_json and 'enabled' in request_json:
                    b58_pubkey = request_json[
                        'b58_pubkey'] if 'b58_pubkey' in request_json else None
                    if request_json['enabled']:
                        for account in request_json['accounts']:
                            await update_fcm_token_for_account(
                                account, request_json['fcm_token'], r,
                                b58_pubkey)
                    else:
                        for account in request_json['accounts']:
                            await delete_fcm_token(account,
                                                   request_json['fcm_token'],
                                                   r)
            elif request_json['action'] == 'fcm_delete_account':
                if 'fcm_token' in request_json and 'account' in request_json:
                    b58_pubkey = request_json[
                        'b58_pubkey'] if 'b58_pubkey' in request_json else None
                    await delete_fcm_account(request_json['account'],
                                             request_json['fcm_token'],
                                             r,
                                             explicit=True)
    except Exception as e:
        log.server_logger.exception('uncaught error;%s;%s',
                                    util.get_request_ip(r), uid)
        ret = json.dumps({
            'error': 'general error',
            'detail': str(sys.exc_info())
        })
    finally:
        r.app['active_messages'].remove(message)
        return ret