|
74 | 74 | r'((?:(?:\s|^)-\s*)?\d*\.?\d*)\s*(?i:us(\s|\d|\.|$)|microseconds?(\s|$))',
|
75 | 75 | )
|
76 | 76 | INSTANCE_NAME_RE = re.compile(
|
77 |
| - r'^([A-Za-z_]\w*)(?:/([A-Za-z_]\w*))?$', |
| 77 | + r'^(\w(?:-?\w)*)$', |
| 78 | + re.ASCII, |
78 | 79 | )
|
| 80 | +CLOUD_INSTANCE_NAME_RE = re.compile( |
| 81 | + r'^([A-Za-z0-9](?:-?[A-Za-z0-9])*)/([A-Za-z0-9](?:-?[A-Za-z0-9])*)$', |
| 82 | + re.ASCII, |
| 83 | +) |
| 84 | +DSN_RE = re.compile( |
| 85 | + r'^[a-z]+://', |
| 86 | + re.IGNORECASE, |
| 87 | +) |
| 88 | +DOMAIN_LABEL_MAX_LENGTH = 63 |
79 | 89 |
|
80 | 90 |
|
81 | 91 | class ClientConfiguration(typing.NamedTuple):
|
@@ -519,11 +529,10 @@ def _parse_connect_dsn_and_args(
|
519 | 529 | ):
|
520 | 530 | resolved_config = ResolvedConnectConfig()
|
521 | 531 |
|
522 |
| - dsn, instance_name = ( |
523 |
| - (dsn, None) |
524 |
| - if dsn is not None and re.match('(?i)^[a-z]+://', dsn) |
525 |
| - else (None, dsn) |
526 |
| - ) |
| 532 | + if dsn and DSN_RE.match(dsn): |
| 533 | + instance_name = None |
| 534 | + else: |
| 535 | + instance_name, dsn = dsn, None |
527 | 536 |
|
528 | 537 | has_compound_options = _resolve_config_options(
|
529 | 538 | resolved_config,
|
@@ -861,6 +870,13 @@ def _parse_cloud_instance_name_into_config(
|
861 | 870 | org_slug: str,
|
862 | 871 | instance_name: str,
|
863 | 872 | ):
|
| 873 | + label = f"{instance_name}--{org_slug}" |
| 874 | + if len(label) > DOMAIN_LABEL_MAX_LENGTH: |
| 875 | + raise ValueError( |
| 876 | + f"invalid instance name: cloud instance name length cannot exceed " |
| 877 | + f"{DOMAIN_LABEL_MAX_LENGTH - 1} characters: " |
| 878 | + f"{org_slug}/{instance_name}" |
| 879 | + ) |
864 | 880 | secret_key = resolved_config.secret_key
|
865 | 881 | if secret_key is None:
|
866 | 882 | try:
|
@@ -889,7 +905,7 @@ def _parse_cloud_instance_name_into_config(
|
889 | 905 | raise errors.ClientConnectionError("Invalid secret key")
|
890 | 906 | payload = f"{org_slug}/{instance_name}".encode("utf-8")
|
891 | 907 | dns_bucket = binascii.crc_hqx(payload, 0) % 100
|
892 |
| - host = f"{instance_name}--{org_slug}.c-{dns_bucket:02d}.i.{dns_zone}" |
| 908 | + host = f"{label}.c-{dns_bucket:02d}.i.{dns_zone}" |
893 | 909 | resolved_config.set_host(host, source)
|
894 | 910 |
|
895 | 911 |
|
@@ -973,23 +989,23 @@ def _resolve_config_options(
|
973 | 989 | else:
|
974 | 990 | creds = cred_utils.validate_credentials(cred_data)
|
975 | 991 | source = "credentials"
|
| 992 | + elif INSTANCE_NAME_RE.match(instance_name[0]): |
| 993 | + source = instance_name[1] |
| 994 | + creds = cred_utils.read_credentials( |
| 995 | + cred_utils.get_credentials_path(instance_name[0]), |
| 996 | + ) |
976 | 997 | else:
|
977 |
| - name_match = INSTANCE_NAME_RE.match(instance_name[0]) |
| 998 | + name_match = CLOUD_INSTANCE_NAME_RE.match(instance_name[0]) |
978 | 999 | if name_match is None:
|
979 | 1000 | raise ValueError(
|
980 | 1001 | f'invalid DSN or instance name: "{instance_name[0]}"'
|
981 | 1002 | )
|
982 | 1003 | source = instance_name[1]
|
983 | 1004 | org, inst = name_match.groups()
|
984 |
| - if inst is not None: |
985 |
| - _parse_cloud_instance_name_into_config( |
986 |
| - resolved_config, source, org, inst |
987 |
| - ) |
988 |
| - return True |
989 |
| - |
990 |
| - creds = cred_utils.read_credentials( |
991 |
| - cred_utils.get_credentials_path(instance_name[0]), |
| 1005 | + _parse_cloud_instance_name_into_config( |
| 1006 | + resolved_config, source, org, inst |
992 | 1007 | )
|
| 1008 | + return True |
993 | 1009 |
|
994 | 1010 | resolved_config.set_host(creds.get('host'), source)
|
995 | 1011 | resolved_config.set_port(creds.get('port'), source)
|
|
0 commit comments