1
0

uri-parser.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. /*
  2. * Copyright 2020 gRPC authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. export interface GrpcUri {
  18. scheme?: string;
  19. authority?: string;
  20. path: string;
  21. }
  22. /*
  23. * The groups correspond to URI parts as follows:
  24. * 1. scheme
  25. * 2. authority
  26. * 3. path
  27. */
  28. const URI_REGEX = /^(?:([A-Za-z0-9+.-]+):)?(?:\/\/([^/]*)\/)?(.+)$/;
  29. export function parseUri(uriString: string): GrpcUri | null {
  30. const parsedUri = URI_REGEX.exec(uriString);
  31. if (parsedUri === null) {
  32. return null;
  33. }
  34. return {
  35. scheme: parsedUri[1],
  36. authority: parsedUri[2],
  37. path: parsedUri[3],
  38. };
  39. }
  40. export interface HostPort {
  41. host: string;
  42. port?: number;
  43. }
  44. const NUMBER_REGEX = /^\d+$/;
  45. export function splitHostPort(path: string): HostPort | null {
  46. if (path.startsWith('[')) {
  47. const hostEnd = path.indexOf(']');
  48. if (hostEnd === -1) {
  49. return null;
  50. }
  51. const host = path.substring(1, hostEnd);
  52. /* Only an IPv6 address should be in bracketed notation, and an IPv6
  53. * address should have at least one colon */
  54. if (host.indexOf(':') === -1) {
  55. return null;
  56. }
  57. if (path.length > hostEnd + 1) {
  58. if (path[hostEnd + 1] === ':') {
  59. const portString = path.substring(hostEnd + 2);
  60. if (NUMBER_REGEX.test(portString)) {
  61. return {
  62. host: host,
  63. port: +portString,
  64. };
  65. } else {
  66. return null;
  67. }
  68. } else {
  69. return null;
  70. }
  71. } else {
  72. return {
  73. host,
  74. };
  75. }
  76. } else {
  77. const splitPath = path.split(':');
  78. /* Exactly one colon means that this is host:port. Zero colons means that
  79. * there is no port. And multiple colons means that this is a bare IPv6
  80. * address with no port */
  81. if (splitPath.length === 2) {
  82. if (NUMBER_REGEX.test(splitPath[1])) {
  83. return {
  84. host: splitPath[0],
  85. port: +splitPath[1],
  86. };
  87. } else {
  88. return null;
  89. }
  90. } else {
  91. return {
  92. host: path,
  93. };
  94. }
  95. }
  96. }
  97. export function combineHostPort(hostPort: HostPort): string {
  98. if (hostPort.port === undefined) {
  99. return hostPort.host;
  100. } else {
  101. // Only an IPv6 host should include a colon
  102. if (hostPort.host.includes(':')) {
  103. return `[${hostPort.host}]:${hostPort.port}`;
  104. } else {
  105. return `${hostPort.host}:${hostPort.port}`;
  106. }
  107. }
  108. }
  109. export function uriToString(uri: GrpcUri): string {
  110. let result = '';
  111. if (uri.scheme !== undefined) {
  112. result += uri.scheme + ':';
  113. }
  114. if (uri.authority !== undefined) {
  115. result += '//' + uri.authority + '/';
  116. }
  117. result += uri.path;
  118. return result;
  119. }