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

View File

@ -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);

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.
*/
#ifndef _B_URL_PROTOCOL_LISTENER_H_
@ -9,6 +9,8 @@
#include <stddef.h>
#include <cstdlib>
#include <UrlResult.h>
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)

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.
*/
#ifndef _B_URL_RESULT_H_
#define _B_URL_RESULT_H_
#include <Archivable.h>
#include <String.h>
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;
};

View File

@ -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);

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);
if (fListener != NULL) {
fListener->HeadersReceived(this);
fListener->HeadersReceived(this, fResult);
fListener->DownloadProgress(this, length, length);
if (length > 0)
fListener->DataReceived(this, payload, 0, length);

View File

@ -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);

View File

@ -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())

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

View File

@ -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;

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.
*
* 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);
}

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.
*
* Authors:
* Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/
#include <UrlProtocolAsynchronousListener.h>
#include <new>
#include <AppKit.h>
#include <UrlProtocolAsynchronousListener.h>
#include <Archivable.h>
#include <Debug.h>
#include <String.h>
#include <UrlResult.h>
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<BUrlResult*>(
instantiate_object(&archive));
HeadersReceived(caller, *result);
delete result;
}
break;
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.
*
* Authors:
* Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/
#include <UrlProtocolDispatchingListener.h>
#include <Debug.h>
#include <UrlResult.h>
#include <assert.h>
@ -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);

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.
*
* Authors:
* Christophe Huriaux, c.huriaux@gmail.com
* Adrien Destugues, pulkomandy@pulkomandy.tk
*/
#include <iostream>
@ -42,7 +43,7 @@ BUrlProtocolListener::ResponseStarted(BUrlRequest*)
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.
*
* Authors:
@ -10,11 +10,44 @@
#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()
{
}
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);
}

View File

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