Implement "Followup improvements for ext/uri" RFC - WHATWG URL building - #22268
Implement "Followup improvements for ext/uri" RFC - WHATWG URL building#22268kocsismate wants to merge 8 commits into
Conversation
97cb0bf to
0655d8c
Compare
| goto failure; | ||
| } | ||
|
|
||
| if (lexbor_base_url != NULL) { |
There was a problem hiding this comment.
What's a big shame is that apparently it's not possible to properly use the builder with a base URL :(
- if we try to add the base URL after the input URL is built, then legitimate relative URLs are rejected (e.g.
/foo+https://example.com), because/foois not a valid URL on its own - If we try to build the input URL with the base URL in the same time, then the parsing algorithm must be used (currently, only setters are used with a hack on line 821). Then the complication is to find some URL component that is suitable for parsing:
- for special, full URLs: scheme + host is needed at least (e.g. https://example.com)
- for non-special full URLs: scheme is needed at least (e.g. https://)
- for relative URLs: the path is needed at least (e.g.
/foo/bar)
And in the 2nd case, the question arises if it's ok to parse only the minimally required components and then set the rest of the components, or the whole input URL must be built and parsed all at once.
There was a problem hiding this comment.
I have limited knowledge of lexbor, but can we build a string out of the components we have in the builder, and then call the parser with that? Assuming that we can build the string unambiguously, any errors from missing components would be reported by the parser.
There was a problem hiding this comment.
thanks for the idea! Unfortunately, - as far as I can see the situation - the main blocker is achiveving unambiguous recomposition. E.g.
$builder = new Uri\WhatWg\UrlBuilder();
$builder->setScheme("git");
$builder->setPath("//refs/heads/main");
$builder->build();
This would be recomposed as git://refs/heads/main, however it should rather be git:////refs/heads/main. So overall, we would end up reimplementing the WHATWG URL spec to prevent some cases. Since it's very long specification with a lot of special cases, I wouldn't even dare to attempt this. :(
There was a problem hiding this comment.
Just brainstorming, but would that work?
- base = parse base url
- url = clone base
- for each component in builder:
lxb_url_parse_basic(..., url, base, ..., override_state, ...)(withoverride_statethe state corresponding to this component, e.g.LXB_URL_STATE_PATH_START_STATEfor the path)
There was a problem hiding this comment.
Very clever idea again, but I'm not exactly sure that "inverting" parsing would work correctly either..
- let
basebehttps://example.com:80/foo - let
urlbescheme://example.net/bar
Then this should be scheme://example.net/bar. However, it would be scheme://example.net:80/bar. I guess, the algorithm could be improved by discarding base if url has a scheme.
@lexborisov do you maybe have some insights whether the above idea would work to support base URLs with the WHATWG URL Builder that was proposed in https://wiki.php.net/rfc/uri_followup#builder ? The complexity is that we store the components separately, and we need to build them together (via the setters) + use the base URL - but the setters obviously don't support the base URL.
The build() is implemented here: https://github.com/php/php-src/pull/22268/changes#diff-1ac5fecaced76e4592af01f30d246ce17edfd60c29ebe0e01d57b2988ec10fbdR892
There was a problem hiding this comment.
@arnaud-lb can we merge what we have so far? and then I can go back to this question a bit later? I'm worried a little bit, because quite a few of the URI followup RFC haven't been merged yet :(
There was a problem hiding this comment.
Hi @kocsismate
I've started looking into this thread, but I'll need some time. I have a lot on my plate right now.
At first glance, it seems like this can be resolved with minimal effort.
0655d8c to
af73d4a
Compare
|
May I have a review soon so that this can potentially be included into alpha 2 at least? :) |
| * The URL is initialized as LXB_URL_SCHEMEL_TYPE__UNDEF but this would prevent the scheme to be updated | ||
| * in case of non-special schemes due to https://github.com/php/php-src/blob/27d7b799c0a13578ee0506b428b8ddc209ffb010/ext/lexbor/lexbor/url/url.c#L1402 | ||
| */ | ||
| if (!php_uri_parser_whatwg_is_special_scheme(scheme)) { |
There was a problem hiding this comment.
I'll try to check if it's something that we can omit somehow...
There was a problem hiding this comment.
I didn't find any way how this workaround could be eliminated...
TimWolla
left a comment
There was a problem hiding this comment.
Had a very superficial first look.
TimWolla
left a comment
There was a problem hiding this comment.
Looked at some tests and some of the C files. Not yet through the PR, but you can likely already make some changes in response to this review.
4a39e3f to
8c50acd
Compare
TimWolla
left a comment
There was a problem hiding this comment.
Went through all tests now. Didn't deeply look at the C code yet, but I'm not super stoked about needing to reimplement the component validation ourselves.
| $builder->setHost("example.com"); | ||
| $builder->setFragment("\tfoo"); | ||
| $errors = []; | ||
| $url = $builder->build(errors: $errors); |
There was a problem hiding this comment.
It seems this is the only test testing $errors.
arnaud-lb
left a comment
There was a problem hiding this comment.
I've only done a partial review yet
9e71a5c to
1fa2162
Compare
…set() (#23144) Originally found in #22268 (comment)
Added lxb_url_parse_host_ipv6() — a public entry point to the IPv6 parser from the WHATWG specification: https://url.spec.whatwg.org/#concept-ipv6-parser The address is accepted both with and without the surrounding square brackets: "::1" and "[::1]" give the same result. #402 The API was requested in #402 for use by php/php-src#22268. Suggested-by: Máté Kocsis (@kocsismate)
1fa2162 to
3733c48
Compare
3733c48 to
a7145f3
Compare
| if (php_uri_whatwg_is_ascii_tab_or_newline(uc)) { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
This is consistent with Url::withScheme(), but also non-standard as per https://url.spec.whatwg.org/#url-writing:
A URL-scheme string must be one ASCII alpha, followed by zero or more of ASCII alphanumeric, U+002B (+), U+002D (-), and U+002E (.).
| if (php_uri_whatwg_is_ascii_tab_or_newline(uc)) { | ||
| continue; |
There was a problem hiding this comment.
Nit: This can skip tabs and newlines in the middle of the scheme
There was a problem hiding this comment.
Yes, this is intentional because of step 3 in https://url.spec.whatwg.org/#url-parsing:
Remove all ASCII tab or newline from input.
Or do you think this step shouldn't apply for the Builder?
There was a problem hiding this comment.
There's also a special case for leading/trailing C0 or space characters:
If url is not given:
- Set url to a new URL
- If input contains any leading or trailing C0 control or space, invalid-URL-unit validation error.
- Remove any leading and trailing C0 control or space from input.
The url parameter is given when we are using the setters. However, IMO the Builder should rather adhere to parsing rules (#22268 (comment)), so this section could apply to our use-case.
But at last, I didn't implement the Remove any leading and trailing C0 control or space from input., since we don't really know where the end of the URL is. Although, the scheme validator could ignore leading CO control or space characters at least...
But I don't really know what the best solution could be :(.
RFC: https://wiki.php.net/rfc/uri_followup#uri_building