-
Notifications
You must be signed in to change notification settings - Fork 770
feat: introduce Headers data type for HTTP
#12127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
algebraic-dev
wants to merge
60
commits into
sofia/async-http-data
Choose a base branch
from
sofia/async-http-headers
base: sofia/async-http-data
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,859
−41
Open
Changes from 11 commits
Commits
Show all changes
60 commits
Select commit
Hold shift + click to select a range
5f3babe
feat: headers data structure
algebraic-dev d3cddf9
fix: Headers.Basic comment
algebraic-dev 8ba86c2
fix: case and usage of native_decide
algebraic-dev bd28a8f
fix: tests and type class
algebraic-dev 65883f8
fix: levenshtein test is using the new Decidable instance
algebraic-dev 44f0868
feat: connection values
algebraic-dev 8c34350
fix: apply suggestions
algebraic-dev 7141144
fix: remove native_decide
algebraic-dev 990e21e
fix: namespace
algebraic-dev 81c67c8
revert: levenshtein test
algebraic-dev 6e26b90
fix: encoding
algebraic-dev dbe0140
fix: enforce validations
algebraic-dev e2fd8a5
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev 63984c8
fix: header value
algebraic-dev f4aad3a
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev b567713
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev 1cec975
fix: imports
algebraic-dev c2f657a
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev 3568464
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev eeff484
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev 9ce1821
feat: add trailers some type of headers
algebraic-dev 9430840
Merge branch 'sofia/async-http-data' of https://github.com/leanprover…
algebraic-dev 153513d
fix: typos
algebraic-dev 1f2671d
merge: branch 'sofia/async-http-data'
algebraic-dev e0d5596
fix: typo
algebraic-dev 87c5488
merge: 'sofia/async-http-headers'
algebraic-dev 6b0f05d
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev a521ba3
fix: typos and connection token
algebraic-dev 157e3b0
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 3fdaf2d
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev ad47193
fix: trailing peridos
algebraic-dev dd09289
fix: duplication
algebraic-dev 78316b9
fix: isToken
algebraic-dev c8c92fc
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 0d3f6e5
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 5a53207
feat: remove direct access
algebraic-dev a9ac33d
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev f97d86c
feat: trim headers
algebraic-dev 2e1bdd9
5914fe3
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 36f87f9
fix: char
algebraic-dev ff99979
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 89c992a
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev de7c029
feat: field content
algebraic-dev e65e20e
feat: field content
algebraic-dev 8492e58
fix: comments
algebraic-dev e9eed5c
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 49d00ae
fix: comments and small formatting errors
algebraic-dev a889085
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 228f0d2
fix: remove unused headers
algebraic-dev ba36c1d
fix: comments
algebraic-dev 91c5b71
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev caf19b8
feat: order
algebraic-dev 2049542
feat: order
algebraic-dev a0dc1db
fix: test
algebraic-dev b7be572
feat: forIn
algebraic-dev 292f297
feat: small comments
algebraic-dev 563189f
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev ff44193
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev 57a4d9a
Merge branch 'sofia/async-http-data' into sofia/async-http-headers
algebraic-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,250 @@ | ||
| /- | ||
| Copyright (c) 2025 Lean FRO, LLC. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: Sofia Rodrigues | ||
| -/ | ||
| module | ||
|
|
||
| prelude | ||
| public import Init.Data.String | ||
| public import Std.Data.HashMap | ||
| public import Std.Internal.Http.Internal | ||
| public import Std.Internal.Http.Data.Headers.Basic | ||
| public import Std.Internal.Http.Data.Headers.Name | ||
| public import Std.Internal.Http.Data.Headers.Value | ||
|
|
||
| public section | ||
|
|
||
| /-! | ||
| # Headers | ||
|
|
||
| This module defines the `Headers` type, which represents an collection of HTTP header name-value pairs. | ||
| -/ | ||
|
|
||
| namespace Std.Http | ||
|
|
||
| set_option linter.all true | ||
|
|
||
| open Internal | ||
|
|
||
| /-- | ||
| A structure for managing HTTP headers as key-value pairs. | ||
| -/ | ||
| structure Headers where | ||
|
|
||
| /-- | ||
| The underlying multimap that stores headers. | ||
| -/ | ||
| map : MultiMap Header.Name Header.Value | ||
| deriving Inhabited, Repr | ||
|
|
||
| instance : Membership Header.Name Headers where | ||
| mem s h := h ∈ s.map | ||
|
|
||
| instance (name : Header.Name) (h : Headers) : Decidable (name ∈ h) := | ||
| inferInstanceAs (Decidable (name ∈ h.map)) | ||
|
|
||
| namespace Headers | ||
|
|
||
| /-- | ||
| Retrieves the first `Header.Value` for the given key. | ||
| -/ | ||
| @[inline] | ||
| def get (headers : Headers) (name : Header.Name) (h : name ∈ headers) : Header.Value := | ||
| headers.map.get name h | ||
|
|
||
| /-- | ||
| Retrieves all `Header.Value` entries for the given key. | ||
| -/ | ||
| @[inline] | ||
| def getAll (headers : Headers) (name : Header.Name) (h : name ∈ headers) : Array Header.Value := | ||
| headers.map.getAll name h | ||
|
|
||
| /-- | ||
| Retrieves all `Header.Value` entries for the given key. | ||
| Returns `none` if the header is absent. | ||
| -/ | ||
| @[inline] | ||
| def getAll? (headers : Headers) (name : Header.Name) : Option (Array Header.Value) := | ||
| headers.map.getAll? name | ||
|
|
||
| /-- | ||
| Retrieves the first `Header.Value` for the given key. | ||
| Returns `none` if the header is absent. | ||
| -/ | ||
| @[inline] | ||
| def get? (headers : Headers) (name : Header.Name) : Option Header.Value := | ||
| headers.map.get? name | ||
|
|
||
| /-- | ||
| Checks if the entry is present in the `Headers`. | ||
| -/ | ||
| @[inline] | ||
| def hasEntry (headers : Headers) (name : Header.Name) (value : Header.Value) : Bool := | ||
| headers.map.data.get? name | ||
| |>.bind (fun x => x.val.find? (· == value)) | ||
| |>.isSome | ||
|
|
||
| /-- | ||
| Retrieves the last header value for the given key. | ||
| Returns `none` if the header is absent. | ||
| -/ | ||
| @[inline] | ||
| def getLast? (headers : Headers) (name : Header.Name) : Option Header.Value := | ||
| headers.map.getLast? name | ||
|
|
||
| /-- | ||
| Like `get?`, but returns a default value if absent. | ||
| -/ | ||
| @[inline] | ||
| def getD (headers : Headers) (name : Header.Name) (d : Header.Value) : Header.Value := | ||
| headers.map.getD name d | ||
|
|
||
| /-- | ||
| Like `get?`, but panics if absent. | ||
| -/ | ||
| @[inline] | ||
| def get! (headers : Headers) (name : Header.Name) : Header.Value := | ||
| headers.map.get! name | ||
|
|
||
| /-- | ||
| Inserts a new key-value pair into the headers. | ||
| -/ | ||
| @[inline] | ||
| def insert (headers : Headers) (key : Header.Name) (value : Header.Value) : Headers := | ||
| { map := headers.map.insert key value } | ||
|
|
||
| /-- | ||
| Adds a header from string name and value, panicking if either is invalid. | ||
| -/ | ||
| @[inline] | ||
| def insert! (headers : Headers) (name : String) (value : String) : Headers := | ||
| headers.insert (Header.Name.ofString! name) (Header.Value.ofString! value) | ||
|
|
||
| /-- | ||
| Inserts a new key with an array of values. | ||
| -/ | ||
| @[inline] | ||
| def insertMany (headers : Headers) (key : Header.Name) (value : Array Header.Value) (p : value.size > 0) : Headers := | ||
| { map := headers.map.insertMany key value p } | ||
|
|
||
| /-- | ||
| Creates empty headers. | ||
| -/ | ||
| def empty : Headers := | ||
| { map := ∅ } | ||
|
|
||
| /-- | ||
| Creates headers from a list of key-value pairs. | ||
| -/ | ||
| def ofList (pairs : List (Header.Name × Header.Value)) : Headers := | ||
| { map := MultiMap.ofList pairs } | ||
|
|
||
| /-- | ||
| Checks if a header with the given name exists. | ||
| -/ | ||
| @[inline] | ||
| def contains (headers : Headers) (name : Header.Name) : Bool := | ||
| headers.map.contains name | ||
|
|
||
| /-- | ||
| Removes a header with the given name. | ||
| -/ | ||
| @[inline] | ||
| def erase (headers : Headers) (name : Header.Name) : Headers := | ||
| { map := headers.map.erase name } | ||
|
|
||
| /-- | ||
| Gets the number of headers. | ||
| -/ | ||
| @[inline] | ||
| def size (headers : Headers) : Nat := | ||
| headers.map.size | ||
|
|
||
| /-- | ||
| Checks if the headers are empty. | ||
| -/ | ||
| @[inline] | ||
| def isEmpty (headers : Headers) : Bool := | ||
| headers.map.isEmpty | ||
|
|
||
| /-- | ||
| Merges two headers, with the second taking precedence for duplicate keys. | ||
| -/ | ||
| def merge (headers1 headers2 : Headers) : Headers := | ||
| { map := headers1.map ∪ headers2.map } | ||
algebraic-dev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /-- | ||
| Converts the headers to a list of key-value pairs (flattened). Each header with multiple values produces | ||
| multiple pairs. | ||
| -/ | ||
| def toList (headers : Headers) : List (Header.Name × Header.Value) := | ||
| headers.map.toList | ||
|
|
||
| /-- | ||
| Converts the headers to an array of key-value pairs (flattened). Each header with multiple values | ||
| produces multiple pairs. | ||
| -/ | ||
| def toArray (headers : Headers) : Array (Header.Name × Header.Value) := | ||
| headers.map.toArray | ||
|
|
||
| /-- | ||
| Folds over all key-value pairs in the headers. | ||
| -/ | ||
| def fold (headers : Headers) (init : α) (f : α → Header.Name → Header.Value → α) : α := | ||
| headers.map.toArray.foldl (fun acc (k, v) => f acc k v) init | ||
|
|
||
| /-- | ||
| Maps a function over all header values, producing new headers. | ||
| -/ | ||
| def mapValues (headers : Headers) (f : Header.Name → Header.Value → Header.Value) : Headers := | ||
| let pairs := headers.map.toArray.map (fun (k, v) => (k, f k v)) | ||
| { map := pairs.foldl (fun acc (k, v) => acc.insert k v) MultiMap.empty } | ||
|
|
||
| /-- | ||
| Filters and maps over header key-value pairs. Returns only the pairs for which the function returns `some`. | ||
| -/ | ||
| def filterMap (headers : Headers) (f : Header.Name → Header.Value → Option Header.Value) : Headers := | ||
| let pairs := headers.map.toArray.filterMap (fun (k, v) => | ||
| match f k v with | ||
| | some v' => some (k, v') | ||
| | none => none) | ||
| { map := pairs.foldl (fun acc (k, v) => acc.insert k v) MultiMap.empty } | ||
|
|
||
| /-- | ||
| Filters header key-value pairs, keeping only those that satisfy the predicate. | ||
| -/ | ||
| def filter (headers : Headers) (f : Header.Name → Header.Value → Bool) : Headers := | ||
| headers.filterMap (fun k v => if f k v then some v else none) | ||
|
|
||
| /-- | ||
| Updates the first value of a header if it exists, or inserts if it doesn't. Replaces all existing values | ||
| for that header with the new value. | ||
| -/ | ||
| def update (headers : Headers) (name : Header.Name) (f : Option Header.Value → Header.Value) : Headers := | ||
| let newValue := f (headers.get? name) | ||
| { map := headers.map.erase name |>.insert name newValue } | ||
|
|
||
| instance : ToString Headers where | ||
| toString headers := | ||
| let pairs := headers.map.toArray.map (fun (k, v) => s!"{k}: {v.value}") | ||
| String.intercalate "\r\n" pairs.toList | ||
|
|
||
| instance : Encode .v11 Headers where | ||
| encode buffer headers := | ||
| headers.fold buffer (fun buf name value => | ||
| buf.writeString s!"{name}: {value}\r\n") | ||
|
|
||
| instance : EmptyCollection Headers := | ||
| ⟨Headers.empty⟩ | ||
|
|
||
| instance : Singleton (Header.Name × Header.Value) Headers := | ||
| ⟨fun ⟨a, b⟩ => (∅ : Headers).insert a b⟩ | ||
|
|
||
| instance : Insert (Header.Name × Header.Value) Headers := | ||
| ⟨fun ⟨a, b⟩ s => s.insert a b⟩ | ||
|
|
||
| instance : Union Headers := | ||
| ⟨merge⟩ | ||
|
|
||
| end Std.Http.Headers | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.