From 78b14420513565ee1b68e0ac5db550fd855b287c Mon Sep 17 00:00:00 2001 From: Leorize Date: Fri, 24 Jul 2020 23:45:42 -0500 Subject: [PATCH] libbnetapi: BUrlRequest now outputs to BDataIO Previously, BUrlRequest returns data received via a callback that can't return any value. This approach have several issues: - It's not possible to signify failures to the request. - Users have to implement custom listeners just to handle the common case of outputting to a buffer/file/etc. - The received data has to be serialized into BMessage when BUrlProtocolDispatchingListener is employed. This can cause a noticible slowdown in real-world scenarios as evident by #10748. With this change, BUrlRequest will output directly into a BDataIO, which exposes a richer API for request handlers to work with (for example a BitTorrent client can request a BPositionIO for non-linear data delivery), as well as simplifying common cases for users. The adaptation only requires one additional API: BHttpRequest::SetStopOnError(). This API simply instructs the HTTP request handler to cancel the request if an HTTP error is occurred. Change-Id: I4160884d77bff0e7678e0a623e2587987704443a Reviewed-on: https://review.haiku-os.org/c/haiku/+/3084 Reviewed-by: Adrien Destugues --- docs/user/netservices/UrlProtocolListener.dox | 24 +- docs/user/netservices/UrlProtocolRoster.dox | 4 +- headers/private/netservices/DataRequest.h | 8 +- headers/private/netservices/FileRequest.h | 7 + headers/private/netservices/GopherRequest.h | 11 + headers/private/netservices/HttpRequest.h | 17 + headers/private/netservices/NetworkRequest.h | 9 + .../UrlProtocolAsynchronousListener.h | 4 +- .../UrlProtocolDispatchingListener.h | 38 +- .../private/netservices/UrlProtocolListener.h | 24 +- .../private/netservices/UrlProtocolRoster.h | 13 +- headers/private/netservices/UrlRequest.h | 18 + .../netservices/UrlSynchronousRequest.h | 28 +- .../plugins/http_streamer/HTTPMediaIO.cpp | 37 +- src/apps/haikudepot/Jamfile | 2 +- .../server/AbstractServerProcess.cpp | 14 +- .../haikudepot/server/WebAppInterface.cpp | 29 +- .../util/LoggingUrlProtocolListener.cpp | 46 ++ .../util/LoggingUrlProtocolListener.h | 34 ++ .../util/ToFileUrlProtocolListener.cpp | 127 ----- .../util/ToFileUrlProtocolListener.h | 50 -- .../network/libnetservices/DataRequest.cpp | 35 +- .../network/libnetservices/FileRequest.cpp | 177 +++++- .../network/libnetservices/Geolocation.cpp | 18 +- .../network/libnetservices/GopherRequest.cpp | 515 +++++++++++++++++- .../network/libnetservices/HttpRequest.cpp | 389 ++++++++++++- .../network/libnetservices/NetworkRequest.cpp | 14 + .../UrlProtocolAsynchronousListener.cpp | 18 +- .../UrlProtocolDispatchingListener.cpp | 55 +- .../libnetservices/UrlProtocolListener.cpp | 29 +- .../libnetservices/UrlProtocolRoster.cpp | 27 + .../network/libnetservices/UrlRequest.cpp | 44 ++ .../libnetservices/UrlSynchronousRequest.cpp | 38 +- src/kits/package/FetchFileJob.cpp | 36 +- src/kits/package/FetchFileJob.h | 2 - src/tests/kits/net/service/DataTest.cpp | 16 +- src/tests/kits/net/service/DataTest.h | 8 +- src/tests/kits/net/service/FileTest.cpp | 24 +- src/tests/kits/net/service/HttpTest.cpp | 25 +- 39 files changed, 1562 insertions(+), 452 deletions(-) create mode 100644 src/apps/haikudepot/util/LoggingUrlProtocolListener.cpp create mode 100644 src/apps/haikudepot/util/LoggingUrlProtocolListener.h delete mode 100644 src/apps/haikudepot/util/ToFileUrlProtocolListener.cpp delete mode 100644 src/apps/haikudepot/util/ToFileUrlProtocolListener.h diff --git a/docs/user/netservices/UrlProtocolListener.dox b/docs/user/netservices/UrlProtocolListener.dox index 25c4c1e002..c857363b02 100644 --- a/docs/user/netservices/UrlProtocolListener.dox +++ b/docs/user/netservices/UrlProtocolListener.dox @@ -113,16 +113,18 @@ */ /*! - \fn virtual void BUrlProtocolListener::DataReceived(BUrlRequest* caller, - const char* data, off_t position, size_t size) - \brief Called each time a block of data is received. + \fn virtual void BUrlProtocolListener::BytesWritten(BUrlRequest* caller, + size_t size) + \brief Called each time a block of data is written. + + This callback is called whenever a block of data is written to the + BDataIO associated with the request. If no BDataIO is associated, the + callback will not be invoked. \b Frequency: Zero or more \param caller The BUrlRequest that invoked this callback. - \param data Pointer to the data block in memory. - \param position Offset of the data in the stream. - \param size Size of the data block. + \param size Size of the written data block. */ /*! @@ -130,15 +132,17 @@ off_t bytesReceived, off_t bytesTotal) \brief Called each time a block of data is downloaded. - This callback will usually be called after DataReceived(). + This callback might still be invoked even when no BDataIO is associated + with the request, as data can still be downloaded and discarded so that the + request can be completed and the socket can be reused. - \b Frequency: Once or more + \b Frequency: Zero or more \param caller The BUrlRequest that invoked this callback. \param bytesReceived Number of data bytes received. This is the number of bytes received prior to any processing and can be smaller than the - size of the data block sent to DataReceived() as the transport might - be compressed. + size of the data block written to the output BDataIO as the transport + might be compressed. \param bytesTotal Total number of data bytes expected. \c 0 will be passed if the total number of data bytes is not available. */ diff --git a/docs/user/netservices/UrlProtocolRoster.dox b/docs/user/netservices/UrlProtocolRoster.dox index 542e04ef28..dd53e223ca 100644 --- a/docs/user/netservices/UrlProtocolRoster.dox +++ b/docs/user/netservices/UrlProtocolRoster.dox @@ -27,10 +27,12 @@ /*! \fn static BUrlRequest* BUrlProtocolRoster::MakeRequest(const BUrl& url, - BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL) + BDataIO* output, BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL) \brief Create a BUrlRequest that can handle the given BUrl \param url The URL to create a request for + \param output The BDataIO to output to \param listener The BUrlProtocolListener to be registered with the created BUrlRequest, can be \c NULL \param context The BUrlContext to be registered with the created diff --git a/headers/private/netservices/DataRequest.h b/headers/private/netservices/DataRequest.h index 8657af1c74..82b72eecff 100644 --- a/headers/private/netservices/DataRequest.h +++ b/headers/private/netservices/DataRequest.h @@ -27,10 +27,16 @@ public: private: friend class BUrlProtocolRoster; +#ifdef LIBNETAPI_DEPRECATED BDataRequest(const BUrl& url, BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL); - +#else + BDataRequest(const BUrl& url, + BDataIO* output, + BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL); +#endif status_t _ProtocolLoop(); private: BUrlResult fResult; diff --git a/headers/private/netservices/FileRequest.h b/headers/private/netservices/FileRequest.h index ffc6d59bf9..f2d1931da4 100644 --- a/headers/private/netservices/FileRequest.h +++ b/headers/private/netservices/FileRequest.h @@ -29,9 +29,16 @@ public: private: friend class BUrlProtocolRoster; +#ifdef LIBNETAPI_DEPRECATED BFileRequest(const BUrl& url, BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL); +#else + BFileRequest(const BUrl& url, + BDataIO* output, + BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL); +#endif status_t _ProtocolLoop(); private: diff --git a/headers/private/netservices/GopherRequest.h b/headers/private/netservices/GopherRequest.h index d39ca5afa1..635d36ab0b 100644 --- a/headers/private/netservices/GopherRequest.h +++ b/headers/private/netservices/GopherRequest.h @@ -29,16 +29,27 @@ public: private: friend class BUrlProtocolRoster; +#ifdef LIBNETAPI_DEPRECATED BGopherRequest(const BUrl& url, BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL); +#else + BGopherRequest(const BUrl& url, + BDataIO* output, + BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL); +#endif status_t _ProtocolLoop(); void _SendRequest(); bool _NeedsParsing(); bool _NeedsLastDotStrip(); +#ifdef LIBNETAPI_DEPRECATED void _ParseInput(bool last); +#else + status_t _ParseInput(bool last); +#endif BString& _HTMLEscapeString(BString &str); diff --git a/headers/private/netservices/HttpRequest.h b/headers/private/netservices/HttpRequest.h index 36cd511e97..ac8383f903 100644 --- a/headers/private/netservices/HttpRequest.h +++ b/headers/private/netservices/HttpRequest.h @@ -41,6 +41,9 @@ public: void SetDiscardData(bool discard); void SetDisableListener(bool disable); void SetAutoReferrer(bool enable); +#ifndef LIBNETAPI_DEPRECATED + void SetStopOnError(bool stop); +#endif void SetUserName(const BString& name); void SetPassword(const BString& password); void SetRangeStart(off_t position); @@ -67,11 +70,20 @@ public: private: friend class BUrlProtocolRoster; +#ifdef LIBNETAPI_DEPRECATED BHttpRequest(const BUrl& url, bool ssl = false, const char* protocolName = "HTTP", BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL); +#else + BHttpRequest(const BUrl& url, + BDataIO* output, + bool ssl = false, + const char* protocolName = "HTTP", + BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL); +#endif BHttpRequest(const BHttpRequest& other); void _ResetOptions(); @@ -101,10 +113,12 @@ private: // Utility methods bool _IsDefaultPort(); +#ifdef LIBNETAPI_DEPRECATED // Listener notification void _NotifyDataReceived(const char* data, off_t pos, ssize_t length, off_t bytesReceived, ssize_t bytesTotal); +#endif private: bool fSSL; @@ -146,6 +160,9 @@ private: bool fOptDiscardData : 1; bool fOptDisableListener : 1; bool fOptAutoReferer : 1; +#ifndef LIBNETAPI_DEPRECATED + bool fOptStopOnError : 1; +#endif }; // Request method diff --git a/headers/private/netservices/NetworkRequest.h b/headers/private/netservices/NetworkRequest.h index 15650d827e..9357e5322d 100644 --- a/headers/private/netservices/NetworkRequest.h +++ b/headers/private/netservices/NetworkRequest.h @@ -23,11 +23,20 @@ namespace Network { class BNetworkRequest: public BUrlRequest { public: +#ifdef LIBNETAPI_DEPRECATED BNetworkRequest(const BUrl& url, BUrlProtocolListener* listener, BUrlContext* context, const char* threadName, const char* protocolName); +#else + BNetworkRequest(const BUrl& url, + BDataIO* output, + BUrlProtocolListener* listener, + BUrlContext* context, + const char* threadName, + const char* protocolName); +#endif virtual status_t Stop(); virtual void SetTimeout(bigtime_t timeout); diff --git a/headers/private/netservices/UrlProtocolAsynchronousListener.h b/headers/private/netservices/UrlProtocolAsynchronousListener.h index 4c5db18279..2c4521cdd5 100644 --- a/headers/private/netservices/UrlProtocolAsynchronousListener.h +++ b/headers/private/netservices/UrlProtocolAsynchronousListener.h @@ -25,9 +25,9 @@ public: // Synchronous listener access BUrlProtocolListener* SynchronousListener(); - + // BHandler interface - virtual void MessageReceived(BMessage* message); + virtual void MessageReceived(BMessage* message); private: BUrlProtocolDispatchingListener* diff --git a/headers/private/netservices/UrlProtocolDispatchingListener.h b/headers/private/netservices/UrlProtocolDispatchingListener.h index 6f32343434..2d993a0ddd 100644 --- a/headers/private/netservices/UrlProtocolDispatchingListener.h +++ b/headers/private/netservices/UrlProtocolDispatchingListener.h @@ -28,7 +28,11 @@ enum { B_URL_PROTOCOL_HOSTNAME_RESOLVED, B_URL_PROTOCOL_RESPONSE_STARTED, B_URL_PROTOCOL_HEADERS_RECEIVED, +#ifdef LIBNETAPI_DEPRECATED B_URL_PROTOCOL_DATA_RECEIVED, +#else + B_URL_PROTOCOL_BYTES_WRITTEN, +#endif B_URL_PROTOCOL_DOWNLOAD_PROGRESS, B_URL_PROTOCOL_UPLOAD_PROGRESS, B_URL_PROTOCOL_REQUEST_COMPLETED, @@ -46,44 +50,44 @@ public: virtual ~BUrlProtocolDispatchingListener(); virtual void ConnectionOpened(BUrlRequest* caller); - virtual void HostnameResolved(BUrlRequest* caller, + virtual void HostnameResolved(BUrlRequest* caller, const char* ip); - virtual void ResponseStarted(BUrlRequest* caller); -#ifdef LIBNETAPI_DEPRECATED - virtual void HeadersReceived(BUrlRequest* caller, - const BUrlResult& result); -#else - virtual void HeadersReceived(BUrlRequest* caller); -#endif - virtual void DataReceived(BUrlRequest* caller, - const char* data, off_t position, - ssize_t size); + virtual void ResponseStarted(BUrlRequest* caller); #ifdef LIBNETAPI_DEPRECATED + virtual void HeadersReceived(BUrlRequest* caller, + const BUrlResult& result); + virtual void DataReceived(BUrlRequest* caller, + const char* data, off_t position, + ssize_t size); virtual void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived, ssize_t bytesTotal); - virtual void UploadProgress(BUrlRequest* caller, + virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent, ssize_t bytesTotal); #else + virtual void HeadersReceived(BUrlRequest* caller); + virtual void BytesWritten(BUrlRequest* caller, + size_t bytesWritten); virtual void DownloadProgress(BUrlRequest* caller, off_t bytesReceived, off_t bytesTotal); - virtual void UploadProgress(BUrlRequest* caller, + virtual void UploadProgress(BUrlRequest* caller, off_t bytesSent, off_t bytesTotal); #endif - virtual void RequestCompleted(BUrlRequest* caller, + virtual void RequestCompleted(BUrlRequest* caller, bool success); - virtual void DebugMessage(BUrlRequest* caller, + virtual void DebugMessage(BUrlRequest* caller, BUrlProtocolDebugMessage type, const char* text); - virtual bool CertificateVerificationFailed( + virtual bool CertificateVerificationFailed( BUrlRequest* caller, BCertificate& certificate, const char* message); private: void _SendMessage(BMessage* message, - int8 notification, BUrlRequest* caller); + int8 notification, + BUrlRequest* caller); private: BMessenger fMessenger; diff --git a/headers/private/netservices/UrlProtocolListener.h b/headers/private/netservices/UrlProtocolListener.h index 68a5892e15..c8d40304fd 100644 --- a/headers/private/netservices/UrlProtocolListener.h +++ b/headers/private/netservices/UrlProtocolListener.h @@ -36,37 +36,35 @@ enum BUrlProtocolDebugMessage { class BUrlProtocolListener { public: virtual void ConnectionOpened(BUrlRequest* caller); - virtual void HostnameResolved(BUrlRequest* caller, + virtual void HostnameResolved(BUrlRequest* caller, const char* ip); - virtual void ResponseStarted(BUrlRequest* caller); + virtual void ResponseStarted(BUrlRequest* caller); #ifdef LIBNETAPI_DEPRECATED - virtual void HeadersReceived(BUrlRequest* caller, + virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result); -#else - virtual void HeadersReceived(BUrlRequest* caller); -#endif - virtual void DataReceived(BUrlRequest* caller, + virtual void DataReceived(BUrlRequest* caller, const char* data, off_t position, ssize_t size); - -#ifdef LIBNETAPI_DEPRECATED virtual void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived, ssize_t bytesTotal); - virtual void UploadProgress(BUrlRequest* caller, + virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent, ssize_t bytesTotal); #else + virtual void HeadersReceived(BUrlRequest* caller); + virtual void BytesWritten(BUrlRequest* caller, + size_t bytesWritten); virtual void DownloadProgress(BUrlRequest* caller, off_t bytesReceived, off_t bytesTotal); virtual void UploadProgress(BUrlRequest* caller, off_t bytesSent, off_t bytesTotal); #endif - virtual void RequestCompleted(BUrlRequest* caller, + virtual void RequestCompleted(BUrlRequest* caller, bool success); - virtual void DebugMessage(BUrlRequest* caller, + virtual void DebugMessage(BUrlRequest* caller, BUrlProtocolDebugMessage type, const char* text); - virtual bool CertificateVerificationFailed( + virtual bool CertificateVerificationFailed( BUrlRequest* caller, BCertificate& certificate, const char* message); diff --git a/headers/private/netservices/UrlProtocolRoster.h b/headers/private/netservices/UrlProtocolRoster.h index b763f4c0c8..3f438e55b9 100644 --- a/headers/private/netservices/UrlProtocolRoster.h +++ b/headers/private/netservices/UrlProtocolRoster.h @@ -10,6 +10,7 @@ #include +class BDataIO; class BUrl; #ifndef LIBNETAPI_DEPRECATED @@ -24,9 +25,15 @@ class BUrlRequest; class BUrlProtocolRoster { public: - static BUrlRequest* MakeRequest(const BUrl& url, - BUrlProtocolListener* listener = NULL, - BUrlContext* context = NULL); +#ifdef LIBNETAPI_DEPRECATED + static BUrlRequest* MakeRequest(const BUrl& url, + BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL); +#else + static BUrlRequest* MakeRequest(const BUrl& url, BDataIO* output, + BUrlProtocolListener* listener = NULL, + BUrlContext* context = NULL); +#endif }; #ifndef LIBNETAPI_DEPRECATED diff --git a/headers/private/netservices/UrlRequest.h b/headers/private/netservices/UrlRequest.h index 74c8eb0002..993bdfe118 100644 --- a/headers/private/netservices/UrlRequest.h +++ b/headers/private/netservices/UrlRequest.h @@ -22,11 +22,20 @@ namespace Network { class BUrlRequest { public: +#ifdef LIBNETAPI_DEPRECATED BUrlRequest(const BUrl& url, BUrlProtocolListener* listener, BUrlContext* context, const char* threadName, const char* protocolName); +#else + BUrlRequest(const BUrl& url, + BDataIO* output, + BUrlProtocolListener* listener, + BUrlContext* context, + const char* threadName, + const char* protocolName); +#endif virtual ~BUrlRequest(); // URL protocol thread management @@ -40,12 +49,18 @@ public: status_t SetUrl(const BUrl& url); status_t SetContext(BUrlContext* context); status_t SetListener(BUrlProtocolListener* listener); +#ifndef LIBNETAPI_DEPRECATED + status_t SetOutput(BDataIO* output); +#endif // URL protocol parameters access const BUrl& Url() const; BUrlContext* Context() const; BUrlProtocolListener* Listener() const; const BString& Protocol() const; +#ifndef LIBNETAPI_DEPRECATED + BDataIO* Output() const; +#endif // URL protocol informations bool IsRunning() const; @@ -63,6 +78,9 @@ protected: BUrl fUrl; BReference fContext; BUrlProtocolListener* fListener; +#ifndef LIBNETAPI_DEPRECATED + BDataIO* fOutput; +#endif bool fQuit; bool fRunning; diff --git a/headers/private/netservices/UrlSynchronousRequest.h b/headers/private/netservices/UrlSynchronousRequest.h index b9f937af1c..267376d753 100644 --- a/headers/private/netservices/UrlSynchronousRequest.h +++ b/headers/private/netservices/UrlSynchronousRequest.h @@ -20,42 +20,40 @@ class BUrlSynchronousRequest : public BUrlRequest, public BUrlProtocolListener { public: BUrlSynchronousRequest(BUrlRequest& asynchronousRequest); virtual ~BUrlSynchronousRequest() { }; - + // Synchronous wait virtual status_t Perform(); virtual status_t WaitUntilCompletion(); // Protocol hooks virtual void ConnectionOpened(BUrlRequest* caller); - virtual void HostnameResolved(BUrlRequest* caller, + virtual void HostnameResolved(BUrlRequest* caller, const char* ip); - virtual void ResponseStarted(BUrlRequest* caller); + virtual void ResponseStarted(BUrlRequest* caller); #ifdef LIBNETAPI_DEPRECATED - virtual void HeadersReceived(BUrlRequest* caller, + virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result); -#else - virtual void HeadersReceived(BUrlRequest* caller); -#endif - virtual void DataReceived(BUrlRequest* caller, + virtual void DataReceived(BUrlRequest* caller, const char* data, off_t position, ssize_t size); - -#ifdef LIBNETAPI_DEPRECATED virtual void DownloadProgress(BUrlRequest* caller, ssize_t bytesReceived, ssize_t bytesTotal); - virtual void UploadProgress(BUrlRequest* caller, + virtual void UploadProgress(BUrlRequest* caller, ssize_t bytesSent, ssize_t bytesTotal); #else + virtual void HeadersReceived(BUrlRequest* caller); + virtual void BytesWritten(BUrlRequest* caller, + size_t bytesWritten); virtual void DownloadProgress(BUrlRequest* caller, off_t bytesReceived, off_t bytesTotal); virtual void UploadProgress(BUrlRequest* caller, off_t bytesSent, off_t bytesTotal); -#endif //LIBNETAPI_DEPRECATED +#endif - virtual void RequestCompleted(BUrlRequest* caller, + virtual void RequestCompleted(BUrlRequest* caller, bool success); - - + + protected: bool fRequestComplete; BUrlRequest& fWrappedRequest; diff --git a/src/add-ons/media/plugins/http_streamer/HTTPMediaIO.cpp b/src/add-ons/media/plugins/http_streamer/HTTPMediaIO.cpp index aa352ead5b..32bf12c050 100644 --- a/src/add-ons/media/plugins/http_streamer/HTTPMediaIO.cpp +++ b/src/add-ons/media/plugins/http_streamer/HTTPMediaIO.cpp @@ -19,7 +19,7 @@ using namespace BPrivate::Network; -class FileListener : public BUrlProtocolListener { +class FileListener : public BUrlProtocolListener, public BDataIO { public: FileListener(HTTPMediaIO* owner) : @@ -50,33 +50,13 @@ public: } void HeadersReceived(BUrlRequest* request) - { - fAdapterIO->UpdateSize(); - } - - void DataReceived(BUrlRequest* request, const char* data, - off_t position, ssize_t size) { if (request != fRequest) { delete request; return; } - BHttpRequest* httpReq = dynamic_cast(request); - if (httpReq != NULL) { - const BHttpResult& httpRes - = (const BHttpResult&)httpReq->Result(); - int32 status = httpRes.StatusCode(); - if (BHttpRequest::IsClientErrorStatusCode(status) - || BHttpRequest::IsServerErrorStatusCode(status)) { - fRunning = false; - } else if (BHttpRequest::IsRedirectionStatusCode(status)) - return; - } - - _ReleaseInit(); - - fInputAdapter->Write(data, size); + fAdapterIO->UpdateSize(); } void RequestCompleted(BUrlRequest* request, bool success) @@ -89,6 +69,13 @@ public: fRequest = NULL; } + ssize_t Write(const void* data, size_t size) + { + _ReleaseInit(); + + return fInputAdapter->Write(data, size); + } + status_t LockOnInit(bigtime_t timeout) { return acquire_sem_etc(fInitSem, 1, B_RELATIVE_TIMEOUT, timeout); @@ -174,11 +161,15 @@ HTTPMediaIO::Open() fListener = new FileListener(this); - fReq = BUrlProtocolRoster::MakeRequest(fUrl, fListener); + fReq = BUrlProtocolRoster::MakeRequest(fUrl, fListener, fListener); if (fReq == NULL) return B_ERROR; + BHttpRequest* httpReq = dynamic_cast(fReq); + if (httpReq != NULL) + httpReq->SetStopOnError(true); + fReqThread = fReq->Run(); if (fReqThread < 0) return B_ERROR; diff --git a/src/apps/haikudepot/Jamfile b/src/apps/haikudepot/Jamfile index f5c944bec0..67875ff2ec 100644 --- a/src/apps/haikudepot/Jamfile +++ b/src/apps/haikudepot/Jamfile @@ -188,7 +188,7 @@ local applicationSources = LocaleUtils.cpp RepositoryUrlUtils.cpp StorageUtils.cpp - ToFileUrlProtocolListener.cpp + LoggingUrlProtocolListener.cpp # package_daemon ProblemWindow.cpp diff --git a/src/apps/haikudepot/server/AbstractServerProcess.cpp b/src/apps/haikudepot/server/AbstractServerProcess.cpp index ae79af8329..80ea1a906b 100644 --- a/src/apps/haikudepot/server/AbstractServerProcess.cpp +++ b/src/apps/haikudepot/server/AbstractServerProcess.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -24,7 +25,7 @@ #include "ServerSettings.h" #include "StandardMetaDataJsonEventListener.h" #include "StorageUtils.h" -#include "ToFileUrlProtocolListener.h" +#include "LoggingUrlProtocolListener.h" using namespace BPrivate::Network; @@ -330,8 +331,11 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, HDINFO("[%s] will stream '%s' to [%s]", Name(), url.UrlString().String(), targetFilePath.Path()); - ToFileUrlProtocolListener listener(targetFilePath, Name(), - Logger::IsTraceEnabled()); + LoggingUrlProtocolListener listener(Name(), Logger::IsTraceEnabled()); + BFile targetFile(targetFilePath.Path(), O_WRONLY | O_CREAT); + status_t err = targetFile.InitCheck(); + if (err != B_OK) + return err; BHttpHeaders headers; ServerSettings::AugmentHeaders(headers); @@ -347,7 +351,8 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, thread_id thread; - BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, &listener); + BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, &targetFile, + &listener); if (request == NULL) return B_NO_MEMORY; @@ -359,6 +364,7 @@ AbstractServerProcess::DownloadToLocalFile(const BPath& targetFilePath, fRequest->SetHeaders(headers); fRequest->SetMaxRedirections(0); fRequest->SetTimeout(TIMEOUT_MICROSECONDS); + fRequest->SetStopOnError(true); thread = fRequest->Run(); wait_for_thread(thread, NULL); diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp b/src/apps/haikudepot/server/WebAppInterface.cpp index 1001449a41..9977f2291f 100644 --- a/src/apps/haikudepot/server/WebAppInterface.cpp +++ b/src/apps/haikudepot/server/WebAppInterface.cpp @@ -37,8 +37,6 @@ using namespace BPrivate::Network; class ProtocolListener : public BUrlProtocolListener { public: ProtocolListener() - : - fDownloadIO(NULL) { } @@ -62,11 +60,8 @@ public: { } - virtual void DataReceived(BUrlRequest* caller, const char* data, - off_t position, ssize_t size) + virtual void BytesWritten(BUrlRequest* caller, size_t bytesWritten) { - if (fDownloadIO != NULL) - fDownloadIO->Write(data, size); } virtual void DownloadProgress(BUrlRequest* caller, off_t bytesReceived, @@ -88,23 +83,16 @@ public: { HDTRACE("jrpc: %s", text); } - - void SetDownloadIO(BDataIO* downloadIO) - { - fDownloadIO = downloadIO; - } - -private: - BDataIO* fDownloadIO; }; static BHttpRequest* -make_http_request(const BUrl& url, BUrlProtocolListener* listener = NULL, +make_http_request(const BUrl& url, BDataIO* output, + BUrlProtocolListener* listener = NULL, BUrlContext* context = NULL) { - BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, listener, - context); + BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, output, + listener, context); BHttpRequest* httpRequest = dynamic_cast(request); if (httpRequest == NULL) { delete request; @@ -864,7 +852,7 @@ WebAppInterface::_SendJsonRequest(const char* domain, headers.AddHeader("Accept", "application/json"); ServerSettings::AugmentHeaders(headers); - BHttpRequest* request = make_http_request(url, &listener, &context); + BHttpRequest* request = make_http_request(url, NULL, &listener, &context); ObjectDeleter _(request); if (request == NULL) return B_ERROR; @@ -884,7 +872,7 @@ WebAppInterface::_SendJsonRequest(const char* domain, request->AdoptInputData(requestData, requestDataSize); BMallocIO replyData; - listener.SetDownloadIO(&replyData); + request->SetOutput(&replyData); thread_id thread = request->Run(); wait_for_thread(thread, NULL); @@ -953,12 +941,11 @@ WebAppInterface::_SendRawGetRequest(const BString urlPathComponents, BUrl url = ServerSettings::CreateFullUrl(urlPathComponents); ProtocolListener listener; - listener.SetDownloadIO(stream); BHttpHeaders headers; ServerSettings::AugmentHeaders(headers); - BHttpRequest *request = make_http_request(url, &listener); + BHttpRequest *request = make_http_request(url, stream, &listener); ObjectDeleter _(request); if (request == NULL) return B_ERROR; diff --git a/src/apps/haikudepot/util/LoggingUrlProtocolListener.cpp b/src/apps/haikudepot/util/LoggingUrlProtocolListener.cpp new file mode 100644 index 0000000000..51d63b4606 --- /dev/null +++ b/src/apps/haikudepot/util/LoggingUrlProtocolListener.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2017-2020, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "LoggingUrlProtocolListener.h" + +#include +#include + +#include "Logger.h" + +using namespace BPrivate::Network; + + +LoggingUrlProtocolListener::LoggingUrlProtocolListener( + BString traceLoggingIdentifier, bool traceLogging) + : + fTraceLogging(traceLogging), + fTraceLoggingIdentifier(traceLoggingIdentifier), + fContentLength(0) +{ +} + + +void +LoggingUrlProtocolListener::BytesWritten(BUrlRequest* caller, + size_t bytesWritten) +{ + fContentLength += bytesWritten; +} + + +void +LoggingUrlProtocolListener::DebugMessage(BUrlRequest* caller, + BUrlProtocolDebugMessage type, const char* text) +{ + HDTRACE("url->file <%s>; %s", fTraceLoggingIdentifier.String(), text); +} + + +size_t +LoggingUrlProtocolListener::ContentLength() +{ + return fContentLength; +} diff --git a/src/apps/haikudepot/util/LoggingUrlProtocolListener.h b/src/apps/haikudepot/util/LoggingUrlProtocolListener.h new file mode 100644 index 0000000000..603150a5a3 --- /dev/null +++ b/src/apps/haikudepot/util/LoggingUrlProtocolListener.h @@ -0,0 +1,34 @@ +/* + * Copyright 2017, Andrew Lindesay . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include +#include + +using BPrivate::Network::BUrlProtocolDebugMessage; +using BPrivate::Network::BUrlProtocolListener; +using BPrivate::Network::BUrlRequest; + + +class LoggingUrlProtocolListener : public BUrlProtocolListener { +public: + LoggingUrlProtocolListener( + BString traceLoggingIdentifier, + bool traceLogging); + + size_t ContentLength(); + + void BytesWritten(BUrlRequest* caller, + size_t bytesWritten); + void DebugMessage(BUrlRequest* caller, + BUrlProtocolDebugMessage type, + const char* text); + +private: + bool fTraceLogging; + BString fTraceLoggingIdentifier; + size_t fContentLength; + + +}; diff --git a/src/apps/haikudepot/util/ToFileUrlProtocolListener.cpp b/src/apps/haikudepot/util/ToFileUrlProtocolListener.cpp deleted file mode 100644 index 1ed7b8a943..0000000000 --- a/src/apps/haikudepot/util/ToFileUrlProtocolListener.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2017-2020, Andrew Lindesay . - * All rights reserved. Distributed under the terms of the MIT License. - */ - -#include "ToFileUrlProtocolListener.h" - -#include -#include - -#include "Logger.h" - - -using namespace BPrivate::Network; - - -ToFileUrlProtocolListener::ToFileUrlProtocolListener(BPath path, - BString traceLoggingIdentifier, bool traceLogging) -{ - fDownloadIO = new BFile(path.Path(), O_WRONLY | O_CREAT); - fTraceLoggingIdentifier = traceLoggingIdentifier; - fTraceLogging = traceLogging; - fShouldDownload = true; - fContentLength = 0; -} - - -ToFileUrlProtocolListener::~ToFileUrlProtocolListener() -{ - delete fDownloadIO; -} - - -void -ToFileUrlProtocolListener::ConnectionOpened(BUrlRequest* caller) -{ -} - - -void -ToFileUrlProtocolListener::HostnameResolved(BUrlRequest* caller, - const char* ip) -{ -} - - -void -ToFileUrlProtocolListener::ResponseStarted(BUrlRequest* caller) -{ -} - - -void -ToFileUrlProtocolListener::HeadersReceived(BUrlRequest* caller) -{ - - // check that the status code is success. Only if it is successful - // should the payload be streamed to the file. - - const BHttpResult& httpResult = dynamic_cast( - caller->Result()); - int32 statusCode = httpResult.StatusCode(); - - if (!BHttpRequest::IsSuccessStatusCode(statusCode)) { - HDINFO("received http status %" B_PRId32 - " --> will not store download to file", statusCode); - fShouldDownload = false; - } - -} - - -void -ToFileUrlProtocolListener::DataReceived(BUrlRequest* caller, const char* data, - off_t position, ssize_t size) -{ - fContentLength += size; - - if (fShouldDownload && fDownloadIO != NULL && size > 0) { - size_t remaining = size; - size_t written = 0; - - do { - written = fDownloadIO->WriteAt(position, &data[size - remaining], - remaining); - remaining -= written; - } while (remaining > 0 && written > 0); - - if (remaining > 0) - HDERROR("unable to write all of the data to the file"); - } -} - - -void -ToFileUrlProtocolListener::DownloadProgress(BUrlRequest* caller, - off_t bytesReceived, off_t bytesTotal) -{ -} - - -void -ToFileUrlProtocolListener::UploadProgress(BUrlRequest* caller, - off_t bytesSent, off_t bytesTotal) -{ -} - - -void -ToFileUrlProtocolListener::RequestCompleted(BUrlRequest* caller, bool success) -{ -} - - -void -ToFileUrlProtocolListener::DebugMessage(BUrlRequest* caller, - BUrlProtocolDebugMessage type, const char* text) -{ - HDTRACE("url->file <%s>; %s", fTraceLoggingIdentifier.String(), text); -} - - -ssize_t -ToFileUrlProtocolListener::ContentLength() -{ - return fContentLength; -} diff --git a/src/apps/haikudepot/util/ToFileUrlProtocolListener.h b/src/apps/haikudepot/util/ToFileUrlProtocolListener.h deleted file mode 100644 index fd94f59566..0000000000 --- a/src/apps/haikudepot/util/ToFileUrlProtocolListener.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017, Andrew Lindesay . - * All rights reserved. Distributed under the terms of the MIT License. - */ - -#include -#include - - -using BPrivate::Network::BUrlProtocolDebugMessage; -using BPrivate::Network::BUrlProtocolListener; -using BPrivate::Network::BUrlRequest; -using BPrivate::Network::BUrlResult; - -class ToFileUrlProtocolListener : public BUrlProtocolListener { -public: - ToFileUrlProtocolListener(BPath path, - BString traceLoggingIdentifier, - bool traceLogging); - virtual ~ToFileUrlProtocolListener(); - - ssize_t ContentLength(); - - void ConnectionOpened(BUrlRequest* caller); - void HostnameResolved(BUrlRequest* caller, - const char* ip); - void ResponseStarted(BUrlRequest* caller); - void HeadersReceived(BUrlRequest* caller); - void DataReceived(BUrlRequest* caller, - const char* data, off_t position, - ssize_t size); - void DownloadProgress(BUrlRequest* caller, - off_t bytesReceived, off_t bytesTotal); - void UploadProgress(BUrlRequest* caller, - off_t bytesSent, off_t bytesTotal); - void RequestCompleted(BUrlRequest* caller, - bool success); - void DebugMessage(BUrlRequest* caller, - BUrlProtocolDebugMessage type, - const char* text); - -private: - bool fShouldDownload; - bool fTraceLogging; - BString fTraceLoggingIdentifier; - BPositionIO* fDownloadIO; - ssize_t fContentLength; - - -}; diff --git a/src/kits/network/libnetservices/DataRequest.cpp b/src/kits/network/libnetservices/DataRequest.cpp index bbac28f887..1f012ade7e 100644 --- a/src/kits/network/libnetservices/DataRequest.cpp +++ b/src/kits/network/libnetservices/DataRequest.cpp @@ -20,6 +20,7 @@ using namespace BPrivate::Network; #endif +#ifdef LIBNETAPI_DEPRECATED BDataRequest::BDataRequest(const BUrl& url, BUrlProtocolListener* listener, BUrlContext* context) : BUrlRequest(url, listener, context, "data URL parser", "data"), @@ -28,6 +29,19 @@ BDataRequest::BDataRequest(const BUrl& url, BUrlProtocolListener* listener, fResult.SetContentType("text/plain"); } +#else + +BDataRequest::BDataRequest(const BUrl& url, BDataIO* output, + BUrlProtocolListener* listener, + BUrlContext* context) + : + BUrlRequest(url, output, listener, context, "data URL parser", "data"), + fResult() +{ + fResult.SetContentType("text/plain"); +} +#endif // LIBNETAPI_DEPRECATED + const BUrlResult& BDataRequest::Result() const @@ -122,17 +136,30 @@ BDataRequest::_ProtocolLoop() fResult.SetLength(length); - if (fListener != NULL) { #ifdef LIBNETAPI_DEPRECATED + if (fListener != NULL) { fListener->HeadersReceived(this, fResult); -#else - fListener->HeadersReceived(this); -#endif if (length > 0) { fListener->DataReceived(this, payload, 0, length); fListener->DownloadProgress(this, length, length); } } +#else + if (fListener != NULL) + fListener->HeadersReceived(this); + if (length > 0) { + if (fOutput != NULL) { + size_t written = 0; + status_t err = fOutput->WriteExactly(payload, length, &written); + if (fListener != NULL && written > 0) + fListener->BytesWritten(this, written); + if (err != B_OK) + return err; + if (fListener != NULL) + fListener->DownloadProgress(this, written, written); + } + } +#endif // LIBNETAPI_DEPRECATED return B_OK; } diff --git a/src/kits/network/libnetservices/FileRequest.cpp b/src/kits/network/libnetservices/FileRequest.cpp index 81fa5c670d..da571c8187 100644 --- a/src/kits/network/libnetservices/FileRequest.cpp +++ b/src/kits/network/libnetservices/FileRequest.cpp @@ -21,6 +21,8 @@ using namespace BPrivate::Network; #endif + +#ifdef LIBNETAPI_DEPRECATED BFileRequest::BFileRequest(const BUrl& url, BUrlProtocolListener* listener, BUrlContext* context) : @@ -30,6 +32,18 @@ BFileRequest::BFileRequest(const BUrl& url, BUrlProtocolListener* listener, fUrl.UrlDecode(true); } +#else + +BFileRequest::BFileRequest(const BUrl& url, BDataIO* output, + BUrlProtocolListener* listener, BUrlContext* context) + : + BUrlRequest(url, output, listener, context, "BUrlProtocol.File", "file"), + fResult() +{ + fUrl.UrlDecode(true); +} +#endif // LIBNETAPI_DEPRECATED + BFileRequest::~BFileRequest() { @@ -46,6 +60,7 @@ BFileRequest::Result() const } +#ifdef LIBNETAPI_DEPRECATED status_t BFileRequest::_ProtocolLoop() { @@ -81,11 +96,7 @@ BFileRequest::_ProtocolLoop() return error; fResult.SetLength(size); -#ifdef LIBNETAPI_DEPRECATED fListener->HeadersReceived(this, fResult); -#else - fListener->HeadersReceived(this); -#endif ssize_t chunkSize = 0; char chunk[4096]; @@ -128,11 +139,7 @@ BFileRequest::_ProtocolLoop() if (fListener != NULL) { fListener->ConnectionOpened(this); -#ifdef LIBNETAPI_DEPRECATED fListener->HeadersReceived(this, fResult); -#else - fListener->HeadersReceived(this); -#endif // Add a parent directory entry. fListener->DataReceived(this, "+/,\t..\r\n", transferredSize, 8); @@ -184,3 +191,157 @@ BFileRequest::_ProtocolLoop() return fQuit ? B_INTERRUPTED : B_OK; } + +#else + +status_t +BFileRequest::_ProtocolLoop() +{ + BNode node(fUrl.Path().String()); + + if (node.IsSymLink()) { + // Traverse the symlink and start over + BEntry entry(fUrl.Path().String(), true); + node = BNode(&entry); + } + + ssize_t transferredSize = 0; + if (node.IsFile()) { + BFile file(fUrl.Path().String(), B_READ_ONLY); + status_t error = file.InitCheck(); + if (error != B_OK) + return error; + + BNodeInfo info(&file); + char mimeType[B_MIME_TYPE_LENGTH + 1]; + if (info.GetType(mimeType) != B_OK) + update_mime_info(fUrl.Path().String(), false, true, false); + if (info.GetType(mimeType) == B_OK) + fResult.SetContentType(mimeType); + + // Send all notifications to listener, if any + if (fListener != NULL) + fListener->ConnectionOpened(this); + + off_t size = 0; + error = file.GetSize(&size); + if (error != B_OK) + return error; + fResult.SetLength(size); + + if (fListener != NULL) + fListener->HeadersReceived(this); + + if (fOutput != NULL) { + ssize_t chunkSize = 0; + char chunk[4096]; + while (!fQuit) { + chunkSize = file.Read(chunk, sizeof(chunk)); + if (chunkSize > 0) { + size_t written = 0; + error = fOutput->WriteExactly(chunk, chunkSize, &written); + if (fListener != NULL && written > 0) + fListener->BytesWritten(this, written); + if (error != B_OK) + return error; + transferredSize += chunkSize; + if (fListener != NULL) + fListener->DownloadProgress(this, transferredSize, + size); + } else + break; + } + if (fQuit) + return B_INTERRUPTED; + // Return error if we didn't transfer everything + if (transferredSize != size) { + if (chunkSize < 0) + return (status_t)chunkSize; + else + return B_IO_ERROR; + } + } + + return B_OK; + } + + node_ref ref; + status_t error = node.GetNodeRef(&ref); + + // Stop here, and don't hit the assert below, if the file doesn't exist. + if (error != B_OK) + return error; + + assert(node.IsDirectory()); + BDirectory directory(&ref); + + fResult.SetContentType("application/x-ftp-directory; charset=utf-8"); + // This tells WebKit to use its FTP directory rendering code. + + if (fListener != NULL) { + fListener->ConnectionOpened(this); + fListener->HeadersReceived(this); + } + + if (fOutput != NULL) { + // Add a parent directory entry. + size_t written = 0; + error = fOutput->WriteExactly("+/,\t..\r\n", 8, &written); + if (fListener != NULL && written > 0) + fListener->BytesWritten(this, written); + if (error != B_OK) + return error; + transferredSize += written; + if (fListener != NULL) + fListener->DownloadProgress(this, transferredSize, 0); + + char name[B_FILE_NAME_LENGTH]; + BEntry entry; + while (!fQuit && directory.GetNextEntry(&entry) != B_ENTRY_NOT_FOUND) { + // We read directories using the EPLF (Easily Parsed List Format) + // This happens to be one of the formats that WebKit can understand, + // and it is not too hard to parse or generate. + // http://tools.ietf.org/html/draft-bernstein-eplf-02 + BString eplf("+"); + if (entry.IsFile() || entry.IsSymLink()) { + eplf += "r,"; + off_t fileSize; + if (entry.GetSize(&fileSize) == B_OK) + eplf << "s" << fileSize << ","; + } else if (entry.IsDirectory()) + eplf += "/,"; + + time_t modification; + if (entry.GetModificationTime(&modification) == B_OK) + eplf << "m" << modification << ","; + + mode_t permissions; + if (entry.GetPermissions(&permissions) == B_OK) + eplf << "up" << BString().SetToFormat("%03o", permissions) << ","; + + node_ref ref; + if (entry.GetNodeRef(&ref) == B_OK) + eplf << "i" << ref.device << "." << ref.node << ","; + + entry.GetName(name); + eplf << "\t" << name << "\r\n"; + size_t written = 0; + error = fOutput->WriteExactly(eplf.String(), eplf.Length(), + &written); + if (fListener != NULL && written > 0) + fListener->BytesWritten(this, written); + if (error != B_OK) + return error; + transferredSize += written; + if (fListener != NULL) + fListener->DownloadProgress(this, transferredSize, 0); + } + + if (!fQuit) + fResult.SetLength(transferredSize); + } + + return fQuit ? B_INTERRUPTED : B_OK; +} + +#endif // LIBNETAPI_DEPRECATED diff --git a/src/kits/network/libnetservices/Geolocation.cpp b/src/kits/network/libnetservices/Geolocation.cpp index 644c6fb06e..e86c5f47c1 100644 --- a/src/kits/network/libnetservices/Geolocation.cpp +++ b/src/kits/network/libnetservices/Geolocation.cpp @@ -40,17 +40,11 @@ class GeolocationListener: public BUrlProtocolListener pthread_mutex_lock(&fLock); } - void DataReceived(BUrlRequest*, const char* data, off_t position, - ssize_t size) { - fResult.WriteAt(position, data, size); - } - void RequestCompleted(BUrlRequest* caller, bool success) { pthread_cond_signal(&fCompletion); pthread_mutex_unlock(&fLock); } - BMallocIO fResult; pthread_cond_t fCompletion; pthread_mutex_t fLock; }; @@ -117,10 +111,11 @@ BGeolocation::LocateSelf(float& latitude, float& longitude) return B_DEVICE_NOT_FOUND; GeolocationListener listener; + BMallocIO resultBuffer; // Send Request (POST JSON message) BUrlRequest* request = BUrlProtocolRoster::MakeRequest(fGeolocationService, - &listener); + &resultBuffer, &listener); if (request == NULL) return B_BAD_DATA; @@ -153,7 +148,7 @@ BGeolocation::LocateSelf(float& latitude, float& longitude) } BMessage data; - result = BJson::Parse((char*)listener.fResult.Buffer(), data); + result = BJson::Parse((char*)resultBuffer.Buffer(), data); delete http; if (result != B_OK) { return result; @@ -192,8 +187,9 @@ BGeolocation::Country(const float latitude, const float longitude, url.SetRequest(requestString); GeolocationListener listener; + BMallocIO resultBuffer; BUrlRequest* request = BUrlProtocolRoster::MakeRequest(url, - &listener); + &resultBuffer, &listener); if (request == NULL) return B_BAD_DATA; @@ -223,9 +219,9 @@ BGeolocation::Country(const float latitude, const float longitude, } off_t length = 0; - listener.fResult.GetSize(&length); + resultBuffer.GetSize(&length); length -= 2; // Remove \r\n from response - BString countryCode((char*)listener.fResult.Buffer(), (int32)length); + BString countryCode((char*)resultBuffer.Buffer(), (int32)length); return country.SetTo(countryCode); } diff --git a/src/kits/network/libnetservices/GopherRequest.cpp b/src/kits/network/libnetservices/GopherRequest.cpp index 2c3d9f2764..37f4c94837 100644 --- a/src/kits/network/libnetservices/GopherRequest.cpp +++ b/src/kits/network/libnetservices/GopherRequest.cpp @@ -183,6 +183,7 @@ static const int32 kGopherBufferSize = 4096; static const bool kInlineImages = true; +#ifdef LIBNETAPI_DEPRECATED BGopherRequest::BGopherRequest(const BUrl& url, BUrlProtocolListener* listener, BUrlContext* context) : @@ -207,6 +208,34 @@ BGopherRequest::BGopherRequest(const BUrl& url, BUrlProtocolListener* listener, } } +#else + +BGopherRequest::BGopherRequest(const BUrl& url, BDataIO* output, + BUrlProtocolListener* listener, BUrlContext* context) + : + BNetworkRequest(url, output, listener, context, "BUrlProtocol.Gopher", + "gopher"), + fItemType(GOPHER_TYPE_NONE), + fPosition(0), + fResult() +{ + fSocket = new(std::nothrow) BSocket(); + + fUrl.UrlDecode(); + // the first part of the path is actually the document type + + fPath = Url().Path(); + if (!Url().HasPath() || fPath.Length() == 0 || fPath == "/") { + // default entry + fItemType = GOPHER_TYPE_DIRECTORY; + fPath = ""; + } else if (fPath.Length() > 1 && fPath[0] == '/') { + fItemType = fPath[1]; + fPath.Remove(0, 2); + } +} +#endif // LIBNETAPI_DEPRECATED + BGopherRequest::~BGopherRequest() { @@ -234,6 +263,165 @@ BGopherRequest::Result() const } +#ifdef LIBNETAPI_DEPRECATED +status_t +BGopherRequest::_ProtocolLoop() +{ + if (fSocket == NULL) + return B_NO_MEMORY; + + if (!_ResolveHostName(fUrl.Host(), fUrl.HasPort() ? fUrl.Port() : 70)) { + _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, + "Unable to resolve hostname (%s), aborting.", + fUrl.Host().String()); + return B_SERVER_NOT_FOUND; + } + + _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s on port %d.", + fUrl.Authority().String(), fRemoteAddr.Port()); + status_t connectError = fSocket->Connect(fRemoteAddr); + + if (connectError != B_OK) { + _EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Socket connection error %s", + strerror(connectError)); + return connectError; + } + + //! ProtocolHook:ConnectionOpened + if (fListener != NULL) + fListener->ConnectionOpened(this); + + _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, + "Connection opened, sending request."); + + _SendRequest(); + _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent."); + + // Receive loop + bool receiveEnd = false; + status_t readError = B_OK; + ssize_t bytesRead = 0; + //ssize_t bytesReceived = 0; + //ssize_t bytesTotal = 0; + bool dataValidated = false; + BStackOrHeapArray chunk(kGopherBufferSize); + + while (!fQuit && !receiveEnd) { + bytesRead = fSocket->Read(chunk, kGopherBufferSize); + + if (bytesRead < 0) { + readError = bytesRead; + break; + } else if (bytesRead == 0) + receiveEnd = true; + + fInputBuffer.AppendData(chunk, bytesRead); + + if (!dataValidated) { + size_t i; + // on error (file doesn't exist, ...) the server sends + // a faked directory entry with an error message + if (fInputBuffer.Size() && fInputBuffer.Data()[0] == '3') { + int tabs = 0; + bool crlf = false; + + // make sure the buffer only contains printable characters + // and has at least 3 tabs before a CRLF + for (i = 0; i < fInputBuffer.Size(); i++) { + char c = fInputBuffer.Data()[i]; + if (c == '\t') { + if (!crlf) + tabs++; + } else if (c == '\r' || c == '\n') { + if (tabs < 3) + break; + crlf = true; + } else if (!isprint(fInputBuffer.Data()[i])) { + crlf = false; + break; + } + } + if (crlf && tabs > 2 && tabs < 5) { + // TODO: + //if enough data + // else continue + fItemType = GOPHER_TYPE_DIRECTORY; + readError = B_RESOURCE_NOT_FOUND; + // continue parsing the error text anyway + } + } + // special case for buggy(?) Gophernicus/1.5 + static const char *buggy = "Error: File or directory not found!"; + if (fInputBuffer.Size() > strlen(buggy) + && !memcmp(fInputBuffer.Data(), buggy, strlen(buggy))) { + fItemType = GOPHER_TYPE_DIRECTORY; + readError = B_RESOURCE_NOT_FOUND; + // continue parsing the error text anyway + // but it won't look good + } + + // now we probably have correct data + dataValidated = true; + + //! ProtocolHook:ResponseStarted + if (fListener != NULL) + fListener->ResponseStarted(this); + + // now we can assign MIME type if we know it + const char *mime = "application/octet-stream"; + for (i = 0; gopher_type_map[i].type != GOPHER_TYPE_NONE; i++) { + if (gopher_type_map[i].type == fItemType) { + mime = gopher_type_map[i].mime; + break; + } + } + fResult.SetContentType(mime); + + // we don't really have headers but well... + //! ProtocolHook:HeadersReceived + if (fListener != NULL) + fListener->HeadersReceived(this, fResult); + } + + if (_NeedsParsing()) + _ParseInput(receiveEnd); + else if (fInputBuffer.Size()) { + // send input directly + if (fListener != NULL) { + fListener->DataReceived(this, (const char *)fInputBuffer.Data(), + fPosition, fInputBuffer.Size()); + } + + fPosition += fInputBuffer.Size(); + + if (fListener != NULL) + fListener->DownloadProgress(this, fPosition, 0); + + // XXX: this is plain stupid, we already copied the data + // and just want to drop it... + char *inputTempBuffer = new(std::nothrow) char[bytesRead]; + if (inputTempBuffer == NULL) { + readError = B_NO_MEMORY; + break; + } + fInputBuffer.RemoveData(inputTempBuffer, fInputBuffer.Size()); + delete[] inputTempBuffer; + } + } + + if (fPosition > 0) + fResult.SetLength(fPosition); + + fSocket->Disconnect(); + + if (readError != B_OK) + return readError; + + return fQuit ? B_INTERRUPTED : B_OK; +} + +# else + status_t BGopherRequest::_ProtocolLoop() { @@ -350,20 +538,22 @@ BGopherRequest::_ProtocolLoop() // we don't really have headers but well... //! ProtocolHook:HeadersReceived if (fListener != NULL) -#ifdef LIBNETAPI_DEPRECATED - fListener->HeadersReceived(this, fResult); -#else fListener->HeadersReceived(this); -#endif } if (_NeedsParsing()) - _ParseInput(receiveEnd); + readError = _ParseInput(receiveEnd); else if (fInputBuffer.Size()) { // send input directly - if (fListener != NULL) { - fListener->DataReceived(this, (const char *)fInputBuffer.Data(), - fPosition, fInputBuffer.Size()); + if (fOutput != NULL) { + size_t written = 0; + readError = fOutput->WriteExactly( + (const char*)fInputBuffer.Data(), fInputBuffer.Size(), + &written); + if (fListener != NULL && written > 0) + fListener->BytesWritten(this, written); + if (readError != B_OK) + break; } fPosition += fInputBuffer.Size(); @@ -393,6 +583,7 @@ BGopherRequest::_ProtocolLoop() return fQuit ? B_INTERRUPTED : B_OK; } +#endif // LIBNETAPI_DEPRECATED void @@ -432,6 +623,7 @@ BGopherRequest::_NeedsLastDotStrip() } +#ifdef LIBNETAPI_DEPRECATED void BGopherRequest::_ParseInput(bool last) { @@ -719,6 +911,313 @@ BGopherRequest::_ParseInput(bool last) } } +#else + +status_t +BGopherRequest::_ParseInput(bool last) +{ + BString line; + + while (_GetLine(line) == B_OK) { + char type = GOPHER_TYPE_NONE; + BStringList fields; + + line.MoveInto(&type, 0, 1); + + line.Split("\t", false, fields); + + if (type != GOPHER_TYPE_ENDOFPAGE + && fields.CountStrings() < FIELD_GPFLAG) + _EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, + "Unterminated gopher item (type '%c')", type); + + BString pageTitle; + BString item; + BString title = fields.StringAt(FIELD_NAME); + BString link("gopher://"); + BString user; + if (fields.CountStrings() > 3) { + link << fields.StringAt(FIELD_HOST); + if (fields.StringAt(FIELD_PORT).Length()) + link << ":" << fields.StringAt(FIELD_PORT); + link << "/" << type; + //if (fields.StringAt(FIELD_SELECTOR).ByteAt(0) != '/') + // link << "/"; + link << fields.StringAt(FIELD_SELECTOR); + } + _HTMLEscapeString(title); + _HTMLEscapeString(link); + + switch (type) { + case GOPHER_TYPE_ENDOFPAGE: + /* end of the page */ + break; + case GOPHER_TYPE_TEXTPLAIN: + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_BINARY: + case GOPHER_TYPE_BINHEX: + case GOPHER_TYPE_BINARCHIVE: + case GOPHER_TYPE_UUENCODED: + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_DIRECTORY: + /* + * directory link + */ + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_ERROR: + item << "" << title << "" + "
\n"; + if (fPosition == 0 && pageTitle.Length() == 0) + pageTitle << "Error: " << title; + break; + case GOPHER_TYPE_QUERY: + /* TODO: handle search better. + * For now we use an unnamed input field and accept sending ?=foo + * as it seems at least Veronica-2 ignores the = but it's unclean. + */ + item << "
" + "" + "" + "
" + "
\n"; + break; + case GOPHER_TYPE_TELNET: + /* telnet: links + * cf. gopher://78.80.30.202/1/ps3 + * -> gopher://78.80.30.202:23/8/ps3/new -> new@78.80.30.202 + */ + link = "telnet://"; + user = fields.StringAt(FIELD_SELECTOR); + if (user.FindLast('/') > -1) { + user.Remove(0, user.FindLast('/')); + link << user << "@"; + } + link << fields.StringAt(FIELD_HOST); + if (fields.StringAt(FIELD_PORT) != "23") + link << ":" << fields.StringAt(FIELD_PORT); + + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_TN3270: + /* tn3270: URI scheme, cf. http://tools.ietf.org/html/rfc6270 */ + link = "tn3270://"; + user = fields.StringAt(FIELD_SELECTOR); + if (user.FindLast('/') > -1) { + user.Remove(0, user.FindLast('/')); + link << user << "@"; + } + link << fields.StringAt(FIELD_HOST); + if (fields.StringAt(FIELD_PORT) != "23") + link << ":" << fields.StringAt(FIELD_PORT); + + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_CSO_SEARCH: + /* CSO search. + * At least Lynx supports a cso:// URI scheme: + * http://lynx.isc.org/lynx2.8.5/lynx2-8-5/lynx_help/lynx_url_support.html + */ + link = "cso://"; + user = fields.StringAt(FIELD_SELECTOR); + if (user.FindLast('/') > -1) { + user.Remove(0, user.FindLast('/')); + link << user << "@"; + } + link << fields.StringAt(FIELD_HOST); + if (fields.StringAt(FIELD_PORT) != "105") + link << ":" << fields.StringAt(FIELD_PORT); + + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_GIF: + case GOPHER_TYPE_IMAGE: + case GOPHER_TYPE_PNG: + case GOPHER_TYPE_BITMAP: + /* quite dangerous, cf. gopher://namcub.accela-labs.com/1/pics */ + if (kInlineImages) { + item << "" + "" << title << " " + "" + "" + "
\n"; + break; + } + /* fallback to default, link them */ + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_HTML: + /* cf. gopher://pineapple.vg/1 */ + if (fields.StringAt(FIELD_SELECTOR).StartsWith("URL:")) { + link = fields.StringAt(FIELD_SELECTOR); + link.Remove(0, 4); + } + /* cf. gopher://sdf.org/1/sdf/classes/ */ + + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_INFO: + // TITLE resource, cf. + // gopher://gophernicus.org/0/doc/gopher/gopher-title-resource.txt + if (fPosition == 0 && pageTitle.Length() == 0 + && fields.StringAt(FIELD_SELECTOR) == "TITLE") { + pageTitle = title; + break; + } + item << "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_AUDIO: + case GOPHER_TYPE_SOUND: + item << "" + "" << title << "" + "" + "
\n"; + break; + case GOPHER_TYPE_PDF: + case GOPHER_TYPE_DOC: + /* generic case for known-to-work items */ + item << "" + "" << title << "" + "
\n"; + break; + case GOPHER_TYPE_MOVIE: + item << "" + "" << title << "" + "