"use strict" ; import { _ } from "streamline-runtime" ; import { Reader } from "../reader" ; import { Writer } from "../writer" ; import * as binary from '../helpers/binary' ; import * as generic from '../devices/generic' ; function parseContentType ( contentType ? ) { if ( ! contentType ) throw new Error ( "content-type missing" ) ; const match = /^multipart\/([\w\-]*)/ . exec ( contentType ) ; if ( ! match ) return null ; const subType = match [ 1 ] ; const atbs = contentType . split ( /\s*;\s*/ ) . reduce ( ( r , s ) => { const kv = s . split ( /\s*=\s*/ ) ; r [ kv [ 0 ] ] = kv [ 1 ] ; return r ; } , { } ) ; return { subType : subType , boundary : atbs . boundary , } ; } export type ParserOptions = { [ name ] : string ; } ; export function parser ( options ) { const ct = parseContentType ( options && options [ "content-type" ] ) ; const boundary = ct && ct . boundary ; if ( ! boundary ) throw new Error ( "multipart boundary missing" ) ; return ( _ , reader , writer ) => { const binReader = binary . reader ( reader ) ; const handshake = _ . handshake ( ) ; while ( true ) { var buf = binReader . readData ( _ , 2048 ) ; if ( ! buf || ! buf . length ) return ; var str = buf . toString ( "binary" ) ; var i = str . indexOf ( boundary ) ; if ( i < 0 ) throw new Error ( "boundary not found" ) ; var lines = str . substring ( 0 , i ) . split ( /\r?\n/ ) ; var headers = lines . slice ( 0 , lines . length - 2 ) . reduce ( ( h , l ) => { const kv = l . split ( /\s*:\s*/ ) ; h [ kv [ 0 ] . toLowerCase ( ) ] = kv [ 1 ] ; return h ; } , { } ) ; i = str . indexOf ( '\n' , i ) ; binReader . unread ( buf . length - i - 1 ) ; var read = ( _ ) => { const len = Math . max ( boundary . length , 256 ) ; const buf = binReader . readData ( _ , 32 * len ) ; if ( ! buf || ! buf . length ) { handshake . notify ( ) ; return ; } const s = buf . toString ( "binary" ) ; const i = s . indexOf ( boundary ) ; if ( i === 0 ) { const j = s . indexOf ( '\n' , boundary . length ) ; if ( j < 0 ) throw new Error ( "newline missing after boundary" ) ; binReader . unread ( buf . length - j - 1 ) ; handshake . notify ( ) ; return undefined ; } else if ( i > 0 ) { var j = s . lastIndexOf ( '\n' , i ) ; if ( s [ j - 1 ] === '\r' ) j -- ; binReader . unread ( buf . length - i ) ; return buf . slice ( 0 , j ) ; } else { binReader . unread ( buf . length - 31 * len ) ; return buf . slice ( 0 , 31 * len ) ; } } ; const partReader = generic . reader ( read ) ; partReader . headers = headers ; writer . write ( _ , partReader ) ; handshake . wait ( _ ) ; } } ; } export interface FormatterOptions { [ name ] : string ; } export function formatter ( options ? ) { const ct = parseContentType ( options && options [ "content-type" ] ) ; const boundary = ct && ct . boundary ; if ( ! boundary ) throw new Error ( "multipart boundary missing" ) ; return ( _ , reader , writer ) => { var part ; while ( ( part = reader . read ( _ ) ) !== undefined ) { var headers = part . headers ; if ( ! headers ) throw new Error ( "part does not have headers" ) ; Object . keys ( part . headers ) . forEach_ ( _ , ( _ , key ) => { writer . write ( _ , Buffer . from ( key + ": " + headers [ key ] + "\n" , "binary" ) ) ; } ) ; writer . write ( _ , Buffer . from ( "\n" + boundary + "\n" ) ) ; part . forEach ( _ , ( _ , data ) => { writer . write ( _ , data ) ; } ) ; writer . write ( _ , Buffer . from ( "\n" + boundary + "\n" ) ) ; } } ; }