URI Parsing Library for Pony #3
Closed
SeanTAllen
started this conversation in
Research
Replies: 1 comment
|
This is complete including the out-of-scope items. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
this originated on ponylang/lori_http_server and was transfered here to ponylang/uri
Context
lori_http_server passes raw request-target strings to handlers as
String val. Discussion ponylang/lori_http_server#2 explicitly deferred URI parsing: "Full RFC 3986 parsing can be a separate package." This plan covers building that capability as aurisubpackage withinhttp_server/.ponylang/http_serverincludes URL parsing (URLclass,URLEncodeprimitive) but has design issues:?) rather than error-as-data typesURLfields arevar— mutable during construction viarefmethodsjoin()is declared but unimplementedcheck_scheme()fails to validate that the first character is alpha (allows1foo:as a scheme)Package Location
The
uripackage lives athttp_server/uri/as a subpackage ofhttp_server, following the same pattern as semver's subpackages (semver/range,semver/solver, etc.). If the package proves useful beyond this project, it can be extracted into its own repo later.corral.json'spackagesarray will include"http_server/uri"to declare it as a provided package. In these initial phases,http_serverdoes not importuri— theHandlertrait continues to pass rawString val. Integration intohttp_server's interface can follow once the uri package is built.Scope
In scope
application/x-www-form-urlencodedkey-value pairs)Out of scope (can be added later)
Design
Error Types
Each operation defines its own error vocabulary. Structural parsing and percent-encoding have separate error types since they come from different operations.
Core Types
All parsed types are
class val— immutable after construction, fields arelet. Construction is through factory primitives that return(T | Error), not partial constructors.URI— the top-level parsed URI-reference:URIAuthority— the parsed authority component:Factory Primitives
ParseURI— parse a URI-reference:ParseURIAuthority— parse an authority string directly:Percent-Encoding Utilities
Component type tags for encoding rules:
Query Parameter Parsing
Separate from the core URI type — RFC 3986 doesn't define query parameter structure. This implements the
application/x-www-form-urlencodedformat used by HTML forms and virtually all HTTP query strings.Path Segment Decomposition
Usage from Application Code
In these phases,
http_serverdoes not yet importuri— theHandlertrait continues to passuri: String val. Application code can import theurisubpackage directly to parse the raw string. Typical usage in a handler:For CONNECT requests (authority-form
host:port):Implementation Phases
Phase 1: Percent-Encoding
Everything else depends on this.
Files:
http_server/uri/percent_encoding.pony—PercentDecode,PercentEncode,URIParttypes,InvalidPercentEncodinghttp_server/_test.pony(modified) — adduse uri = "./uri"anduri.Main.make().tests(test)delegation callBuild integration: The
http_server/uri/_test.ponyMain actor follows the stdlib delegation pattern (new create(env)for standalone use,new make() => None+fun tag tests()for delegation).http_server/_test.ponycallsuri.Main.make().tests(test)to include URI tests in the existing test binary.Testing (
http_server/uri/_test_percent_encoding.pony):PercentDecode(PercentEncode(s, part))roundtrips for arbitrary strings and each URI partPercentEncodeoutput for each part contains only RFC 3986-legal characters for that part%X, non-hex%GG, trailing%) produceInvalidPercentEncodingPercentDecodesucceeds iff input is the valid variantRun:
make ssl=3.0.xPhase 2: URI Parsing
Structural decomposition of URI-references.
Files:
http_server/uri/uri.pony— package docstring andURIclass withstring()andeq()methodshttp_server/uri/uri_authority.pony—URIAuthorityclass withstring()andeq()methodshttp_server/uri/parse_uri.pony—ParseURIfactoryhttp_server/uri/parse_uri_authority.pony—ParseURIAuthorityfactoryhttp_server/uri/uri_parse_error.pony—InvalidPort,InvalidHost,URIParseErrorunionhttp_server/uri/_mort.pony—_Unreachable,_IllegalState(same issue URL as http_server — both are in this repo). This is an intentional duplication ofhttp_server/_mort.ponybecause these primitives are package-private (_-prefixed) and cannot be shared across packages in PonyThe package docstring in
http_server/uri/uri.ponyshould guide users toward the right entry point:ParseURIfor standard URI-references,ParseURIAuthorityfor HTTP CONNECT authority-form targets,PercentDecode/PercentEncodefor encoding operations, andParseQueryParameters/PathSegmentsfor higher-level query and path access.Equality implementation note:
URIAuthority.eq()must be implemented first sinceURI.eq()depends on it. Both must handle(String | None)field comparisons (match on both sides, equal when bothNoneor bothStringwith equal content).URI.eq()also matches on(URIAuthority | None)for the authority field, delegating toURIAuthority.eq()when both are present.Generator strategy for URI property tests:
Build generators compositionally from the RFC 3986 grammar:
+|-|.)oneofacross reg-name (unreserved/pct-encoded chars), IPv4 dotted-quad, IPv6 literal in brackets/+ pchar segments/|?) characters/|?) charactersInvalid generators negate specific rules: port > 65535, unmatched IPv6 brackets, non-hex in port, etc.
Testing (
http_server/uri/_test_parse_uri.pony,http_server/uri/_test_parse_uri_authority.pony):ParseURI(uri.string())produces an equal URI (usingEquatable)/path,/path?query,/path?query#frag) parse withscheme = Noneandauthority = NoneInvalidPortscheme is None— they are treated as relative references, not as errorsInvalidHostftp://ftp.is.co.za/...,ldap://[2001:db8::7]/..., etc.)/index.html?page=1, absolute-formhttp://www.example.org/pub/WWW/TheProject.html, asterisk-form*?key), empty query present (/path?), fragment only (#frag), authority without port, empty authority (file:///etc/hosts)http://example.com/a%2Fb?c%3Fdshould parse as path/a%2Fb, queryc%3FdRun:
make ssl=3.0.xPhase 3: Path and Query Utilities
Higher-level operations built on the core types.
Files:
http_server/uri/path_segments.pony—PathSegmentsprimitivehttp_server/uri/query_parameters.pony—ParseQueryParametersprimitiveTesting (
http_server/uri/_test_query_parameters.pony,http_server/uri/_test_path_segments.pony):PathSegmentscount equals the number of/-delimited parts in the generated path/reconstructs the original pathParseQueryParametersroundtrips — generate key-value pairs, serialize ask=v&k2=v2, parse back, verify match+in query values decodes as space=, empty values, empty keys all handledParseQueryParameters— valid query strings succeed, strings with bad percent-encoding faila=1&b=2produces[("a","1"), ("b","2")]key=hello+worldproduces[("key", "hello world")]a=1&a=2produces[("a","1"), ("a","2")](duplicate keys preserved)Run:
make ssl=3.0.xPhase 4: Integration with http_server
Wire the URI package into the example and documentation.
http_serverdoes not yet importuriin these phases — that integration can follow separately."http_server/uri"tocorral.json'spackagesarray to declare it as a provided packageexamples/basic/main.ponyto demonstrate URI parsing and query parameter access (usinguse uri = "../../http_server/uri")Handlerdocstrings to show how to useParseURIwith the rawuristringCLAUDE.mdfile layout section with the newhttp_server/uri/subpackage and its filesFile Layout
All reactions