如何扩展Boost Beast异步HTTP服务器示例代码以支持HTTP Range字段?
Great question! The good news is that Boost.Beast (version 1.70 included) has built-in tools to handle HTTP Range requests without requiring you to write custom POSIX file handling logic. You can extend your existing file body code with just a few extra steps—let me walk you through it.
Step 1: Parse the Range Request Header
First, you'll need to extract and validate the Range header from the incoming HTTP request. Boost.Beast provides the http::parse_range function that does all the heavy lifting here: it checks if the Range header is valid, ensures the requested range fits within the file's bounds, and returns a structured result you can work with.
Step 2: Get the File Size
Before processing the range, retrieve the total size of the opened file using the body.size() method. This helps validate the range and construct the correct response headers later.
Step 3: Adjust the File Body for the Requested Range
Since http::file_body supports random access, you can use body.seek() to jump directly to the start of the requested range. No need to read the entire file or handle low-level file operations yourself.
Step 4: Construct the Partial Content Response
For valid range requests, you'll return a 206 Partial Content status code, set the Content-Range header (using Boost.Beast's http::make_content_range to format it correctly), and set the response's content length to the size of the requested range.
Updated Code Example
Here's how to integrate this into your existing code snippet:
// Attempt to open the file beast::error_code ec; http::file_body::value_type body; body.open(path.c_str(), beast::file_mode::scan, ec); if(ec) return fail(ec, "open"); // Get total file size std::uint64_t file_size = body.size(ec); if(ec) return fail(ec, "get file size"); // Check for Range request in headers auto range_result = http::parse_range(request[http::field::range], file_size); if(range_result) { // Valid range request: process partial content auto const& requested_range = range_result.value(); // Seek to the start of the requested range body.seek(requested_range.offset, beast::file_seek::begin, ec); if(ec) return fail(ec, "seek to range start"); // Build partial content response http::response<http::file_body> res{http::status::partial_content, request.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); // Use built-in helper to format Content-Range header res.set(http::field::content_range, http::make_content_range(requested_range, file_size)); res.content_length(requested_range.length); res.body() = std::move(body); res.prepare_payload(); // Send the response (use your existing async send logic here) } else { // No valid Range request: send full file as before http::response<http::file_body> res{http::status::ok, request.version()}; res.set(http::field::server, BOOST_BEAST_VERSION_STRING); res.set(http::field::content_type, mime_type(path)); res.content_length(file_size); res.body() = std::move(body); res.prepare_payload(); // Send the response (use your existing async send logic here) }
Key Notes
- Error Handling: Don't forget to handle cases where
http::parse_rangereturns an error (e.g., invalid range format, range beyond file size). In those cases, you should return a416 Requested Range Not Satisfiablestatus code. - Efficiency: Using
body.seek()is efficient because it leverages the OS's file system capabilities—you're not reading unnecessary parts of the file. - No Custom File Logic: All file operations and HTTP header formatting are handled by Boost.Beast, so you avoid reinventing the wheel.
内容的提问来源于stack exchange,提问作者user5685755

