netscape_cookie_file_parser/lib.rs
1//! Parser for Netscape/curl cookie jar files.
2//!
3//! Netscape cookie files are line-oriented. Each cookie line contains seven
4//! tab-separated fields: domain, tail-match flag, path, secure flag, expires,
5//! name, and value. This crate keeps cookie data as raw bytes so non-UTF-8
6//! cookie names, values, paths, and domains can be represented without UTF-8
7//! conversion.
8//!
9//! Empty lines and ordinary `#` comments are skipped. curl's `#HttpOnly_`
10//! extension is treated as metadata on the cookie rather than as a comment.
11//!
12//! ```
13//! use netscape_cookie_file_parser::{parse_line, CookiePrefix};
14//!
15//! let cookie = parse_line("#HttpOnly_.example.com\tTRUE\t/\tTRUE\t0\t__Secure-SID\tabc")
16//! .unwrap()
17//! .unwrap();
18//!
19//! assert_eq!(cookie.domain, b"example.com");
20//! assert!(cookie.http_only);
21//! assert_eq!(cookie.prefix, CookiePrefix::Secure);
22//! ```
23
24use std::io::BufRead;
25
26mod cookie;
27mod error;
28mod parser;
29#[cfg(test)]
30mod tests;
31
32pub use cookie::{Cookie, CookiePrefix};
33pub use error::{ParseError, ParseErrorKind};
34pub use parser::NetscapeCookieParser;
35
36/// Parses all valid cookie lines from a buffered reader.
37///
38/// One leading dot is removed from the domain field. Path fields are preserved
39/// as they appear in the file, except that curl's `#HttpOnly_` line marker is
40/// represented as [`Cookie::http_only`]. curl's legacy missing-path records are
41/// accepted with `/` as the path.
42///
43/// The first malformed cookie line or I/O error stops parsing and returns a
44/// [`ParseError`] with the 1-based line number.
45pub fn parse<R: BufRead>(reader: R) -> Result<Vec<Cookie>, ParseError> {
46 NetscapeCookieParser::new(reader).collect()
47}
48
49/// Parses all valid cookie lines and skips malformed cookie records.
50///
51/// I/O errors are still returned because the parser cannot safely continue once
52/// the underlying reader fails.
53pub fn parse_lossy<R: BufRead>(reader: R) -> Result<Vec<Cookie>, ParseError> {
54 let mut cookies = Vec::new();
55
56 for result in NetscapeCookieParser::new(reader) {
57 match result {
58 Ok(cookie) => cookies.push(cookie),
59 Err(error) if matches!(error.kind, ParseErrorKind::Io(_)) => return Err(error),
60 Err(_) => {}
61 }
62 }
63
64 Ok(cookies)
65}
66
67/// Parses one Netscape cookie file line.
68///
69/// Returns `Ok(None)` for blank lines, ordinary comments, and `#HttpOnly_`
70/// lines whose payload is still a comment.
71pub fn parse_line(line: impl AsRef<[u8]>) -> Result<Option<Cookie>, ParseErrorKind> {
72 parser::parse_line_inner(line.as_ref())
73}