|  | @@ -60,13 +60,80 @@ static grpc_uri *bad_uri(const char *uri_text, int pos, const char *section,
 | 
	
		
			
				|  |  |    return NULL;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -static char *copy_fragment(const char *src, int begin, int end) {
 | 
	
		
			
				|  |  | +/** Returns a copy of \a src[begin, end) */
 | 
	
		
			
				|  |  | +static char *copy_component(const char *src, int begin, int end) {
 | 
	
		
			
				|  |  |    char *out = gpr_malloc(end - begin + 1);
 | 
	
		
			
				|  |  |    memcpy(out, src + begin, end - begin);
 | 
	
		
			
				|  |  |    out[end - begin] = 0;
 | 
	
		
			
				|  |  |    return out;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar
 | 
	
		
			
				|  |  | + * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent
 | 
	
		
			
				|  |  | + * sign not followed by two hex digits), -1 is returned. */
 | 
	
		
			
				|  |  | +static int parse_pchar(const char *uri_text, int i) {
 | 
	
		
			
				|  |  | +  /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
 | 
	
		
			
				|  |  | +   * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
 | 
	
		
			
				|  |  | +   * pct-encoded = "%" HEXDIG HEXDIG
 | 
	
		
			
				|  |  | +   * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
 | 
	
		
			
				|  |  | +                / "*" / "+" / "," / ";" / "=" */
 | 
	
		
			
				|  |  | +  char c = uri_text[i];
 | 
	
		
			
				|  |  | +  if ( ((c >= 'A') && (c <= 'Z')) ||
 | 
	
		
			
				|  |  | +       ((c >= 'a') && (c <= 'z')) ||
 | 
	
		
			
				|  |  | +       ((c >= '0') && (c <= '9')) ||
 | 
	
		
			
				|  |  | +       (c == '-' || c == '.' || c == '_' || c == '~') || /* unreserved */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +       (c == '!' || c == '$' || c == '&' || c == '\'' || c == '$' || c == '&' ||
 | 
	
		
			
				|  |  | +        c == '(' || c == ')' || c == '*' || c == '+' || c == ',' || c == ';' ||
 | 
	
		
			
				|  |  | +        c == '=') /* sub-delims */ ) {
 | 
	
		
			
				|  |  | +    return 1;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (c == '%') { /* pct-encoded */
 | 
	
		
			
				|  |  | +    int j;
 | 
	
		
			
				|  |  | +    if (uri_text[i+1] == 0 || uri_text[i+2] == 0) {
 | 
	
		
			
				|  |  | +      return -1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    for (j = i + 1; j < 2; j++) {
 | 
	
		
			
				|  |  | +      c = uri_text[j];
 | 
	
		
			
				|  |  | +      if (!(((c >= '0') && (c <= '9')) ||
 | 
	
		
			
				|  |  | +            ((c >= 'a') && (c <= 'f')) ||
 | 
	
		
			
				|  |  | +            ((c >= 'A') && (c <= 'F')))) {
 | 
	
		
			
				|  |  | +        return -1;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    return 2;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return 0;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* *( pchar / "?" / "/" ) */
 | 
	
		
			
				|  |  | +static int parse_query(const char *uri_text, int i) {
 | 
	
		
			
				|  |  | +  char c;
 | 
	
		
			
				|  |  | +  while ((c = uri_text[i]) != 0) {
 | 
	
		
			
				|  |  | +    const int advance = parse_pchar(uri_text, i); /* pchar */
 | 
	
		
			
				|  |  | +    switch (advance) {
 | 
	
		
			
				|  |  | +      case 0: /* uri_text[i] isn't in pchar */
 | 
	
		
			
				|  |  | +        /* maybe it's ? or / */
 | 
	
		
			
				|  |  | +        if (uri_text[i] == '?' || uri_text[i] == '/') {
 | 
	
		
			
				|  |  | +          i++;
 | 
	
		
			
				|  |  | +          break;
 | 
	
		
			
				|  |  | +        } else {
 | 
	
		
			
				|  |  | +          return i;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      case 1:
 | 
	
		
			
				|  |  | +      case 2:
 | 
	
		
			
				|  |  | +        i += advance;
 | 
	
		
			
				|  |  | +        break;
 | 
	
		
			
				|  |  | +      default: /* uri_text[i] introduces an invalid URI */
 | 
	
		
			
				|  |  | +        return -i;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return i; /* first uri_text position past the \a query production, maybe \0 */
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* alias for consistency */
 | 
	
		
			
				|  |  | +static int (*parse_fragment)(const char *uri_text, int i) = parse_query;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) {
 | 
	
		
			
				|  |  |    grpc_uri *uri;
 | 
	
		
			
				|  |  |    int scheme_begin = 0;
 | 
	
	
		
			
				|  | @@ -75,6 +142,10 @@ grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) {
 | 
	
		
			
				|  |  |    int authority_end = -1;
 | 
	
		
			
				|  |  |    int path_begin = -1;
 | 
	
		
			
				|  |  |    int path_end = -1;
 | 
	
		
			
				|  |  | +  int query_begin = -1;
 | 
	
		
			
				|  |  | +  int query_end = -1;
 | 
	
		
			
				|  |  | +  int fragment_begin = -1;
 | 
	
		
			
				|  |  | +  int fragment_end = -1;
 | 
	
		
			
				|  |  |    int i;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    for (i = scheme_begin; uri_text[i] != 0; i++) {
 | 
	
	
		
			
				|  | @@ -99,15 +170,9 @@ grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) {
 | 
	
		
			
				|  |  |    if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') {
 | 
	
		
			
				|  |  |      authority_begin = scheme_end + 3;
 | 
	
		
			
				|  |  |      for (i = authority_begin; uri_text[i] != 0 && authority_end == -1; i++) {
 | 
	
		
			
				|  |  | -      if (uri_text[i] == '/') {
 | 
	
		
			
				|  |  | +      if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') {
 | 
	
		
			
				|  |  |          authority_end = i;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      if (uri_text[i] == '?') {
 | 
	
		
			
				|  |  | -        return bad_uri(uri_text, i, "query_not_supported", suppress_errors);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  | -      if (uri_text[i] == '#') {
 | 
	
		
			
				|  |  | -        return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors);
 | 
	
		
			
				|  |  | -      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      if (authority_end == -1 && uri_text[i] == 0) {
 | 
	
		
			
				|  |  |        authority_end = i;
 | 
	
	
		
			
				|  | @@ -122,20 +187,48 @@ grpc_uri *grpc_uri_parse(const char *uri_text, int suppress_errors) {
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    for (i = path_begin; uri_text[i] != 0; i++) {
 | 
	
		
			
				|  |  | -    if (uri_text[i] == '?') {
 | 
	
		
			
				|  |  | -      return bad_uri(uri_text, i, "query_not_supported", suppress_errors);
 | 
	
		
			
				|  |  | +    if (uri_text[i] == '?' || uri_text[i] == '#') {
 | 
	
		
			
				|  |  | +      path_end = i;
 | 
	
		
			
				|  |  | +      break;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    if (uri_text[i] == '#') {
 | 
	
		
			
				|  |  | -      return bad_uri(uri_text, i, "fragment_not_supported", suppress_errors);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (path_end == -1 && uri_text[i] == 0) {
 | 
	
		
			
				|  |  | +    path_end = i;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (path_end == -1) {
 | 
	
		
			
				|  |  | +    return bad_uri(uri_text, i, "path", suppress_errors);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  if (uri_text[i] == '?') {
 | 
	
		
			
				|  |  | +    query_begin = i + 1;
 | 
	
		
			
				|  |  | +    i = parse_query(uri_text, query_begin);
 | 
	
		
			
				|  |  | +    if (i < 0) {
 | 
	
		
			
				|  |  | +      return bad_uri(uri_text, -i, "query", suppress_errors);
 | 
	
		
			
				|  |  | +    } else if (uri_text[i] != 0 && uri_text[i] != '#') {
 | 
	
		
			
				|  |  | +      /* We must be at the end or at the beginning of a fragment */
 | 
	
		
			
				|  |  | +      return bad_uri(uri_text, i, "query", suppress_errors);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    query_end = i;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (uri_text[i] == '#') {
 | 
	
		
			
				|  |  | +    fragment_begin = i + 1;
 | 
	
		
			
				|  |  | +    i = parse_fragment(uri_text, fragment_begin);
 | 
	
		
			
				|  |  | +    if (i < 0) {
 | 
	
		
			
				|  |  | +      return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors);
 | 
	
		
			
				|  |  | +    } else if (uri_text[i] != 0) {
 | 
	
		
			
				|  |  | +      /* We must be at the end */
 | 
	
		
			
				|  |  | +      return bad_uri(uri_text, i, "fragment", suppress_errors);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +    fragment_end = i;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | -  path_end = i;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    uri = gpr_malloc(sizeof(*uri));
 | 
	
		
			
				|  |  |    memset(uri, 0, sizeof(*uri));
 | 
	
		
			
				|  |  | -  uri->scheme = copy_fragment(uri_text, scheme_begin, scheme_end);
 | 
	
		
			
				|  |  | -  uri->authority = copy_fragment(uri_text, authority_begin, authority_end);
 | 
	
		
			
				|  |  | -  uri->path = copy_fragment(uri_text, path_begin, path_end);
 | 
	
		
			
				|  |  | +  uri->scheme = copy_component(uri_text, scheme_begin, scheme_end);
 | 
	
		
			
				|  |  | +  uri->authority = copy_component(uri_text, authority_begin, authority_end);
 | 
	
		
			
				|  |  | +  uri->path = copy_component(uri_text, path_begin, path_end);
 | 
	
		
			
				|  |  | +  uri->query = copy_component(uri_text, query_begin, query_end);
 | 
	
		
			
				|  |  | +  uri->fragment = copy_component(uri_text, fragment_begin, fragment_end);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    return uri;
 | 
	
		
			
				|  |  |  }
 | 
	
	
		
			
				|  | @@ -145,5 +238,7 @@ void grpc_uri_destroy(grpc_uri *uri) {
 | 
	
		
			
				|  |  |    gpr_free(uri->scheme);
 | 
	
		
			
				|  |  |    gpr_free(uri->authority);
 | 
	
		
			
				|  |  |    gpr_free(uri->path);
 | 
	
		
			
				|  |  | +  gpr_free(uri->query);
 | 
	
		
			
				|  |  | +  gpr_free(uri->fragment);
 | 
	
		
			
				|  |  |    gpr_free(uri);
 | 
	
		
			
				|  |  |  }
 |