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