friendship ended with social-app. php is my new best friend
1<?php 2 3namespace React\Http\Io; 4 5use Evenement\EventEmitter; 6use React\Stream\ReadableStreamInterface; 7use React\Stream\Util; 8use React\Stream\WritableStreamInterface; 9use Exception; 10 11/** 12 * [Internal] Decodes "Transfer-Encoding: chunked" from given stream and returns only payload data. 13 * 14 * This is used internally to decode incoming requests with this encoding. 15 * 16 * @internal 17 */ 18class ChunkedDecoder extends EventEmitter implements ReadableStreamInterface 19{ 20 const CRLF = "\r\n"; 21 const MAX_CHUNK_HEADER_SIZE = 1024; 22 23 private $closed = false; 24 private $input; 25 private $buffer = ''; 26 private $chunkSize = 0; 27 private $transferredSize = 0; 28 private $headerCompleted = false; 29 30 public function __construct(ReadableStreamInterface $input) 31 { 32 $this->input = $input; 33 34 $this->input->on('data', array($this, 'handleData')); 35 $this->input->on('end', array($this, 'handleEnd')); 36 $this->input->on('error', array($this, 'handleError')); 37 $this->input->on('close', array($this, 'close')); 38 } 39 40 public function isReadable() 41 { 42 return !$this->closed && $this->input->isReadable(); 43 } 44 45 public function pause() 46 { 47 $this->input->pause(); 48 } 49 50 public function resume() 51 { 52 $this->input->resume(); 53 } 54 55 public function pipe(WritableStreamInterface $dest, array $options = array()) 56 { 57 Util::pipe($this, $dest, $options); 58 59 return $dest; 60 } 61 62 public function close() 63 { 64 if ($this->closed) { 65 return; 66 } 67 68 $this->buffer = ''; 69 70 $this->closed = true; 71 72 $this->input->close(); 73 74 $this->emit('close'); 75 $this->removeAllListeners(); 76 } 77 78 /** @internal */ 79 public function handleEnd() 80 { 81 if (!$this->closed) { 82 $this->handleError(new Exception('Unexpected end event')); 83 } 84 } 85 86 /** @internal */ 87 public function handleError(Exception $e) 88 { 89 $this->emit('error', array($e)); 90 $this->close(); 91 } 92 93 /** @internal */ 94 public function handleData($data) 95 { 96 $this->buffer .= $data; 97 98 while ($this->buffer !== '') { 99 if (!$this->headerCompleted) { 100 $positionCrlf = \strpos($this->buffer, static::CRLF); 101 102 if ($positionCrlf === false) { 103 // Header shouldn't be bigger than 1024 bytes 104 if (isset($this->buffer[static::MAX_CHUNK_HEADER_SIZE])) { 105 $this->handleError(new Exception('Chunk header size inclusive extension bigger than' . static::MAX_CHUNK_HEADER_SIZE. ' bytes')); 106 } 107 return; 108 } 109 110 $header = \strtolower((string)\substr($this->buffer, 0, $positionCrlf)); 111 $hexValue = $header; 112 113 if (\strpos($header, ';') !== false) { 114 $array = \explode(';', $header); 115 $hexValue = $array[0]; 116 } 117 118 if ($hexValue !== '') { 119 $hexValue = \ltrim(\trim($hexValue), "0"); 120 if ($hexValue === '') { 121 $hexValue = "0"; 122 } 123 } 124 125 $this->chunkSize = @\hexdec($hexValue); 126 if (!\is_int($this->chunkSize) || \dechex($this->chunkSize) !== $hexValue) { 127 $this->handleError(new Exception($hexValue . ' is not a valid hexadecimal number')); 128 return; 129 } 130 131 $this->buffer = (string)\substr($this->buffer, $positionCrlf + 2); 132 $this->headerCompleted = true; 133 if ($this->buffer === '') { 134 return; 135 } 136 } 137 138 $chunk = (string)\substr($this->buffer, 0, $this->chunkSize - $this->transferredSize); 139 140 if ($chunk !== '') { 141 $this->transferredSize += \strlen($chunk); 142 $this->emit('data', array($chunk)); 143 $this->buffer = (string)\substr($this->buffer, \strlen($chunk)); 144 } 145 146 $positionCrlf = \strpos($this->buffer, static::CRLF); 147 148 if ($positionCrlf === 0) { 149 if ($this->chunkSize === 0) { 150 $this->emit('end'); 151 $this->close(); 152 return; 153 } 154 $this->chunkSize = 0; 155 $this->headerCompleted = false; 156 $this->transferredSize = 0; 157 $this->buffer = (string)\substr($this->buffer, 2); 158 } elseif ($this->chunkSize === 0) { 159 // end chunk received, skip all trailer data 160 $this->buffer = (string)\substr($this->buffer, $positionCrlf); 161 } 162 163 if ($positionCrlf !== 0 && $this->chunkSize !== 0 && $this->chunkSize === $this->transferredSize && \strlen($this->buffer) > 2) { 164 // the first 2 characters are not CRLF, send error event 165 $this->handleError(new Exception('Chunk does not end with a CRLF')); 166 return; 167 } 168 169 if ($positionCrlf !== 0 && \strlen($this->buffer) < 2) { 170 // No CRLF found, wait for additional data which could be a CRLF 171 return; 172 } 173 } 174 } 175}