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.
This commit is contained in:
Adrien Destugues 2017-01-29 19:25:02 +01:00
parent ab880b1753
commit f9e1854f19
18 changed files with 206 additions and 40 deletions

View File

@ -7,6 +7,7 @@
#include <List.h> #include <List.h>
#include <Message.h>
#include <String.h> #include <String.h>
@ -66,6 +67,10 @@ public:
bool AddHeader(const char* name, bool AddHeader(const char* name,
int32 value); int32 value);
// Archiving
void PopulateFromArchive(BMessage*);
void Archive(BMessage*) const;
// Header deletion // Header deletion
void Clear(); void Clear();

View File

@ -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. * Distributed under the terms of the MIT License.
*/ */
#ifndef _B_HTTP_RESULT_H_ #ifndef _B_HTTP_RESULT_H_
@ -22,12 +22,13 @@ class BHttpResult: public BUrlResult {
public: public:
BHttpResult(const BUrl& url); BHttpResult(const BUrl& url);
BHttpResult(BMessage*);
BHttpResult(const BHttpResult& other); BHttpResult(const BHttpResult& other);
~BHttpResult(); ~BHttpResult();
// Result parameters modifications // Result parameters modifications
void SetUrl(const BUrl& url); void SetUrl(const BUrl& url);
// Result parameters access // Result parameters access
const BUrl& Url() const; const BUrl& Url() const;
BString ContentType() const; BString ContentType() const;
@ -37,13 +38,15 @@ public:
const BHttpHeaders& Headers() const; const BHttpHeaders& Headers() const;
const BString& StatusText() const; const BString& StatusText() const;
int32 StatusCode() const; int32 StatusCode() const;
// Result tests // Result tests
bool HasHeaders() const; bool HasHeaders() const;
// Overloaded members // Overloaded members
BHttpResult& operator=(const BHttpResult& other); BHttpResult& operator=(const BHttpResult& other);
virtual status_t Archive(BMessage*, bool) const;
static BArchivable* Instantiate(BMessage*);
private: private:
BUrl fUrl; BUrl fUrl;

View File

@ -44,7 +44,8 @@ public:
virtual void HostnameResolved(BUrlRequest* caller, virtual void HostnameResolved(BUrlRequest* caller,
const char* ip); const char* ip);
virtual void ResponseStarted(BUrlRequest* caller); virtual void ResponseStarted(BUrlRequest* caller);
virtual void HeadersReceived(BUrlRequest* caller); virtual void HeadersReceived(BUrlRequest* caller,
const BUrlResult& result);
virtual void DataReceived(BUrlRequest* caller, virtual void DataReceived(BUrlRequest* caller,
const char* data, off_t position, const char* data, off_t position,
ssize_t size); ssize_t size);

View File

@ -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. * Distributed under the terms of the MIT License.
*/ */
#ifndef _B_URL_PROTOCOL_LISTENER_H_ #ifndef _B_URL_PROTOCOL_LISTENER_H_
@ -9,6 +9,8 @@
#include <stddef.h> #include <stddef.h>
#include <cstdlib> #include <cstdlib>
#include <UrlResult.h>
class BCertificate; class BCertificate;
class BUrlRequest; class BUrlRequest;
@ -61,7 +63,8 @@ public:
Called when all the server response metadata (such as headers) have Called when all the server response metadata (such as headers) have
been read and parsed. been read and parsed.
*/ */
virtual void HeadersReceived(BUrlRequest* caller); virtual void HeadersReceived(BUrlRequest* caller,
const BUrlResult& result);
/** /**
DataReceived(data, position, size) DataReceived(data, position, size)

View File

@ -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. * Distributed under the terms of the MIT License.
*/ */
#ifndef _B_URL_RESULT_H_ #ifndef _B_URL_RESULT_H_
#define _B_URL_RESULT_H_ #define _B_URL_RESULT_H_
#include <Archivable.h>
#include <String.h> #include <String.h>
class BUrlResult { class BUrlResult: public BArchivable {
public: public:
BUrlResult();
BUrlResult(BMessage*);
virtual ~BUrlResult(); virtual ~BUrlResult();
virtual status_t Archive(BMessage*, bool) const;
void SetContentType(BString contentType); void SetContentType(BString contentType);
void SetLength(size_t length); void SetLength(size_t length);
virtual BString ContentType() const; virtual BString ContentType() const;
virtual size_t Length() const; virtual size_t Length() const;
static BArchivable* Instantiate(BMessage*);
private: private:
BString fContentType; BString fContentType;
BString fCharset;
size_t fLength; size_t fLength;
}; };

View File

@ -24,7 +24,8 @@ public:
virtual void HostnameResolved(BUrlRequest* caller, virtual void HostnameResolved(BUrlRequest* caller,
const char* ip); const char* ip);
virtual void ResponseStarted(BUrlRequest* caller); virtual void ResponseStarted(BUrlRequest* caller);
virtual void HeadersReceived(BUrlRequest* caller); virtual void HeadersReceived(BUrlRequest* caller,
const BUrlResult& result);
virtual void DataReceived(BUrlRequest* caller, virtual void DataReceived(BUrlRequest* caller,
const char* data, off_t position, const char* data, off_t position,
ssize_t size); ssize_t size);

View File

@ -210,7 +210,7 @@ public:
{ {
} }
virtual void HeadersReceived(BUrlRequest* caller) virtual void HeadersReceived(BUrlRequest* caller, const BUrlResult& result)
{ {
} }

View File

@ -118,7 +118,7 @@ BDataRequest::_ProtocolLoop()
fResult.SetLength(length); fResult.SetLength(length);
if (fListener != NULL) { if (fListener != NULL) {
fListener->HeadersReceived(this); fListener->HeadersReceived(this, fResult);
fListener->DownloadProgress(this, length, length); fListener->DownloadProgress(this, length, length);
if (length > 0) if (length > 0)
fListener->DataReceived(this, payload, 0, length); fListener->DataReceived(this, payload, 0, length);

View File

@ -77,7 +77,7 @@ BFileRequest::_ProtocolLoop()
return error; return error;
fResult.SetLength(size); fResult.SetLength(size);
fListener->HeadersReceived(this); fListener->HeadersReceived(this, fResult);
ssize_t chunkSize; ssize_t chunkSize;
char chunk[4096]; char chunk[4096];
@ -113,7 +113,7 @@ BFileRequest::_ProtocolLoop()
if (fListener != NULL) { if (fListener != NULL) {
fListener->ConnectionOpened(this); fListener->ConnectionOpened(this);
fListener->HeadersReceived(this); fListener->HeadersReceived(this, fResult);
// Add a parent directory entry. // Add a parent directory entry.
fListener->DataReceived(this, "+/,\t..\r\n", transferredSize, 8); fListener->DataReceived(this, "+/,\t..\r\n", transferredSize, 8);

View File

@ -325,7 +325,6 @@ BGopherRequest::_ProtocolLoop()
// continue parsing the error text anyway // continue parsing the error text anyway
// but it won't look good // but it won't look good
} }
// now we probably have correct data // now we probably have correct data
dataValidated = true; dataValidated = true;
@ -334,11 +333,6 @@ BGopherRequest::_ProtocolLoop()
if (fListener != NULL) if (fListener != NULL)
fListener->ResponseStarted(this); 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 // now we can assign MIME type if we know it
const char *mime = "application/octet-stream"; const char *mime = "application/octet-stream";
for (i = 0; gopher_type_map[i].type != GOPHER_TYPE_NONE; i++) { for (i = 0; gopher_type_map[i].type != GOPHER_TYPE_NONE; i++) {
@ -348,6 +342,11 @@ BGopherRequest::_ProtocolLoop()
} }
} }
fResult.SetContentType(mime); fResult.SetContentType(mime);
// we don't really have headers but well...
//! ProtocolHook:HeadersReceived
if (fListener != NULL)
fListener->HeadersReceived(this, fResult);
} }
if (_NeedsParsing()) if (_NeedsParsing())

View File

@ -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 // #pragma mark Header deletion

View File

@ -613,7 +613,7 @@ BHttpRequest::_MakeRequest()
//! ProtocolHook:HeadersReceived //! ProtocolHook:HeadersReceived
if (fListener != NULL) if (fListener != NULL)
fListener->HeadersReceived(this); fListener->HeadersReceived(this, fResult);
// Parse received cookies // Parse received cookies
if (fContext != NULL) { if (fContext != NULL) {
@ -696,8 +696,6 @@ BHttpRequest::_MakeRequest()
} }
chunkSize = strtol(chunkHeader.String(), NULL, 16); chunkSize = strtol(chunkHeader.String(), NULL, 16);
PRINT(("BHP[%p] Chunk %s=%ld\n", this,
chunkHeader.String(), chunkSize));
if (chunkSize == 0) if (chunkSize == 0)
fRequestStatus = kRequestContentReceived; fRequestStatus = kRequestContentReceived;

View File

@ -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. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Christophe Huriaux, c.huriaux@gmail.com * 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) BHttpResult::BHttpResult(const BHttpResult& other)
: :
fUrl(other.fUrl), fUrl(other.fUrl),
@ -114,7 +129,7 @@ BHttpResult::operator=(const BHttpResult& other)
{ {
if (this == &other) if (this == &other)
return *this; return *this;
fUrl = other.fUrl; fUrl = other.fUrl;
fHeaders = other.fHeaders; fHeaders = other.fHeaders;
fStatusCode = other.fStatusCode; fStatusCode = other.fStatusCode;
@ -122,3 +137,34 @@ BHttpResult::operator=(const BHttpResult& other)
return *this; 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);
}

View File

@ -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. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Christophe Huriaux, c.huriaux@gmail.com * Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/ */
#include <UrlProtocolAsynchronousListener.h>
#include <new> #include <new>
#include <AppKit.h> #include <AppKit.h>
#include <UrlProtocolAsynchronousListener.h> #include <Archivable.h>
#include <Debug.h> #include <Debug.h>
#include <String.h> #include <String.h>
#include <UrlResult.h>
extern const char* kUrlProtocolMessageType; extern const char* kUrlProtocolMessageType;
extern const char* kUrlProtocolCaller; extern const char* kUrlProtocolCaller;
BUrlProtocolAsynchronousListener::BUrlProtocolAsynchronousListener( BUrlProtocolAsynchronousListener::BUrlProtocolAsynchronousListener(
bool transparent) bool transparent)
: :
@ -92,7 +98,14 @@ BUrlProtocolAsynchronousListener::MessageReceived(BMessage* message)
break; break;
case B_URL_PROTOCOL_HEADERS_RECEIVED: case B_URL_PROTOCOL_HEADERS_RECEIVED:
HeadersReceived(caller); {
BMessage archive;
message->FindMessage("url:result", &archive);
BUrlResult* result = dynamic_cast<BUrlResult*>(
instantiate_object(&archive));
HeadersReceived(caller, *result);
delete result;
}
break; break;
case B_URL_PROTOCOL_DATA_RECEIVED: case B_URL_PROTOCOL_DATA_RECEIVED:

View File

@ -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. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Christophe Huriaux, c.huriaux@gmail.com * Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/ */
#include <UrlProtocolDispatchingListener.h> #include <UrlProtocolDispatchingListener.h>
#include <Debug.h> #include <Debug.h>
#include <UrlResult.h>
#include <assert.h> #include <assert.h>
@ -27,7 +30,7 @@ BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener
BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener
(const BMessenger& messenger) (const BMessenger& messenger)
: :
fMessenger(messenger) fMessenger(messenger)
{ {
} }
@ -66,9 +69,17 @@ BUrlProtocolDispatchingListener::ResponseStarted(BUrlRequest* caller)
void 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 message(B_URL_PROTOCOL_NOTIFICATION);
BMessage archive;
result.Archive(&archive, true);
message.AddMessage("url:result", &archive);
_SendMessage(&message, B_URL_PROTOCOL_HEADERS_RECEIVED, caller); _SendMessage(&message, B_URL_PROTOCOL_HEADERS_RECEIVED, caller);
} }
@ -157,7 +168,7 @@ BUrlProtocolDispatchingListener::CertificateVerificationFailed(
void void
BUrlProtocolDispatchingListener::_SendMessage(BMessage* message, BUrlProtocolDispatchingListener::_SendMessage(BMessage* message,
int8 notification, BUrlRequest* caller) int8 notification, BUrlRequest* caller)
{ {
ASSERT(message != NULL); ASSERT(message != NULL);

View File

@ -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. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* Christophe Huriaux, c.huriaux@gmail.com * Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/ */
#include <iostream> #include <iostream>
@ -42,7 +43,7 @@ BUrlProtocolListener::ResponseStarted(BUrlRequest*)
void void
BUrlProtocolListener::HeadersReceived(BUrlRequest*) BUrlProtocolListener::HeadersReceived(BUrlRequest*, const BUrlResult& result)
{ {
} }

View File

@ -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. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
@ -10,11 +10,44 @@
#include <UrlResult.h> #include <UrlResult.h>
BUrlResult::BUrlResult()
:
BArchivable(),
fContentType(),
fLength(0)
{
}
BUrlResult::BUrlResult(BMessage* archive)
:
BArchivable(archive)
{
fContentType = archive->FindString("ContentType");
fLength = archive->FindInt32("Length");
}
BUrlResult::~BUrlResult() 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 void
BUrlResult::SetContentType(BString contentType) BUrlResult::SetContentType(BString contentType)
{ {
@ -41,3 +74,12 @@ BUrlResult::Length() const
{ {
return fLength; return fLength;
} }
/*static*/ BArchivable*
BUrlResult::Instantiate(BMessage* archive)
{
if (!validate_instantiation(archive, "BUrlResult"))
return NULL;
return new BUrlResult(archive);
}

View File

@ -71,7 +71,7 @@ BUrlSynchronousRequest::ResponseStarted(BUrlRequest*)
void void
BUrlSynchronousRequest::HeadersReceived(BUrlRequest*) BUrlSynchronousRequest::HeadersReceived(BUrlRequest*, const BUrlResult& result)
{ {
PRINT(("SynchronousRequest::HeadersReceived()\n")); PRINT(("SynchronousRequest::HeadersReceived()\n"));
} }