async def bancho_handler(conn: Connection) -> bytes: if ('User-Agent' not in conn.headers or conn.headers['User-Agent'] != 'osu!'): return # check for 'osu-token' in the headers. # if it's not there, this is a login request. if 'osu-token' not in conn.headers: # login is a bit of a special case, # so we'll handle it separately. async with glob.players._lock: resp, token = await login(conn.body, conn.headers['X-Real-IP'], conn.headers) conn.add_resp_header(f'cho-token: {token}') return resp # get the player from the specified osu token. player = await glob.players.get(token=conn.headers['osu-token']) if not player: # token was not found; changes are, we just restarted # the server. just tell their client to re-connect. return packets.restartServer(0) # bancho connections can be comprised of multiple packets; # our reader is designed to iterate through them individually, # allowing logic to be implemented around the actual handler. # NOTE: the reader will internally discard any # packets whose logic has not been defined. # TODO: why is the packet reader async lol async for packet in BanchoPacketReader(conn.body): await packet.handle(player) if glob.config.debug: log(f'{packet.type!r}', Ansi.LMAGENTA) player.last_recv_time = time.time() # TODO: this could probably be done better? resp = bytearray() while not player.queue_empty(): # read all queued packets into stream resp += player.dequeue() conn.add_resp_header('Content-Type: text/html; charset=UTF-8') resp = bytes(resp) # even if the packet is empty, we have to # send back an empty response so the client # knows it was successfully delivered. return resp
async def bancho_handler(conn: Connection) -> bytes: if ('User-Agent' not in conn.headers or conn.headers['User-Agent'] != 'osu!'): return # check for 'osu-token' in the headers. # if it's not there, this is a login request. if 'osu-token' not in conn.headers: # login is a bit of a special case, # so we'll handle it separately. async with glob.players._lock: resp, token = await login(conn.body, conn.headers['X-Real-IP']) conn.add_resp_header(f'cho-token: {token}') return resp # get the player from the specified osu token. player = glob.players.get(token=conn.headers['osu-token']) if not player: # token was not found; changes are, we just restarted # the server. just tell their client to re-connect. return packets.notification('Server is restarting') + \ packets.restartServer(0) # send 0ms since server is up # restricted users may only use certain packet handlers. if not player.restricted: packet_map = glob.bancho_packets['all'] else: packet_map = glob.bancho_packets['restricted'] # bancho connections can be comprised of multiple packets; # our reader is designed to iterate through them individually, # allowing logic to be implemented around the actual handler. # NOTE: the reader will internally discard any # packets whose logic has not been defined. packets_read = [] for packet in BanchoPacketReader(conn.body, packet_map): await packet.handle(player) packets_read.append(packet.type) if glob.config.debug: packets_str = ', '.join([p.name for p in packets_read]) or 'None' log(f'[BANCHO] {player} | {packets_str}.', AnsiRGB(0xff68ab)) player.last_recv_time = time.time() conn.add_resp_header('Content-Type: text/html; charset=UTF-8') return player.dequeue() or b''
async def get_banner(conn: Connection) -> Optional[bytes]: filename = conn.path[9:] if '.' in filename: # user id & file extension provided path = BANNERS_PATH / filename if not path.exists(): path = DEFAULT_BANNER elif filename not in ('', 'favicon.ico'): # user id provided - determine file extension for ext in ('jpg', 'jpeg', 'png'): path = BANNERS_PATH / f'{filename}.{ext}' if path.exists(): break else: # no file exists path = DEFAULT_BANNER else: # empty path or favicon, serve default banner path = DEFAULT_BANNER ext = 'png' if path.suffix == '.png' else 'jpeg' conn.add_resp_header(f'Content-Type: image/{ext}') return path.read_bytes()
async def bancho_handler(conn: Connection) -> bytes: if 'User-Agent' not in conn.headers: return if conn.headers['User-Agent'] != 'osu!': # most likely a request from a browser. return b'<!DOCTYPE html>' + '<br>'.join(( f'Running gulag v{glob.version}', f'Players online: {len(glob.players) - 1}', '<a href="https://github.com/cmyui/gulag">Source code</a>', '', '<b>Packets handled</b>', '<br>'.join(f'{p.name} ({p.value})' for p in glob.bancho_packets) )).encode() # check for 'osu-token' in the headers. # if it's not there, this is a login request. if 'osu-token' not in conn.headers: # login is a bit of a special case, # so we'll handle it separately. resp, token = await login( conn.body, conn.headers['X-Real-IP'] ) conn.add_resp_header(f'cho-token: {token}') return resp # get the player from the specified osu token. player = await glob.players.get(token=conn.headers['osu-token']) if not player: # token was not found; changes are, we just restarted # the server. just tell their client to re-connect. return packets.notification('Server is restarting') + \ packets.restartServer(0) # send 0ms since server is up # bancho connections can be comprised of multiple packets; # our reader is designed to iterate through them individually, # allowing logic to be implemented around the actual handler. # NOTE: the reader will internally discard any # packets whose logic has not been defined. async for packet in BanchoPacketReader(conn.body): await packet.handle(player) if glob.config.debug: log(f'{packet.type!r}', Ansi.LMAGENTA) player.last_recv_time = time.time() # TODO: this could probably be done better? resp = bytearray() while not player.queue_empty(): # read all queued packets into stream resp += player.dequeue() conn.add_resp_header('Content-Type: text/html; charset=UTF-8') resp = bytes(resp) # even if the packet is empty, we have to # send back an empty response so the client # knows it was successfully delivered. return resp
async def everything(conn: Connection) -> Optional[bytes]: conn.resp_headers['Location'] = f'https://b.ppy.sh{conn.path}' return (301, b'')