1 /*
2  +----------------------------------------------------------------------+
3  | Swoole                                                               |
4  +----------------------------------------------------------------------+
5  | This source file is subject to version 2.0 of the Apache license,    |
6  | that is bundled with this package in the file LICENSE, and is        |
7  | available through the world-wide-web at the following url:           |
8  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
9  | If you did not receive a copy of the Apache2.0 license and are unable|
10  | to obtain it through the world-wide-web, please send a note to       |
11  | license@swoole.com so we can mail you a copy immediately.            |
12  +----------------------------------------------------------------------+
13  | Author: Tianfeng Han  <mikan.tenny@gmail.com>                        |
14  +----------------------------------------------------------------------+
15  */
16 
17 #include "swoole_http.h"
18 #include "swoole_server.h"
19 
20 #include <string>
21 
22 #include "swoole_util.h"
23 #include "swoole_http2.h"
24 #include "swoole_websocket.h"
25 #include "swoole_static_handler.h"
26 
27 using std::string;
28 using swoole::http_server::Request;
29 using swoole::http_server::StaticHandler;
30 using swoole::network::SendfileTask;
31 using swoole::network::Socket;
32 
33 // clang-format off
34 static const char *method_strings[] = {
35     "DELETE", "GET", "HEAD", "POST", "PUT", "PATCH", "CONNECT", "OPTIONS", "TRACE", "COPY", "LOCK", "MKCOL", "MOVE",
36     "PROPFIND", "PROPPATCH", "UNLOCK", "REPORT", "MKACTIVITY", "CHECKOUT", "MERGE", "M-SEARCH", "NOTIFY",
37     "SUBSCRIBE", "UNSUBSCRIBE", "PURGE", "PRI",
38 };
39 // clang-format on
40 
41 namespace swoole {
42 
select_static_handler(http_server::Request * request,Connection * conn)43 bool Server::select_static_handler(http_server::Request *request, Connection *conn) {
44     const char *url = request->buffer_->str + request->url_offset_;
45     size_t url_length = request->url_length_;
46 
47     StaticHandler handler(this, url, url_length);
48     if (!handler.hit()) {
49         return false;
50     }
51 
52     char header_buffer[1024];
53     SendData response;
54     response.info.fd = conn->session_id;
55     response.info.type = SW_SERVER_EVENT_SEND_DATA;
56 
57     if (handler.status_code == SW_HTTP_NOT_FOUND) {
58         response.info.len = sw_snprintf(header_buffer,
59                                         sizeof(header_buffer),
60                                         "HTTP/1.1 %s\r\n"
61                                         "Server: " SW_HTTP_SERVER_SOFTWARE "\r\n"
62                                         "Content-Length: %zu\r\n"
63                                         "\r\n%s",
64                                         http_server::get_status_message(SW_HTTP_NOT_FOUND),
65                                         sizeof(SW_HTTP_PAGE_404) - 1,
66                                         SW_HTTP_PAGE_404);
67         response.data = header_buffer;
68         send_to_connection(&response);
69 
70         return true;
71     }
72 
73     auto date_str = handler.get_date();
74     auto date_str_last_modified = handler.get_date_last_modified();
75 
76     string date_if_modified_since = request->get_date_if_modified_since();
77     if (!date_if_modified_since.empty() && handler.is_modified(date_if_modified_since)) {
78         response.info.len = sw_snprintf(header_buffer,
79                                         sizeof(header_buffer),
80                                         "HTTP/1.1 304 Not Modified\r\n"
81                                         "%s"
82                                         "Date: %s\r\n"
83                                         "Last-Modified: %s\r\n"
84                                         "Server: %s\r\n\r\n",
85                                         request->keep_alive ? "Connection: keep-alive\r\n" : "",
86                                         date_str.c_str(),
87                                         date_str_last_modified.c_str(),
88                                         SW_HTTP_SERVER_SOFTWARE);
89         response.data = header_buffer;
90         send_to_connection(&response);
91 
92         return true;
93     }
94 
95     auto task = handler.get_task();
96 
97     std::set<std::string> dir_files;
98     std::string index_file = "";
99     /**
100      * if http_index_files is enabled, need to search the index file first.
101      * if the index file is found, set filename to index filename.
102      */
103     if (http_index_files && !http_index_files->empty() && handler.is_dir()) {
104         handler.get_dir_files(dir_files);
105         index_file = swoole::intersection(*http_index_files, dir_files);
106 
107         if (index_file != "" && !handler.set_filename(index_file)) {
108             return false;
109         } else if (index_file == "" && !http_autoindex) {
110             return false;
111         }
112     }
113     /**
114      * the index file was not found in the current directory,
115      * if http_autoindex is enabled, should show the list of files in the current directory.
116      */
117     if (index_file == "" && http_autoindex && handler.is_dir()) {
118         if (dir_files.empty()) {
119             handler.get_dir_files(dir_files);
120         }
121         size_t body_length = handler.get_index_page(dir_files, sw_tg_buffer()->str, sw_tg_buffer()->size);
122 
123         response.info.len = sw_snprintf(header_buffer,
124                                         sizeof(header_buffer),
125                                         "HTTP/1.1 200 OK\r\n"
126                                         "%s"
127                                         "Content-Length: %ld\r\n"
128                                         "Content-Type: text/html\r\n"
129                                         "Date: %s\r\n"
130                                         "Last-Modified: %s\r\n"
131                                         "Server: %s\r\n\r\n",
132                                         request->keep_alive ? "Connection: keep-alive\r\n" : "",
133                                         (long) body_length,
134                                         date_str.c_str(),
135                                         date_str_last_modified.c_str(),
136                                         SW_HTTP_SERVER_SOFTWARE);
137         response.data = header_buffer;
138         send_to_connection(&response);
139 
140         response.info.len = body_length;
141         response.data = sw_tg_buffer()->str;
142         send_to_connection(&response);
143         return true;
144     }
145 
146     response.info.len = sw_snprintf(header_buffer,
147                                     sizeof(header_buffer),
148                                     "HTTP/1.1 200 OK\r\n"
149                                     "%s"
150                                     "Content-Length: %ld\r\n"
151                                     "Content-Type: %s\r\n"
152                                     "Date: %s\r\n"
153                                     "Last-Modified: %s\r\n"
154                                     "Server: %s\r\n\r\n",
155                                     request->keep_alive ? "Connection: keep-alive\r\n" : "",
156                                     (long) task->length,
157                                     handler.get_mimetype(),
158                                     date_str.c_str(),
159                                     date_str_last_modified.c_str(),
160                                     SW_HTTP_SERVER_SOFTWARE);
161 
162     response.data = header_buffer;
163 
164     // Use tcp_nopush to improve sending efficiency
165     conn->socket->cork();
166 
167     // Send HTTP header
168     send_to_connection(&response);
169 
170     // Send HTTP body
171     if (task->length != 0) {
172         response.info.type = SW_SERVER_EVENT_SEND_FILE;
173         response.info.len = sizeof(*task) + task->length + 1;
174         response.data = (char *) task;
175         send_to_connection(&response);
176     }
177 
178     // Close the connection if keepalive is not used
179     if (!request->keep_alive) {
180         response.info.type = SW_SERVER_EVENT_CLOSE;
181         response.info.len = 0;
182         response.data = nullptr;
183         send_to_connection(&response);
184     }
185 
186     return true;
187 }
188 
destroy_http_request(Connection * conn)189 void Server::destroy_http_request(Connection *conn) {
190     auto request = reinterpret_cast<swoole::http_server::Request *>(conn->object);
191     if (!request) {
192         return;
193     }
194     delete request;
195     conn->object = nullptr;
196 }
197 
198 namespace http_server {
199 //-----------------------------------------------------------------
200 
get_status_message(int code)201 const char *get_status_message(int code) {
202     switch (code) {
203     case 100:
204         return "100 Continue";
205     case 101:
206         return "101 Switching Protocols";
207     case 201:
208         return "201 Created";
209     case 202:
210         return "202 Accepted";
211     case 203:
212         return "203 Non-Authoritative Information";
213     case 204:
214         return "204 No Content";
215     case 205:
216         return "205 Reset Content";
217     case 206:
218         return "206 Partial Content";
219     case 207:
220         return "207 Multi-Status";
221     case 208:
222         return "208 Already Reported";
223     case 226:
224         return "226 IM Used";
225     case 300:
226         return "300 Multiple Choices";
227     case 301:
228         return "301 Moved Permanently";
229     case 302:
230         return "302 Found";
231     case 303:
232         return "303 See Other";
233     case 304:
234         return "304 Not Modified";
235     case 305:
236         return "305 Use Proxy";
237     case 307:
238         return "307 Temporary Redirect";
239     case 400:
240         return "400 Bad Request";
241     case 401:
242         return "401 Unauthorized";
243     case 402:
244         return "402 Payment Required";
245     case 403:
246         return "403 Forbidden";
247     case 404:
248         return "404 Not Found";
249     case 405:
250         return "405 Method Not Allowed";
251     case 406:
252         return "406 Not Acceptable";
253     case 407:
254         return "407 Proxy Authentication Required";
255     case 408:
256         return "408 Request Timeout";
257     case 409:
258         return "409 Conflict";
259     case 410:
260         return "410 Gone";
261     case 411:
262         return "411 Length Required";
263     case 412:
264         return "412 Precondition Failed";
265     case 413:
266         return "413 Request Entity Too Large";
267     case 414:
268         return "414 Request URI Too Long";
269     case 415:
270         return "415 Unsupported Media Type";
271     case 416:
272         return "416 Requested Range Not Satisfiable";
273     case 417:
274         return "417 Expectation Failed";
275     case 418:
276         return "418 I'm a teapot";
277     case 421:
278         return "421 Misdirected Request";
279     case 422:
280         return "422 Unprocessable Entity";
281     case 423:
282         return "423 Locked";
283     case 424:
284         return "424 Failed Dependency";
285     case 426:
286         return "426 Upgrade Required";
287     case 428:
288         return "428 Precondition Required";
289     case 429:
290         return "429 Too Many Requests";
291     case 431:
292         return "431 Request Header Fields Too Large";
293     case 500:
294         return "500 Internal Server Error";
295     case 501:
296         return "501 Method Not Implemented";
297     case 502:
298         return "502 Bad Gateway";
299     case 503:
300         return "503 Service Unavailable";
301     case 504:
302         return "504 Gateway Timeout";
303     case 505:
304         return "505 HTTP Version Not Supported";
305     case 506:
306         return "506 Variant Also Negotiates";
307     case 507:
308         return "507 Insufficient Storage";
309     case 508:
310         return "508 Loop Detected";
311     case 510:
312         return "510 Not Extended";
313     case 511:
314         return "511 Network Authentication Required";
315     case 200:
316     default:
317         return "200 OK";
318     }
319 }
320 
url_htoi(char * s)321 static int url_htoi(char *s) {
322     int value;
323     int c;
324 
325     c = ((unsigned char *) s)[0];
326     if (isupper(c)) {
327         c = tolower(c);
328     }
329     value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
330 
331     c = ((unsigned char *) s)[1];
332     if (isupper(c)) {
333         c = tolower(c);
334     }
335     value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
336 
337     return (value);
338 }
339 
340 /* return value: length of decoded string */
url_decode(char * str,size_t len)341 size_t url_decode(char *str, size_t len) {
342     char *dest = str;
343     char *data = str;
344 
345     while (len--) {
346         if (*data == '+') {
347             *dest = ' ';
348         } else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1)) && isxdigit((int) *(data + 2))) {
349             *dest = (char) url_htoi(data + 1);
350             data += 2;
351             len -= 2;
352         } else {
353             *dest = *data;
354         }
355         data++;
356         dest++;
357     }
358     *dest = '\0';
359 
360     return dest - str;
361 }
362 
url_encode(char const * str,size_t len)363 char *url_encode(char const *str, size_t len) {
364     static uchar hexchars[] = "0123456789ABCDEF";
365 
366     size_t x, y;
367     char *ret = (char *) sw_malloc(len * 3);
368 
369     for (x = 0, y = 0; len--; x++, y++) {
370         char c = str[x];
371 
372         ret[y] = c;
373         if ((c < '0' && c != '-' && c != '.') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a' && c != '_') ||
374             (c > 'z' && c != '~')) {
375             ret[y++] = '%';
376             ret[y++] = hexchars[(unsigned char) c >> 4];
377             ret[y] = hexchars[(unsigned char) c & 15];
378         }
379     }
380     ret[y] = '\0';
381 
382     do {
383         size_t size = y + 1;
384         char *tmp = (char *) sw_malloc(size);
385         memcpy(tmp, ret, size);
386         sw_free(ret);
387         ret = tmp;
388     } while (0);
389 
390     return ret;
391 }
392 
393 /**
394  * only GET/POST
395  */
get_protocol()396 int Request::get_protocol() {
397     char *p = buffer_->str;
398     char *pe = p + buffer_->length;
399 
400     if (buffer_->length < (sizeof("GET / HTTP/1.x\r\n") - 1)) {
401         return SW_ERR;
402     }
403 
404     // http method
405     if (memcmp(p, SW_STRL("GET")) == 0) {
406         method = SW_HTTP_GET;
407         p += 3;
408     } else if (memcmp(p, SW_STRL("POST")) == 0) {
409         method = SW_HTTP_POST;
410         p += 4;
411     } else if (memcmp(p, SW_STRL("PUT")) == 0) {
412         method = SW_HTTP_PUT;
413         p += 3;
414     } else if (memcmp(p, SW_STRL("PATCH")) == 0) {
415         method = SW_HTTP_PATCH;
416         p += 5;
417     } else if (memcmp(p, SW_STRL("DELETE")) == 0) {
418         method = SW_HTTP_DELETE;
419         p += 6;
420     } else if (memcmp(p, SW_STRL("HEAD")) == 0) {
421         method = SW_HTTP_HEAD;
422         p += 4;
423     } else if (memcmp(p, SW_STRL("OPTIONS")) == 0) {
424         method = SW_HTTP_OPTIONS;
425         p += 7;
426     } else if (memcmp(p, SW_STRL("COPY")) == 0) {
427         method = SW_HTTP_COPY;
428         p += 4;
429     } else if (memcmp(p, SW_STRL("LOCK")) == 0) {
430         method = SW_HTTP_LOCK;
431         p += 4;
432     } else if (memcmp(p, SW_STRL("MKCOL")) == 0) {
433         method = SW_HTTP_MKCOL;
434         p += 5;
435     } else if (memcmp(p, SW_STRL("MOVE")) == 0) {
436         method = SW_HTTP_MOVE;
437         p += 4;
438     } else if (memcmp(p, SW_STRL("PROPFIND")) == 0) {
439         method = SW_HTTP_PROPFIND;
440         p += 8;
441     } else if (memcmp(p, SW_STRL("PROPPATCH")) == 0) {
442         method = SW_HTTP_PROPPATCH;
443         p += 9;
444     } else if (memcmp(p, SW_STRL("UNLOCK")) == 0) {
445         method = SW_HTTP_UNLOCK;
446         p += 6;
447     } else if (memcmp(p, SW_STRL("REPORT")) == 0) {
448         method = SW_HTTP_REPORT;
449         p += 6;
450     } else if (memcmp(p, SW_STRL("PURGE")) == 0) {
451         method = SW_HTTP_PURGE;
452         p += 5;
453     }
454 #ifdef SW_USE_HTTP2
455     // HTTP2 Connection Preface
456     else if (memcmp(p, SW_STRL("PRI")) == 0) {
457         method = SW_HTTP_PRI;
458         if (buffer_->length >= (sizeof(SW_HTTP2_PRI_STRING) - 1) && memcmp(p, SW_STRL(SW_HTTP2_PRI_STRING)) == 0) {
459             buffer_->offset = sizeof(SW_HTTP2_PRI_STRING) - 1;
460             return SW_OK;
461         } else {
462             goto _excepted;
463         }
464     }
465 #endif
466     else {
467     _excepted:
468         excepted = 1;
469         return SW_ERR;
470     }
471 
472     // http version
473     char state = 0;
474     for (; p < pe; p++) {
475         switch (state) {
476         case 0:
477             if (isspace(*p)) {
478                 continue;
479             }
480             state = 1;
481             url_offset_ = p - buffer_->str;
482             break;
483         case 1:
484             if (isspace(*p)) {
485                 state = 2;
486                 url_length_ = p - buffer_->str - url_offset_;
487                 continue;
488             }
489             break;
490         case 2:
491             if (isspace(*p)) {
492                 continue;
493             }
494             if ((size_t)(pe - p) < (sizeof("HTTP/1.x") - 1)) {
495                 return SW_ERR;
496             }
497             if (memcmp(p, SW_STRL("HTTP/1.1")) == 0) {
498                 version = SW_HTTP_VERSION_11;
499                 goto _end;
500             } else if (memcmp(p, SW_STRL("HTTP/1.0")) == 0) {
501                 version = SW_HTTP_VERSION_10;
502                 goto _end;
503             } else {
504                 goto _excepted;
505             }
506         default:
507             break;
508         }
509     }
510 _end:
511     p += sizeof("HTTP/1.x") - 1;
512     request_line_length_ = buffer_->offset = p - buffer_->str;
513     return SW_OK;
514 }
515 
516 /**
517  * simple get headers info
518  */
parse_header_info()519 void Request::parse_header_info() {
520     // header field start
521     char *p = buffer_->str + request_line_length_ + (sizeof("\r\n") - 1);
522     // point-end: start + strlen(all-header) without strlen("\r\n\r\n")
523     char *pe = buffer_->str + header_length_ - (sizeof("\r\n\r\n") - 1);
524 
525     for (; p < pe; p++) {
526         if (*(p - 1) == '\n' && *(p - 2) == '\r') {
527             if (SW_STRCASECT(p, pe - p, "Content-Length:")) {
528                 unsigned long long content_length;
529                 // strlen("Content-Length:")
530                 p += (sizeof("Content-Length:") - 1);
531                 // skip spaces
532                 while (*p == ' ') {
533                     p++;
534                 }
535                 content_length = strtoull(p, nullptr, 10);
536                 content_length_ = SW_MIN(content_length, UINT32_MAX);
537                 known_length = 1;
538             } else if (SW_STRCASECT(p, pe - p, "Connection:")) {
539                 // strlen("Connection:")
540                 p += (sizeof("Connection:") - 1);
541                 // skip spaces
542                 while (*p == ' ') {
543                     p++;
544                 }
545                 if (SW_STRCASECT(p, pe - p, "keep-alive")) {
546                     keep_alive = 1;
547                 }
548             } else if (SW_STRCASECT(p, pe - p, "Transfer-Encoding:")) {
549                 // strlen("Transfer-Encoding:")
550                 p += (sizeof("Transfer-Encoding:") - 1);
551                 // skip spaces
552                 while (*p == ' ') {
553                     p++;
554                 }
555                 if (SW_STRCASECT(p, pe - p, "chunked")) {
556                     chunked = 1;
557                 }
558             }
559         }
560     }
561 
562     header_parsed = 1;
563     if (chunked && known_length && content_length_ == 0) {
564         nobody_chunked = 1;
565     }
566 }
567 
568 #ifdef SW_HTTP_100_CONTINUE
has_expect_header()569 bool Request::has_expect_header() {
570     // char *buf = buffer->str + buffer->offset;
571     char *buf = buffer_->str;
572     // int len = buffer->length - buffer->offset;
573     int len = buffer_->length;
574 
575     char *pe = buf + len;
576     char *p;
577 
578     for (p = buf; p < pe; p++) {
579         if (*p == '\r' && pe - p > sizeof("\r\nExpect")) {
580             p += 2;
581             if (SW_STRCASECT(p, pe - p, "Expect: ")) {
582                 p += sizeof("Expect: ") - 1;
583                 if (SW_STRCASECT(p, pe - p, "100-continue")) {
584                     return true;
585                 } else {
586                     return false;
587                 }
588             } else {
589                 p++;
590             }
591         }
592     }
593     return false;
594 }
595 #endif
596 
get_header_length()597 int Request::get_header_length() {
598     char *p = buffer_->str + buffer_->offset;
599     char *pe = buffer_->str + buffer_->length;
600 
601     for (; p <= pe - (sizeof("\r\n\r\n") - 1); p++) {
602         if (memcmp(p, SW_STRL("\r\n\r\n")) == 0) {
603             // strlen(header) + strlen("\r\n\r\n")
604             header_length_ = buffer_->offset = p - buffer_->str + (sizeof("\r\n\r\n") - 1);
605             return SW_OK;
606         }
607     }
608 
609     buffer_->offset = p - buffer_->str;
610     return SW_ERR;
611 }
612 
get_chunked_body_length()613 int Request::get_chunked_body_length() {
614     char *p = buffer_->str + buffer_->offset;
615     char *pe = buffer_->str + buffer_->length;
616 
617     while (1) {
618         if ((size_t)(pe - p) < (1 + (sizeof("\r\n") - 1))) {
619             /* need the next chunk */
620             return SW_ERR;
621         }
622         char *head = p;
623         size_t n_parsed;
624         size_t chunk_length = swoole_hex2dec(head, &n_parsed);
625         head += n_parsed;
626         if (*head != '\r') {
627             excepted = 1;
628             return SW_ERR;
629         }
630         p = head + (sizeof("\r\n") - 1) + chunk_length + (sizeof("\r\n") - 1);
631         /* used to check package_max_length */
632         content_length_ = p - (buffer_->str + header_length_);
633         if (p > pe) {
634             /* need recv chunk body again */
635             return SW_ERR;
636         }
637         buffer_->offset = p - buffer_->str;
638         if (chunk_length == 0) {
639             break;
640         }
641     }
642     known_length = 1;
643 
644     return SW_OK;
645 }
646 
get_date_if_modified_since()647 string Request::get_date_if_modified_since() {
648     char *p = buffer_->str + url_offset_ + url_length_ + 10;
649     char *pe = buffer_->str + header_length_;
650 
651     string result;
652 
653     char *date_if_modified_since = nullptr;
654     size_t length_if_modified_since = 0;
655 
656     int state = 0;
657     for (; p < pe; p++) {
658         switch (state) {
659         case 0:
660             if (SW_STRCASECT(p, pe - p, "If-Modified-Since")) {
661                 p += sizeof("If-Modified-Since");
662                 state = 1;
663             }
664             break;
665         case 1:
666             if (!isspace(*p)) {
667                 date_if_modified_since = p;
668                 state = 2;
669             }
670             break;
671         case 2:
672             if (SW_STRCASECT(p, pe - p, "\r\n")) {
673                 length_if_modified_since = p - date_if_modified_since;
674                 return string(date_if_modified_since, length_if_modified_since);
675             }
676             break;
677         default:
678             break;
679         }
680     }
681 
682     return string("");
683 }
684 
get_method(const char * method_str,size_t method_len)685 int get_method(const char *method_str, size_t method_len) {
686     int i = 0;
687     for (; i < SW_HTTP_PRI; i++) {
688         if (swoole_strcaseeq(method_strings[i], strlen(method_strings[i]), method_str, method_len)) {
689             return i + 1;
690         }
691     }
692     return -1;
693 }
694 
get_method_string(int method)695 const char *get_method_string(int method) {
696     if (method < 0 || method > SW_HTTP_PRI) {
697         return nullptr;
698     }
699     return method_strings[method - 1];
700 }
701 
702 //-----------------------------------------------------------------
703 
704 #ifdef SW_USE_HTTP2
705 
protocol_status_error(Socket * socket,Connection * conn)706 static void protocol_status_error(Socket *socket, Connection *conn) {
707     swoole_error_log(SW_LOG_WARNING,
708                      SW_ERROR_PROTOCOL_ERROR,
709                      "unexpected protocol status of session#%ld<%s:%d>",
710                      conn->session_id,
711                      conn->info.get_ip(),
712                      conn->info.get_port());
713 }
714 
get_package_length(const Protocol * protocol,Socket * socket,PacketLength * pl)715 ssize_t get_package_length(const Protocol *protocol, Socket *socket, PacketLength *pl) {
716     Connection *conn = (Connection *) socket->object;
717     if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {
718         return websocket::get_package_length(protocol, socket, pl);
719     } else if (conn->http2_stream) {
720         return http2::get_frame_length(protocol, socket, pl);
721     } else {
722         protocol_status_error(socket, conn);
723         return SW_ERR;
724     }
725 }
726 
get_package_length_size(Socket * socket)727 uint8_t get_package_length_size(Socket *socket) {
728     Connection *conn = (Connection *) socket->object;
729     if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {
730         return SW_WEBSOCKET_HEADER_LEN + SW_WEBSOCKET_MASK_LEN + sizeof(uint64_t);
731     } else if (conn->http2_stream) {
732         return SW_HTTP2_FRAME_HEADER_SIZE;
733     } else {
734         protocol_status_error(socket, conn);
735         return 0;
736     }
737 }
738 
dispatch_frame(const Protocol * proto,Socket * socket,const RecvData * rdata)739 int dispatch_frame(const Protocol *proto, Socket *socket, const RecvData *rdata) {
740     Connection *conn = (Connection *) socket->object;
741     if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {
742         return websocket::dispatch_frame(proto, socket, rdata);
743     } else if (conn->http2_stream) {
744         return Server::dispatch_task(proto, socket, rdata);
745     } else {
746         protocol_status_error(socket, conn);
747         return SW_ERR;
748     }
749 }
750 #endif
751 }  // namespace http_server
752 }  // namespace swoole
753