Чтение HTTP POST Body с использованием C ++ boost :: asio :: async_read_until - PullRequest
0 голосов
/ 20 февраля 2020

Я пытаюсь написать свой собственный HTTP-сервер, используя библиотеку boost :: asio для учебных целей. Я могу запустить сервер и просто отправить запрос GET (строка 40 stdout: GET /index.html HTTP / 1.1), но когда я делаю запрос POST, я также хочу прочитать данные, но опять же, строка 40 печатает только : POST /index.html HTTP / 1.1 (данные не включены). Как я могу прочитать все, включая данные?

Сервис. cpp

#include "service.h"

Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock) :
    _sock(sock),
    _request(4096),
    _response_status_code(200),
    _resource_size_bytes(0)
{
    std::cout << "[Service Constructor] Created an instance of service constructor." << std::endl;
}

void Service::start_handling() {
    std::cout << "[Service start_handling()] Triggered!" << std::endl;

    // LOOK HERE :)
    asio::async_read_until(*_sock.get(), _request, "\r\n\r\n",[this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
        on_request_line_received(ec, bytes_transferred);        
    });
}


void Service::on_request_line_received(const boost::system::error_code& ec, std::size_t bytes_transferred) {

    if (ec != 0) {
        std::cout << "Error. Code: " << ec.value() << " Message: " << ec.message() << std::endl;

        if (ec == asio::error::not_found) {
            _response_status_code = 413;
            send_response();
            return;
        } else {
            on_finish();
            return;
        }
    }

    std::string request_line;
    std::istream request_stream(&_request);
    std::getline(request_stream, request_line);

    std::cout << "request_line: " << request_line << std::endl; // GET /index.html HTTP/1.1

    request_stream.get(); // Remove '\n' from the buffer

    // Parse requested line
    std::string request_method;
    std::istringstream request_line_stream(request_line);
    request_line_stream >> request_method;

    // We only support GET method
    if (request_method.compare("GET") != 0) {
        _response_status_code = 501;
        send_response();
        return;
    }

    request_line_stream >> _requested_resource;

    std::string request_http_version;
    request_line_stream >> request_http_version;

    if (request_http_version.compare("HTTP/1.1") != 0) {
        _response_status_code = 505;
        send_response();
        return;
    }

    asio::async_read_until(*_sock.get(), _request, "\r\n\r\n", [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
        on_headers_received(ec, bytes_transferred);     
    });

    return;
}

void Service::on_headers_received(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    if (ec != 0) {
        std::cout << "Error. Code: " << ec.value() << " Message: " << ec.message();

        if (ec == asio::error::not_found) {
            _response_status_code = 413;
            send_response();
            return;
        } else {
            on_finish();
            return;
        }
    }

    std::istream request_stream(&_request);
    std::string header_name, header_value;

    while (!request_stream.eof()) {
        std::getline(request_stream, header_name, ':');
        if (!request_stream.eof()) {
            std::getline(request_stream, header_value, '\r');
            request_stream.get();
            _request_headers[header_name] = header_value;
        }
    }

    // Now we need to process the request
    process_request();
    send_response();

    return;
}

void Service::process_request() {
    std::string resource_file_path = std::string("/home/robin/Programming/Boost_Asio_Tutorials/HTTPServer/html_files" + _requested_resource);

    if (!boost::filesystem::exists(resource_file_path)) {
        _response_status_code = 404;
        return;
    }

    std::ifstream resource_fstream(resource_file_path, std::ifstream::binary);

    if (!resource_fstream.is_open()) {
        _response_status_code = 500;
        return;
    }

    resource_fstream.seekg(0, std::ifstream::end);
    _resource_size_bytes = static_cast<std::size_t>(resource_fstream.tellg());

    _resource_buffer.reset(new char[_resource_size_bytes]);

    resource_fstream.seekg(std::ifstream::beg);
    resource_fstream.read(_resource_buffer.get(), _resource_size_bytes);

    _response_headers += std::string("content-length") + ": " + std::to_string(_resource_size_bytes) + "\r\n";
}

void Service::send_response() {
    _sock->shutdown(asio::ip::tcp::socket::shutdown_receive);

    auto status_line = http_status_table.at(_response_status_code);

    _response_status_line = std::string("HTTP/1.1 ") + status_line + "\r\n";

    _response_headers += "\r\n";

    std::vector<asio::const_buffer> response_buffers;
    response_buffers.push_back(asio::buffer(_response_status_line));

    if (_response_headers.length() > 0) {
        response_buffers.push_back(asio::buffer(_response_headers));
    }

    if (_resource_size_bytes > 0) {
        response_buffers.push_back(asio::buffer(_resource_buffer.get(), _resource_size_bytes));
    }

    asio::async_write(*_sock.get(), response_buffers, [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
        on_response_sent(ec, bytes_transferred);     
    });

}

void Service::on_response_sent(const boost::system::error_code& ec, std::size_t bytes_transferred) {
    if (ec != 0) {
        std::cout << "Error. Code: " << ec.value() << " Message: " << ec.message();
    }

    _sock->shutdown(asio::ip::tcp::socket::shutdown_both);

    on_finish();
}

void Service::on_finish() {
    delete this;
}

Уведомление asio::async_read_until(*_sock.get(), _request, "\r\n\r\n".... Вот полный код: https://wandbox.org/permlink/Xvoo5Zm1Y2LZag3S

Читайте, пока "\ r \ n \ r \ n" не будет правильным, верно? Я также захватил пакет, используя wireshark, чтобы убедиться:

enter image description here

...