Overview
Features
Download
Documentation
Community
Add-Ons & Services

support http resume Download

Please post support and help requests here.

support http resume Download

Postby ahmedbenahmed » 09 Aug 2011, 15:37

hi all
i'd like to know if poco supports resume of http download.
if yes how can i implement it
thanks
ahmedbenahmed
 
Posts: 1
Joined: 09 Aug 2011, 15:00

Re: support http resume Download

Postby guenter » 12 Aug 2011, 07:16

You'll need to implement support for byte-range requests.

Here's a request handler implementation that supports this:


Code: Select all
class MediaRequestHandler: public Poco::Net::HTTPRequestHandler
{
public:
   enum
   {
      BUFFER_SIZE = 8192
   };
   
   MediaRequestHandler(const std::string& mediaPath):
      _mediaPath(mediaPath)
   {
   }
   
   void handleRequest(Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse& response)
   {
      std::string decodedPath;
      Poco::URI::decode(request.getURI(), decodedPath);
      Poco::Path requestPath(decodedPath, Poco::Path::PATH_UNIX);
      Poco::Path mediaPath(_mediaPath);
      mediaPath.makeDirectory();
      for (int i = 1; i < requestPath.depth(); i++)
      {
         mediaPath.pushDirectory(requestPath[i]);
      }
      mediaPath.setFileName(requestPath.getFileName());
      Poco::File mediaFile(mediaPath.toString());
      if (mediaFile.path().find("..") != std::string::npos)
      {
         response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
         response.send();
      }
      if (mediaFile.exists())
      {
         Poco::Timestamp dateTime    = mediaFile.getLastModified();
         Poco::File::FileSize length = mediaFile.getSize();
         std::string contentType     = ContentDirectory1Impl::contentTypeForExtension(mediaPath.getExtension());
         if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD || request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
         {
            response.set("Last-Modified", Poco::DateTimeFormatter::format(dateTime, Poco::DateTimeFormat::HTTP_FORMAT));
            response.set("Accept-Ranges", "bytes");
            response.setContentType(contentType);
         }
         if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_HEAD)
         {
            response.setContentLength(static_cast<int>(length));
            response.send();            
         }
         else if (request.getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
         {
            if (request.has("Range"))
            {
               Poco::File::FileSize rangeStart  = 0;
               Poco::File::FileSize rangeLength = 0;
               if (parseRange(request.get("Range"), length, rangeStart, rangeLength))
               {
                  sendFileRange(response, mediaPath.toString(), length, rangeStart, rangeLength);
               }
               else
               {
                  response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE);
                  response.send();
               }
            }
            else
            {
               response.sendFile(mediaPath.toString(), contentType);
            }
         }
         else
         {
            response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_METHOD_NOT_ALLOWED);
            response.send();
         }
      }
      else
      {
         response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
         response.send();
      }
   }
   
   bool parseRange(const std::string& range, Poco::File::FileSize fileSize, Poco::File::FileSize& rangeStart, Poco::File::FileSize& rangeLength)
   {
      std::string::const_iterator it = range.begin();
      std::string::const_iterator end = range.end();
      while (it != end && Poco::Ascii::isSpace(*it)) ++it;
      std::string unit;
      while (it != end && *it != '=') unit += *it++;
      if (unit == "bytes" && it != end)
      {
         ++it;
         if (it != end && *it == '-')
         {
            ++it;
            rangeLength = 0;
            while (it != end && Poco::Ascii::isDigit(*it))
            {
               rangeLength *= 10;
               rangeLength += *it - '0';
               ++it;
            }
            rangeStart = fileSize - rangeLength;
            return true;
         }
         else if (it != end && Poco::Ascii::isDigit(*it))
         {
            rangeStart = 0;
            while (it != end && Poco::Ascii::isDigit(*it))
            {
               rangeStart *= 10;
               rangeStart += *it - '0';
               ++it;
            }
            if (it != end && *it == '-')
            {
               ++it;
               if (it != end)
               {
                  rangeLength = 0;
                  while (it != end && Poco::Ascii::isDigit(*it))
                  {
                     rangeLength *= 10;
                     rangeLength += *it - '0';
                     ++it;
                  }
                  rangeLength = rangeLength - rangeStart + 1;
               }
               else
               {
                  rangeLength = fileSize - rangeStart;
               }
               return true;
            }
         }
      }
      return false;
   }
   
   void sendFileRange(Poco::Net::HTTPServerResponse& response, const std::string& path, Poco::File::FileSize length, Poco::File::FileSize rangeStart, Poco::File::FileSize rangeLength)
   {
      response.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_PARTIAL_CONTENT);
      response.setContentLength(static_cast<int>(rangeLength));
      response.set("Content-Range", Poco::format("bytes %?d-%?d/%?d", rangeStart, rangeStart + rangeLength - 1, length));
      std::ostream& ostr = response.send();
      
      Poco::FileInputStream istr(path);
      istr.seekg(static_cast<std::streampos>(rangeStart));
      Poco::Buffer<char> buffer(BUFFER_SIZE);
      while (rangeLength > 0)
      {
         std::streamsize chunk = BUFFER_SIZE;
         if (chunk > rangeLength) chunk = static_cast<std::streamsize>(rangeLength);
         istr.read(buffer.begin(), chunk);
         std::streamsize n = istr.gcount();
         if (n == 0) break;
         ostr.write(buffer.begin(), n);
         rangeLength -= n;
      }
   }
   
private:
   std::string _mediaPath;
};
guenter
 
Posts: 1153
Joined: 11 Jul 2006, 16:27
Location: Austria

Re: support http resume Download

Postby Hitnrun » 13 Jan 2013, 19:02

Also HTTPClientSession doesn't support resume?
Hitnrun
 
Posts: 20
Joined: 01 Apr 2009, 18:32

Re: support http resume Download

Postby guenter » 13 Jan 2013, 20:05

There's nothing special about resuming a download. Just add a proper Range header to the request to resume a download.
guenter
 
Posts: 1153
Joined: 11 Jul 2006, 16:27
Location: Austria


Return to Support

Who is online

Users browsing this forum: No registered users and 4 guests

cron