import { NetworkCookie } from './NetworkCookie' ; interface KeyValue { key : string ; value ? : string ; position : number ; } export class CookieParser { private _input ? : string ; private _originalInputLength ? : number ; private _lastCookie ? : NetworkCookie ; private _lastCookiePosition : number = 0 ; private _cookies ? : NetworkCookie [ ] ; public parseCookie ( cookieHeader ) { if ( ! this . _initialize ( cookieHeader ! ) ) { return ; } for ( let kv = this . _extractKeyValue ( ) ; kv ; kv = this . _extractKeyValue ( ) ) { if ( kv . key . charAt ( 0 ) === '$' && this . _lastCookie ) { this . _lastCookie . addAttribute ( kv . key . slice ( 1 ) , kv . value ) ; } else if ( kv . key . toLowerCase ( ) !== '$version' && typeof kv . value === 'string' ) { this . addCookie ( kv ) ; } this . advanceAndCheckCookieDelimiter ( ) ; } this . flushCookie ( ) ; return this . _cookies ; } public parseSetCookie ( setCookieHeader ) { if ( ! this . _initialize ( setCookieHeader ) ) { return ; } for ( let kv = this . _extractKeyValue ( ) ; kv ; kv = this . _extractKeyValue ( ) ) { if ( this . _lastCookie ) { this . _lastCookie . addAttribute ( kv . key , kv . value ) ; } else { this . addCookie ( kv ) ; } if ( this . advanceAndCheckCookieDelimiter ( ) ) { this . flushCookie ( ) ; } } this . flushCookie ( ) ; return this . _cookies ; } private _initialize ( headerValue ) { this . _input = headerValue ; if ( typeof headerValue !== 'string' ) { return false ; } this . _cookies = [ ] ; this . _lastCookie = undefined ; this . _originalInputLength = this . _input ! . length ; return true ; } private flushCookie ( ) { if ( this . _lastCookie ) { this . _lastCookie . size = this . _originalInputLength ! - this . _input ! . length - this . _lastCookiePosition ; } delete this . _lastCookie ; } private _extractKeyValue ( ) { if ( ! this . _input ?. length ) { return ; } const keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/i . exec ( this . _input ) ; if ( ! keyValueMatch ) { return ; } const result = { key : this . toCamelCase ( keyValueMatch [ 1 ] ) , value : keyValueMatch [ 2 ] ?. trim ( ) , position : this . _originalInputLength ! - this . _input . length } ; this . _input = this . _input . slice ( keyValueMatch [ 0 ] . length ) ; return result ; } private toCamelCase ( str ) { return str . replace ( /\s(.)/g , ( m ) : string => m . toUpperCase ( ) ) . replace ( /\s/g , '' ) . replace ( /^(.)/ , ( m ) : string => m . toLowerCase ( ) ) ; } private advanceAndCheckCookieDelimiter ( ) { const match = /^\s*[\n;]\s*/ . exec ( this . _input ! ) ; if ( ! match ) { return false ; } this . _input = this . _input ! . slice ( match [ 0 ] . length ) ; return match [ 0 ] . match ( '\n' ) !== null ; } private addCookie ( keyValue ) { if ( this . _lastCookie ) { this . _lastCookie . size = keyValue . position - this . _lastCookiePosition ; } this . _lastCookie = typeof keyValue . value === 'string' ? new NetworkCookie ( keyValue . key , keyValue . value ) : new NetworkCookie ( '' , keyValue . key ) ; this . _lastCookiePosition = keyValue . position ; this . _cookies ! . push ( this . _lastCookie ) ; } }