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