@@ -299,7 +299,7 @@ class DataSink {
299299using  ContentProvider =
300300    std::function<bool (size_t  offset, size_t  length, DataSink &sink)>;
301301
302- using  ChunkedContentProvider  =
302+ using  ContentProviderWithoutLength  =
303303    std::function<bool (size_t  offset, DataSink &sink)>;
304304
305305using  ContentReceiver =
@@ -404,8 +404,12 @@ struct Response {
404404      size_t  length, const  char  *content_type, ContentProvider provider,
405405      std::function<void ()> resource_releaser = [] {});
406406
407+   void  set_content_provider (
408+       const  char  *content_type, ContentProviderWithoutLength provider,
409+       std::function<void ()> resource_releaser = [] {});
410+ 
407411  void  set_chunked_content_provider (
408-       const  char  *content_type, ChunkedContentProvider  provider,
412+       const  char  *content_type, ContentProviderWithoutLength  provider,
409413      std::function<void ()> resource_releaser = [] {});
410414
411415  Response () = default ;
@@ -423,6 +427,7 @@ struct Response {
423427  size_t  content_length_ = 0 ;
424428  ContentProvider content_provider_;
425429  std::function<void ()> content_provider_resource_releaser_;
430+   bool  is_chunked_content_provider = false ;
426431};
427432
428433class  Stream  {
@@ -2664,19 +2669,19 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
26642669                             size_t  offset, size_t  length, T is_shutting_down) {
26652670  size_t  begin_offset = offset;
26662671  size_t  end_offset = offset + length;
2667- 
26682672  auto  ok = true ;
2669- 
26702673  DataSink data_sink;
2674+ 
26712675  data_sink.write  = [&](const  char  *d, size_t  l) {
26722676    if  (ok) {
26732677      offset += l;
26742678      if  (!write_data (strm, d, l)) { ok = false ; }
26752679    }
26762680  };
2681+ 
26772682  data_sink.is_writable  = [&](void ) { return  ok && strm.is_writable (); };
26782683
2679-   while  (ok &&  offset < end_offset && !is_shutting_down ()) {
2684+   while  (offset < end_offset && !is_shutting_down ()) {
26802685    if  (!content_provider (offset, end_offset - offset, data_sink)) {
26812686      return  -1 ;
26822687    }
@@ -2686,14 +2691,41 @@ inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
26862691  return  static_cast <ssize_t >(offset - begin_offset);
26872692}
26882693
2694+ template  <typename  T>
2695+ inline  ssize_t  write_content_without_length (Stream &strm,
2696+                                             ContentProvider content_provider,
2697+                                             T is_shutting_down) {
2698+   size_t  offset = 0 ;
2699+   auto  data_available = true ;
2700+   auto  ok = true ;
2701+   DataSink data_sink;
2702+ 
2703+   data_sink.write  = [&](const  char  *d, size_t  l) {
2704+     if  (ok) {
2705+       offset += l;
2706+       if  (!write_data (strm, d, l)) { ok = false ; }
2707+     }
2708+   };
2709+ 
2710+   data_sink.done  = [&](void ) { data_available = false ; };
2711+ 
2712+   data_sink.is_writable  = [&](void ) { return  ok && strm.is_writable (); };
2713+ 
2714+   while  (data_available && !is_shutting_down ()) {
2715+     if  (!content_provider (offset, 0 , data_sink)) { return  -1 ; }
2716+     if  (!ok) { return  -1 ; }
2717+   }
2718+ 
2719+   return  static_cast <ssize_t >(offset);
2720+ }
2721+ 
26892722template  <typename  T, typename  U>
26902723inline  ssize_t  write_content_chunked (Stream &strm,
26912724                                     ContentProvider content_provider,
26922725                                     T is_shutting_down, U &compressor) {
26932726  size_t  offset = 0 ;
26942727  auto  data_available = true ;
26952728  ssize_t  total_written_length = 0 ;
2696- 
26972729  auto  ok = true ;
26982730  DataSink data_sink;
26992731
@@ -3544,17 +3576,31 @@ Response::set_content_provider(size_t in_length, const char *content_type,
35443576    return  provider (offset, length, sink);
35453577  };
35463578  content_provider_resource_releaser_ = resource_releaser;
3579+   is_chunked_content_provider = false ;
3580+ }
3581+ 
3582+ inline  void  Response::set_content_provider (
3583+     const  char  *content_type, ContentProviderWithoutLength provider,
3584+     std::function<void ()> resource_releaser) {
3585+   set_header (" Content-Type"  , content_type);
3586+   content_length_ = 0 ;
3587+   content_provider_ = [provider](size_t  offset, size_t , DataSink &sink) {
3588+     return  provider (offset, sink);
3589+   };
3590+   content_provider_resource_releaser_ = resource_releaser;
3591+   is_chunked_content_provider = false ;
35473592}
35483593
35493594inline  void  Response::set_chunked_content_provider (
3550-     const  char  *content_type, ChunkedContentProvider  provider,
3595+     const  char  *content_type, ContentProviderWithoutLength  provider,
35513596    std::function<void ()> resource_releaser) {
35523597  set_header (" Content-Type"  , content_type);
35533598  content_length_ = 0 ;
35543599  content_provider_ = [provider](size_t  offset, size_t , DataSink &sink) {
35553600    return  provider (offset, sink);
35563601  };
35573602  content_provider_resource_releaser_ = resource_releaser;
3603+   is_chunked_content_provider = true ;
35583604}
35593605
35603606//  Rstream implementation
@@ -3893,7 +3939,7 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
38933939  }
38943940
38953941  if  (!res.has_header (" Content-Type"  ) &&
3896-       (!res.body .empty () || res.content_length_  > 0 )) {
3942+       (!res.body .empty () || res.content_length_  > 0  || res. content_provider_ )) {
38973943    res.set_header (" Content-Type"  , " text/plain"  );
38983944  }
38993945
@@ -3939,11 +3985,13 @@ inline bool Server::write_response(Stream &strm, bool close_connection,
39393985      res.set_header (" Content-Length"  , std::to_string (length));
39403986    } else  {
39413987      if  (res.content_provider_ ) {
3942-         res.set_header (" Transfer-Encoding"  , " chunked"  );
3943-         if  (type == detail::EncodingType::Gzip) {
3944-           res.set_header (" Content-Encoding"  , " gzip"  );
3945-         } else  if  (type == detail::EncodingType::Brotli) {
3946-           res.set_header (" Content-Encoding"  , " br"  );
3988+         if  (res.is_chunked_content_provider ) {
3989+           res.set_header (" Transfer-Encoding"  , " chunked"  );
3990+           if  (type == detail::EncodingType::Gzip) {
3991+             res.set_header (" Content-Encoding"  , " gzip"  );
3992+           } else  if  (type == detail::EncodingType::Brotli) {
3993+             res.set_header (" Content-Encoding"  , " br"  );
3994+           }
39473995        }
39483996      } else  {
39493997        res.set_header (" Content-Length"  , " 0"  );
@@ -4033,7 +4081,7 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
40334081    return  this ->svr_sock_  == INVALID_SOCKET;
40344082  };
40354083
4036-   if  (res.content_length_ ) {
4084+   if  (res.content_length_  >  0 ) {
40374085    if  (req.ranges .empty ()) {
40384086      if  (detail::write_content (strm, res.content_provider_ , 0 ,
40394087                                res.content_length_ , is_shutting_down) < 0 ) {
@@ -4055,25 +4103,32 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
40554103      }
40564104    }
40574105  } else  {
4058-     auto  type = detail::encoding_type (req, res);
4106+     if  (res.is_chunked_content_provider ) {
4107+       auto  type = detail::encoding_type (req, res);
40594108
4060-     std::shared_ptr<detail::compressor> compressor;
4061-     if  (type == detail::EncodingType::Gzip) {
4109+        std::shared_ptr<detail::compressor> compressor;
4110+        if  (type == detail::EncodingType::Gzip) {
40624111#ifdef  CPPHTTPLIB_ZLIB_SUPPORT
4063-       compressor = std::make_shared<detail::gzip_compressor>();
4112+          compressor = std::make_shared<detail::gzip_compressor>();
40644113#endif 
4065-     } else  if  (type == detail::EncodingType::Brotli) {
4114+        } else  if  (type == detail::EncodingType::Brotli) {
40664115#ifdef  CPPHTTPLIB_BROTLI_SUPPORT
4067-       compressor = std::make_shared<detail::brotli_compressor>();
4116+          compressor = std::make_shared<detail::brotli_compressor>();
40684117#endif 
4069-     } else  {
4070-       compressor = std::make_shared<detail::nocompressor>();
4071-     }
4072-     assert (compressor != nullptr );
4118+        } else  {
4119+          compressor = std::make_shared<detail::nocompressor>();
4120+        }
4121+        assert (compressor != nullptr );
40734122
4074-     if  (detail::write_content_chunked (strm, res.content_provider_ ,
4075-                                       is_shutting_down, *compressor) < 0 ) {
4076-       return  false ;
4123+       if  (detail::write_content_chunked (strm, res.content_provider_ ,
4124+                                         is_shutting_down, *compressor) < 0 ) {
4125+         return  false ;
4126+       }
4127+     } else  {
4128+       if  (detail::write_content_without_length (strm, res.content_provider_ ,
4129+                                                is_shutting_down) < 0 ) {
4130+         return  false ;
4131+       }
40774132    }
40784133  }
40794134  return  true ;
0 commit comments