Avoid private time parsing APIs - #259
Conversation
| pub static FMT2: StaticFormatDescription = format_description!(version = 2, | ||
| "[optional [[weekday], ]][day]-[month repr:short]-[year repr:last_two] [hour]:[minute]:[second] GMT" | ||
| "[optional [[weekday], ]][day]-[month repr:short]-[year padding:none] [hour]:[minute]:[second] GMT" |
There was a problem hiding this comment.
This is not the format specified in https://datatracker.ietf.org/doc/html/rfc2616#section-3.3.1. If we are going to deviate, we must only do so to maintain compatibility with the ecosystem at large. This would mean presenting evidence (via code) from some such project such as a popular web browser or web framework.
There was a problem hiding this comment.
Two options to avoid this, as I believe it was only done to allow parsing to succeed. One is to increase the version of time (and probably your MSRV as well) and use parse_with_defaults. The other is to do it a bit more manually, constructing Parsed with the "year century" as zero before parsing with Parsed::parse_item and calling try_into to create the PrimitiveDateTime from the now-mutated Parsed struct.
If you go this way, you can also map the year before converting to the final type, though there is likely no difference in performance between that and using replace_year.
There was a problem hiding this comment.
Is this the intended behavior? I see that we get an InsufficientInformation for two digit years now, but isn't this what the format description is saying is acceptable?
I see that the docs say:
Users can choose between three representations: the full year (the default), the century and the last two digits of the year. This should be relatively straightforward. Note that when parsing, if only the century or last two digits of the year are present, the value returned may not be what was expected — if the return is successful at all (it’s not guaranteed).
Which is...surprisingly vague.
There was a problem hiding this comment.
I have been working on a specification for increased clarity, but it's not yet public. The insufficient information is because only the last two digits are known, not the century. That's where the default needs to be specified somewhere.
There was a problem hiding this comment.
Essentially there are two phases: textually parsing and then converting into the final type. With only the last two digits, the second phase fails.
There was a problem hiding this comment.
Addressed by keeping FMT2 as [year repr:last_two]. This PR no longer broadens the accepted long-weekday dash format to full years, so it does not need browser/framework evidence for that behavior change.
I also added a characterization test showing that Sunday, 06-Nov-1994 08:49:37 GMT remains unsupported.
| }; | ||
|
|
||
| Ok(PrimitiveDateTime::try_from(date)?.assume_utc()) | ||
| Ok(date_time.replace_year(year)?.assume_utc()) |
There was a problem hiding this comment.
Doe this always result in the same behavior as the code had prior?
There was a problem hiding this comment.
Yes, that is now covered more directly. The first commit is now only characterization tests and passes before the implementation change. The implementation commit then passes the same tests.
The year cases cover the RFC 6265 cutoff edges (00/01/68/69/70/71/99), full-year guard cases, and the cutoff behavior across each supported cookie-rs date format.
|
Out of curiosity, was this AI assisted? The documentation feels excessive, and the added tests are not idiomatic to the present style. Otherwise, seems okay, except for the 1) definite and 2) possible deviations in behavior, with other minor tweaks for style, and a significant reduction (and humanification) of the docs. |
jhpratt
left a comment
There was a problem hiding this comment.
Without looking at the tests or checking for spec compliance (re. the change to how year is handled), I can confirm that this avoids invoking private APIs.
|
I think the current diff makes the tests/docs look more like churn than intended. The goal is to make this behavior-preserving, so I’m going to split the branch into:
The docs are intentionally more explicit than the previous comment because the parser is not a full RFC6265 tokenizer, while parts of the behavior are RFC6265-aligned. Without spelling that out near the code, future changes can easily “simplify” this back into either private I’ll also re-check the Also - yes this was AI assisted with human judgment in the loop and guided intentionally to add characterization tests and significant documentation around this to clarify the rationale and edge cases here. |
The private APIs have been locked down using technical measures, and once this usage is fixed the final one will be removed. Even now, there is a blindingly obvious deprecation warning. |
63524a9 to
0821e3e
Compare
0821e3e to
fbc617e
Compare
|
Since this did not preserve MSRV, I cannot merge it. Fixed in d4472e3. |
The MSRV check https://github.com/rwf2/cookie-rs/actions/runs/30510223790/job/90861072938?pr=259 passed on this PR - is that broken? |
Use documented time parsing entry points for cookie expiration dates so the crate does not rely on internal parsed-field methods from the time crate.
This replaces #256 with an approach that tests, documents and uses the public method instead of the private one.