Boost.Asio學習之簡單的HTTP伺服器的完善
阿新 • • 發佈:2019-01-01
以上鍊接裡面的原始碼至少有下面兩個問題:
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()); } }); }