Brad Bishop | 08902b0 | 2019-08-20 09:16:51 -0400 | [diff] [blame] | 1 | # Copyright (C) 2018-2019 Garmin Ltd. |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 2 | # |
Brad Bishop | c342db3 | 2019-05-15 21:57:59 -0400 | [diff] [blame] | 3 | # SPDX-License-Identifier: GPL-2.0-only |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 4 | # |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 5 | |
Andrew Geissler | 6ce62a2 | 2020-11-30 19:58:47 -0600 | [diff] [blame] | 6 | import asyncio |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 7 | from contextlib import closing |
| 8 | import re |
Andrew Geissler | 475cb72 | 2020-07-10 16:00:51 -0500 | [diff] [blame] | 9 | import itertools |
| 10 | import json |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 11 | from collections import namedtuple |
| 12 | from urllib.parse import urlparse |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 13 | |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 14 | UNIX_PREFIX = "unix://" |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 15 | WS_PREFIX = "ws://" |
| 16 | WSS_PREFIX = "wss://" |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 17 | |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 18 | ADDR_TYPE_UNIX = 0 |
| 19 | ADDR_TYPE_TCP = 1 |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 20 | ADDR_TYPE_WS = 2 |
Brad Bishop | 1932369 | 2019-04-05 15:28:33 -0400 | [diff] [blame] | 21 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 22 | User = namedtuple("User", ("username", "permissions")) |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 23 | |
| 24 | |
| 25 | def parse_address(addr): |
| 26 | if addr.startswith(UNIX_PREFIX): |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 27 | return (ADDR_TYPE_UNIX, (addr[len(UNIX_PREFIX) :],)) |
| 28 | elif addr.startswith(WS_PREFIX) or addr.startswith(WSS_PREFIX): |
| 29 | return (ADDR_TYPE_WS, (addr,)) |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 30 | else: |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 31 | m = re.match(r"\[(?P<host>[^\]]*)\]:(?P<port>\d+)$", addr) |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 32 | if m is not None: |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 33 | host = m.group("host") |
| 34 | port = m.group("port") |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 35 | else: |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 36 | host, port = addr.split(":") |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 37 | |
| 38 | return (ADDR_TYPE_TCP, (host, int(port))) |
| 39 | |
| 40 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 41 | def create_server( |
| 42 | addr, |
| 43 | dbname, |
| 44 | *, |
| 45 | sync=True, |
| 46 | upstream=None, |
| 47 | read_only=False, |
| 48 | db_username=None, |
| 49 | db_password=None, |
| 50 | anon_perms=None, |
| 51 | admin_username=None, |
| 52 | admin_password=None, |
| 53 | ): |
| 54 | def sqlite_engine(): |
| 55 | from .sqlite import DatabaseEngine |
Andrew Geissler | 475cb72 | 2020-07-10 16:00:51 -0500 | [diff] [blame] | 56 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 57 | return DatabaseEngine(dbname, sync) |
Andrew Geissler | 475cb72 | 2020-07-10 16:00:51 -0500 | [diff] [blame] | 58 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 59 | def sqlalchemy_engine(): |
| 60 | from .sqlalchemy import DatabaseEngine |
Andrew Geissler | 475cb72 | 2020-07-10 16:00:51 -0500 | [diff] [blame] | 61 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 62 | return DatabaseEngine(dbname, db_username, db_password) |
| 63 | |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 64 | from . import server |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 65 | |
| 66 | if "://" in dbname: |
| 67 | db_engine = sqlalchemy_engine() |
| 68 | else: |
| 69 | db_engine = sqlite_engine() |
| 70 | |
| 71 | if anon_perms is None: |
| 72 | anon_perms = server.DEFAULT_ANON_PERMS |
| 73 | |
| 74 | s = server.Server( |
| 75 | db_engine, |
| 76 | upstream=upstream, |
| 77 | read_only=read_only, |
| 78 | anon_perms=anon_perms, |
| 79 | admin_username=admin_username, |
| 80 | admin_password=admin_password, |
| 81 | ) |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 82 | |
| 83 | (typ, a) = parse_address(addr) |
| 84 | if typ == ADDR_TYPE_UNIX: |
| 85 | s.start_unix_server(*a) |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 86 | elif typ == ADDR_TYPE_WS: |
| 87 | url = urlparse(a[0]) |
| 88 | s.start_websocket_server(url.hostname, url.port) |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 89 | else: |
| 90 | s.start_tcp_server(*a) |
| 91 | |
| 92 | return s |
| 93 | |
| 94 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 95 | def create_client(addr, username=None, password=None): |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 96 | from . import client |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 97 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 98 | c = client.Client(username, password) |
Brad Bishop | a34c030 | 2019-09-23 22:34:48 -0400 | [diff] [blame] | 99 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 100 | try: |
| 101 | (typ, a) = parse_address(addr) |
| 102 | if typ == ADDR_TYPE_UNIX: |
| 103 | c.connect_unix(*a) |
| 104 | elif typ == ADDR_TYPE_WS: |
| 105 | c.connect_websocket(*a) |
| 106 | else: |
| 107 | c.connect_tcp(*a) |
| 108 | return c |
| 109 | except Exception as e: |
| 110 | c.close() |
| 111 | raise e |
Andrew Geissler | 6ce62a2 | 2020-11-30 19:58:47 -0600 | [diff] [blame] | 112 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 113 | |
| 114 | async def create_async_client(addr, username=None, password=None): |
Andrew Geissler | 6ce62a2 | 2020-11-30 19:58:47 -0600 | [diff] [blame] | 115 | from . import client |
Andrew Geissler | 6ce62a2 | 2020-11-30 19:58:47 -0600 | [diff] [blame] | 116 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 117 | c = client.AsyncClient(username, password) |
Andrew Geissler | 6ce62a2 | 2020-11-30 19:58:47 -0600 | [diff] [blame] | 118 | |
Patrick Williams | ac13d5f | 2023-11-24 18:59:46 -0600 | [diff] [blame] | 119 | try: |
| 120 | (typ, a) = parse_address(addr) |
| 121 | if typ == ADDR_TYPE_UNIX: |
| 122 | await c.connect_unix(*a) |
| 123 | elif typ == ADDR_TYPE_WS: |
| 124 | await c.connect_websocket(*a) |
| 125 | else: |
| 126 | await c.connect_tcp(*a) |
| 127 | |
| 128 | return c |
| 129 | except Exception as e: |
| 130 | await c.close() |
| 131 | raise e |