[sylar]C++高性能服务器框架——HttpConnection模块

521 阅读5分钟

HttpConnection模块概述

class HttpConnection继承自class SocketStream,发送请求报文,接收响应报文。

封装struct HttpResultHTTP响应结果

实现连接池class HttpConnectionPool,仅在长连接有效时使用。

封装class URI,使用状态机解析URI

URI详解

URI格式

foo://user@example.com:8042/over/there?name=ferret#nose
_/   ___________________/_________/ _________/ __/
 |              |              |            |        |
scheme      authority         path        query   fragment
 |   __________________________|__
/ \ /                             \
urn:example:animal:ferret:nose
​
authority   = [ userinfo "@" ] host [ ":" port ]

class Uri

再有限状态机action中设置保存解析出来的字段。

mumber(成员变量)

// schema
std::string m_scheme;
// 用户信息
std::string m_userinfo;
// host
std::string m_host;
// 路径
std::string m_path;
// 查询参数
std::string m_query;
// fragment
std::string m_fragment;
// 端口
uint16_t m_port;

Uri(构造函数)

Uri::Uri()
    :m_port(0) {
}

Create(创建URI)

静态方法

Uri::ptr Uri::Create(const std::string& uristr) {
    Uri::ptr uri(new Uri);
    int cs = 0;
    const char* mark = 0;
    // 有限状态机初始化
    %% write init;
    // URI字符串的起始位置
    const char* p = uristr.c_str();
    // 结束位置
    const char* pe = p + uristr.size();
    const char* eof = pe;
    // 执行有限状态机
    %% write exec;
    if(cs == uri_parser_error) {
        return nullptr;
      // 达到最终状态或更高
    } else if(cs >= uri_parser_first_final) {
        return uri;
    }
    return nullptr;
}

isDefaultPort(是否为默认端口:80,443)

bool Uri::isDefaultPort() const {
    if(m_port == 0) {
        return true;
    }
​
    if(m_scheme == "http") {
        return m_port == 80; 
    } else if(m_scheme == "https") {
        return m_port == 443;
    }
    return false;
}

getPort(返回端口号)

uint16_t Uri::getPort() const {
    if(m_port) {
        return m_port;
    }
    if(m_scheme == "http") {
        return 80;
    } else if(m_scheme == "https"){
        return 443;
    }
    return m_port;
}

getPath(返回路径)

const std::string& Uri::getPath() const {
    static std::string s_default_path = "/";
    return m_path.empty() ? s_default_path : m_path;
}

createAddress(创建地址)

Address::ptr Uri::createAddress() const {
    auto addr = sylar::Address::LookupAnyIPAddress(m_host);
    if(addr) {
        addr->setPort(getPort());
    }
    return addr;
}

HttpConnection详解

HttpResult

HTTP响应结果

struct HttpResult {
    typedef std::shared_ptr<HttpResult> ptr;

    enum class Error {
        /// 正常
        OK = 0,
        /// 非法URL
        INVALID_URL = 1,
        /// 无法解析HOST
        INVALID_HOST = 2,
        /// 连接失败
        CONNECT_FAIL = 3,
        /// 连接被对端关闭
        SEND_CLOSE_BY_PEER = 4,
        /// 发送请求产生Socket错误
        SEND_SOCKET_ERROR = 5,
        /// 超时
        TIMEOUT = 6,
        /// 创建Socket失败
        CREATE_SOCKET_ERROR = 7,
        /// 从连接池中取连接失败
        POOL_GET_CONNECTION = 8,
        /// 无效的连接
        POOL_INVALID_CONNECTION = 9,
    };

    HttpResult(int _result
            , HttpResponse::ptr _response
            , const std::string& _error)
        /// 错误码
        int result;
        /// HTTP响应结构体
        HttpResponse::ptr response;
        /// 错误描述
        std::string error {
    }

    std::string toString() const;
    
    // 错误码
    int result;
    // HTTP响应结构体
    HttpResponse::ptr response;
    // 错误描述
    std::string error;

};

class HttpConnection

mumber(成员变量)

// 创建时间
uint64_t m_createTime = 0;
// 请求超时时间
uint64_t m_request = 0;

HttpConnection(构造函数)

HttpConnection::HttpConnection(Socket::ptr sock, bool owern)
    :SocketStream(sock, owern) {
    
}

recvResponse(接收响应报文)

支持chunk解析

HttpResponse::ptr HttpConnection::recvResponse() {
    HttpResponseParser::ptr parser(new HttpResponseParser);
    uint64_t buff_size = HttpResponseParser::GetHttpResponseBufferSize();
    // 智能指针接管
    std::shared_ptr<char> buffer(
        new char[buff_size], [](char* ptr) {
            delete[] ptr;
        });
    char* data = buffer.get();
    int offset = 0;
    do {
        // 在offset后面接着读数据
        int len = read(data + offset, buff_size - offset);
        if (len <= 0) {
            close();
            return nullptr;
        }
        // 当前已经读取的数据长度
        len += offset;
        data[len] = '\0';
        // 解析缓冲区data中的数据
        // execute会将data向前移动nparse个字节,nparse为已经成功解析的字节数
        size_t nparse = parser->execute(data, len, false);
        if (parser->hasError()) {
            close();
            return nullptr;
        }
        // 此时data还剩下len - nparse个字节
        offset = len - nparse;
        // 缓冲区满了还没解析完
        if (offset == (int)buff_size) {
            close();
            return nullptr;
        }
        // 解析结束
        if (parser->isFinished()) {
            break;
        }
    } while (true);
    // 这里返回引用
    auto& client_parser = parser->getParser();
    // 是否为chunk
    if (client_parser.chunked) {
        std::string body;、
        // 缓冲区剩余数据
        int len = offset;
        do {
            do {
                // 继续读数据
                int rt = read(data + len, buff_size - len);
                if (rt <= 0) {
                    close();
                    return nullptr;
                }
                // 更新缓冲区数据数量
                len += rt;
                // 再末尾添加
                data[len] = '\0';
                // 解析报文,重新初始化parser
                size_t nparser = parser->execute(data, len, true);
                if (parser->hasError()) {
                    close();
                    return nullptr;
                }
                len -= nparser;
                if (len == (int)buff_size) {
                    close();
                    return nullptr;
                }
            } while (!parser->isFinished());
            len -= 2;
            // body小于缓冲区的数据
            if (client_parser.content_len <= len) {
                // 将body数据加进来
                body.append(data, client_parser.content_len);
                // 移动data
                memmove(data, data + client_parser.content_len, len - client_parser.content_len);
                // 缓冲区剩余数据数
                len -= client_parser.content_len;
            } else {
                // 将缓冲区全部数据加进来
                body.append(data, len);
                // 还剩多少body数据
                int left = client_parser.content_len - len;
                while (left > 0) {
                    // 读最多缓冲区大小的数据
                    int rt = read(data, left > (int)buff_size ? (int)buff_size : left);
                    if (rt <= 0) {
                        close();
                        return nullptr;
                    }
                    // 加到body
                    body.append(data, rt);
                    left -= rt;
                }
                len = 0;
            }
        } while (!client_parser.chunks_done); 
        parser->getData()->setBody(body);
    } else {
        // 获得body的长度
        int64_t length = parser->getContentLength();
        if (length > 0) {
            std::string body;
            body.resize(length);

            int len = 0;
            // 如果长度比缓冲区剩余的还大,将缓冲区全部加进来
            if (length >= offset) {
                mempcpy(&body[0], data, offset);
                len = offset;
            } else {
                // 否则将取length
                memcpy(&body[0], data, length);
                len = length;
            }

            length -= offset;
            // 缓冲区里的数据也不够,继续读取直到满足length
            if (length > 0) {
                if (readFixSize(&body[len], length) <= 0) {
                    close();
                    return nullptr;
                }
            }
            // 设置body
            parser->getData()->setBody(body);
        }
    }
    //返回解析完的HttpResponse
    return parser->getData();
}

sendRequest(发送请求报文)

int HttpConnection::sendRequest(HttpRequest::ptr req) {
    std::stringstream ss;
    ss << *req;
    std::string data = ss.str();
    return writeFixSize(data.c_str(), data.size());
}

DoRequest(请求)

最后所有的请求都是由这个方法实现的

HttpResult::ptr HttpConnection::DoRequest(HttpMethod method
        , Uri::ptr uri
        , uint64_t timeout_ms
        , const std::map<std::string, std::string>& headers
        , const std::string& body) {
    // 创建http请求报文
    HttpRequest::ptr req = std::make_shared<HttpRequest>();
    // 设置path
    req->setPath(uri->getPath());
    // 设置qurey
    req->setQuery(uri->getQuery());
    // 设置fragment
    req->setFragment(uri->getFragment());
    // 设置请求方法
    req->setMethod(method);
    // 是否有主机号
    bool has_host = false;
    for (auto& i : headers) {
        if (strcasecmp(i.first.c_str(), "connection") == 0) {
            if (strcasecmp(i.second.c_str(), "keep-alive") == 0) {
                req->setClose(false);
            }
            continue;
        }
		
        // 看有没有设置host
        if (!has_host && strcasecmp(i.first.c_str(), "host") == 0) {
            has_host = !i.second.empty();
        }
        // 设置header
        req->setHeader(i.first, i.second);
    }
    //若没有host,则用uri的host
    if (!has_host) {
        req->setHeader("Host", uri->getHost());
    }
    // 设置body
    req->setBody(body);
    
    return DoRequest(req, uri, timeout_ms);
}
HttpResult::ptr HttpConnection::DoRequest(HttpRequest::ptr req
        , Uri::ptr uri
        , uint64_t timeout_ms) {
    // 通过uri创建address
    Address::ptr addr = uri->createAddress();
    if (!addr) {
        return std::make_shared<HttpResult>((int)HttpResult::Error::INVAILD_HOST
            , nullptr, "invalid host: " + uri->getHost());
    }
    // 创建TCPsocket
    Socket::ptr sock = Socket::CreateTCP(addr);
    if (!sock) {
        return std::make_shared<HttpResult>((int)HttpResult::Error::CREATE_SOCKET_ERROR
            , nullptr, "create socket fail: " + addr->toString()
            + " errno=" + std::to_string(errno)
            + " errstr=" + std::string(strerror(errno)));
    }
    // 发起请求连接
    if (!sock->connect(addr)) {
        return std::make_shared<HttpResult>((int)HttpResult::Error::CONNECT_FAIL
            , nullptr, "connect fail: " + addr->toString());
    }
    // 设置接收超时时间
    sock->setRecvTimeout(timeout_ms);
    // 创建httpconnection
    HttpConnection::ptr conn = std::make_shared<HttpConnection>(sock);
    // 发送请求报文
    int rt = conn->sendRequest(req);
    // 若为0,则表示远端关闭连接
    if (rt == 0) {
        return std::make_shared<HttpResult>((int)HttpResult::Error::SEND_CLOSE_BY_PEER
            , nullptr, "send request closed by peer: " + addr->toString());
    }
    // 小于0,失败
    if (rt < 0) {
        return std::make_shared<HttpResult>((int)HttpResult::Error::SEND_SOCKET_ERROR
            , nullptr, "send request socket error, errno = " + std::to_string(errno)
            + " errstr = " + std::string(strerror(errno)));
    }
    // 接收响应报文
    auto rsp = conn->recvResponse();
    if (!rsp) {
        return std::make_shared<HttpResult>((int)HttpResult::Error::TIMEOUT
            , nullptr, "recv response fail, addr: " + addr->toString()
            + ", timeout_ms: " + std::to_string(timeout_ms));
    }
    // 结果成功,返回响应报文
    return std::make_shared<HttpResult>((int)HttpResult::Error::OK, rsp, "ok");
}

HttpConnectionPool

连接池,仅在长连接时有效,Connection: keep-alive

mumber(成员变量)

// 主机
std::string m_host;
std::string m_vhost;
// 端口号
uint16_t m_port;
// 连接最大数
uint32_t m_maxSize;
// 连接时长
uint32_t m_maxAliveTime;
// 请求时长
uint32_t m_maxRequest;

// 锁
MutexType m_mutex;
// HttpConnection指针链表
std::list<HttpConnection*> m_conns;
// 连接的数量
std::atomic<int32_t> m_total = { 0 };

HttpConnectionPool(构造函数)

HttpConnectionPool::HttpConnectionPool(const std::string& host
        , const std::string& vhost
        , uint16_t port
        , uint32_t max_size
        , uint32_t max_alive_time
        , uint32_t max_request)
    :m_host(host)
    ,m_vhost(vhost)
    ,m_port(port)
    ,m_maxSize(max_size)
    ,m_maxAliveTime(max_alive_time)
    ,m_maxRequest(max_request) {
    
}

getConnection(获得连接)

HttpConnection::ptr HttpConnectionPool::getConnection() {
    uint64_t now_ts = sylar::GetCurrentMS();
    // 非法的连接
    std::vector<HttpConnection*> invalid_conns;
    HttpConnection* ptr = nullptr;
    Mutex::Lock lock(m_mutex);
    // 若连接池不为空
    while (!m_conns.empty()) {
        // 取出第一个connection
        auto conn = *m_conns.begin();
        // 弹掉
        m_conns.pop_front();
        // 不在连接状态,放入非法vec中
        if (!conn->isConnected()) {
            invalid_conns.push_back(conn);
            continue;
        }
        // 已经超过了最大连接时间,放入非法vec中
        if ((conn->m_createTime + m_maxAliveTime) > now_ts) {
            invalid_conns.push_back(conn);
            continue;
        }
        // 获得当前connection
        ptr = conn;
        break;
    }
    lock.unlock();
    // 将非法con删除
    for (auto& i : invalid_conns) {
        delete i;
    }
    // 更新连接总数
    m_total -= invalid_conns.size();
    
    // 若没有连接
    if (!ptr) {
        // 根据host创建addr
        IPAddress::ptr addr = Address::LookupAnyIPAddress(m_host);
        if (!addr) {
            SYLAR_LOG_ERROR(g_logger) << "get addr fail: " << m_host;
            return nullptr;
        }
        // 设置端口号
        addr->setPort(m_port);
        // 创建TCPsocket
        Socket::ptr sock = Socket::CreateTCP(addr);
        if (!sock) {
            SYLAR_LOG_ERROR(g_logger) << "create sock fail: " << *addr;
            return nullptr;
        }
        // 连接
        if (!sock->connect(addr)) {
            SYLAR_LOG_ERROR(g_logger) << "sock connect fail: " << *addr;
            return nullptr;
        }
		
        ptr = new HttpConnection(sock);
        ++m_total;
    }
    
    return HttpConnection::ptr(ptr, std::bind(&HttpConnectionPool::ReleasePtr
        , std::placeholders::_1, this)); 
}

ReleasePtr(释放connection)

void HttpConnectionPool::ReleasePtr(HttpConnection* ptr, HttpConnectionPool* pool) {
    // 已经关闭了链接,超时,超过最大请求数量
    if (!ptr->isConnected()
        || ((ptr->m_createTime + pool->m_maxAliveTime) >= sylar::GetCurrentMS())
        || ptr->m_request >= pool->m_maxRequest) {
        delete ptr;
        --pool->m_total;
        return;
    }
    // 请求次数+1
    ++ptr->m_request;
    MutexType::Lock lock(pool->m_mutex);
    // 重新放入连接池中
    pool->m_conns.push_back(ptr);
}