Issues (87)

src/Transformers/Response.php (1 issue)

1
<?php
2
3
namespace SwooleTW\Http\Transformers;
4
5
use Illuminate\Http\Response as IlluminateResponse;
6
use Swoole\Http\Response as SwooleResponse;
7
use Swoole\Http\Request as SwooleRequest;
8
use Symfony\Component\HttpFoundation\BinaryFileResponse;
9
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
10
use Symfony\Component\HttpFoundation\StreamedResponse;
11
12
class Response
13
{
14
    const CHUNK_SIZE = 8192;
15
16
    /**
17
     * @var \Swoole\Http\Response
18
     */
19
    protected $swooleResponse;
20
21
    /**
22
     * @var \Swoole\Http\Request
23
     */
24
    protected $swooleRequest;
25
26
    /**
27
     * @var \Illuminate\Http\Response
28
     */
29
    protected $illuminateResponse;
30
31
    /**
32
     * Make a response.
33
     *
34
     * @param $illuminateResponse
35
     * @param \Swoole\Http\Response $swooleResponse
36
     * @param \Swoole\Http\Request $swooleRequest
37
     *
38
     * @return \SwooleTW\Http\Transformers\Response
39
     */
40
    public static function make($illuminateResponse, SwooleResponse $swooleResponse, SwooleRequest $swooleRequest)
41
    {
42
        return new static($illuminateResponse, $swooleResponse, $swooleRequest);
43
    }
44
45
    /**
46
     * Response constructor.
47
     *
48
     * @param mixed $illuminateResponse
49
     * @param \Swoole\Http\Response $swooleResponse
50
     * @param \Swoole\Http\Request $swooleRequest
51
     */
52
    public function __construct($illuminateResponse, SwooleResponse $swooleResponse, SwooleRequest $swooleRequest)
53
    {
54
        $this->setIlluminateResponse($illuminateResponse);
55
        $this->setSwooleResponse($swooleResponse);
56
        $this->setSwooleRequest($swooleRequest);
57
    }
58
59
    /**
60
     * Send HTTP headers and content.
61
     *
62
     * @throws \InvalidArgumentException
63
     */
64
    public function send()
65
    {
66
        $this->sendHeaders();
67
        $this->sendContent();
68
    }
69
70
    /**
71
     * Send HTTP headers.
72
     *
73
     * @throws \InvalidArgumentException
74
     */
75
    protected function sendHeaders()
76
    {
77
        $illuminateResponse = $this->getIlluminateResponse();
78
79
        /* RFC2616 - 14.18 says all Responses need to have a Date */
80
        if (! $illuminateResponse->headers->has('Date')) {
81
            $illuminateResponse->setDate(\DateTime::createFromFormat('U', time()));
82
        }
83
84
        // headers
85
        // allPreserveCaseWithoutCookies() doesn't exist before Laravel 5.3
86
        $headers = $illuminateResponse->headers->allPreserveCase();
87
        if (isset($headers['Set-Cookie'])) {
88
            unset($headers['Set-Cookie']);
89
        }
90
        foreach ($headers as $name => $values) {
91
            foreach ($values as $value) {
92
                $this->swooleResponse->header($name, $value);
93
            }
94
        }
95
96
        // status
97
        $this->swooleResponse->status($illuminateResponse->getStatusCode());
98
99
        // cookies
100
        // $cookie->isRaw() is supported after symfony/http-foundation 3.1
101
        // and Laravel 5.3, so we can add it back now
102
        foreach ($illuminateResponse->headers->getCookies() as $cookie) {
103
            $method = $cookie->isRaw() ? 'rawcookie' : 'cookie';
104
            $this->swooleResponse->$method(
105
                $cookie->getName(),
106
                $cookie->getValue(),
107
                $cookie->getExpiresTime(),
108
                $cookie->getPath(),
109
                $cookie->getDomain(),
110
                $cookie->isSecure(),
111
                $cookie->isHttpOnly()
112
            );
113
        }
114
    }
115
116
    /**
117
     * Send HTTP content.
118
     */
119
    protected function sendContent()
120
    {
121
        $illuminateResponse = $this->getIlluminateResponse();
122
123
        if ($illuminateResponse instanceof StreamedResponse && property_exists($illuminateResponse, 'output')) {
0 ignored issues
show
$illuminateResponse is never a sub-type of Symfony\Component\HttpFoundation\StreamedResponse.
Loading history...
124
            // TODO Add Streamed Response with output
125
            $this->swooleResponse->end($illuminateResponse->output);
126
        } elseif ($illuminateResponse instanceof BinaryFileResponse) {
127
            $this->swooleResponse->sendfile($illuminateResponse->getFile()->getPathname());
128
        } else {
129
            $chunkGzip = $this->canGzipContent($illuminateResponse->headers->get('Content-Encoding'));
130
            $this->sendInChunk($illuminateResponse->getContent(), $chunkGzip);
131
        }
132
    }
133
134
    /**
135
     * Send content in chunk
136
     *
137
     * @param string $content
138
     * @param bool $chunkGzip
139
     */
140
    protected function sendInChunk($content, $chunkGzip)
141
    {
142
        if (strlen($content) <= static::CHUNK_SIZE) {
143
            $this->swooleResponse->end($content);
144
            return;
145
        }
146
147
        // Swoole Chunk mode does not support compress by default, this patch only supports gzip
148
        if ($chunkGzip) {
149
            $this->swooleResponse->header('Content-Encoding', 'gzip');
150
            $content = gzencode($content, config('swoole_http.server.options.http_compression_level', 3));
151
        }
152
153
        foreach (str_split($content, static::CHUNK_SIZE) as $chunk) {
154
            $this->swooleResponse->write($chunk);
155
        }
156
157
        $this->swooleResponse->end();
158
    }
159
160
    /**
161
     * @param \Swoole\Http\Response $swooleResponse
162
     *
163
     * @return \SwooleTW\Http\Transformers\Response
164
     */
165
    protected function setSwooleResponse(SwooleResponse $swooleResponse)
166
    {
167
        $this->swooleResponse = $swooleResponse;
168
169
        return $this;
170
    }
171
172
    /**
173
     * @return \Swoole\Http\Response
174
     */
175
    public function getSwooleResponse()
176
    {
177
        return $this->swooleResponse;
178
    }
179
180
    /**
181
     * @param mixed illuminateResponse
182
     *
183
     * @return \SwooleTW\Http\Transformers\Response
184
     */
185
    protected function setIlluminateResponse($illuminateResponse)
186
    {
187
        if (! $illuminateResponse instanceof SymfonyResponse) {
188
            $content = (string) $illuminateResponse;
189
            $illuminateResponse = new IlluminateResponse($content);
190
        }
191
192
        $this->illuminateResponse = $illuminateResponse;
193
194
        return $this;
195
    }
196
197
    /**
198
     * @return \Illuminate\Http\Response
199
     */
200
    public function getIlluminateResponse()
201
    {
202
        return $this->illuminateResponse;
203
    }
204
205
    /**
206
     * @param \Swoole\Http\Request $swooleRequest
207
     *
208
     * @return \SwooleTW\Http\Transformers\Response
209
     */
210
    protected function setSwooleRequest(SwooleRequest $swooleRequest)
211
    {
212
        $this->swooleRequest = $swooleRequest;
213
214
        return $this;
215
    }
216
217
    /**
218
     * @return \Swoole\Http\Request
219
     */
220
    public function getSwooleRequest()
221
    {
222
        return $this->swooleRequest;
223
    }
224
225
    /**
226
     * @param string $responseContentEncoding
227
     * @return bool
228
     */
229
    protected function canGzipContent($responseContentEncoding)
230
    {
231
        return empty($responseContentEncoding) &&
232
            config('swoole_http.server.options.http_compression', true) &&
233
            !empty($this->swooleRequest->header['accept-encoding']) &&
234
            strpos($this->swooleRequest->header['accept-encoding'], 'gzip') !== false &&
235
            function_exists('gzencode');
236
    }
237
}
238