From f9e1854f198d4200f21a9cbe29fdfb0fabbe192f Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Sun, 29 Jan 2017 19:25:02 +0100 Subject: [PATCH] libbnetapi: fix access to HTTP headers The asynchronous listener had no reliable way to access HTTP result and headers from the callbacks. As the callbacks are triggered asynchronously, they can be run after the request has carried on and, for example, followed an HTTP redirect, clearing its internal state. The HeadersReceived callback now passes a reference to BUrlResult for the request. There are two cases: - Synchronous listener: passes a reference to the request's results directly - Asynchronous listener: archives a copy of the result into the notification message, and passes a reference to the unarchived copy. Unfortunately this comes with several ABI and API breakages: - Change to the prototype of HeadersReceived() - Change to the class hierarchy of BUrlResult (implements BArchivable) All users of HTTP requests will need to be updated if they implemented in HeadersReceived or used BUrlResult. --- headers/os/net/HttpHeaders.h | 5 ++ headers/os/net/HttpResult.h | 17 ++++--- .../os/net/UrlProtocolDispatchingListener.h | 3 +- headers/os/net/UrlProtocolListener.h | 7 ++- headers/os/net/UrlResult.h | 13 +++-- headers/os/net/UrlSynchronousRequest.h | 3 +- .../haikudepot/server/WebAppInterface.cpp | 2 +- src/kits/network/libnetapi/DataRequest.cpp | 2 +- src/kits/network/libnetapi/FileRequest.cpp | 4 +- src/kits/network/libnetapi/GopherRequest.cpp | 11 ++-- src/kits/network/libnetapi/HttpHeaders.cpp | 36 +++++++++++++ src/kits/network/libnetapi/HttpRequest.cpp | 4 +- src/kits/network/libnetapi/HttpResult.cpp | 50 ++++++++++++++++++- .../UrlProtocolAsynchronousListener.cpp | 19 +++++-- .../UrlProtocolDispatchingListener.cpp | 19 +++++-- .../network/libnetapi/UrlProtocolListener.cpp | 5 +- src/kits/network/libnetapi/UrlResult.cpp | 44 +++++++++++++++- .../libnetapi/UrlSynchronousRequest.cpp | 2 +- 18 files changed, 206 insertions(+), 40 deletions(-) diff --git a/headers/os/net/HttpHeaders.h b/headers/os/net/HttpHeaders.h index d3100dde3a..0b22845b72 100644 --- a/headers/os/net/HttpHeaders.h +++ b/headers/os/net/HttpHeaders.h @@ -7,6 +7,7 @@ #include +#include #include @@ -66,6 +67,10 @@ public: bool AddHeader(const char* name, int32 value); + // Archiving + void PopulateFromArchive(BMessage*); + void Archive(BMessage*) const; + // Header deletion void Clear(); diff --git a/headers/os/net/HttpResult.h b/headers/os/net/HttpResult.h index f2a6519b05..9875f99d5b 100644 --- a/headers/os/net/HttpResult.h +++ b/headers/os/net/HttpResult.h @@ -1,5 +1,5 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _B_HTTP_RESULT_H_ @@ -22,12 +22,13 @@ class BHttpResult: public BUrlResult { public: BHttpResult(const BUrl& url); + BHttpResult(BMessage*); BHttpResult(const BHttpResult& other); ~BHttpResult(); - + // Result parameters modifications void SetUrl(const BUrl& url); - + // Result parameters access const BUrl& Url() const; BString ContentType() const; @@ -37,13 +38,15 @@ public: const BHttpHeaders& Headers() const; const BString& StatusText() const; int32 StatusCode() const; - + // Result tests bool HasHeaders() const; - + // Overloaded members - BHttpResult& operator=(const BHttpResult& other); - + BHttpResult& operator=(const BHttpResult& other); + + virtual status_t Archive(BMessage*, bool) const; + static BArchivable* Instantiate(BMessage*); private: BUrl fUrl; diff --git a/headers/os/net/UrlProtocolDispatchingListener.h b/headers/os/net/UrlProtocolDispatchingListener.h index 29fa46f2c9..0107c2cfdc 100644 --- a/headers/os/net/UrlProtocolDispatchingListener.h +++ b/headers/os/net/UrlProtocolDispatchingListener.h @@ -44,7 +44,8 @@ public: virtual void HostnameResolved(BUrlRequest* caller, const char* ip); virtual void ResponseStarted(BUrlRequest* caller); - virtual void HeadersReceived(BUrlRequest* caller); + virtual void HeadersReceived(BUrlRequest* caller, + const BUrlResult& result); virtual void DataReceived(BUrlRequest* caller, const char* data, off_t position, ssize_t size); diff --git a/headers/os/net/UrlProtocolListener.h b/headers/os/net/UrlProtocolListener.h index 35c6f7cf15..bc98826240 100644 --- a/headers/os/net/UrlProtocolListener.h +++ b/headers/os/net/UrlProtocolListener.h @@ -1,5 +1,5 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _B_URL_PROTOCOL_LISTENER_H_ @@ -9,6 +9,8 @@ #include #include +#include + class BCertificate; class BUrlRequest; @@ -61,7 +63,8 @@ public: Called when all the server response metadata (such as headers) have been read and parsed. */ - virtual void HeadersReceived(BUrlRequest* caller); + virtual void HeadersReceived(BUrlRequest* caller, + const BUrlResult& result); /** DataReceived(data, position, size) diff --git a/headers/os/net/UrlResult.h b/headers/os/net/UrlResult.h index c9beb00e20..a0feb35dfd 100644 --- a/headers/os/net/UrlResult.h +++ b/headers/os/net/UrlResult.h @@ -1,26 +1,33 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _B_URL_RESULT_H_ #define _B_URL_RESULT_H_ +#include #include -class BUrlResult { +class BUrlResult: public BArchivable { public: + BUrlResult(); + BUrlResult(BMessage*); virtual ~BUrlResult(); + + virtual status_t Archive(BMessage*, bool) const; + void SetContentType(BString contentType); void SetLength(size_t length); virtual BString ContentType() const; virtual size_t Length() const; + static BArchivable* Instantiate(BMessage*); + private: BString fContentType; - BString fCharset; size_t fLength; }; diff --git a/headers/os/net/UrlSynchronousRequest.h b/headers/os/net/UrlSynchronousRequest.h index ae73736135..4a7dbfc3ef 100644 --- a/headers/os/net/UrlSynchronousRequest.h +++ b/headers/os/net/UrlSynchronousRequest.h @@ -24,7 +24,8 @@ public: virtual void HostnameResolved(BUrlRequest* caller, const char* ip); virtual void ResponseStarted(BUrlRequest* caller); - virtual void HeadersReceived(BUrlRequest* caller); + virtual void HeadersReceived(BUrlRequest* caller, + const BUrlResult& result); virtual void DataReceived(BUrlRequest* caller, const char* data, off_t position, ssize_t size); diff --git a/src/apps/haikudepot/server/WebAppInterface.cpp b/src/apps/haikudepot/server/WebAppInterface.cpp index 58f76b1e21..e1b2b7fbdf 100644 --- a/src/apps/haikudepot/server/WebAppInterface.cpp +++ b/src/apps/haikudepot/server/WebAppInterface.cpp @@ -210,7 +210,7 @@ public: { } - virtual void HeadersReceived(BUrlRequest* caller) + virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result) { } diff --git a/src/kits/network/libnetapi/DataRequest.cpp b/src/kits/network/libnetapi/DataRequest.cpp index b26b94f552..627f7b957b 100644 --- a/src/kits/network/libnetapi/DataRequest.cpp +++ b/src/kits/network/libnetapi/DataRequest.cpp @@ -118,7 +118,7 @@ BDataRequest::_ProtocolLoop() fResult.SetLength(length); if (fListener != NULL) { - fListener->HeadersReceived(this); + fListener->HeadersReceived(this, fResult); fListener->DownloadProgress(this, length, length); if (length > 0) fListener->DataReceived(this, payload, 0, length); diff --git a/src/kits/network/libnetapi/FileRequest.cpp b/src/kits/network/libnetapi/FileRequest.cpp index 6c7410bedc..f8af6555b4 100644 --- a/src/kits/network/libnetapi/FileRequest.cpp +++ b/src/kits/network/libnetapi/FileRequest.cpp @@ -77,7 +77,7 @@ BFileRequest::_ProtocolLoop() return error; fResult.SetLength(size); - fListener->HeadersReceived(this); + fListener->HeadersReceived(this, fResult); ssize_t chunkSize; char chunk[4096]; @@ -113,7 +113,7 @@ BFileRequest::_ProtocolLoop() if (fListener != NULL) { fListener->ConnectionOpened(this); - fListener->HeadersReceived(this); + fListener->HeadersReceived(this, fResult); // Add a parent directory entry. fListener->DataReceived(this, "+/,\t..\r\n", transferredSize, 8); diff --git a/src/kits/network/libnetapi/GopherRequest.cpp b/src/kits/network/libnetapi/GopherRequest.cpp index 13f3a8f11d..8b4bf68555 100644 --- a/src/kits/network/libnetapi/GopherRequest.cpp +++ b/src/kits/network/libnetapi/GopherRequest.cpp @@ -325,7 +325,6 @@ BGopherRequest::_ProtocolLoop() // continue parsing the error text anyway // but it won't look good } - // now we probably have correct data dataValidated = true; @@ -334,11 +333,6 @@ BGopherRequest::_ProtocolLoop() if (fListener != NULL) fListener->ResponseStarted(this); - // we don't really have headers but well... - //! ProtocolHook:HeadersReceived - if (fListener != NULL) - fListener->HeadersReceived(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++) { @@ -348,6 +342,11 @@ BGopherRequest::_ProtocolLoop() } } fResult.SetContentType(mime); + + // we don't really have headers but well... + //! ProtocolHook:HeadersReceived + if (fListener != NULL) + fListener->HeadersReceived(this, fResult); } if (_NeedsParsing()) diff --git a/src/kits/network/libnetapi/HttpHeaders.cpp b/src/kits/network/libnetapi/HttpHeaders.cpp index 352f2c1c64..fa17809992 100644 --- a/src/kits/network/libnetapi/HttpHeaders.cpp +++ b/src/kits/network/libnetapi/HttpHeaders.cpp @@ -245,6 +245,42 @@ BHttpHeaders::AddHeader(const char* name, int32 value) } +// #pragma mark Archiving + + +void +BHttpHeaders::PopulateFromArchive(BMessage* archive) +{ + Clear(); + + int32 index = 0; + char* nameFound; + for(;;) { + if (archive->GetInfo(B_STRING_TYPE, index, &nameFound, NULL) != B_OK) + return; + + BString value = archive->FindString(nameFound); + AddHeader(nameFound, value); + + index++; + } +} + + +void +BHttpHeaders::Archive(BMessage* message) const +{ + int32 count = CountHeaders(); + int32 i; + + for (i = 0; i < count; i++) + { + BHttpHeader& header = HeaderAt(i); + message->AddString(header.Name(), header.Value()); + } +} + + // #pragma mark Header deletion diff --git a/src/kits/network/libnetapi/HttpRequest.cpp b/src/kits/network/libnetapi/HttpRequest.cpp index d91654a9e6..6ee1463d7a 100644 --- a/src/kits/network/libnetapi/HttpRequest.cpp +++ b/src/kits/network/libnetapi/HttpRequest.cpp @@ -613,7 +613,7 @@ BHttpRequest::_MakeRequest() //! ProtocolHook:HeadersReceived if (fListener != NULL) - fListener->HeadersReceived(this); + fListener->HeadersReceived(this, fResult); // Parse received cookies if (fContext != NULL) { @@ -696,8 +696,6 @@ BHttpRequest::_MakeRequest() } chunkSize = strtol(chunkHeader.String(), NULL, 16); - PRINT(("BHP[%p] Chunk %s=%ld\n", this, - chunkHeader.String(), chunkSize)); if (chunkSize == 0) fRequestStatus = kRequestContentReceived; diff --git a/src/kits/network/libnetapi/HttpResult.cpp b/src/kits/network/libnetapi/HttpResult.cpp index eccca7b67b..04ee6e1c35 100644 --- a/src/kits/network/libnetapi/HttpResult.cpp +++ b/src/kits/network/libnetapi/HttpResult.cpp @@ -1,9 +1,10 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Christophe Huriaux, c.huriaux@gmail.com + * Adrien Destugues, pulkomandy@pulkomandy.tk */ @@ -23,6 +24,20 @@ BHttpResult::BHttpResult(const BUrl& url) } +BHttpResult::BHttpResult(BMessage* archive) + : + BUrlResult(archive) +{ + fUrl = archive->FindString("http:url"); + fStatusCode = archive->FindInt32("http:statusCode"); + fStatusString = archive->FindString("http:statusString"); + + BMessage headers; + archive->FindMessage("http:headers", &headers); + fHeaders.PopulateFromArchive(&headers); +} + + BHttpResult::BHttpResult(const BHttpResult& other) : fUrl(other.fUrl), @@ -114,7 +129,7 @@ BHttpResult::operator=(const BHttpResult& other) { if (this == &other) return *this; - + fUrl = other.fUrl; fHeaders = other.fHeaders; fStatusCode = other.fStatusCode; @@ -122,3 +137,34 @@ BHttpResult::operator=(const BHttpResult& other) return *this; } + + +status_t +BHttpResult::Archive(BMessage* target, bool deep) const +{ + status_t result = BUrlResult::Archive(target, deep); + if (result != B_OK) + return result; + + target->AddString("http:url", fUrl); + target->AddInt32("http:statusCode", fStatusCode); + target->AddString("http:statusString", fStatusString); + + BMessage headers; + fHeaders.Archive(&headers); + target->AddMessage("http:headers", &headers); + + return B_OK; +} + + +/*static*/ BArchivable* +BHttpResult::Instantiate(BMessage* archive) +{ + if (!validate_instantiation(archive, "BHttpResult")) + { + return NULL; + } + + return new BHttpResult(archive); +} diff --git a/src/kits/network/libnetapi/UrlProtocolAsynchronousListener.cpp b/src/kits/network/libnetapi/UrlProtocolAsynchronousListener.cpp index dc53a42351..00c783277d 100644 --- a/src/kits/network/libnetapi/UrlProtocolAsynchronousListener.cpp +++ b/src/kits/network/libnetapi/UrlProtocolAsynchronousListener.cpp @@ -1,22 +1,28 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Christophe Huriaux, c.huriaux@gmail.com + * Adrien Destugues, pulkomandy@pulkomandy.tk */ +#include + #include #include -#include +#include #include #include +#include + extern const char* kUrlProtocolMessageType; extern const char* kUrlProtocolCaller; + BUrlProtocolAsynchronousListener::BUrlProtocolAsynchronousListener( bool transparent) : @@ -92,7 +98,14 @@ BUrlProtocolAsynchronousListener::MessageReceived(BMessage* message) break; case B_URL_PROTOCOL_HEADERS_RECEIVED: - HeadersReceived(caller); + { + BMessage archive; + message->FindMessage("url:result", &archive); + BUrlResult* result = dynamic_cast( + instantiate_object(&archive)); + HeadersReceived(caller, *result); + delete result; + } break; case B_URL_PROTOCOL_DATA_RECEIVED: diff --git a/src/kits/network/libnetapi/UrlProtocolDispatchingListener.cpp b/src/kits/network/libnetapi/UrlProtocolDispatchingListener.cpp index ee4f184d2e..59b40901f5 100644 --- a/src/kits/network/libnetapi/UrlProtocolDispatchingListener.cpp +++ b/src/kits/network/libnetapi/UrlProtocolDispatchingListener.cpp @@ -1,14 +1,17 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Christophe Huriaux, c.huriaux@gmail.com + * Adrien Destugues, pulkomandy@pulkomandy.tk */ #include + #include +#include #include @@ -27,7 +30,7 @@ BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener (const BMessenger& messenger) - : + : fMessenger(messenger) { } @@ -66,9 +69,17 @@ BUrlProtocolDispatchingListener::ResponseStarted(BUrlRequest* caller) void -BUrlProtocolDispatchingListener::HeadersReceived(BUrlRequest* caller) +BUrlProtocolDispatchingListener::HeadersReceived(BUrlRequest* caller, + const BUrlResult& result) { + /* The URL request does not keep the headers valid after calling this + * method. For asynchronous delivery to work, we need to archive them + * into the message. */ BMessage message(B_URL_PROTOCOL_NOTIFICATION); + BMessage archive; + result.Archive(&archive, true); + message.AddMessage("url:result", &archive); + _SendMessage(&message, B_URL_PROTOCOL_HEADERS_RECEIVED, caller); } @@ -157,7 +168,7 @@ BUrlProtocolDispatchingListener::CertificateVerificationFailed( void -BUrlProtocolDispatchingListener::_SendMessage(BMessage* message, +BUrlProtocolDispatchingListener::_SendMessage(BMessage* message, int8 notification, BUrlRequest* caller) { ASSERT(message != NULL); diff --git a/src/kits/network/libnetapi/UrlProtocolListener.cpp b/src/kits/network/libnetapi/UrlProtocolListener.cpp index 70447d9710..3a96d94a1f 100644 --- a/src/kits/network/libnetapi/UrlProtocolListener.cpp +++ b/src/kits/network/libnetapi/UrlProtocolListener.cpp @@ -1,9 +1,10 @@ /* - * Copyright 2010 Haiku Inc. All rights reserved. + * Copyright 2010-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Christophe Huriaux, c.huriaux@gmail.com + * Adrien Destugues, pulkomandy@pulkomandy.tk */ #include @@ -42,7 +43,7 @@ BUrlProtocolListener::ResponseStarted(BUrlRequest*) void -BUrlProtocolListener::HeadersReceived(BUrlRequest*) +BUrlProtocolListener::HeadersReceived(BUrlRequest*, const BUrlResult& result) { } diff --git a/src/kits/network/libnetapi/UrlResult.cpp b/src/kits/network/libnetapi/UrlResult.cpp index 67191a2da8..454c71e397 100644 --- a/src/kits/network/libnetapi/UrlResult.cpp +++ b/src/kits/network/libnetapi/UrlResult.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013 Haiku Inc. All rights reserved. + * Copyright 2013-2017 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -10,11 +10,44 @@ #include +BUrlResult::BUrlResult() + : + BArchivable(), + fContentType(), + fLength(0) +{ +} + + +BUrlResult::BUrlResult(BMessage* archive) + : + BArchivable(archive) +{ + fContentType = archive->FindString("ContentType"); + fLength = archive->FindInt32("Length"); +} + + BUrlResult::~BUrlResult() { } +status_t +BUrlResult::Archive(BMessage* archive, bool deep) const +{ + status_t result = BArchivable::Archive(archive, deep); + + if (result != B_OK) + return result; + + archive->AddString("ContentType", fContentType); + archive->AddInt32("Length", fLength); + + return B_OK; +} + + void BUrlResult::SetContentType(BString contentType) { @@ -41,3 +74,12 @@ BUrlResult::Length() const { return fLength; } + + +/*static*/ BArchivable* +BUrlResult::Instantiate(BMessage* archive) +{ + if (!validate_instantiation(archive, "BUrlResult")) + return NULL; + return new BUrlResult(archive); +} diff --git a/src/kits/network/libnetapi/UrlSynchronousRequest.cpp b/src/kits/network/libnetapi/UrlSynchronousRequest.cpp index 859484b993..687ee224a1 100644 --- a/src/kits/network/libnetapi/UrlSynchronousRequest.cpp +++ b/src/kits/network/libnetapi/UrlSynchronousRequest.cpp @@ -71,7 +71,7 @@ BUrlSynchronousRequest::ResponseStarted(BUrlRequest*) void -BUrlSynchronousRequest::HeadersReceived(BUrlRequest*) +BUrlSynchronousRequest::HeadersReceived(BUrlRequest*, const BUrlResult& result) { PRINT(("SynchronousRequest::HeadersReceived()\n")); }