@@ -54,150 +54,90 @@ pub fn cookie_to_string(cookie : Array[CookieItem]) -> String {
5454///|
5555pub fn parse_cookie (cookie : StringView ) -> Map [String , CookieItem ] {
5656 let res = Map ::new ()
57- let mut view = cookie
58- let mut last_cookie_name = ""
59- while view .length () > 0 {
60- // Skip whitespace using lexmatch
61- while true {
62- if view lexmatch? (" " , rest ) {
63- view = rest
64- continue
65- }
66- if view lexmatch? ("\t " , rest ) {
67- view = rest
68- continue
69- }
70- break
71- }
72- if view .length () == 0 {
73- break
74- }
75-
76- // Parse key
77- let mut key_end = 0
78- while key_end < view .length () &&
79- view [key_end ] != '=' &&
80- view [key_end ] != ';' {
81- key_end = key_end + 1
57+ let mut last_cookie_item : (String , CookieItem )? = None
58+ fn dequote (value : StringView ) -> StringView {
59+ if value is ['"' , .. rest , '"' ] {
60+ rest
61+ } else {
62+ value
8263 }
83- let key = view [0 :key_end ].trim_space ().to_string () catch { _ => "" }
84- view = view [key_end :] catch { _ => view }
64+ }
8565
86- // Parse value
87- let mut val = ""
88- if view lexmatch? ("=" , rest ) {
89- view = rest
90- // Skip whitespace
91- while true {
92- if view lexmatch? (" " , rest ) {
93- view = rest
94- continue
66+ fn on_key_value (key : StringView , value : StringView ) -> Unit {
67+ let key = key .to_string ()
68+ let value = dequote (value ).to_string ()
69+ if last_cookie_item is Some ((name , item )) {
70+ let new_item = lexmatch key with longest {
71+ "(?i:path)" => { ..item, path : Some (value ) }
72+ "(?i:domain)" => { ..item, domain : Some (value ) }
73+ "(?i:max-age)" =>
74+ {
75+ ..item,
76+ max_age : try @strconv .parse_int (value ) catch {
77+ _ => None
78+ } noraise {
79+ x => Some (x )
80+ },
81+ }
82+ "(?i:secure)" => {
83+ println ("Found secure attribute" )
84+ { ..item, secure : Some (true ) }
9585 }
96- if view lexmatch? ("\t " , rest ) {
97- view = rest
98- continue
86+ "(?i:httponly)" => { ..item, http_only : Some (true ) }
87+ "(?i:samesite)" => {
88+ let same_site = lexmatch value with longest {
89+ "(?i:lax)" => Some (Lax )
90+ "(?i:strict)" => Some (Strict )
91+ "(?i:none)" => Some (SameSiteNone )
92+ _ => None
93+ }
94+ { ..item, same_site , }
9995 }
100- break
96+ _ => item
10197 }
102- if view lexmatch? ("\" " , rest ) {
103- // Quoted value
104- view = rest
105- let mut q_end = 0
106- while q_end < view .length () && view [q_end ] != '"' {
107- q_end = q_end + 1
108- }
109- val = view [0 :q_end ].to_string () catch { _ => "" }
110- // Skip closing quote if present
111- if q_end < view .length () {
112- view = view [q_end + 1 :] catch {
113- _ => view [q_end :] catch { _ => view }
114- }
115- } else {
116- view = view [q_end :] catch { _ => view }
117- }
118- } else {
119- // Unquoted value
120- let mut v_end = 0
121- while v_end < view .length () && view [v_end ] != ';' {
122- v_end = v_end + 1
123- }
124- val = view [0 :v_end ].trim_space ().to_string () catch { _ => "" }
125- view = view [v_end :] catch { _ => view }
98+ if !physical_equal (new_item , item ) {
99+ res .set (name , new_item )
100+ last_cookie_item = Some ((name , new_item ))
101+ return
126102 }
127103 }
128- if key != "" {
129- let lower_key = key .to_lower ()
130- if last_cookie_name != "" &&
131- (
132- lower_key == "path" ||
133- lower_key == "domain" ||
134- lower_key == "max-age" ||
135- lower_key == "secure" ||
136- lower_key == "httponly" ||
137- lower_key == "samesite"
138- ) {
139- // It's an attribute for the last cookie
140- match res .get (last_cookie_name ) {
141- Some (item ) => {
142- let mut new_item : CookieItem = item
143- match lower_key {
144- "path" => new_item = { ..item, path : Some (val ) }
145- "domain" => new_item = { ..item, domain : Some (val ) }
146- "max-age" =>
147- new_item = {
148- ..item,
149- max_age : Some (@strconv .parse_int (val )) catch {
150- _ => None
151- },
152- }
153- "secure" => new_item = { ..item, secure : Some (true ) }
154- "httponly" => new_item = { ..item, http_only : Some (true ) }
155- "samesite" => {
156- let same_site = match val .to_lower () {
157- "lax" => Some (Lax )
158- "strict" => Some (Strict )
159- "none" => Some (SameSiteNone )
160- _ => None
161- }
162- new_item = { ..item, same_site , }
163- }
164- _ => ()
165- }
166- res .set (last_cookie_name , new_item )
167- }
168- None => ()
169- }
170- } else {
171- // New cookie
172- let item : CookieItem = {
173- name : key ,
174- value : val ,
175- max_age : None ,
176- path : None ,
177- domain : None ,
178- secure : None ,
179- http_only : None ,
180- same_site : None ,
181- }
182- res .set (key , item )
183- last_cookie_name = key
184- }
104+ let item = CookieItem ::{
105+ name : key ,
106+ value ,
107+ max_age : None ,
108+ path : None ,
109+ domain : None ,
110+ secure : None ,
111+ http_only : None ,
112+ same_site : None ,
185113 }
114+ res .set (key , item )
115+ last_cookie_item = Some ((key , item ))
116+ }
186117
187- // Skip until next semicolon
188- if view lexmatch? (";" , rest ) {
189- view = rest
190- } else {
191- // Skip invalid chars until ;
192- let mut i = 0
193- while i < view .length () && view [i ] != ';' {
194- i = i + 1
118+ for curr = cookie {
119+ // TODO: more spec compliance
120+ lexmatch curr with longest {
121+ ("[ \t ]+" , rest ) => continue rest
122+ (
123+ ("[^ \t =;]([ \t ]*[^ \t =;])*" as key )
124+ "[ \t ]*"
125+ "="
126+ "[ \t ]*"
127+ ("[^ \t ;]([ \t ]*[^ \t ;])*" as value )
128+ "[ \t ]*"
129+ ";?" ,
130+ rest
131+ ) => {
132+ on_key_value (key , value )
133+ continue rest
195134 }
196- if i < view .length () {
197- view = view [i + 1 :] catch { _ => view }
198- } else {
199- view = view [i :] catch { _ => view }
135+ (("[^ \t =;]([ \t ]*[^ \t =;])*" as key ) "[ \t ]*" "(=[ \t ]*)?" ";?" , rest ) => {
136+ on_key_value (key , "" )
137+ continue rest
200138 }
139+ "" => break
140+ _ => break
201141 }
202142 }
203143 res
0 commit comments