1. 程式人生 > >Boost.Asio學習之簡單的HTTP伺服器的完善

Boost.Asio學習之簡單的HTTP伺服器的完善

以上鍊接裡面的原始碼至少有下面兩個問題:
1.只解析了http協議的頭部資訊(header)。
2.沒有對tcp資料包進行合併的處理,如果客戶端的tcp資料是分包發過來的,該程式碼的解析會出錯,所以這是不可預料的錯誤。

以下是修改後的部分程式碼:

1.在 request.hpp 中新增兩個成員變數

        std::string short_uri;//客戶端請求的uri,去掉‘?’後面的引數
        std::vector<header> params;//客戶端請求所帶的引數,包括get和post表單裡的引數

2.在 request_parser.cpp 裡新增成員方法 parse_param ,用來獲得表單提交的引數:

    void request_parser::parse_param(request& req, std::string& data_)
    {
        //開始解析get引數
        int index = req.uri.find_first_of("?");
        if (index >= 0)
        {
            req.short_uri = req.uri.substr(0, index);

            std::string param_str = req.uri.substr(index + 1, req.uri.size());

            std::vector<std::string> split_result;
            boost::split(split_result, param_str, boost::is_any_of("&"));

            for (int i = 0; i < split_result.size(); i++)
            {
                std::vector<std::string> split_result_temp;
                boost::split(split_result_temp, split_result.at(i), boost::is_any_of("="));
                if (split_result_temp.size() >= 2)
                {
                    req.params.push_back(header());
                    req.params.back().name = split_result_temp.at(0);
                    req.params.back().value = split_result_temp.at(1);
                }
            }
        }
        else
        {
            req.short_uri = req.uri;
        }
        //解析get引數結束

        std::string content_type;
        for (int i = 0; i < req.headers.size(); i++)
        {
            if (boost::algorithm::iequals(req.headers[i].name, "content_type"))
            {
                content_type = req.headers[i].value;
                break;
            }
        }

        int index_content_type = content_type.find_first_of(';');
        if (index_content_type > 0)
        {
            content_type = content_type.substr(0, index_content_type);
        }

        //開始解析post引數
        if (boost::algorithm::iequals(req.method, "post")
            && boost::algorithm::iequals(content_type, "multipart/form-data"))
        {
            const char* find_str = "\nContent-Disposition: form-data; name=\"";
            int index = 0;
            auto it = boost::algorithm::ifind_nth(data_, find_str, index);
            while (!it.empty())
            {
                req.params.push_back(header());
                auto it_temp = it.end();
                while (it_temp != data_.end())
                {
                    if (*it_temp == '\"')
                    {
                        break;
                    }

                    req.params.back().name.push_back(*it_temp);
                    it_temp++;
                }

                it_temp++;
                while (*it_temp == '\r' || *it_temp == '\n')
                {
                    it_temp++;
                }

                while (it_temp != data_.end())
                {
                    if (*it_temp == '\r')
                    {
                        break;
                    }

                    req.params.back().value.push_back(*it_temp);
                    it_temp++;
                }

                index++;
                it = boost::algorithm::ifind_nth(data_, find_str, index);
            }
        }
        //解析post引數結束
    }

3.在 connection.hpp 裡新增兩個成員變數:

        std::string data_;//合併tcp資料包後的資料
        bool is_header_parsed;//是否已經解析過header資訊

4.對 connection.cpp 裡的 do_read() 方法修改如下(關鍵就是修改這個方法,讓它能夠合併tcp資料包)

    void connection::do_read()
    {
        auto self(shared_from_this());
        socket_.async_read_some(boost::asio::buffer(buffer_),
            [this, self](boost::system::error_code ec, std::size_t bytes_transferred)
        {
            if (!ec)
            {
                std::string this_data(buffer_.data(), bytes_transferred);
                data_.append(this_data);

                int header_end = data_.find("\r\n\r\n");//http協議的頭部結束標識
                if (header_end < 0)
                {
                    do_read();
                    return;
                }

                request_parser::result_type result;
                if (!is_header_parsed)
                {
                    std::tie(result, std::ignore) = request_parser_.parse(
                        request_, data_.begin(), data_.end());
                }

                if (is_header_parsed || result == request_parser::good)
                {
                    is_header_parsed = true;

                    std::string length_str;
                    for (int i = 0; i < request_.headers.size(); i++)
                    {
                        if (boost::algorithm::iequals(request_.headers[i].name, "content-length"))
                        {
                            length_str = request_.headers[i].value;
                            break;
                        }
                    }

                    int content_length = 0;
                    if (!length_str.empty())
                    {
                        content_length = atoi(length_str.c_str());
                    }

                    if (data_.size() < (content_length + header_end + 4))
                    {//資料不全,繼續接受tcp資料
                        do_read();
                        return;
                    }

                    //data_ = utf8_to_ascii(data_);//

                    request_parser_.parse_param(request_, data_);//新加的方法

                    request_handler_.handle_request(request_, reply_);
                    do_write();
                }
                else if (result == request_parser::bad)
                {
                    reply_ = reply::stock_reply(reply::bad_request);
                    do_write();
                }
                else
                {
                    do_read();
                }
            }
            else if (ec != boost::asio::error::operation_aborted)
            {
                connection_manager_.stop(shared_from_this());
            }
        });
    }