1 /*
2 +----------------------------------------------------------------------+
3 | Swoole |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2012-2015 The Swoole Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 2.0 of the Apache license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.apache.org/licenses/LICENSE-2.0.html |
11 | If you did not receive a copy of the Apache2.0 license and are unable|
12 | to obtain it through the world-wide-web, please send a note to |
13 | license@swoole.com so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Tianfeng Han <mikan.tenny@gmail.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 #pragma once
20
21 #include "swoole_http.h"
22 #ifdef SW_USE_HTTP2
23 #include "swoole_http2.h"
24 #endif
25 #include "thirdparty/swoole_http_parser.h"
26 #include "thirdparty/multipart_parser.h"
27
28 #include <unordered_map>
29
30 #ifdef SW_HAVE_ZLIB
31 #include <zlib.h>
32 #define SW_ZLIB_ENCODING_RAW -0xf
33 #define SW_ZLIB_ENCODING_GZIP 0x1f
34 #define SW_ZLIB_ENCODING_DEFLATE 0x0f
35 #define SW_ZLIB_ENCODING_ANY 0x2f
36 #endif
37
38 #ifdef SW_USE_HTTP2
39 #include "thirdparty/nghttp2/nghttp2.h"
40 #endif
41
42 enum http_header_flag {
43 HTTP_HEADER_SERVER = 1u << 1,
44 HTTP_HEADER_CONNECTION = 1u << 2,
45 HTTP_HEADER_CONTENT_LENGTH = 1u << 3,
46 HTTP_HEADER_DATE = 1u << 4,
47 HTTP_HEADER_CONTENT_TYPE = 1u << 5,
48 HTTP_HEADER_TRANSFER_ENCODING = 1u << 6,
49 HTTP_HEADER_ACCEPT_ENCODING = 1u << 7,
50 };
51
52 enum http_compress_method {
53 HTTP_COMPRESS_NONE,
54 HTTP_COMPRESS_GZIP,
55 HTTP_COMPRESS_DEFLATE,
56 HTTP_COMPRESS_BR,
57 };
58
59 namespace swoole {
60 class Server;
61 #ifdef SW_USE_HTTP2
62 class Coroutine;
63 namespace http2 {
64 class Stream;
65 class Session;
66 } // namespace http2
67 #endif
68
69 namespace http {
70
71 struct Request {
72 int version;
73 char *path;
74 uint32_t path_len;
75 const char *ext;
76 uint32_t ext_len;
77 uint8_t post_form_urlencoded;
78
79 zval zdata;
80 const char *body_at;
81 size_t body_length;
82 String *chunked_body;
83 #ifdef SW_USE_HTTP2
84 String *h2_data_buffer;
85 #endif
86
87 // Notice: Do not change the order
88 zval *zobject;
89 zval _zobject;
90 zval *zserver;
91 zval _zserver;
92 zval *zheader;
93 zval _zheader;
94 zval *zget;
95 zval _zget;
96 zval *zpost;
97 zval _zpost;
98 zval *zcookie;
99 zval _zcookie;
100 zval *zfiles;
101 zval _zfiles;
102 zval *ztmpfiles;
103 zval _ztmpfiles;
104 };
105
106 struct Response {
107 enum swoole_http_method method;
108 int version;
109 int status;
110 char *reason;
111
112 // Notice: Do not change the order
113 zval *zobject;
114 zval _zobject;
115 zval *zheader;
116 zval _zheader;
117 zval *zcookie;
118 zval _zcookie;
119 zval *ztrailer;
120 zval _ztrailer;
121 };
122
123 struct Context {
124 SessionId fd;
125 uchar completed : 1;
126 uchar end_ : 1;
127 uchar send_header_ : 1;
128 #ifdef SW_HAVE_COMPRESSION
129 uchar enable_compression : 1;
130 uchar accept_compression : 1;
131 #endif
132 uchar send_chunked : 1;
133 uchar recv_chunked : 1;
134 uchar send_trailer_ : 1;
135 uchar keepalive : 1;
136 uchar websocket : 1;
137 #ifdef SW_HAVE_ZLIB
138 uchar websocket_compression : 1;
139 #endif
140 uchar upgrade : 1;
141 uchar detached : 1;
142 uchar parse_cookie : 1;
143 uchar parse_body : 1;
144 uchar parse_files : 1;
145 uchar co_socket : 1;
146
147 #ifdef SW_USE_HTTP2
148 uchar http2 : 1;
149 http2::Stream *stream;
150 #endif
151
152 #ifdef SW_HAVE_COMPRESSION
153 int8_t compression_level;
154 int8_t compression_method;
155 uint32_t compression_min_length;
156 #endif
157
158 Request request;
159 Response response;
160
161 swoole_http_parser parser;
162 multipart_parser *mt_parser;
163
164 uint16_t input_var_num;
165 char *current_header_name;
166 size_t current_header_name_len;
167 char *current_input_name;
168 size_t current_input_name_len;
169 char *current_form_data_name;
170 size_t current_form_data_name_len;
171 zval *current_multipart_header;
172
173 std::string upload_tmp_dir;
174
175 void *private_data;
176 bool (*send)(Context *ctx, const char *data, size_t length);
177 bool (*sendfile)(Context *ctx, const char *file, uint32_t l_file, off_t offset, size_t length);
178 bool (*close)(Context *ctx);
179
180 void init(Server *server);
181 void init(coroutine::Socket *socket);
182 void bind(Server *server);
183 void bind(coroutine::Socket *socket);
184 void copy(Context *ctx);
185 bool parse_form_data(const char *boundary_str, int boundary_len);
186 size_t parse(const char *data, size_t length);
187 bool set_header(const char *, size_t, zval *, bool);
188 bool set_header(const char *, size_t, const char *, size_t, bool);
189 void end(zval *zdata, zval *return_value);
190 bool send_file(const char *file, uint32_t l_file, off_t offset, size_t length);
191 void send_trailer(zval *return_value);
192 String *get_write_buffer();
193 void build_header(String *http_buffer, size_t body_length);
194 ssize_t build_trailer(String *http_buffer);
195
196 #ifdef SW_HAVE_COMPRESSION
197 void set_compression_method(const char *accept_encoding, size_t length);
198 const char *get_content_encoding();
199 #endif
200
201 #ifdef SW_USE_HTTP2
202 void http2_end(zval *zdata, zval *return_value);
203 bool http2_send_file(const char *file, uint32_t l_file, off_t offset, size_t length);
204 #endif
205
206 void free();
207 };
208
209 } // namespace http
210
211 #ifdef SW_USE_HTTP2
212 namespace http2 {
213 class Stream {
214 public:
215 http::Context *ctx;
216 // uint8_t priority; // useless now
217 uint32_t id;
218 // flow control
219 uint32_t remote_window_size;
220 uint32_t local_window_size;
221 Coroutine *waiting_coroutine = nullptr;
222
223 Stream(Session *client, uint32_t _id);
224 ~Stream();
225
226 bool send_header(size_t body_length, bool end_stream);
227 bool send_body(String *body, bool end_stream, size_t max_frame_size, off_t offset = 0, size_t length = 0);
228 bool send_trailer();
229
230 void reset(uint32_t error_code);
231 };
232
233 class Session {
234 public:
235 int fd;
236 std::unordered_map<uint32_t, Stream *> streams;
237
238 nghttp2_hd_inflater *inflater = nullptr;
239 nghttp2_hd_deflater *deflater = nullptr;
240
241 http2::Settings local_settings = {};
242 http2::Settings remote_settings = {};
243
244 uint32_t last_stream_id;
245 bool shutting_down;
246 bool is_coro;
247
248 http::Context *default_ctx = nullptr;
249 void *private_data = nullptr;
250
251 void (*handle)(Session *, Stream *) = nullptr;
252
253 Session(SessionId _fd);
254 ~Session();
255 };
256 } // namespace http2
257 #endif
258
259 } // namespace swoole
260
261 extern zend_class_entry *swoole_http_server_ce;
262 extern zend_class_entry *swoole_http_request_ce;
263 extern zend_class_entry *swoole_http_response_ce;
264
265 extern swoole::String *swoole_http_buffer;
266 extern swoole::String *swoole_http_form_data_buffer;
267 #ifdef SW_HAVE_COMPRESSION
268 extern swoole::String *swoole_zlib_buffer;
269 #endif
270
271 swoole::http::Context *swoole_http_context_new(swoole::SessionId fd);
272 swoole::http::Context *php_swoole_http_request_get_and_check_context(zval *zobject);
273 swoole::http::Context *php_swoole_http_response_get_and_check_context(zval *zobject);
274
swoole_http_init_and_read_property(zend_class_entry * ce,zval * zobject,zval ** zproperty_store_pp,const char * name,size_t name_len)275 static sw_inline zval *swoole_http_init_and_read_property(
276 zend_class_entry *ce, zval *zobject, zval **zproperty_store_pp, const char *name, size_t name_len) {
277 if (UNEXPECTED(!*zproperty_store_pp)) {
278 // Notice: swoole http server properties can not be unset anymore, so we can read it without checking
279 zval rv, *property = zend_read_property(ce, SW_Z8_OBJ_P(zobject), name, name_len, 0, &rv);
280 array_init(property);
281 *zproperty_store_pp = (zval *) (zproperty_store_pp + 1);
282 **zproperty_store_pp = *property;
283 }
284 return *zproperty_store_pp;
285 }
286
swoole_http_has_crlf(const char * value,size_t length)287 static inline bool swoole_http_has_crlf(const char *value, size_t length) {
288 /* new line/NUL character safety check */
289 for (size_t i = 0; i < length; i++) {
290 /* RFC 7230 ch. 3.2.4 deprecates folding support */
291 if (value[i] == '\n' || value[i] == '\r') {
292 php_swoole_error(E_WARNING, "Header may not contain more than a single header, new line detected");
293 return true;
294 }
295 if (value[i] == '\0') {
296 php_swoole_error(E_WARNING, "Header may not contain NUL bytes");
297 return true;
298 }
299 }
300
301 return false;
302 }
303
304 void swoole_http_parse_cookie(zval *array, const char *at, size_t length, bool url_decode = true);
305
306 swoole::http::Context *php_swoole_http_request_get_context(zval *zobject);
307 void php_swoole_http_request_set_context(zval *zobject, swoole::http::Context *context);
308 swoole::http::Context *php_swoole_http_response_get_context(zval *zobject);
309 void php_swoole_http_response_set_context(zval *zobject, swoole::http::Context *context);
310
311 #ifdef SW_HAVE_COMPRESSION
312 int swoole_http_response_compress(const char *data, size_t length, int method, int level);
313 #endif
314
315 #ifdef SW_HAVE_ZLIB
316 voidpf php_zlib_alloc(voidpf opaque, uInt items, uInt size);
317 void php_zlib_free(voidpf opaque, voidpf address);
318 #endif
319
320 #ifdef SW_HAVE_BROTLI
321 void *php_brotli_alloc(void *opaque, size_t size);
322 void php_brotli_free(void *opaque, void *address);
323 #endif
324
325 #ifdef SW_USE_HTTP2
326
php_nghttp2_mem()327 static sw_inline nghttp2_mem *php_nghttp2_mem() {
328 static nghttp2_mem mem = {nullptr,
329 [](size_t size, void *mem_user_data) { return emalloc(size); },
330 [](void *ptr, void *mem_user_data) { return efree(ptr); },
331 [](size_t nmemb, size_t size, void *mem_user_data) { return ecalloc(nmemb, size); },
332 [](void *ptr, size_t size, void *mem_user_data) { return erealloc(ptr, size); }};
333 return &mem;
334 }
335
336 namespace swoole {
337 namespace http2 {
338 //-----------------------------------namespace begin--------------------------------------------
339 class HeaderSet {
340 public:
HeaderSet(size_t size)341 HeaderSet(size_t size) : size(size), index(0) {
342 nvs = (nghttp2_nv *) ecalloc(size, sizeof(nghttp2_nv));
343 }
344
get()345 inline nghttp2_nv *get() {
346 return nvs;
347 }
348
len()349 inline size_t len() {
350 return index;
351 }
352
reserve_one()353 void reserve_one() {
354 index++;
355 }
356
357 inline void add(size_t index,
358 const char *name,
359 size_t name_len,
360 const char *value,
361 size_t value_len,
362 const uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
363 if (sw_likely(index < size || nvs[index].name == nullptr)) {
364 nghttp2_nv *nv = &nvs[index];
365 name = zend_str_tolower_dup(name, name_len); // auto to lower
366 nv->name = (uchar *) name;
367 nv->namelen = name_len;
368 nv->value = (uchar *) emalloc(value_len);
369 memcpy(nv->value, value, value_len);
370 nv->valuelen = value_len;
371 nv->flags = flags | NGHTTP2_NV_FLAG_NO_COPY_NAME | NGHTTP2_NV_FLAG_NO_COPY_VALUE;
372 swoole_trace_log(SW_TRACE_HTTP2,
373 "name=(%zu)[" SW_ECHO_LEN_BLUE "], value=(%zu)[" SW_ECHO_LEN_CYAN "]",
374 name_len,
375 (int) name_len,
376 name,
377 value_len,
378 (int) value_len,
379 value);
380 } else {
381 php_swoole_fatal_error(
382 E_WARNING, "unexpect http2 header [%.*s] (duplicated or overflow)", (int) name_len, name);
383 }
384 }
385
386 inline void add(const char *name,
387 size_t name_len,
388 const char *value,
389 size_t value_len,
390 const uint8_t flags = NGHTTP2_NV_FLAG_NONE) {
391 add(index++, name, name_len, value, value_len, flags);
392 }
393
~HeaderSet()394 ~HeaderSet() {
395 for (size_t i = 0; i < size; ++i) {
396 if (sw_likely(nvs[i].name /* && nvs[i].value */)) {
397 efree((void *) nvs[i].name);
398 efree((void *) nvs[i].value);
399 }
400 }
401 efree(nvs);
402 }
403
404 private:
405 nghttp2_nv *nvs;
406 size_t size;
407 size_t index;
408 };
409 //-----------------------------------namespace end--------------------------------------------
410 } // namespace http2
411 } // namespace swoole
412 #endif
413