HTTP access enhancments

Added the ability for asyn requests.
Fixed a memory lead issue in WebDAV access.
This commit is contained in:
Matthias Koefferlein 2018-01-07 08:56:06 +01:00
parent ffb56335fb
commit bf5f932ff1
6 changed files with 112 additions and 5 deletions

View File

@ -123,7 +123,7 @@ InputHttpStream::InputHttpStream (const std::string &url)
InputHttpStream::~InputHttpStream ()
{
// .. nothing yet ..
// .. nothing yet ..
}
void
@ -166,6 +166,7 @@ InputHttpStream::finished (QNetworkReply *reply)
issue_request (QUrl (redirect_target.toString ()));
} else {
mp_reply = reply;
m_ready ();
}
}
@ -205,6 +206,14 @@ InputHttpStream::issue_request (const QUrl &url)
#endif
}
void
InputHttpStream::send ()
{
if (mp_reply == 0) {
issue_request (QUrl (tl::to_qstring (m_url)));
}
}
size_t
InputHttpStream::read (char *b, size_t n)
{

View File

@ -25,6 +25,7 @@
#define HDR_tlHttpStream
#include "tlStream.h"
#include "tlEvents.h"
#include <QObject>
#include <QBuffer>
@ -84,6 +85,19 @@ public:
*/
virtual ~InputHttpStream ();
/**
* @brief Sends the request for data
* To ensure prompt delivery of data, this method can be used prior to
* "read" to trigger the download from the given URL.
* This method will return immediately. When the reply is available,
* the "ready" event will be triggered. "read" can then be used to
* read the data or - in case of an error - throw an exception.
* If "send" is not used before "read", "read" will block until data
* is available.
* If a request has already been sent, this method will do nothing.
*/
void send ();
/**
* @brief Sets the request verb
* The default verb is "GET"
@ -115,6 +129,23 @@ public:
*/
virtual size_t read (char *b, size_t n);
/**
* @brief Gets the "ready" event
* Connect to this event for the asynchroneous interface.
*/
tl::Event &ready ()
{
return m_ready;
}
/**
* @brief Gets a value indicating whether data is available
*/
bool data_available ()
{
return mp_reply != 0;
}
virtual void reset ();
virtual std::string source () const
@ -140,6 +171,7 @@ private:
QByteArray m_data;
QBuffer *mp_buffer;
std::map<std::string, std::string> m_headers;
tl::Event m_ready;
void issue_request (const QUrl &url);
};

View File

@ -311,6 +311,14 @@ InputStream::InputStream (InputStreamBase &delegate)
mp_buffer = new char [m_bcap];
}
InputStream::InputStream (InputStreamBase *delegate)
: m_pos (0), mp_bptr (0), mp_delegate (delegate), m_owns_delegate (true), mp_inflate (0)
{
m_bcap = 4096; // initial buffer capacity
m_blen = 0;
mp_buffer = new char [m_bcap];
}
InputStream::InputStream (const std::string &abstract_path)
: m_pos (0), mp_bptr (0), mp_delegate (0), m_owns_delegate (false), mp_inflate (0)
{
@ -357,7 +365,15 @@ std::string InputStream::absolute_path (const std::string &abstract_path)
InputStream::~InputStream ()
{
if (mp_delegate && m_owns_delegate) {
delete mp_delegate;
// NOTE: HTTP stream objects should not be deleted now, since events
// may be pending that deliver the finished signal to the object.
tl::InputHttpStream *http = dynamic_cast<tl::InputHttpStream *>(mp_delegate);
if (http) {
http->ready ().clear (); // avoids events from deleted streams
http->deleteLater ();
} else {
delete mp_delegate;
}
mp_delegate = 0;
}
if (mp_inflate) {

View File

@ -172,11 +172,16 @@ class TL_PUBLIC InputStream
public:
/**
* @brief Default constructor
*
* This constructor takes a delegate object.
* This constructor takes a delegate object, but does not take ownership.
*/
InputStream (InputStreamBase &delegate);
/**
* @brief Default constructor
* This constructor takes a delegate object, and takes ownership.
*/
InputStream (InputStreamBase *delegate);
/**
* @brief Opens a stream from a abstract path
*
@ -305,6 +310,14 @@ public:
* @brief Gets the absolute path for a given URL
*/
static std::string absolute_path (const std::string &path);
/**
* @brief Gets the base reader (delegate)
*/
InputStreamBase *base ()
{
return mp_delegate;
}
protected:
void reset_pos ()

View File

@ -256,7 +256,7 @@ WebDAVObject::download_item (const std::string &url)
tl::InputHttpStream *http = new tl::InputHttpStream (url);
// This trick allows accessing GitHub repos through their SVN API
http->add_header ("User-Agent", "SVN");
return new tl::InputStream (*http);
return new tl::InputStream (http);
}
bool

View File

@ -24,6 +24,8 @@
#include "tlHttpStream.h"
#include "tlUnitTest.h"
#include <QCoreApplication>
static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text");
static std::string test_url2 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/dir1");
@ -73,3 +75,38 @@ TEST(2)
"</D:multistatus>\n"
);
}
namespace
{
class Receiver : public tl::Object
{
public:
Receiver () : flag (false) { }
void handle () { flag = true; }
bool flag;
};
}
// async mode
TEST(3)
{
tl::InputHttpStream stream (test_url1);
Receiver r;
stream.ready ().add (&r, &Receiver::handle);
stream.send ();
EXPECT_EQ (stream.data_available (), false);
while (! stream.data_available ()) {
QCoreApplication::processEvents (QEventLoop::ExcludeUserInputEvents);
}
EXPECT_EQ (r.flag, true);
char b[100];
size_t n = stream.read (b, sizeof (b));
std::string res (b, n);
EXPECT_EQ (res, "hello, world.\n");
}