HEX
Server: Apache/2.4.59 (Debian)
System: Linux keymana 4.19.0-21-cloud-amd64 #1 SMP Debian 4.19.249-2 (2022-06-30) x86_64
User: lijunjie (1003)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //home/lijunjie/swoole-cli/swoole-src-4.8.13/src/server/port.cc
/*
  +----------------------------------------------------------------------+
  | Swoole                                                               |
  +----------------------------------------------------------------------+
  | This source file is subject to version 2.0 of the Apache license,    |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.apache.org/licenses/LICENSE-2.0.html                      |
  | If you did not receive a copy of the Apache2.0 license and are unable|
  | to obtain it through the world-wide-web, please send a note to       |
  | license@swoole.com so we can mail you a copy immediately.            |
  +----------------------------------------------------------------------+
  | Author: Tianfeng Han  <rango@swoole.com>                             |
  +----------------------------------------------------------------------+
*/

#include "swoole_server.h"
#include "swoole_http.h"
#include "swoole_http2.h"
#include "swoole_websocket.h"
#include "swoole_mqtt.h"
#include "swoole_redis.h"

using swoole::http_server::Request;
using swoole::network::Address;
using swoole::network::Socket;

namespace swoole {

static int Port_onRead_raw(Reactor *reactor, ListenPort *lp, Event *event);
static int Port_onRead_check_length(Reactor *reactor, ListenPort *lp, Event *event);
static int Port_onRead_check_eof(Reactor *reactor, ListenPort *lp, Event *event);
static int Port_onRead_http(Reactor *reactor, ListenPort *lp, Event *event);
static int Port_onRead_redis(Reactor *reactor, ListenPort *lp, Event *event);

ListenPort::ListenPort() {
    protocol.package_length_type = 'N';
    protocol.package_length_size = 4;
    protocol.package_body_offset = 4;
    protocol.package_max_length = SW_INPUT_BUFFER_SIZE;

    protocol.package_eof_len = sizeof(SW_DATA_EOF) - 1;
    memcpy(protocol.package_eof, SW_DATA_EOF, protocol.package_eof_len);
}

#ifdef SW_USE_OPENSSL

bool ListenPort::ssl_add_sni_cert(const std::string &name, SSLContext *ctx) {
    if (!ssl_create_context(ctx)) {
        return false;
    }
    sni_contexts.emplace(name, std::shared_ptr<SSLContext>(ctx));
    return true;
}

static bool ssl_matches_wildcard_name(const char *subjectname, const char *certname) {
    const char *wildcard = NULL;
    ptrdiff_t prefix_len;
    size_t suffix_len, subject_len;

    if (strcasecmp(subjectname, certname) == 0) {
        return 1;
    }

    /* wildcard, if present, must only be present in the left-most component */
    if (!(wildcard = strchr(certname, '*')) || memchr(certname, '.', wildcard - certname)) {
        return 0;
    }

    /* 1) prefix, if not empty, must match subject */
    prefix_len = wildcard - certname;
    if (prefix_len && strncasecmp(subjectname, certname, prefix_len) != 0) {
        return 0;
    }

    suffix_len = strlen(wildcard + 1);
    subject_len = strlen(subjectname);
    if (suffix_len <= subject_len) {
        /* 2) suffix must match
         * 3) no . between prefix and suffix
         **/
        return strcasecmp(wildcard + 1, subjectname + subject_len - suffix_len) == 0 &&
               memchr(subjectname + prefix_len, '.', subject_len - suffix_len - prefix_len) == NULL;
    }

    return 0;
}

static int ssl_server_sni_callback(SSL *ssl, int *al, void *arg) {
    const char *server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
    if (!server_name) {
        return SSL_TLSEXT_ERR_NOACK;
    }

    ListenPort *port = (ListenPort *) SSL_get_ex_data(ssl, swoole_ssl_get_ex_port_index());

    if (port->sni_contexts.empty()) {
        return SSL_TLSEXT_ERR_NOACK;
    }

    for (auto i = port->sni_contexts.begin(); i != port->sni_contexts.end(); i++) {
        if (ssl_matches_wildcard_name(server_name, i->first.c_str())) {
            SSL_set_SSL_CTX(ssl, i->second->get_context());
            return SSL_TLSEXT_ERR_OK;
        }
    }

    return SSL_TLSEXT_ERR_NOACK;
}

bool ListenPort::ssl_init() {
    if (!ssl_create_context(ssl_context)) {
        return false;
    }
    if (sni_contexts.size() > 0) {
        SSL_CTX_set_tlsext_servername_callback(ssl_context->get_context(), ssl_server_sni_callback);
    }
    return true;
}

bool ListenPort::ssl_create(Connection *conn, Socket *sock) {
    if (sock->ssl_create(ssl_context, SW_SSL_SERVER) < 0) {
        return false;
    }
    conn->ssl = 1;
    if (SSL_set_ex_data(sock->ssl, swoole_ssl_get_ex_port_index(), this) == 0) {
        swoole_warning("SSL_set_ex_data() failed");
        return false;
    }
    return true;
}

bool ListenPort::ssl_create_context(SSLContext *context) {
    if (context->cert_file.empty() || context->key_file.empty()) {
        swoole_warning("SSL error, require ssl_cert_file and ssl_key_file");
        return false;
    }
    if (open_http_protocol) {
        context->http = 1;
    }
    if (open_http2_protocol) {
        context->http_v2 = 1;
    }
    if (!context->create()) {
        swoole_warning("swSSL_get_context() error");
        return false;
    }
    return true;
}
#endif

int ListenPort::listen() {
    // listen stream socket
    if (!listening && socket->listen(backlog) < 0) {
        swoole_sys_warning("listen(%s:%d, %d) failed", host.c_str(), port, backlog);
        return SW_ERR;
    }
    listening = true;

#ifdef TCP_DEFER_ACCEPT
    if (tcp_defer_accept) {
        if (socket->set_option(IPPROTO_TCP, TCP_DEFER_ACCEPT, tcp_defer_accept) != 0) {
            swoole_sys_warning("setsockopt(TCP_DEFER_ACCEPT) failed");
        }
    }
#endif

#ifdef SO_ACCEPTFILTER
    if (tcp_defer_accept) {
        struct accept_filter_arg a;
        memset(&a, 0, sizeof(a));
        strcpy(a.af_name, "httpready");
        if (socket->set_option(SOL_SOCKET, SO_ACCEPTFILTER, &a, sizeof(a)) != 0) {
            swoole_sys_warning("setsockopt(SO_ACCEPTFILTER) failed");
        }
    }
#endif

#ifdef TCP_FASTOPEN
    if (tcp_fastopen) {
        if (socket->set_option(IPPROTO_TCP, TCP_FASTOPEN, tcp_fastopen) != 0) {
            swoole_sys_warning("setsockopt(TCP_FASTOPEN) failed");
        }
    }
#endif

#ifdef SO_KEEPALIVE
    if (open_tcp_keepalive == 1) {
        if (socket->set_option(SOL_SOCKET, SO_KEEPALIVE, 1) != 0) {
            swoole_sys_warning("setsockopt(SO_KEEPALIVE) failed");
        }
#ifdef TCP_KEEPIDLE
        if (socket->set_option(IPPROTO_TCP, TCP_KEEPIDLE, tcp_keepidle) < 0) {
            swoole_sys_warning("setsockopt(TCP_KEEPIDLE) failed");
        }
        if (socket->set_option(IPPROTO_TCP, TCP_KEEPINTVL, tcp_keepinterval) < 0) {
            swoole_sys_warning("setsockopt(TCP_KEEPINTVL) failed");
        }
        if (socket->set_option(IPPROTO_TCP, TCP_KEEPCNT, tcp_keepcount) < 0) {
            swoole_sys_warning("setsockopt(TCP_KEEPCNT) failed");
        }
#endif
#ifdef TCP_USER_TIMEOUT
        if (tcp_user_timeout > 0 && socket->set_option(IPPROTO_TCP, TCP_USER_TIMEOUT, tcp_user_timeout) != 0) {
            swoole_sys_warning("setsockopt(TCP_USER_TIMEOUT) failed");
        }
#endif
    }
#endif

    buffer_high_watermark = socket_buffer_size * 0.8;
    buffer_low_watermark = 0;

    return SW_OK;
}

void Server::init_port_protocol(ListenPort *ls) {
    ls->protocol.private_data_2 = this;
    // Thread mode must copy the data.
    // will free after onFinish
    if (ls->open_eof_check) {
        if (ls->protocol.package_eof_len > SW_DATA_EOF_MAXLEN) {
            ls->protocol.package_eof_len = SW_DATA_EOF_MAXLEN;
        }
        ls->protocol.onPackage = Server::dispatch_task;
        ls->onRead = Port_onRead_check_eof;
    } else if (ls->open_length_check) {
        if (ls->protocol.package_length_type != '\0') {
            ls->protocol.get_package_length = Protocol::default_length_func;
        }
        ls->protocol.onPackage = Server::dispatch_task;
        ls->onRead = Port_onRead_check_length;
    } else if (ls->open_http_protocol) {
#ifdef SW_USE_HTTP2
        if (ls->open_http2_protocol && ls->open_websocket_protocol) {
            ls->protocol.get_package_length = http_server::get_package_length;
            ls->protocol.get_package_length_size = http_server::get_package_length_size;
            ls->protocol.onPackage = http_server::dispatch_frame;
        } else if (ls->open_http2_protocol) {
            ls->protocol.package_length_size = SW_HTTP2_FRAME_HEADER_SIZE;
            ls->protocol.get_package_length = http2::get_frame_length;
            ls->protocol.onPackage = Server::dispatch_task;
        } else
#endif
            if (ls->open_websocket_protocol) {
            ls->protocol.package_length_size = SW_WEBSOCKET_MESSAGE_HEADER_SIZE;
            ls->protocol.get_package_length = websocket::get_package_length;
            ls->protocol.onPackage = websocket::dispatch_frame;
        }
        ls->protocol.package_length_offset = 0;
        ls->protocol.package_body_offset = 0;
        ls->onRead = Port_onRead_http;
    } else if (ls->open_mqtt_protocol) {
        mqtt::set_protocol(&ls->protocol);
        ls->protocol.onPackage = Server::dispatch_task;
        ls->onRead = Port_onRead_check_length;
    } else if (ls->open_redis_protocol) {
        ls->protocol.onPackage = Server::dispatch_task;
        ls->onRead = Port_onRead_redis;
    } else {
        ls->onRead = Port_onRead_raw;
    }
}

/**
 * @description: import listen port from socket-fd
 */
bool ListenPort::import(int sock) {
    int _type, _family;

    socket = new Socket();
    socket->fd = sock;

    // get socket type
    if (socket->get_option(SOL_SOCKET, SO_TYPE, &_type) < 0) {
        swoole_sys_warning("getsockopt(%d, SOL_SOCKET, SO_TYPE) failed", sock);
        return false;
    }
    if (socket->get_name(&socket->info) < 0) {
        swoole_sys_warning("getsockname(%d) failed", sock);
        return false;
    }

    _family = socket->info.addr.ss.sa_family;
    socket->socket_type = socket->info.type = type = Socket::convert_to_type(_family, _type);
    host = socket->info.get_addr();
    port = socket->info.get_port();
    listening = true;

    socket->fd_type = socket->is_dgram() ? SW_FD_DGRAM_SERVER : SW_FD_STREAM_SERVER;
    socket->removed = 1;

    return true;
}

void ListenPort::clear_protocol() {
    open_eof_check = 0;
    open_length_check = 0;
    open_http_protocol = 0;
    open_websocket_protocol = 0;
#ifdef SW_USE_HTTP2
    open_http2_protocol = 0;
#endif
    open_mqtt_protocol = 0;
    open_redis_protocol = 0;
}

static int Port_onRead_raw(Reactor *reactor, ListenPort *port, Event *event) {
    ssize_t n;
    Socket *_socket = event->socket;
    Connection *conn = (Connection *) _socket->object;
    Server *serv = (Server *) reactor->ptr;
    RecvData rdata{};

    String *buffer = serv->get_recv_buffer(_socket);
    if (!buffer) {
        return SW_ERR;
    }

    n = _socket->recv(buffer->str, buffer->size, 0);
    if (n < 0) {
        switch (_socket->catch_read_error(errno)) {
        case SW_ERROR:
            swoole_sys_warning("recv from connection#%d failed", event->fd);
            return SW_OK;
        case SW_CLOSE:
            conn->close_errno = errno;
            goto _close_fd;
        default:
            return SW_OK;
        }
    } else if (n == 0) {
    _close_fd:
        reactor->trigger_close_event(event);
        return SW_OK;
    } else {
        buffer->offset = buffer->length = n;
        rdata.info.len = n;
        rdata.data = buffer->str;
        return Server::dispatch_task(&port->protocol, _socket, &rdata);
    }
}

static int Port_onRead_check_length(Reactor *reactor, ListenPort *port, Event *event) {
    Socket *_socket = event->socket;
    Connection *conn = (Connection *) _socket->object;
    Protocol *protocol = &port->protocol;
    Server *serv = (Server *) reactor->ptr;

    String *buffer = serv->get_recv_buffer(_socket);
    if (!buffer) {
        reactor->trigger_close_event(event);
        return SW_ERR;
    }

    if (protocol->recv_with_length_protocol(_socket, buffer) < 0) {
        swoole_trace("Close Event.FD=%d|From=%d", event->fd, event->reactor_id);
        conn->close_errno = errno;
        reactor->trigger_close_event(event);
    }

    /**
     * if the length is 0, which means the onPackage has been called, we can free the buffer.
     */
    if (_socket->recv_buffer && _socket->recv_buffer->length == 0 &&
        _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) {
        delete _socket->recv_buffer;
        _socket->recv_buffer = nullptr;
    }

    return SW_OK;
}

#define CLIENT_INFO_FMT " from session#%ld on %s:%d"
#define CLIENT_INFO_ARGS conn->session_id, port->host.c_str(), port->port

/**
 * For Http Protocol
 */
static int Port_onRead_http(Reactor *reactor, ListenPort *port, Event *event) {
    Socket *_socket = event->socket;
    Connection *conn = (Connection *) _socket->object;
    Server *serv = (Server *) reactor->ptr;
    RecvData dispatch_data{};

    if (conn->websocket_status >= websocket::STATUS_HANDSHAKE) {
        if (conn->http_upgrade == 0) {
            serv->destroy_http_request(conn);
            conn->websocket_status = websocket::STATUS_ACTIVE;
            conn->http_upgrade = 1;
        }
        return Port_onRead_check_length(reactor, port, event);
    }

#ifdef SW_USE_HTTP2
    if (conn->http2_stream) {
        return Port_onRead_check_length(reactor, port, event);
    }
#endif

    Request *request = nullptr;
    Protocol *protocol = &port->protocol;

    if (conn->object == nullptr) {
        request = new Request();
        conn->object = request;
    } else {
        request = reinterpret_cast<Request *>(conn->object);
    }

    if (!request->buffer_) {
        request->buffer_ = serv->get_recv_buffer(_socket);
        if (!request->buffer_) {
            reactor->trigger_close_event(event);
            return SW_ERR;
        }
    }

    String *buffer = request->buffer_;

_recv_data:
    ssize_t n = _socket->recv(buffer->str + buffer->length, buffer->size - buffer->length, 0);
    if (n < 0) {
        switch (_socket->catch_read_error(errno)) {
        case SW_ERROR:
            swoole_sys_warning("recv from connection#%d failed", event->fd);
            return SW_OK;
        case SW_CLOSE:
            conn->close_errno = errno;
            goto _close_fd;
        default:
            return SW_OK;
        }
    }

    if (n == 0) {
        if (0) {
        _bad_request:
#ifdef SW_HTTP_BAD_REQUEST_PACKET
            _socket->send(SW_STRL(SW_HTTP_BAD_REQUEST_PACKET), 0);
#endif
        }
        if (0) {
        _too_large:
#ifdef SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET
            _socket->send(SW_STRL(SW_HTTP_REQUEST_ENTITY_TOO_LARGE_PACKET), 0);
#endif
        }
        if (0) {
        _unavailable:
#ifdef SW_HTTP_SERVICE_UNAVAILABLE_PACKET
            _socket->send(SW_STRL(SW_HTTP_SERVICE_UNAVAILABLE_PACKET), 0);
#endif
        }
    _close_fd:
        serv->destroy_http_request(conn);
        reactor->trigger_close_event(event);
        return SW_OK;
    }

    buffer->length += n;

_parse:
    if (request->method == 0 && request->get_protocol() < 0) {
        if (!request->excepted && buffer->length < SW_HTTP_HEADER_MAX_SIZE) {
            return SW_OK;
        }
        swoole_error_log(SW_LOG_TRACE,
                         SW_ERROR_HTTP_INVALID_PROTOCOL,
                         "Bad Request: unknown protocol" CLIENT_INFO_FMT,
                         CLIENT_INFO_ARGS);
        goto _bad_request;
    }

    if (request->method > SW_HTTP_PRI) {
        swoole_error_log(SW_LOG_TRACE,
                         SW_ERROR_HTTP_INVALID_PROTOCOL,
                         "Bad Request: unknown HTTP method" CLIENT_INFO_FMT,
                         CLIENT_INFO_ARGS);
        goto _bad_request;
    } else if (request->method == SW_HTTP_PRI) {
#ifdef SW_USE_HTTP2
        if (sw_unlikely(!port->open_http2_protocol)) {
#endif
            swoole_error_log(SW_LOG_TRACE,
                             SW_ERROR_HTTP_INVALID_PROTOCOL,
                             "Bad Request: can not handle HTTP2 request" CLIENT_INFO_FMT,
                             CLIENT_INFO_ARGS);
            goto _bad_request;
#ifdef SW_USE_HTTP2
        }
        conn->http2_stream = 1;
        http2::send_setting_frame(protocol, _socket);
        if (buffer->length == sizeof(SW_HTTP2_PRI_STRING) - 1) {
            serv->destroy_http_request(conn);
            buffer->clear();
            return SW_OK;
        }
        buffer->reduce(buffer->offset);
        serv->destroy_http_request(conn);
        conn->socket->skip_recv = 1;
        return Port_onRead_check_length(reactor, port, event);
#endif
    }

    // http header is not the end
    if (request->header_length_ == 0) {
        if (request->get_header_length() < 0) {
            if (buffer->size == buffer->length) {
                swoole_error_log(SW_LOG_TRACE,
                                 SW_ERROR_HTTP_INVALID_PROTOCOL,
                                 "Bad Request: request header size is too large" CLIENT_INFO_FMT,
                                 CLIENT_INFO_ARGS);
                goto _bad_request;
            }
            goto _recv_data;
        }
    }

    // parse http header and got http body length
    if (!request->header_parsed) {
        request->parse_header_info();
        swoole_trace_log(SW_TRACE_SERVER,
                         "content-length=%" PRIu64 ", keep-alive=%u, chunked=%u",
                         request->content_length_,
                         request->keep_alive,
                         request->chunked);
    }

    // content length (equal to 0) or (field not found but not chunked)
    if (!request->tried_to_dispatch) {
        // recv nobody_chunked eof
        if (request->nobody_chunked) {
            if (buffer->length < request->header_length_ + (sizeof(SW_HTTP_CHUNK_EOF) - 1)) {
                goto _recv_data;
            }
            request->header_length_ += (sizeof("0\r\n\r\n") - 1);
        }
        request->tried_to_dispatch = 1;
        // (know content-length is equal to 0) or (no content-length field and no chunked)
        if (request->content_length_ == 0 && (request->known_length || !request->chunked)) {
            buffer->offset = request->header_length_;
            // send static file content directly in the reactor thread
            if (!serv->enable_static_handler || !serv->select_static_handler(request, conn)) {
                // dynamic request, dispatch to worker
                dispatch_data.info.len = request->header_length_;
                dispatch_data.data = buffer->str;
                if (Server::dispatch_task(protocol, _socket, &dispatch_data) < 0) {
                    goto _close_fd;
                }
            }
            if (!conn->active || _socket->removed) {
                return SW_OK;
            }
            if (buffer->length > request->header_length_) {
                // http pipeline, multi requests, parse the next one
                buffer->reduce(request->header_length_);
                request->clean();
                goto _parse;
            } else {
                serv->destroy_http_request(conn);
                buffer->clear();
                return SW_OK;
            }
        }
    }

    size_t request_length;
    if (request->chunked) {
        /* unknown length, should find chunked eof */
        if (request->get_chunked_body_length() < 0) {
            if (request->excepted) {
                swoole_error_log(SW_LOG_TRACE,
                                 SW_ERROR_HTTP_INVALID_PROTOCOL,
                                 "Bad Request: protocol error when parse chunked length" CLIENT_INFO_FMT,
                                 CLIENT_INFO_ARGS);
                goto _bad_request;
            }
            request_length = request->header_length_ + request->content_length_;
            if (request_length > protocol->package_max_length) {
                swoole_error_log(SW_LOG_WARNING,
                                 SW_ERROR_HTTP_INVALID_PROTOCOL,
                                 "Request Entity Too Large: request length (chunked) has already been greater than the "
                                 "package_max_length(%u)" CLIENT_INFO_FMT,
                                 protocol->package_max_length,
                                 CLIENT_INFO_ARGS);
                goto _too_large;
            }
            if (buffer->length == buffer->size && !buffer->extend()) {
                goto _unavailable;
            }
            if (request_length > buffer->size && !buffer->extend_align(request_length)) {
                goto _unavailable;
            }
            goto _recv_data;
        } else {
            request_length = request->header_length_ + request->content_length_;
        }
        swoole_trace_log(
            SW_TRACE_SERVER, "received chunked eof, real content-length=%" PRIu64, request->content_length_);
    } else {
        request_length = request->header_length_ + request->content_length_;
        if (request_length > protocol->package_max_length) {
            swoole_error_log(SW_LOG_WARNING,
                             SW_ERROR_HTTP_INVALID_PROTOCOL,
                             "Request Entity Too Large: header-length (%u) + content-length (%" PRIu64
                             ") is greater than the "
                             "package_max_length(%u)" CLIENT_INFO_FMT,
                             request->header_length_,
                             request->content_length_,
                             protocol->package_max_length,
                             CLIENT_INFO_ARGS);
            goto _too_large;
        }

        if (request_length > buffer->size && !buffer->extend(request_length)) {
            goto _unavailable;
        }

        if (buffer->length < request_length) {
#ifdef SW_HTTP_100_CONTINUE
            // Expect: 100-continue
            if (request->has_expect_header()) {
                _socket->send(SW_STRL(SW_HTTP_100_CONTINUE_PACKET), 0);
            } else {
                swoole_trace_log(
                    SW_TRACE_SERVER,
                    "PostWait: request->content_length=%d, buffer->length=%zu, request->header_length=%d\n",
                    request->content_length,
                    buffer_->length,
                    request->header_length);
            }
#endif
            goto _recv_data;
        }
    }

    // discard the redundant data
    if (buffer->length > request_length) {
        swoole_error_log(SW_LOG_TRACE,
                         SW_ERROR_HTTP_INVALID_PROTOCOL,
                         "Invalid Request: %zu bytes has been discard" CLIENT_INFO_FMT,
                         buffer->length - request_length,
                         CLIENT_INFO_ARGS);
        buffer->length = request_length;
    }

    buffer->offset = request_length;
    dispatch_data.data = buffer->str;
    dispatch_data.info.len = buffer->length;

    if (Server::dispatch_task(protocol, _socket, &dispatch_data) < 0) {
        goto _close_fd;
    }

    if (conn->active && !_socket->removed) {
        serv->destroy_http_request(conn);
        if (_socket->recv_buffer && _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) {
            delete _socket->recv_buffer;
            _socket->recv_buffer = nullptr;
        } else {
            buffer->clear();
        }
    }

    return SW_OK;
}

static int Port_onRead_redis(Reactor *reactor, ListenPort *port, Event *event) {
    Socket *_socket = event->socket;
    Connection *conn = (Connection *) _socket->object;
    Protocol *protocol = &port->protocol;
    Server *serv = (Server *) reactor->ptr;

    String *buffer = serv->get_recv_buffer(_socket);
    if (!buffer) {
        reactor->trigger_close_event(event);
        return SW_ERR;
    }

    if (redis::recv_packet(protocol, conn, buffer) < 0) {
        conn->close_errno = errno;
        reactor->trigger_close_event(event);
    }

    return SW_OK;
}

static int Port_onRead_check_eof(Reactor *reactor, ListenPort *port, Event *event) {
    Socket *_socket = event->socket;
    Connection *conn = (Connection *) _socket->object;
    Protocol *protocol = &port->protocol;
    Server *serv = (Server *) reactor->ptr;

    String *buffer = serv->get_recv_buffer(_socket);
    if (!buffer) {
        reactor->trigger_close_event(event);
        return SW_ERR;
    }

    if (protocol->recv_with_eof_protocol(_socket, buffer) < 0) {
        conn->close_errno = errno;
        reactor->trigger_close_event(event);
    }

    // If the length is 0, which means the onPackage has been called, we can free the buffer.
    if (_socket->recv_buffer && _socket->recv_buffer->length == 0 &&
        _socket->recv_buffer->size > SW_BUFFER_SIZE_BIG * 2) {
        delete _socket->recv_buffer;
        _socket->recv_buffer = nullptr;
    }

    return SW_OK;
}

void ListenPort::close() {
#ifdef SW_USE_OPENSSL
    if (ssl) {
        if (ssl_context) {
            delete ssl_context;
        }
#ifdef SW_SUPPORT_DTLS
        if (dtls_sessions) {
            delete dtls_sessions;
        }
#endif
    }
#endif

    if (socket) {
        socket->free();
        socket = nullptr;
    }

    // remove unix socket file
    if (type == SW_SOCK_UNIX_STREAM || type == SW_SOCK_UNIX_DGRAM) {
        unlink(host.c_str());
    }
}

const char *ListenPort::get_protocols() {
    if (is_dgram()) {
        return "dgram";
    }
    if (open_eof_check) {
        return "eof";
    } else if (open_length_check) {
        return "length";
    } else if (open_http_protocol) {
#ifdef SW_USE_HTTP2
        if (open_http2_protocol && open_websocket_protocol) {
            return "http|http2|websocket";
        } else if (open_http2_protocol) {
            return "http|http2";
        } else
#endif
            if (open_websocket_protocol) {
            return "http|websocket";
        } else {
            return "http";
        }
    } else if (open_mqtt_protocol) {
        return "mqtt";
    } else if (open_redis_protocol) {
        return "redis";
    } else {
        return "raw";
    }
}

}  // namespace swoole