Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 2 : // for details. All rights reserved. Use of this source code is governed by a 3 : // BSD-style license that can be found in the LICENSE file. 4 : 5 : import 'dart:convert'; 6 : import 'dart:typed_data'; 7 : 8 : import 'package:charcode/ascii.dart'; 9 : 10 : /// The canonical instance of [ChunkedCodingEncoder]. 11 : const chunkedCodingEncoder = ChunkedCodingEncoder._(); 12 : 13 : /// The chunk indicating that the chunked message has finished. 14 0 : final _doneChunk = Uint8List.fromList([$0, $cr, $lf, $cr, $lf]); 15 : 16 : /// A converter that encodes byte arrays into chunks with size tags. 17 : class ChunkedCodingEncoder extends Converter<List<int>, List<int>> { 18 11 : const ChunkedCodingEncoder._(); 19 : 20 0 : @override 21 : List<int> convert(List<int> input) => 22 0 : _convert(input, 0, input.length, isLast: true); 23 : 24 0 : @override 25 : ByteConversionSink startChunkedConversion(Sink<List<int>> sink) => 26 0 : _Sink(sink); 27 : } 28 : 29 : /// A conversion sink for the chunked transfer encoding. 30 : class _Sink extends ByteConversionSinkBase { 31 : /// The underlying sink to which encoded byte arrays will be passed. 32 : final Sink<List<int>> _sink; 33 : 34 0 : _Sink(this._sink); 35 : 36 0 : @override 37 : void add(List<int> chunk) { 38 0 : _sink.add(_convert(chunk, 0, chunk.length)); 39 : } 40 : 41 0 : @override 42 : void addSlice(List<int> chunk, int start, int end, bool isLast) { 43 0 : RangeError.checkValidRange(start, end, chunk.length); 44 0 : _sink.add(_convert(chunk, start, end, isLast: isLast)); 45 0 : if (isLast) _sink.close(); 46 : } 47 : 48 0 : @override 49 : void close() { 50 0 : _sink.add(_doneChunk); 51 0 : _sink.close(); 52 : } 53 : } 54 : 55 : /// Returns a new list a chunked transfer encoding header followed by the slice 56 : /// of [bytes] from [start] to [end]. 57 : /// 58 : /// If [isLast] is `true`, this adds the footer that indicates that the chunked 59 : /// message is complete. 60 0 : List<int> _convert(List<int> bytes, int start, int end, {bool isLast = false}) { 61 0 : if (end == start) return isLast ? _doneChunk : const []; 62 : 63 0 : final size = end - start; 64 0 : final sizeInHex = size.toRadixString(16); 65 0 : final footerSize = isLast ? _doneChunk.length : 0; 66 : 67 : // Add 4 for the CRLF sequences that follow the size header and the bytes. 68 0 : final list = Uint8List(sizeInHex.length + 4 + size + footerSize); 69 0 : list.setRange(0, sizeInHex.length, sizeInHex.codeUnits); 70 : 71 0 : var cursor = sizeInHex.length; 72 0 : list[cursor++] = $cr; 73 0 : list[cursor++] = $lf; 74 0 : list.setRange(cursor, cursor + end - start, bytes, start); 75 0 : cursor += end - start; 76 0 : list[cursor++] = $cr; 77 0 : list[cursor++] = $lf; 78 : 79 : if (isLast) { 80 0 : list.setRange(list.length - footerSize, list.length, _doneChunk); 81 : } 82 : return list; 83 : }