Haskell WebSocket рд╕рд░реНрд╡рд░

рдПрдХ рдмрд╛рд░, рдХреБрдЫ рднреА рдирд╣реАрдВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдореИрдВрдиреЗ рдПрдХ WebSocket рд╕рд░реНрд╡рд░ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛, рдФрд░ рд▓рд┐рдЦрдиреЗ рдХреЗ рдмрд╛рдж, рдореИрдВрдиреЗ рд╕реЛрдЪрд╛ рдХрд┐ рдХрд┐рд╕реА рдХреЛ рдпрд╣ рджрд┐рд▓рдЪрд╕реНрдк рд▓рдЧ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдЖрд▓рд╕реНрдп, рдХрд╛рд░реНрдпрд╛рддреНрдордХ рд╕рдлрд╛рдИ рдФрд░ рдЕрдиреНрдп рд▓реИрдореНрдмреНрдбрд╛ рдпрд╣рд╛рдБ рдХреИрд╕реЗ рдорджрдж рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред



рдпрд╣ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж рдХрд┐ рд╕рд░реНрд╡рд░ рд╕рд╛рдорд╛рдиреНрдп рд╢рдмреНрджреЛрдВ рдореЗрдВ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рдореИрдВ рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдмреИрда рдЧрдпрд╛ред рдкреНрд░реЛрдЯреЛрдХреЙрд▓, рд╡реИрд╕реЗ, рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИред рдХреНрд▓рд╛рдЗрдВрдЯ рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рдЪрд╛рдмрд┐рдпрд╛рдБ рднреЗрдЬрддрд╛ рд╣реИ, рдЬрд╡рд╛рдм рдореЗрдВ рд╕рд░реНрд╡рд░ рдХрдиреЗрдХреНрд╢рди рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рддрд╛ рд╣реИ, рдЗрди рдЪрд╛рдмрд┐рдпреЛрдВ рдХреЗ рд╕рдВрдпреЛрдЬрди рд╕реЗ md5 рднреЗрдЬ рд░рд╣рд╛ рд╣реИред рдФрд░ рдлрд┐рд░ рд╡реЗ рдПрдХ-рджреВрд╕рд░реЗ рдХреЛ рдмрд╛рдЗрдирд░реА рдпрд╛ рдЯреЗрдХреНрд╕реНрдЯ рдбреЗрдЯрд╛ рднреЗрдЬрддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдХреЗ рджреНрд╡рд╛рд░рд╛ рдФрд░ рдмрдбрд╝реЗ рдЕрд▓рдЧ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВред



рд╣рд╛рде рдорд┐рд▓рд╛рдирд╛




рдбреНрд░рд╛рдлреНрдЯ рдЦреЛрд▓реЗрдВ рдФрд░ рд╣реИрдВрдбрд╢реЗрдХ рдлреЙрд░реНрдореЗрдЯ рдХрд╛ рд╡рд┐рд╡рд░рдг рджреЗрдЦреЗрдВ:

field = 1*name-char colon [ space ] *any-char cr lf

colon = %x003A ; U+003A COLON (:)

space = %x0020 ; U+0020 SPACE

cr = %x000D ; U+000D CARRIAGE RETURN (CR)

lf = %x000A ; U+000A LINE FEED (LF)

name-char = %x0000-0009 / %x000B-000C / %x000E-0039 / %x003B-10FFFF

; a Unicode character other than U+000A LINE FEED (LF), U+000D CARRIAGE RETURN (CR), or U+003A COLON (:)

any-char = %x0000-0009 / %x000B-000C / %x000E-10FFFF

; a Unicode character other than U+000A LINE FEED (LF) or U+000D CARRIAGE RETURN (CR)









рдареАрдХ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЗрд╕реЗ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:

field = ( many1 nameChar <& colon <& spaces ) <&> ( many anyChar <& cr <& lf ) where <br>

spaces = ignore ( many space ) [ () ] <br>

colon = lit ':' char<br>

space = lit ' ' char<br>

cr = lit '\r' char<br>

lf = lit '\n' char<br>

unicodeChar = optIf ( <= '\x10FFFF' ) char<br>

nameChar = optIf ( `notElem` ": \r\n" ) unicodeChar<br>

anyChar = optIf ( `notElem` "\r\n" ) unicodeChar<br>









рдореИрдВ рд╕рдордЭрд╛рдКрдВрдЧрд╛ рдХрд┐ рдкрд╣рд▓реА рдкрдВрдХреНрддрд┐ рдХреЗ рдЙрджрд╛рд╣рд░рдг рдкрд░ рдХреНрдпрд╛ рд╣реЛ рд░рд╣рд╛ рд╣реИред

field = ( many1 nameChar <& colon <& spaces ) <&> ( many anyChar <& cr <& lf ) where <br>

spaces = ignore ( many space ) [ () ] <br>









many1



рдПрдХ рдорд╛рди рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддрд╛ рд╣реИ рдЬреЛ 1 рдпрд╛ рдЕрдзрд┐рдХ рдмрд╛рд░ рд╣реЛрддрд╛ рд╣реИ, many



- 0 рдпрд╛ рдЕрдзрд┐рдХред рдСрдкрд░реЗрдЯрд░ &>



рдФрд░ <&



рдХреНрд░рдорд┐рдХ рд░реВрдк рд╕реЗ рджреЛ рдирд┐рдпрдореЛрдВ рдХреЛ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ, рдЬрдмрдХрд┐ рдпрд╣ рджрд░реНрд╢рд╛рддрд╛ рд╣реИ рдХрд┐ рд╣рдо рдХреЗрд╡рд▓ рдЙрдирдореЗрдВ рд╕реЗ рдПрдХ рдХреЗ рдЕрд░реНрде рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВред рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рдЬреЛ рдореВрд▓реНрдп colon



рдФрд░ spaces



рдХреЗ рдирд┐рдпрдореЛрдВ рдХрд╛ рдкрд╛рд▓рди рдХрд░рддреЗ рд╣реИрдВ, рд╡реЗ рд╣рдореЗрдВ рд░реБрдЪрд┐ рдирд╣реАрдВ рджреЗрддреЗ рд╣реИрдВред рдСрдкрд░реЗрдЯрд░ <&>



рдЖрдкрдХреЛ рдЯреБрдкрд▓ рдХреЗ рд░реВрдк рдореЗрдВ рджреЛрдиреЛрдВ рдорд╛рди рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред

lit



рдлрд╝рдВрдХреНрд╢рди рд╕рд╛рдордирд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдард┐рди рдорд╛рди рд╕реЗрдЯ рдХрд░рддрд╛ рд╣реИ, рдФрд░ optIf



рдПрдХ рдмрд╛рдзрд╛ optIf



рд╣реИред



рд╕рдВрджреЗрд╢ рдореЗрдВ рд╣реЗрдбрд░ рд▓рд╛рдЗрди, рдлрд╝реАрд▓реНрдб рдФрд░ рдлрд╝реАрд▓реНрдб рдХреЗ рдмрд╛рдж рдПрдХ рдирд┐рд╢реНрдЪрд┐рдд рд▓рдВрдмрд╛рдИ рдХрд╛ рдбреЗрдЯрд╛ рд╢рд╛рдорд┐рд▓ рд╣реЛрддрд╛ рд╣реИред

рдпрд╣ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рдирд╣реАрдВ рд▓рд┐рдЦрд╛ рд╣реИ:

message = ( toMessage , fromMessage ) `wrap` ( leadingLine <&> many field ) where <br>

toMessage ( ll , fs ) = Message { <br>

messageLeadingLine = ll , <br>

messageFields = fs } <br>

fromMessage ( Message { messageLeadingLine = ll , messageFields = fs } ) = ( ll , fs ) <br>

<br>

body len = cr &> lf &> times len unicodeChar<br>

<br>

leadingLine = many anyChar <& cr <& lf<br>









leadingLine



рдФрд░ body



рд╕рдм рдХреБрдЫ рд╕рд░рд▓ рд╣реИ, рд▓реЗрдХрд┐рди wrap



рдлрд╝рдВрдХреНрд╢рди message



рдкрд░рд┐рднрд╛рд╖рд╛ рдореЗрдВ рджрд┐рдЦрд╛рдИ рджреЗрддрд╛ рд╣реИред рддрдереНрдп рдпрд╣ рд╣реИ рдХрд┐ рдирд┐рдпрдо a <&> b



рдЯрдкрд▓ рдХреЗ рд▓рд┐рдП рдирд┐рдпрдо рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдФрд░ рд╣рдореЗрдВ рдХрд┐рд╕реА рдкреНрд░рдХрд╛рд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдЗрд╕рд▓рд┐рдП, рд╣рдо рдПрдХ рдЯреНрдпреВрдкрд▓ рд╕реЗ рдкрд░рд┐рд╡рд░реНрддрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рджреЛ рдХрд╛рд░реНрдп рдкреНрд░рджрд╛рди рдХрд░рддреЗ рд╣реИрдВред



рдареАрдХ рд╣реИ, рд╣рдордиреЗ рд╕реАрдЦрд╛ рдХрд┐ рдЦреЗрддреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рд╛рд░ рд╕рдВрджреЗрд╢ рдХреЛ рдкрд╛рд░реНрд╕ рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛рдП, рдЕрдм рдЖрдк Opening



(рдХреНрд▓рд╛рдЗрдВрдЯ рд╕реЗ) рдФрд░ Response



(рд╕рд░реНрд╡рд░ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛) рдХреА рджрд┐рд╢рд╛ рдореЗрдВ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВред

рдЙрджреНрдШрд╛рдЯрди рдореЗрдВ рдХреБрдЫ рдлрд╝реАрд▓реНрдбреНрд╕ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдП (рдХреБрдЫ рд╡реИрдХрд▓реНрдкрд┐рдХ рд╣реИрдВ), рдЗрд╕рд▓рд┐рдП рд╣рдо optIf



рдореЗрдВ message



рдирд┐рдпрдо рдХреЛ optIf



; рдФрд░ рд▓рдВрдмрд╛рдИ рдореЗрдВ 8 рдмрд╛рдЗрдЯреНрд╕ рд╡рд╛рд▓рд╛ рд╢рд░реАрд░ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИред

opening = ( toOpening , fromOpening ) `wrap` ( optIf hasFields message <&> body 8 ) where <br>







рдХрд╛рд░реНрдп toOpening



, рдореИрдВ рдирд╣реАрдВ рджреВрдВрдЧрд╛ред

Response



рд╕рд╛рде Response



рдпрд╣ рдмрд┐рд▓реНрдХреБрд▓ рд╡реИрд╕рд╛ рд╣реА рд╣реИред



рдлреНрд░реЗрдореНрд╕




рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╣рд╛рдереЛрдВ рд╕реЗ рд╣рд┐рд▓рд╛ рдХреЗ рд╕рд╛рде, рдЕрдм рдпрд╣ рд╕рдВрджреЗрд╢ рд▓реЗрдиреЗ рдХреЗ рд▓рд╛рдпрдХ рд╣реИред

рдПрдХ рд╣реА рдбреНрд░рд╛рдлреНрдЯ рдЕрдиреБрднрд╛рдЧ рдореЗрдВ, рдЖрдк рдлреНрд░реЗрдо рдкреНрд░рд╛рд░реВрдк рдХрд╛ рд╡рд┐рд╡рд░рдг рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:

frames = *frame

frame = text-frame / binary-frame

text-frame = (%x00-7F) *(%x00-FE) %xFF

binary-frame = (%x80-FF) length < as many bytes as given by the length >

length = *(%x80-FF) (%x00-7F)







рд╣рдо рдПрдХрдорд╛рддреНрд░ рдЕрдкрд╡рд╛рдж рдХреЗ рд╕рд╛рде рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдЬреЛ рд╣рдо closing-frame



рдЫреЛрдбрд╝ рджреЗрддреЗ рд╣реИрдВ:

frames = ( takeWhile ( not . isClosing ) , takeWhile ( not . isClosing ) ) `wrap` many frame<br>

frame = optIf isText textFrame <|> optIf isBinary binaryFrame <|> optIf isClosing closingFrame<br>







<|>



рдСрдкрд░реЗрдЯрд░ рдПрдХ рд╡рд┐рдХрд▓реНрдк рд╣реИред рдкрд╣рд▓реЗ рдмрд╛рдПрдВ рдирд┐рдпрдо рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реИ, рдЕрдЧрд░ рдпрд╣ рд╡рд┐рдлрд▓ рд╣реЛрддрд╛ рд╣реИ, рддреЛ рд╕рд╣реАред



рдЦреБрдж рдлреНрд░реЗрдо:

textFrame = ( TextFrame , \ ( TextFrame s ) -> s ) `wrap` ( textFlag &> many frameChar <& frameFF ) where <br>

textFlag = ignore ( optIf ( <= 0x7F ) word8 ) 0x00 <br>

binaryFrame = ( BinaryFrame , \ ( BinaryFrame s ) -> s ) `wrap` ( binaryFlag &> byteSourceLength frameLength ) where <br>

binaryFlag = ignore ( optIf ( liftM2 ( && ) ( > 0x7F ) ( /= 0xFF ) ) word8 ) 0xF0 <br>

closingFrame = check ( 0xFF , 0x00 ) ( word8 <&> word8 ) ClosingFrame <br>







ignore



рдлрд╝рдВрдХреНрд╢рди рд╕рдВрдмрдВрдзрд┐рдд рдорд╛рди рдХреЛ ignore



рдХрд░рддрд╛ рд╣реИ, рдФрд░ рд▓рд┐рдЦрддреЗ рд╕рдордп, рджреВрд╕рд░реЗ рддрд░реНрдХ рджреНрд╡рд╛рд░рд╛ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдореВрд▓реНрдп рдХреЛ рдкреНрд░рддрд┐рд╕реНрдерд╛рдкрд┐рдд рдХрд░рддрд╛ рд╣реИред рдпрд╛рдиреА textFrame



рдкрдврд╝рддреЗ рд╕рдордп textFrame



рд╣рдо рдЯреЗрдХреНрд╕реНрдЯ рдХреЛ рд╕рднреА рдлреНрд░реЗрдо рдорд╛рдирддреЗ рд╣реИрдВ textFrame



рдЬрд┐рд╕рдХрд╛ рдЭрдВрдбрд╛ 0x7F рд╕реЗ рдЕрдзрд┐рдХ рдирд╣реАрдВ рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐, рд╕рдВрджреЗрд╢ рдХреЛ textFrame



рдХрд░рддреЗ рд╕рдордп, рд╣рдо рд╣рдореЗрд╢рд╛ 0 рд╕реЗрдЯ рдХрд░рддреЗ рд╣реИрдВред

byteSourceLength



рдмрд╛рдЗрдЯреНрд╕ рдХреЗ рдПрдХ рдмрд╛рджрд▓ рдХреЛ рд▓реЛрдб / рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рддрд╛ рд╣реИ, рдЗрд╕реЗ рдЗрди рдмрд╛рдЗрдЯреНрд╕ рдХреА рд╕рдВрдЦреНрдпрд╛ рдХреЗ рд╕рд╛рде byteSourceLength



рдХрд░рддрд╛ рд╣реИ, рдЬреЛ рдХрд┐ рдкрд╛рд░рд┐рдд рдирд┐рдпрдо ( frameLength



) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд▓реЛрдб / рд╕рд╣реЗрдЬрд╛ рдЬрд╛рдПрдЧрд╛ред

WebSocket рдореЗрдВ рд▓рдВрдмрд╛рдИ рдмрд╛рдЗрдЯреНрд╕ рдореЗрдВ рдПрдХ рдЪрд░ рдЖрдХрд╛рд░ рд╣реИред рдЕрдВрддрд┐рдо рдмрд╛рдЗрдЯ рдХрд╛ рд╕рдВрдХреЗрдд рдкрд░реЗрд╢рд╛рди рдЙрдЪреНрдЪ рдмрд┐рдЯ рд╣реИред

frameLength = ( \ ( hs , l ) -> toLength ( hs ++ [ l ] ) , ( init &&& last ) . fromLength ) `wrap` ( many highWord <&> lowWord ) where <br>







toLength



, fromLength



, highWord



рдФрд░ lowWord



рдЫреЛрдбрд╝ рджреВрдВрдЧрд╛ред



рд╕рд░реНрд╡рд░




рдЕрдм рдЖрдк рд╕рд░реНрд╡рд░ рдЬреИрд╕рд╛ рдХреБрдЫ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред

start port onAccept = do <br>

sock <- S . socket S . AF_INET S . Stream S . defaultProtocol<br>

S . bindSocket sock $ S . SockAddrInet port S . iNADDR_ANY<br>

S . listen sock S . maxListenQueue<br>

let <br>

-- . ( ), <br>

-- "" . <br>

canDie e = if fromException e == Just ThreadKilled then throwIO ThreadKilled else return () <br>

-- . <br>

th <- fork $ forever $ canDie `handle` acceptClient sock onAccept<br>

return $ Server th<br>









рдХрдиреЗрдХреНрд╢рди рдкреНрд░рддреАрдХреНрд╖рд╛рд░рдд рд╕рдорд╛рд░реЛрд╣:

acceptClient socket onAccept = ignore $ accept socket onReceived where <br>







accept



рдХрдиреЗрдХреНрд╢рди рдХреЛ accept



рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЖрд▓рд╕реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рд░реВрдк рдореЗрдВ onReceived



рдлрд╝рдВрдХреНрд╢рди рдХреЛ рд╕рдВрдкреВрд░реНрдг рдЗрдирдкреБрдЯ рд╕реНрдЯреНрд░реАрдо рдкрд╛рд╕ рдХрд░рддрд╛ рд╣реИред



onReceived sock income = do <br>

-- . , anything ( ), <br>

-- , opening. <br>

( o , tailData ) <- letFail $ decode ( opening <&> anything ) income<br>

-- . <br>

r <- letFail ( responseTo o >>= mapException show . encode response ) <br>

-- . <br>

send sock r<br>

let con = connection ( openingChannel o ) ( openingHost o ) ( openingOrigin o ) ( openingProtocol o ) sock<br>

let <br>

-- . callback. <br>

onConnect ClosingFrame = close con `finally` acceptOnClose handlers con<br>

-- . <br>

onConnect f = acceptOnMessage handlers con f<br>

-- callback "". <br>

fork $ acceptOnOpen handlers con<br>

<br>

-- - , . <br>

switch ( const $ return () ) ( mapM_ onConnect ) $ decode frames tailData<br>









рдЖрд▓рд╕реА рд╕реВрдЪрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдирд╛ рд╕рдордЭ рдХреЗ рд▓рд┐рдП рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИ: рд╕рдВрджреЗрд╢реЛрдВ рдХреА рдПрдХ рд╕реВрдЪреА рд╣реИ, рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рдХреЗ рд▓рд┐рдП рд╕рдВрдмрдВрдзрд┐рдд callback



рдХрд╣рддреЗ рд╣реИрдВ; рд▓реЗрдХрд┐рди рд╡рд╣рд╛рдБ рдПрдХ рдЪреЗрддрд╛рд╡рдиреА рд╣реИред



рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рд╣рдо рдЖрд▓рд╕реА ByteString



рд░реВрдк рдореЗрдВ рд╕рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдирдкреБрдЯ рдХрд╛ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред

рдЕрдЧрд░ рд╣рдо рдЗрд╕реЗ рдЗрд╕ рддрд░рд╣ рд▓рд┐рдЦрддреЗ рд╣реИрдВ:

input <- fix $ \ loop -> unsafeInterleaveIO $ liftM2 ( : ) getLine loop<br>

let byteString = pack $ map charToByte input<br>







рдлрд┐рд░ рдПрдХ рдЖрд▓рд╕реА ByteString



рдХреЛ рдкреНрд░рд┐рдВрдЯ рдХрд░рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рддреЗ ByteString



, рд╣рдо рдкреНрд░рднрд╛рд╡ рдХреА рдХрдореА рдкрд░ рдмрд╣реБрдд рдЖрд╢реНрдЪрд░реНрдпрдЪрдХрд┐рдд рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рдорд╛рдорд▓рд╛ pack



рдлрд╝рдВрдХреНрд╢рди рдХреЗ рдХрдареЛрд░рддрд╛ рдореЗрдВ рдкреНрд░рд╛рдердорд┐рдХ рд╣реИ, рдЗрд╕реЗ рдПрдХ рд╣реА рдмрд╛рд░ рдореЗрдВ рдкреВрд░реА рд▓рд╛рдЗрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред

рдЗрд╕ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕рднреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдирдкреБрдЯ рдХреА рдПрдХ рдЖрд▓рд╕реА рд╕реВрдЪреА рдкреНрд░рд╛рдкреНрдд рдХрд░рдирд╛ рдЕрдзрд┐рдХ рд╕рд╣реА рд╣реЛрдЧрд╛, рдФрд░ рдлрд┐рд░ fromChunks



рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдлрд┐рд░ рдЬреИрд╕реЗ рд╣реА рдЖрдк рдЯрд╛рдЗрдк рдХрд░рддреЗ рд╣реИрдВ, рд╣рдорд╛рд░рд╛ ByteString



рдЕрдм рдПрдХ рдЦрд╛рд▓реА рд╡рд╛рджрд╛ рдирд╣реАрдВ ByteString



, рд▓реЗрдХрд┐рди рдИрдорд╛рдирджрд╛рд░реА рд╕реЗ рдкреВрд░реЗ рдЗрдирдкреБрдЯ рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рд╣реЛрдЧрд╛ред



рдирд┐рд╖реНрдХрд░реНрд╖




рдореИрдВрдиреЗ рдпрд╣ рд╕рдм рдХреНрдпреЛрдВ рд▓рд┐рдЦрд╛? рдЦреИрд░, рдореБрдЭреЗ рдЖрд╢рд╛ рд╣реИ рдХрд┐ рдХрд┐рд╕реА рдХреЗ рдкрд╛рд╕ рд╣рд╛рд╕реНрдХреЗрд▓ рдореЗрдВ рдЕрддрд┐рд░рд┐рдХреНрдд рд░реБрдЪрд┐ рд╣реИ, рдпрд╛ рдХрд╛рд░реНрдпрд╛рддреНрдордХ рдХреНрд╖рдп рдХреА рдирд┐рд░рд░реНрдердХрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХрдо, рд╕рдВрджреЗрд╣ рд╣реИред



All Articles