@@ -10,6 +10,172 @@ The format is based on [Keep a Changelog][], and this project adheres to [Semant
1010[ Keep a Changelog ] : https://keepachangelog.com/en/1.1.0/
1111[ Semantic Versioning ] : https://semver.org/spec/v2.0.0.html
1212
13+ ## [ v0.99.80] - 2025-09-05
14+
15+ - Review referenced RFCs for compliance, clarity and currency
16+ - Rename ` Regex::UUID ` to ` UUID_V4 `
17+ - Add ` Regex::UUID ` to match all UUIDs and adopt in ` AbstractSyncProvider::isValidIdentifier() `
18+ - Normalise exception variable names as ` $ex `
19+
20+ ### Added
21+
22+ #### ` Cli `
23+
24+ - Add ` CliUtil ` with ` getJson() ` and consolidate similar methods
25+
26+ #### ` Collection `
27+
28+ - Add dictionary interface, class and trait
29+
30+ #### ` Http `
31+
32+ - Add ` getHeaderLines() ` alongside existing ` getHeaderLine() ` methods
33+ - Add ` HeadersInterface ` methods ` hasBadWhitespace() ` and ` hasObsoleteLineFolding() `
34+ - Add ` MultipartStreamInterface::getParts() ` for completeness
35+
36+ ### Changed
37+
38+ #### ` Collection `
39+
40+ - Allow ` Arrayable::toArray() ` return type variations by adding an optional ` TArrayValue ` template to ` DictionaryInterface ` and ` CollectionInterface `
41+ - Move ` toArray() ` from ` ReadOnlyCollectionTrait ` to ` RecursiveArrayableCollectionTrait `
42+ - Add non-recursive implementation of ` toArray() ` to ` ArrayableCollectionTrait `
43+
44+ #### ` Curler `
45+
46+ - Pass the number of entities returned from the endpoint via previous pages to ` CurlerPagerInterface::getPage() `
47+ - Rename ` METHOD_HAS_BODY ` to ` REQUEST_METHOD_HAS_BODY ` (to prevent conflict with other ` METHOD_* ` constants)
48+ - Rename methods for consistency with ` Http ` changes:
49+ - ` getPublicHttpHeaders() ` -> ` getPublicHeaders() `
50+ - ` hasAccessToken() ` -> ` hasCredential() `
51+ - ` withAccessToken() ` -> ` withCredential() `
52+
53+ #### ` Http `
54+
55+ <details >
56+ <summary >Move and rename various interfaces, classes, traits and members</summary >
57+
58+ - Rename:
59+ - ` AccessTokenInterface ` -> ` CredentialInterface `
60+ - ` getTokenType() ` -> ` getAuthenticationScheme() `
61+ - ` getToken() ` -> ` getCredential() `
62+ - ` HttpHeadersInterface ` -> ` HeadersInterface `
63+ - ` hasLastLine() ` -> ` hasEmptyLine() `
64+ - ` append() ` -> ` addValue() `
65+ - ` canonicalize() ` -> ` normalise() `
66+ - ` HasHttpHeaders ` (interface) -> ` HasInnerHeaders `
67+ - ` getHttpHeaders() ` -> ` getInnerHeaders() `
68+ - ` HttpHeaders ` -> ` Headers `
69+ - ` HasHttpHeaders ` (trait) -> ` HasInnerHeadersTrait `
70+ - ` HttpServer ` -> ` Server ` and move to separate namespace
71+ - ` Stream::copyToString() ` -> ` getStreamContents() ` and move to ` HttpUtil `
72+ - ` Stream::copyToStream() ` -> ` copyStream() ` and move to ` HttpUtil `
73+ - ` UriInterface::toParts() ` -> ` getComponents() `
74+ - ` UriInterface::isReference() ` -> ` isRelativeReference() `
75+ - ` Uri::EXPAND_EMPTY_PATH ` -> ` NORMALISE_EMPTY_PATH `
76+ - ` Uri::COLLAPSE_MULTIPLE_SLASHES ` -> ` NORMALISE_MULTIPLE_SLASHES `
77+ - ` HttpUtil::escapeQuotedString() ` -> ` quoteString() `
78+ - ` HttpUtil::getNameValueGenerator() ` -> ` getNameValuePairs() `
79+ - ` HttpServerException ` -> ` ServerException `
80+ - ` StreamDetachedException ` -> ` StreamClosedException `
81+ - ` StreamInvalidRequestException ` -> ` InvalidStreamRequestException `
82+ - ` UploadedFileException ` -> ` UploadFailedException `
83+ - Move:
84+ - ` Headers::getContentLength() ` to ` HttpUtil `
85+ - ` Headers::getMultipartBoundary() ` to ` HttpUtil `
86+ - ` Headers::getPreferences() ` to ` HttpUtil `
87+ - ` Headers::mergePreferences() ` to ` HttpUtil `
88+ - ` Headers::getRetryAfter() ` to ` HttpUtil `
89+ - ` Uri::isAuthorityForm() ` to ` HttpUtil `
90+ - Move message-related interfaces to their own namespace and rename:
91+ - ` HttpMessageInterface ` -> ` MessageInterface `
92+ - ` HttpMultipartStreamInterface ` -> ` MultipartStreamInterface `
93+ - ` HttpRequestInterface ` -> ` RequestInterface `
94+ - ` HttpResponseInterface ` -> ` ResponseInterface `
95+ - ` HttpServerRequestInterface ` -> ` ServerRequestInterface `
96+ - ` HttpStreamInterface ` -> ` StreamInterface `
97+ - ` HttpMultipartStreamPartInterface ` -> ` StreamPartInterface `
98+ - ` getFallbackFilename() ` -> ` getAsciiFilename() `
99+ - ` getContent() ` -> ` getBody() `
100+ - Move message-related classes to their own namespace and rename:
101+ - ` AbstractHttpMessage ` -> ` AbstractMessage `
102+ - ` AbstractHttpRequest ` -> ` AbstractRequest `
103+ - ` HttpFactory ` -> ` MessageFactory `
104+ - ` HttpMultipartStream ` -> ` MultipartStream `
105+ - ` HttpRequest ` -> ` Request `
106+ - ` HttpResponse ` -> ` Response `
107+ - ` HttpServerRequest ` -> ` ServerRequest `
108+ - ` HttpServerRequestUpload ` -> ` ServerRequestUpload `
109+ - ` HttpStream ` -> ` Stream `
110+ - ` HttpMultipartStreamPart ` -> ` StreamPart `
111+ - Rename ` FormDataFlag ` to ` HasFormDataFlag ` , add ` DATA_ ` prefix to the names of its constants and access them via implementations
112+ - Rename ` MimeType ` to ` HasMediaType ` , add ` TYPE_ ` prefix to the names of its constants and access them via implementations
113+ - Rename ` HttpRequestMethod ` to ` HasRequestMethod ` , add ` METHOD_ ` prefix to the names of its constants and access them via implementations
114+ - Rename ` HttpHeader ` to ` HasHttpHeader ` , add ` HEADER_ ` prefix to the names of its constants and access them via implementations
115+ - Rename ` HttpHeaderGroup ` to ` HasHttpHeaders ` , add ` HEADERS_ ` prefix to the names of its constants and access them via implementations
116+ </details >
117+
118+ - In ` HeadersInterface ` , extend ` DictionaryInterface ` (new) instead of ` CollectionInterface `
119+ - In ` Headers::addLine() ` :
120+ - Throw an exception on invalid header field syntax or line folding even if ` $strict ` is ` false `
121+ - Throw ` LogicException ` if called after headers are applied via another method
122+ - Throw ` InvalidHeaderException ` instead of ` InvalidArgumentException `
123+ - In ` Headers::map() ` , return the same instance if mapped values are equivalent
124+ - In ` HttpUtil::quoteString() ` , add double quotes
125+ - In ` UriInterface ` , remove nullability from ` parse() ` parameter ` $component `
126+ - In ` Uri::parse() ` , replicate ` parse_url() ` behaviour when URI and component are both invalid (i.e. return ` false ` )
127+ - In ` AbstractMessage ` /` AbstractRequest ` :
128+ - Don't apply an implicit media type to messages with multipart bodies
129+ - Normalise headers when casting messages to strings
130+ - In ` MultipartStream ` , do not remain operational after ` close() ` or ` detach() ` are called
131+ - Refactor ` Server ` for compliance and consistency
132+ - Rename:
133+ - ` getProxyTls() ` -> ` proxyHasTls() `
134+ - ` getProxyBasePath() ` -> ` getProxyPath() `
135+ - ` getBaseUri() ` -> ` getUri() ` and return ` Uri ` , not ` string `
136+ - Add:
137+ - ` getLocalIpAddress() `
138+ - ` getLocalPort() ` to improve support for dynamic port allocation
139+ - Allow ` stop() ` to be called when the server is not running
140+ - In ` listen() ` :
141+ - Replace ` $callback ` with ` $listener ` , which returns a ` ServerResponse ` instead of receiving control variables by reference
142+ - Keep listening for requests until a response has a return value unless a non-negative ` $limit ` is given
143+ - Add request target validity checks
144+ - Respond to invalid requests with "400 Bad Request" or "501 Not Implemented" and don't throw the underlying exception by default
145+ - Throw ` LogicException ` when:
146+ - ` getProxy*() ` is called on an instance with no proxy, or
147+ - an assertion fails (instead of ` ServerException ` )
148+ - Adopt terminology and behaviour of \[ RFC9112] , \[ RFC9110] and \[ RFC8187] , which supercede \[ RFC7230] , \[ RFC7231] and \[ RFC5987]
149+ - Replace "header line" with "field line" where appropriate
150+ - In ` Headers::addLine() ` , don't throw ` InvalidHeaderException ` when bad whitespace is received and ` $strict = true `
151+ - Use ` getLastHeaderValue() ` when retrieving ` Content-Type ` headers
152+ - Use ` getOnlyHeaderValue() ` when retrieving ` Location ` headers
153+
154+ ### Removed
155+
156+ #### ` Http `
157+
158+ - Remove superfluous ` MessageInterface::getHttpPayload() `
159+ - Remove ` $strict ` parameter from ` HeadersInterface::addLine() ` (implementation detail)
160+ - Remove unused ` Uri ` methods:
161+ - ` fromParts() `
162+ - ` unparse() `
163+ - ` resolveReference() `
164+ - ` removeDotSegments() `
165+ - Remove superfluous ` Server::getScheme() `
166+ - Remove unused ` HttpRequestHandlerInterface `
167+
168+ ### Fixed
169+
170+ #### ` Http `
171+
172+ - Fix issue where message objects cast to strings may have too many empty lines between headers and body
173+ - Fix ` AbstractRequest ` issue where request target type ` origin-form ` may be incorrectly detected when the given URI has an empty path
174+ - Fix ` Headers::addLine() ` issue where invalid line folding in the first trailer may not be detected
175+ - Fix ` Headers::getHeaderLine() ` issue where headers with unquoted commas may be inadvertently modified
176+ - Fix ` HttpUtil::getRetryAfter() ` issue where negative delay seconds are parsed as a timestamp
177+ - Fix ` Server ` issue where large responses might not be written in full
178+
13179## [ v0.99.79] - 2025-03-31
14180
15181### Added
@@ -4935,6 +5101,7 @@ This is the final release of `lkrms/util`. It is moving to [Salient](https://git
49355101
49365102- Allow `CliOption` value names to contain arbitrary characters
49375103
5104+ [v0.99.80]: https://github.com/salient-labs/toolkit/compare/v0.99.79...v0.99.80
49385105[v0.99.79]: https://github.com/salient-labs/toolkit/compare/v0.99.78...v0.99.79
49395106[v0.99.78]: https://github.com/salient-labs/toolkit/compare/v0.99.77...v0.99.78
49405107[v0.99.77]: https://github.com/salient-labs/toolkit/compare/v0.99.76...v0.99.77
0 commit comments