@@ -676,8 +676,14 @@ class Server {
676676 const HandlersForContentReader &handlers);
677677
678678 bool parse_request_line (const char *s, Request &req);
679+ void apply_ranges (const Request &req, Response &res,
680+ std::string &content_type, std::string &boundary);
679681 bool write_response (Stream &strm, bool close_connection, const Request &req,
680682 Response &res);
683+ bool write_response_with_content (Stream &strm, bool close_connection,
684+ const Request &req, Response &res,
685+ std::string &content_type,
686+ std::string &boundary);
681687 bool write_content_with_provider (Stream &strm, const Request &req,
682688 Response &res, const std::string &boundary,
683689 const std::string &content_type);
@@ -3171,9 +3177,7 @@ get_range_offset_and_length(const Request &req, size_t content_length,
31713177 r.second = slen - 1 ;
31723178 }
31733179
3174- if (r.second == -1 ) {
3175- r.second = slen - 1 ;
3176- }
3180+ if (r.second == -1 ) { r.second = slen - 1 ; }
31773181 return std::make_pair (r.first , static_cast <size_t >(r.second - r.first ) + 1 );
31783182}
31793183
@@ -3223,21 +3227,21 @@ bool process_multipart_ranges_data(const Request &req, Response &res,
32233227 return true ;
32243228}
32253229
3226- inline std::string make_multipart_ranges_data (const Request &req, Response &res,
3227- const std::string &boundary,
3228- const std::string &content_type) {
3229- std::string data;
3230-
3231- process_multipart_ranges_data (
3230+ inline bool make_multipart_ranges_data (const Request &req, Response &res,
3231+ const std::string &boundary,
3232+ const std::string &content_type,
3233+ std::string &data) {
3234+ return process_multipart_ranges_data (
32323235 req, res, boundary, content_type,
32333236 [&](const std::string &token) { data += token; },
32343237 [&](const char *token) { data += token; },
32353238 [&](size_t offset, size_t length) {
3236- data += res.body .substr (offset, length);
3237- return true ;
3239+ if (offset < res.body .size ()) {
3240+ data += res.body .substr (offset, length);
3241+ return true ;
3242+ }
3243+ return false ;
32383244 });
3239-
3240- return data;
32413245}
32423246
32433247inline size_t
@@ -4006,18 +4010,19 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
40064010
40074011inline bool Server::write_response (Stream &strm, bool close_connection,
40084012 const Request &req, Response &res) {
4013+ std::string content_type;
4014+ std::string boundary;
4015+ return write_response_with_content (strm, close_connection, req, res,
4016+ content_type, boundary);
4017+ }
4018+
4019+ inline bool Server::write_response_with_content (
4020+ Stream &strm, bool close_connection, const Request &req, Response &res,
4021+ std::string &content_type, std::string &boundary) {
40094022 assert (res.status != -1 );
40104023
40114024 if (400 <= res.status && error_handler_) { error_handler_ (req, res); }
40124025
4013- detail::BufferStream bstrm;
4014-
4015- // Response line
4016- if (!bstrm.write_format (" HTTP/1.1 %d %s\r\n " , res.status ,
4017- detail::status_message (res.status ))) {
4018- return false ;
4019- }
4020-
40214026 // Headers
40224027 if (close_connection || req.get_header_value (" Connection" ) == " close" ) {
40234028 res.set_header (" Connection" , " close" );
@@ -4033,109 +4038,21 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
40334038 res.set_header (" Content-Type" , " text/plain" );
40344039 }
40354040
4036- if (!res.has_header (" Accept-Ranges" ) && req.method == " HEAD" ) {
4037- res.set_header (" Accept-Ranges" , " bytes" );
4041+ if (!res.has_header (" Content-Length" ) && res.body .empty () &&
4042+ !res.content_length_ && !res.content_provider_ ) {
4043+ res.set_header (" Content-Length" , " 0" );
40384044 }
40394045
4040- std::string content_type;
4041- std::string boundary;
4042-
4043- if (req.ranges .size () > 1 ) {
4044- boundary = detail::make_multipart_data_boundary ();
4045-
4046- auto it = res.headers .find (" Content-Type" );
4047- if (it != res.headers .end ()) {
4048- content_type = it->second ;
4049- res.headers .erase (it);
4050- }
4051-
4052- res.headers .emplace (" Content-Type" ,
4053- " multipart/byteranges; boundary=" + boundary);
4046+ if (!res.has_header (" Accept-Ranges" ) && req.method == " HEAD" ) {
4047+ res.set_header (" Accept-Ranges" , " bytes" );
40544048 }
40554049
4056- auto type = detail::encoding_type (req, res);
4057-
4058- if (res.body .empty ()) {
4059- if (res.content_length_ > 0 ) {
4060- size_t length = 0 ;
4061- if (req.ranges .empty ()) {
4062- length = res.content_length_ ;
4063- } else if (req.ranges .size () == 1 ) {
4064- auto offsets =
4065- detail::get_range_offset_and_length (req, res.content_length_ , 0 );
4066- auto offset = offsets.first ;
4067- length = offsets.second ;
4068- auto content_range = detail::make_content_range_header_field (
4069- offset, length, res.content_length_ );
4070- res.set_header (" Content-Range" , content_range);
4071- } else {
4072- length = detail::get_multipart_ranges_data_length (req, res, boundary,
4073- content_type);
4074- }
4075- res.set_header (" Content-Length" , std::to_string (length));
4076- } else {
4077- if (res.content_provider_ ) {
4078- if (res.is_chunked_content_provider ) {
4079- res.set_header (" Transfer-Encoding" , " chunked" );
4080- if (type == detail::EncodingType::Gzip) {
4081- res.set_header (" Content-Encoding" , " gzip" );
4082- } else if (type == detail::EncodingType::Brotli) {
4083- res.set_header (" Content-Encoding" , " br" );
4084- }
4085- }
4086- } else {
4087- res.set_header (" Content-Length" , " 0" );
4088- }
4089- }
4090- } else {
4091- if (req.ranges .empty ()) {
4092- ;
4093- } else if (req.ranges .size () == 1 ) {
4094- auto offsets =
4095- detail::get_range_offset_and_length (req, res.body .size (), 0 );
4096- auto offset = offsets.first ;
4097- auto length = offsets.second ;
4098- auto content_range = detail::make_content_range_header_field (
4099- offset, length, res.body .size ());
4100- res.set_header (" Content-Range" , content_range);
4101- res.body = res.body .substr (offset, length);
4102- } else {
4103- res.body =
4104- detail::make_multipart_ranges_data (req, res, boundary, content_type);
4105- }
4106-
4107- if (type != detail::EncodingType::None) {
4108- std::unique_ptr<detail::compressor> compressor;
4109-
4110- if (type == detail::EncodingType::Gzip) {
4111- #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4112- compressor = detail::make_unique<detail::gzip_compressor>();
4113- res.set_header (" Content-Encoding" , " gzip" );
4114- #endif
4115- } else if (type == detail::EncodingType::Brotli) {
4116- #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4117- compressor = detail::make_unique<detail::brotli_compressor>();
4118- res.set_header (" Content-Encoding" , " brotli" );
4119- #endif
4120- }
4121-
4122- if (compressor) {
4123- std::string compressed;
4124-
4125- if (!compressor->compress (res.body .data (), res.body .size (), true ,
4126- [&](const char *data, size_t data_len) {
4127- compressed.append (data, data_len);
4128- return true ;
4129- })) {
4130- return false ;
4131- }
4132-
4133- res.body .swap (compressed);
4134- }
4135- }
4050+ detail::BufferStream bstrm;
41364051
4137- auto length = std::to_string (res.body .size ());
4138- res.set_header (" Content-Length" , length);
4052+ // Response line
4053+ if (!bstrm.write_format (" HTTP/1.1 %d %s\r\n " , res.status ,
4054+ detail::status_message (res.status ))) {
4055+ return false ;
41394056 }
41404057
41414058 if (!detail::write_headers (bstrm, res, Headers ())) { return false ; }
@@ -4535,6 +4452,116 @@ inline bool Server::dispatch_request(Request &req, Response &res,
45354452 return false ;
45364453}
45374454
4455+ inline void Server::apply_ranges (const Request &req, Response &res,
4456+ std::string &content_type,
4457+ std::string &boundary) {
4458+ if (req.ranges .size () > 1 ) {
4459+ boundary = detail::make_multipart_data_boundary ();
4460+
4461+ auto it = res.headers .find (" Content-Type" );
4462+ if (it != res.headers .end ()) {
4463+ content_type = it->second ;
4464+ res.headers .erase (it);
4465+ }
4466+
4467+ res.headers .emplace (" Content-Type" ,
4468+ " multipart/byteranges; boundary=" + boundary);
4469+ }
4470+
4471+ auto type = detail::encoding_type (req, res);
4472+
4473+ if (res.body .empty ()) {
4474+ if (res.content_length_ > 0 ) {
4475+ size_t length = 0 ;
4476+ if (req.ranges .empty ()) {
4477+ length = res.content_length_ ;
4478+ } else if (req.ranges .size () == 1 ) {
4479+ auto offsets =
4480+ detail::get_range_offset_and_length (req, res.content_length_ , 0 );
4481+ auto offset = offsets.first ;
4482+ length = offsets.second ;
4483+ auto content_range = detail::make_content_range_header_field (
4484+ offset, length, res.content_length_ );
4485+ res.set_header (" Content-Range" , content_range);
4486+ } else {
4487+ length = detail::get_multipart_ranges_data_length (req, res, boundary,
4488+ content_type);
4489+ }
4490+ res.set_header (" Content-Length" , std::to_string (length));
4491+ } else {
4492+ if (res.content_provider_ ) {
4493+ if (res.is_chunked_content_provider ) {
4494+ res.set_header (" Transfer-Encoding" , " chunked" );
4495+ if (type == detail::EncodingType::Gzip) {
4496+ res.set_header (" Content-Encoding" , " gzip" );
4497+ } else if (type == detail::EncodingType::Brotli) {
4498+ res.set_header (" Content-Encoding" , " br" );
4499+ }
4500+ }
4501+ }
4502+ }
4503+ } else {
4504+ if (req.ranges .empty ()) {
4505+ ;
4506+ } else if (req.ranges .size () == 1 ) {
4507+ auto offsets =
4508+ detail::get_range_offset_and_length (req, res.body .size (), 0 );
4509+ auto offset = offsets.first ;
4510+ auto length = offsets.second ;
4511+ auto content_range = detail::make_content_range_header_field (
4512+ offset, length, res.body .size ());
4513+ res.set_header (" Content-Range" , content_range);
4514+ if (offset < res.body .size ()) {
4515+ res.body = res.body .substr (offset, length);
4516+ } else {
4517+ res.body .clear ();
4518+ res.status = 416 ;
4519+ }
4520+ } else {
4521+ std::string data;
4522+ if (detail::make_multipart_ranges_data (req, res, boundary, content_type,
4523+ data)) {
4524+ res.body .swap (data);
4525+ } else {
4526+ res.body .clear ();
4527+ res.status = 416 ;
4528+ }
4529+ }
4530+
4531+ if (type != detail::EncodingType::None) {
4532+ std::unique_ptr<detail::compressor> compressor;
4533+ std::string content_encoding;
4534+
4535+ if (type == detail::EncodingType::Gzip) {
4536+ #ifdef CPPHTTPLIB_ZLIB_SUPPORT
4537+ compressor = detail::make_unique<detail::gzip_compressor>();
4538+ content_encoding = " gzip" ;
4539+ #endif
4540+ } else if (type == detail::EncodingType::Brotli) {
4541+ #ifdef CPPHTTPLIB_BROTLI_SUPPORT
4542+ compressor = detail::make_unique<detail::brotli_compressor>();
4543+ content_encoding = " brotli" ;
4544+ #endif
4545+ }
4546+
4547+ if (compressor) {
4548+ std::string compressed;
4549+ if (compressor->compress (res.body .data (), res.body .size (), true ,
4550+ [&](const char *data, size_t data_len) {
4551+ compressed.append (data, data_len);
4552+ return true ;
4553+ })) {
4554+ res.body .swap (compressed);
4555+ res.set_header (" Content-Encoding" , content_encoding);
4556+ }
4557+ }
4558+ }
4559+
4560+ auto length = std::to_string (res.body .size ());
4561+ res.set_header (" Content-Length" , length);
4562+ }
4563+ }
4564+
45384565inline bool Server::dispatch_request_for_content_reader (
45394566 Request &req, Response &res, ContentReader content_reader,
45404567 const HandlersForContentReader &handlers) {
@@ -4626,7 +4653,12 @@ Server::process_request(Stream &strm, bool close_connection,
46264653 if (res.status == -1 ) { res.status = 404 ; }
46274654 }
46284655
4629- return write_response (strm, close_connection, req, res);
4656+ std::string content_type;
4657+ std::string boundary;
4658+ apply_ranges (req, res, content_type, boundary);
4659+
4660+ return write_response_with_content (strm, close_connection, req, res,
4661+ content_type, boundary);
46304662}
46314663
46324664inline bool Server::is_valid () const { return true ; }
0 commit comments