export function httpParser(regexoid) { return regexoid(({ seq, any, rep, markBreak, chars, mark, capture, captureMulti, output, captureOutput, cond, map, limit, }) => { let field = (out='fields') => seq( capture('key', rep([1], chars.none([' :\r\n']))), ':', rep([],' '), capture('value', rep([1], chars.none(['\r\n']))), '\r\n', map(({ get, set, add }) => { let k = get('key').toLowerCase(); let v = get('value').trim(); if (!v) { return false; } set('value', null); set('key', null); add(out,[k,v]); }) ); let packFields = (out='fields') => ( map(({ get, set }) => { let o = Object.create(null); for (let [k,v] of [...(get(out)??[])].reverse()) { if (!(k in o)) { o[k] = [v]; } else { o[k].push(v); } } set(out, o); }) ) let hexdigit = (b=true) => chars[b?'any':'none'](['0','9'],['A','Z'],['a','z']); let chunkExt = () => rep([,1], ';', rep([], chars.none(['\r\n']))); let chunkSize = (quick) => ( limit(256, capture('chunksize', rep([1,], hexdigit())), ...(quick ? [ hexdigit(false) ] : [ chunkExt(), '\r\n' ]) ) ); return any( seq(mark('type', 'h2'), 'PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n', output('type'), mark('type', null)), seq(mark('type', 'h1'), limit(32768, capture('method', rep([1,12], chars.any(['A','Z']))), ' ', capture('path',rep([1,], chars.none([' \r\n']))), ' ', 'HTTP/1.',any('1','0'), '\r\n', rep([], field()), '\r\n' ), packFields(), map(({ get, set }) => { let o = get('fields'); let enc = null; let CE = o['content-length']; let TE = o['transfer-encoding']; if ((CE!=null)+(TE!=null) != 1) return false; if (CE != null) { if (CE.length > 1) return false; enc = parseInt(CE[0]) if (enc != enc) return false; } else if (TE != null) { if (TE.length > 1) return false; if (TE[0] != 'chunked') return false; enc = 'chunked'; } set('bodyFraming', enc) }), output('type'), output('method'),output('path'), output('fields'), mark('type', null), mark('method', null),mark('path', null), mark('fields', null), cond( [map(({ get }) => get('bodyFraming') == 'chunked'), rep.strict([], cond( [seq(chunkSize(true), map(({ get }) => get('chunksize') != '0')), chunkSize(), map(({ get, set }) => void(set('chunksize', parseInt(get('chunksize'), 16)))), map(({ get }, c) => (c = get('chunksize'), c > 0 && c < 2**52)), captureOutput('body', rep('chunksize', chars.none())), '\r\n', mark('chunksize', null), ], ['', markBreak()] ) ), limit(256, '0', chunkExt(), '\r\n' ), limit(8192, rep([], field('trailers'))), '\r\n', packFields('trailers'), output('trailers'), mark('trailers', null), mark('bodyFraming', null), ], [map(({ get }) => typeof get('bodyFraming') == 'number'), captureOutput('body', rep('bodyFraming', chars.none())), mark('bodyFraming', null), ] ) ) ) }) }