Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIREBREATH-236 & FIREBREATH-237 Patch #110

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/NpapiCore/NpapiPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ NPError NpapiPlugin::NewStream(NPMIMEType type, NPStream* stream, NPBool seekabl
newstream->AttachObserver(sink);
} else {
HttpCallback callback(streamReq.getCallback());
if (callback) {
HttpChunkCallback chunkCallback(streamReq.getChunkCallback());
if (callback || chunkCallback) {
SimpleStreamHelper::AsyncRequest(m_npHost, newstream, streamReq);
} else {
FBLOG_WARN("NpapiPlugin", "Unsolicited request accepted but no callback or sink provided");
Expand Down
81 changes: 81 additions & 0 deletions src/PluginCore/BrowserStreamRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ namespace FB {
private:
PluginEventSinkPtr sinkPtr;
HttpCallback callback;
HttpProgressCallback progressCallback;
HttpChunkCallback chunkCallback;
HttpCompletedCallback completedCallback;
bool accepted;
std::string postdata;
std::string postheaders;
Expand Down Expand Up @@ -168,6 +171,83 @@ namespace FB {
////////////////////////////////////////////////////////////////////////////////////////////////////
void setBufferSize(size_t size) { internalBufferSize = size; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void FB::BrowserStreamRequest::setProgressCallback(const HttpProgressCallback& ptr);
///
/// @brief If you have registered a callback and you want to receive progress events you can
/// register an HttpProgressCallback.
///
/// This is used in conjuction with setCallback. It registers a callback function that will be
/// called for every chunk of data being downloaded. The first argument on the HttpProgressCallback
/// is the bytes currently received. The second is either 0 if no Content-Length header was received,
/// otherwise, it's the total number of bytes of the object received.
///
/// @param ptr const HttpProgressCallback & A callback that will receive the progress events
///
/// @author wavesoft
////////////////////////////////////////////////////////////////////////////////////////////////////
void setProgressCallback(const HttpProgressCallback& ptr) { progressCallback = ptr; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn FB::PluginEventSinkPtr FB::BrowserStreamRequest::getProgressCallback();
///
/// @brief Returns the HttpProgressCallback functor assigned to the object, or a NULL HttpProgressCallback
/// if none.
///
/// @author wavesoft
////////////////////////////////////////////////////////////////////////////////////////////////////
HttpProgressCallback getProgressCallback() const { return progressCallback; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void FB::BrowserStreamRequest::setCompletedCallback(const HttpCompletedCallback& ptr);
///
/// @brief This function registers a HttpCompletedCallback function that will be fired when the
/// download process is completed.
///
/// This is used in conjuction with setCallback or setChunkCallback. It will be fired when the
/// download process is completed.
///
/// @param ptr const HttpProgressCallback & A callback that will receive the progress events
///
/// @author wavesoft
////////////////////////////////////////////////////////////////////////////////////////////////////
void setCompletedCallback(const HttpCompletedCallback& ptr) { completedCallback = ptr; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn FB::PluginEventSinkPtr FB::BrowserStreamRequest::getCompletedCallback();
///
/// @brief Returns the HttpCompletedCallback functor assigned to the object, or a NULL HttpProgressCallback
/// if none.
///
/// @author wavesoft
////////////////////////////////////////////////////////////////////////////////////////////////////
HttpCompletedCallback getCompletedCallback() const { return completedCallback; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void FB::BrowserStreamRequest::setChunkCallback(const HttpProgressCallback& ptr);
///
/// @brief This function is an alternative to setCallback. This function will be called for every
/// data chunk that arrives.
///
/// This is used in with or without setCallback, but like setCallback, it can't be used in conjunction
/// with setEventSink. This function will be fired for every chunk of incoming data.
///
/// @param ptr const HttpChunkCallback & A callback that will be called for every incoming data chunk
///
/// @author wavesoft
////////////////////////////////////////////////////////////////////////////////////////////////////
void setChunkCallback(const HttpChunkCallback& ptr) { chunkCallback = ptr; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn FB::PluginEventSinkPtr FB::BrowserStreamRequest::getChunkCallback();
///
/// @brief Returns the HttpChunkCallback functor assigned to the object, or a NULL HttpProgressCallback
/// if none.
///
/// @author wavesoft
////////////////////////////////////////////////////////////////////////////////////////////////////
HttpChunkCallback getChunkCallback() const { return chunkCallback; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn void FB::BrowserStreamRequest::setEventSink(const PluginEventSinkPtr& ptr);
///
Expand All @@ -185,6 +265,7 @@ namespace FB {
/// @author taxilian
////////////////////////////////////////////////////////////////////////////////////////////////////
void setEventSink(const PluginEventSinkPtr& ptr) { sinkPtr = ptr; accepted = true; }

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn FB::PluginEventSinkPtr FB::BrowserStreamRequest::getEventSink();
///
Expand Down
110 changes: 78 additions & 32 deletions src/PluginCore/SimpleStreamHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Copyright 2011 Richard Bateman,
\**********************************************************/

#include "BrowserHost.h"
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/bind.hpp>
#include "precompiled_headers.h" // On windows, everything above this line in PCH
Expand Down Expand Up @@ -50,7 +51,7 @@ FB::SimpleStreamHelperPtr FB::SimpleStreamHelper::AsyncPost(const FB::BrowserHos

FB::SimpleStreamHelperPtr FB::SimpleStreamHelper::AsyncRequest( const FB::BrowserHostConstPtr& host,
const BrowserStreamRequest& req ) {
if (!req.getCallback()) {
if (!req.getCallback() && !req.getChunkCallback()) {
throw std::runtime_error("Invalid callback");
}
if (!host->isMainThread()) {
Expand All @@ -68,7 +69,18 @@ FB::SimpleStreamHelperPtr FB::SimpleStreamHelper::AsyncRequest( const FB::Browse
// This must be run from the main thread
return host->CallOnMainThread(boost::bind(&AsyncRequest, host, stream, req));
}
FB::SimpleStreamHelperPtr ptr(boost::make_shared<FB::SimpleStreamHelper>(req.getCallback(), req.internalBufferSize));

// Create a SimpleStreamHelper instance
FB::SimpleStreamHelperPtr ptr(
boost::make_shared<FB::SimpleStreamHelper>(
req.getCallback(),
req.getProgressCallback(),
req.getChunkCallback(),
req.getCompletedCallback(),
req.internalBufferSize
)
);

// This is kinda a weird trick; it's responsible for freeing itself, unless something decides
// to hold a reference to it.
ptr->keepReference(ptr);
Expand Down Expand Up @@ -106,7 +118,7 @@ struct SyncHTTPHelper
};


FB::HttpStreamResponsePtr FB::SimpleStreamHelper::SynchronousRequest( const FB::BrowserHostPtr& host, const BrowserStreamRequest& req )
FB::HttpStreamResponsePtr FB::SimpleStreamHelper::SynchronousRequest( const FB::BrowserHostPtr& host, BrowserStreamRequest& req )
{
// We can't ever block on the main thread, so SynchronousGet can't be called from there.
// Also, if you could block the main thread, that still wouldn't work because the request
Expand All @@ -115,6 +127,7 @@ FB::HttpStreamResponsePtr FB::SimpleStreamHelper::SynchronousRequest( const FB::
SyncHTTPHelper helper;
try {
FB::HttpCallback cb(boost::bind(&SyncHTTPHelper::getURLCallback, &helper, _1, _2, _3, _4));
req.setCallback(cb);
FB::SimpleStreamHelperPtr ptr = AsyncRequest(host, req);
helper.setPtr(ptr);
helper.waitForDone();
Expand Down Expand Up @@ -144,8 +157,14 @@ FB::HttpStreamResponsePtr FB::SimpleStreamHelper::SynchronousPost( const FB::Bro
return SynchronousRequest(host, req);
}

FB::SimpleStreamHelper::SimpleStreamHelper( const HttpCallback& callback, const size_t blockSize )
: blockSize(blockSize), received(0), callback(callback)
FB::SimpleStreamHelper::SimpleStreamHelper(
const HttpCallback& callback,
const HttpProgressCallback& progressCallback,
const HttpChunkCallback& chunkCallback,
const HttpCompletedCallback& completedCallback,
const size_t blockSize )

: blockSize(blockSize), received(0), callback(callback), progressCallback(progressCallback), chunkCallback(chunkCallback), completedCallback(completedCallback)
{

}
Expand All @@ -155,6 +174,12 @@ bool FB::SimpleStreamHelper::onStreamCompleted( FB::StreamCompletedEvent *evt, F
if (!evt->success) {
if (callback)
callback(false, FB::HeaderMap(), boost::shared_array<uint8_t>(), received);
if (completedCallback)
completedCallback(false, FB::HeaderMap());

progressCallback.clear();
chunkCallback.clear();
completedCallback.clear();
callback.clear();
self.reset();
return false;
Expand All @@ -175,50 +200,71 @@ bool FB::SimpleStreamHelper::onStreamCompleted( FB::StreamCompletedEvent *evt, F
// Free all the old blocks
blocks.clear();
}
if (callback && stream) {
if ((callback || completedCallback) && stream) {
std::multimap<std::string, std::string> headers;
headers = parse_http_headers(stream->getHeaders());
callback(true, headers, data, received);
if (callback)
callback(true, headers, data, received);
if (completedCallback)
completedCallback(true, headers);
}

progressCallback.clear();
chunkCallback.clear();
completedCallback.clear();
callback.clear();
self.reset();
return false; // Always return false to make sure the browserhost knows to let go of the object
}

bool FB::SimpleStreamHelper::onStreamOpened( FB::StreamOpenedEvent *evt, FB::BrowserStream * )
bool FB::SimpleStreamHelper::onStreamOpened( FB::StreamOpenedEvent *evt, FB::BrowserStream * stream )
{
// We can't reliably find the actual length, so we won't try
return false;
}

bool FB::SimpleStreamHelper::onStreamDataArrived( FB::StreamDataArrivedEvent *evt, FB::BrowserStream * )
bool FB::SimpleStreamHelper::onStreamDataArrived( FB::StreamDataArrivedEvent *evt, FB::BrowserStream * s )
{
// Forward the received buffer size
received += evt->getLength();
const uint8_t* buf = reinterpret_cast<const uint8_t*>(evt->getData());
const uint8_t* endbuf = buf + evt->getLength();

int len = evt->getLength();
int offset = evt->getDataPosition();
while (buf < endbuf) {
size_t n = offset / blockSize;
size_t pos = offset % blockSize;
if (blocks.size() < n+1) {
blocks.push_back(boost::shared_array<uint8_t>(new uint8_t[blockSize]));
}
uint8_t *destBuf = blocks.back().get();
//if (pos + len > )
int curLen = len;
if (pos + len >= blockSize) {
// If there isn't room in the current block, copy what there is room for
// and loop
curLen = blockSize-pos;

// Call the chunk callback
if (chunkCallback)
chunkCallback( evt->getData(), evt->getLength() );

// If we have a simple callback, build buffer
if (callback) {
const uint8_t* buf = reinterpret_cast<const uint8_t*>(evt->getData());
const uint8_t* endbuf = buf + evt->getLength();
int offset = evt->getDataPosition();
int len = evt->getLength();
while (buf < endbuf) {
size_t n = offset / blockSize;
size_t pos = offset % blockSize;
if (blocks.size() < n+1) {
blocks.push_back(boost::shared_array<uint8_t>(new uint8_t[blockSize]));
}
uint8_t *destBuf = blocks.back().get();
//if (pos + len > )
int curLen = len;
if (pos + len >= blockSize) {
// If there isn't room in the current block, copy what there is room for
// and loop
curLen = blockSize-pos;
}
// Copy the bytes that fit in this buffer
std::copy(buf, buf+curLen, destBuf+pos);
buf += curLen;
offset += curLen;
len -= curLen;
}
// Copy the bytes that fit in this buffer
std::copy(buf, buf+curLen, destBuf+pos);
buf += curLen;
offset += curLen;
len -= curLen;
}

// Call the progress callback
if (progressCallback)
progressCallback( received, s->getLength() );

// Forward the event
return false;
}

Expand Down
18 changes: 15 additions & 3 deletions src/PluginCore/SimpleStreamHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ namespace FB {
FB_FORWARD_PTR(SimpleStreamHelper);

typedef std::multimap<std::string, std::string> HeaderMap;
typedef boost::function<void (bool, const FB::HeaderMap&, const boost::shared_array<uint8_t>&, const size_t)> HttpCallback;
typedef boost::function<void (bool, const FB::HeaderMap&, const boost::shared_array<uint8_t>&, const size_t)> HttpCallback;
typedef boost::function<void (const void *, const size_t)> HttpChunkCallback;
typedef boost::function<void (const size_t, const size_t)> HttpProgressCallback;
typedef boost::function<void (bool, const FB::HeaderMap&)> HttpCompletedCallback;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @class HttpStreamResponse
Expand Down Expand Up @@ -241,7 +244,7 @@ namespace FB {
/// @author taxilian
/// @since 1.7
////////////////////////////////////////////////////////////////////////////////////////////////////
static HttpStreamResponsePtr SynchronousRequest(const FB::BrowserHostPtr& host, const BrowserStreamRequest& req );
static HttpStreamResponsePtr SynchronousRequest(const FB::BrowserHostPtr& host, BrowserStreamRequest& req );

////////////////////////////////////////////////////////////////////////////////////////////////////
/// @fn public static FB::HttpStreamResponsePtr FB::SimpleStreamHelper::SynchronousPost(const FB::BrowserHostPtr& host, const FB::URI& uri, const std::string& postdata, const bool cache = true, const size_t bufferSize = 128*1024)
Expand Down Expand Up @@ -280,7 +283,12 @@ namespace FB {


public:
SimpleStreamHelper( const HttpCallback& callback, const size_t blockSize = 128*1024 );
SimpleStreamHelper(
const HttpCallback& callback,
const HttpProgressCallback& progressCallback,
const HttpChunkCallback& chunkCallback,
const HttpCompletedCallback& completedCallback,
const size_t blockSize = 128*1024 );

virtual bool onStreamDataArrived(FB::StreamDataArrivedEvent *evt, FB::BrowserStream *);
virtual bool onStreamOpened(FB::StreamOpenedEvent *evt, FB::BrowserStream *);
Expand All @@ -293,7 +301,11 @@ namespace FB {
boost::shared_array<uint8_t> data;
const size_t blockSize;
size_t received;
size_t total;
HttpCallback callback;
HttpProgressCallback progressCallback;
HttpChunkCallback chunkCallback;
HttpCompletedCallback completedCallback;

private:
void keepReference(const SimpleStreamHelperPtr& ptr);
Expand Down
2 changes: 1 addition & 1 deletion src/ScriptingCore/BrowserHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ FB::BrowserStreamPtr FB::BrowserHost::createStream( const std::string& url,
FB::BrowserStreamPtr FB::BrowserHost::createStream( const BrowserStreamRequest& req, const bool enable_async ) const
{
assertMainThread();
if (enable_async && req.getCallback() && !req.getEventSink()) {
if (enable_async && (req.getCallback() || req.getChunkCallback()) && !req.getEventSink()) {
// If a callback was provided, use SimpleStreamHelper to create it;
// This will actually call back into this function with an event sink
BrowserStreamRequest newReq(req);
Expand Down