Patch done by Christophe Huriaux as part of GSoC 2010 "Services Kit" project:

Integrated the classes in the Network Kit (libbnetapi.so). Only the foundation
classed BUrl, BUrlContext, BNetworkCookie, BNetworkCookieJar and the private
HttpTime code is currently compiled. The BUrlProtocol currently contains some
misplaced BUrlProtocolHttp specific stuff, and the HTTP stuff itself has a
dependency on libcrypto and should live in an add-on instead. I've sprinkled
some TODOs in the code, and I've done some renaming compared to the last
version of the GSoC patch. Any help to bring this further along is appreciated.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@39161 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stephan Aßmus
2010-10-27 14:03:31 +00:00
parent 1294543de9
commit 45939109b4
34 changed files with 7746 additions and 1 deletions
+94
View File
@@ -0,0 +1,94 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_HTTP_AUTHENTICATION_H_
#define _B_HTTP_AUTHENTICATION_H_
#include <Url.h>
#include <String.h>
// HTTP authentication method
enum BHttpAuthenticationMethod {
B_HTTP_AUTHENTICATION_NONE = 0,
// No authentication
B_HTTP_AUTHENTICATION_BASIC = 1,
// Basic base64 authentication method (unsecure)
B_HTTP_AUTHENTICATION_DIGEST = 2,
// Digest authentication
B_HTTP_AUTHENTICATION_IE_DIGEST = 4
// Slightly modified digest authentication to mimic old IE one
};
enum BHttpAuthenticationAlgorithm {
B_HTTP_AUTHENTICATION_ALGORITHM_NONE,
B_HTTP_AUTHENTICATION_ALGORITHM_MD5,
B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS
};
enum BHttpAuthenticationQop {
B_HTTP_QOP_NONE,
B_HTTP_QOP_AUTH,
B_HTTP_QOP_AUTHINT
};
class BHttpAuthentication {
public:
BHttpAuthentication();
BHttpAuthentication(const BString& username,
const BString& password);
// Field modification
void SetUserName(const BString& username);
void SetPassword(const BString& password);
void SetMethod(
BHttpAuthenticationMethod type);
status_t Initialize(const BString& wwwAuthenticate);
// Field access
const BString& UserName() const;
const BString& Password() const;
BHttpAuthenticationMethod Method() const;
BString Authorization(const BUrl& url,
const BString& method) const;
// Base64 encoding
// TODO: Move to a common place. We may have multiple implementations
// in the Haiku tree...
static BString Base64Encode(const BString& string);
static BString Base64Decode(const BString& string);
private:
BString _DigestResponse(const BString& uri,
const BString& method) const;
// TODO: Rename these? _H seems to return a hash value,
// _KD returns a hash value of the "data" prepended by
// the "secret" string...
BString _H(const BString& value) const;
BString _KD(const BString& secret,
const BString& data) const;
private:
BHttpAuthenticationMethod fAuthenticationMethod;
BString fUserName;
BString fPassword;
BString fRealm;
BString fDigestNonce;
mutable BString fDigestCnonce;
mutable int fDigestNc;
BString fDigestOpaque;
bool fDigestStale;
BHttpAuthenticationAlgorithm fDigestAlgorithm;
BHttpAuthenticationQop fDigestQop;
BString fAuthorizationString;
};
#endif // _B_HTTP_AUTHENTICATION_H_
+192
View File
@@ -0,0 +1,192 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_HTTP_FORM_H_
#define _B_HTTP_FORM_H_
#include <Path.h>
#include <String.h>
#include <map>
enum form_type {
B_HTTP_FORM_URL_ENCODED,
B_HTTP_FORM_MULTIPART
};
enum form_content_type {
B_HTTPFORM_UNKNOWN,
B_HTTPFORM_STRING,
B_HTTPFORM_FILE,
B_HTTPFORM_BUFFER
};
class BHttpFormData {
private:
// Empty constructor is only kept for compatibility with map<>::operator[],
// but never used (see BHttpForm::operator[] which does the necessary
// check up)
BHttpFormData();
friend class std::map<BString, BHttpFormData>;
public:
BHttpFormData(const BString& name,
const BString& value);
BHttpFormData(const BString& name,
const BPath& file);
BHttpFormData(const BString& name,
const void* buffer, ssize_t size);
BHttpFormData(const BHttpFormData& other);
~BHttpFormData();
// Retrieve data informations
bool InitCheck() const;
const BString& Name() const;
const BString& String() const;
const BPath& File() const;
const void* Buffer() const;
ssize_t BufferSize() const;
bool IsFile() const;
const BString& Filename() const;
const BString& MimeType() const;
form_content_type Type() const;
// Change behavior
status_t MarkAsFile(const BString& filename,
const BString& mimeType);
status_t MarkAsFile(const BString& filename);
void UnmarkAsFile();
status_t CopyBuffer();
// Overloaded operators
BHttpFormData& operator=(const BHttpFormData& other);
private:
form_content_type fDataType;
bool fCopiedBuffer;
bool fFileMark;
BString fName;
BString fStringValue;
BPath fPathValue;
const void* fBufferValue;
ssize_t fBufferSize;
BString fFilename;
BString fMimeType;
};
class BHttpForm {
public:
// Nested types
class Iterator;
typedef std::map<BString, BHttpFormData> FormStorage;
public:
BHttpForm();
BHttpForm(const BHttpForm& other);
BHttpForm(const BString& formString);
~BHttpForm();
// Form string parsing
void ParseString(const BString& formString);
BString RawData() const;
// Form add
status_t AddString(const BString& name,
const BString& value);
status_t AddInt(const BString& name, int32 value);
status_t AddFile(const BString& fieldName,
const BPath& file);
status_t AddBuffer(const BString& fieldName,
const void* buffer, ssize_t size);
status_t AddBufferCopy(const BString& fieldName,
const void* buffer, ssize_t size);
// Mark a field as a filename
void MarkAsFile(const BString& fieldName,
const BString& filename,
const BString& mimeType);
void MarkAsFile(const BString& fieldName,
const BString& filename);
void UnmarkAsFile(const BString& fieldName);
// Change form type
void SetFormType(form_type type);
// Form test
bool HasField(const BString& name) const;
// Form retrieve
BString GetMultipartHeader(const BString& fieldName)
const;
form_content_type GetType(const BString& fieldname) const;
// Form informations
form_type GetFormType() const;
const BString& GetMultipartBoundary() const;
BString GetMultipartFooter() const;
ssize_t ContentLength() const;
// Form iterator
Iterator GetIterator();
// Form clear
void Clear();
// Overloaded operators
BHttpFormData& operator[](const BString& name);
private:
void _ExtractNameValuePair(const BString& string,
int32* index);
void _GenerateMultipartBoundary();
BString _GetMultipartHeader(
const BHttpFormData* element) const;
form_content_type _GetType(FormStorage::const_iterator it) const;
void _Erase(FormStorage::iterator it);
private:
friend class Iterator;
FormStorage fFields;
form_type fType;
BString fMultipartBoundary;
};
class BHttpForm::Iterator {
public:
Iterator(const Iterator& other);
BHttpFormData* Next();
bool HasNext() const;
void Remove();
BString MultipartHeader();
Iterator& operator=(const Iterator& other);
private:
Iterator(BHttpForm* form);
void _FindNext();
private:
friend class BHttpForm;
BHttpForm* fForm;
BHttpForm::FormStorage::iterator
fStdIterator;
BHttpFormData* fElement;
BHttpFormData* fPrevElement;
};
#endif // _B_HTTP_FORM_H_
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_HTTP_HEADERS_H_
#define _B_HTTP_HEADERS_H_
#include <List.h>
#include <String.h>
class BHttpHeader {
public:
BHttpHeader();
BHttpHeader(const char* string);
BHttpHeader(const char* name,
const char* value);
BHttpHeader(const BHttpHeader& copy);
// Header data modification
void SetName(const char* name);
void SetValue(const char* value);
bool SetHeader(const char* string);
// Header data access
const char* Name() const;
const char* Value() const;
const char* Header() const;
// Header data test
bool NameIs(const char* name) const;
// Overloaded members
BHttpHeader& operator=(const BHttpHeader& other);
private:
BString fName;
BString fValue;
mutable BString fRawHeader;
mutable bool fRawHeaderValid;
};
class BHttpHeaders {
public:
BHttpHeaders();
BHttpHeaders(const BHttpHeaders& copy);
~BHttpHeaders();
// Header list access
const char* HeaderValue(const char* name) const;
BHttpHeader& HeaderAt(int32 index) const;
// Header count
int32 CountHeaders() const;
// Header list tests
int32 HasHeader(const char* name) const;
// Header add or replacement
bool AddHeader(const char* line);
bool AddHeader(const char* name,
const char* value);
bool AddHeader(const char* name,
int32 value);
// Header deletion
void Clear();
// Overloaded operators
BHttpHeaders& operator=(const BHttpHeaders& other);
BHttpHeader& operator[](int32 index) const;
const char* operator[](const char* name) const;
private:
void _EraseData();
private:
BList fHeaderList;
};
#endif // _B_HTTP_HEADERS_H_
+126
View File
@@ -0,0 +1,126 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_NETWORK_COOKIE_H_
#define _B_NETWORK_COOKIE_H_
#include <Archivable.h>
#include <DateTime.h>
#include <Message.h>
#include <String.h>
#include <Url.h>
class BNetworkCookie : public BArchivable {
public:
BNetworkCookie(const char* name,
const char* value);
BNetworkCookie(const BNetworkCookie& other);
BNetworkCookie(const BString& cookieString);
BNetworkCookie(const BString& cookieString,
const BUrl& url);
BNetworkCookie(BMessage* archive);
BNetworkCookie();
virtual ~BNetworkCookie();
// Parse a "SetCookie" string, or "name=value"
BNetworkCookie& ParseCookieStringFromUrl(const BString& string,
const BUrl& url);
BNetworkCookie& ParseCookieString(const BString& cookieString);
// Modify the cookie fields
BNetworkCookie& SetComment(const BString& comment);
BNetworkCookie& SetCommentUrl(const BString& commentUrl);
BNetworkCookie& SetDiscard(bool discard);
BNetworkCookie& SetDomain(const BString& domain);
BNetworkCookie& SetMaxAge(int32 maxAge);
BNetworkCookie& SetExpirationDate(time_t expireDate);
BNetworkCookie& SetExpirationDate(BDateTime& expireDate);
BNetworkCookie& SetPath(const BString& path);
BNetworkCookie& SetSecure(bool secure);
BNetworkCookie& SetVersion(int8 version);
BNetworkCookie& SetName(const BString& name);
BNetworkCookie& SetValue(const BString& value);
// Access the cookie fields
const BString& CommentUrl() const;
const BString& Comment() const;
bool Discard() const;
const BString& Domain() const;
int32 MaxAge() const;
time_t ExpirationDate() const;
const BString& ExpirationString() const;
const BString& Path() const;
bool Secure() const;
int8 Version() const;
const BString& Name() const;
const BString& Value() const;
const BString& RawCookie(bool full) const;
bool IsSessionCookie() const;
bool IsValid(bool strict = false) const;
bool IsValidForUrl(const BUrl& url) const;
bool IsValidForDomain(const BString& domain) const;
bool IsValidForPath(const BString& path) const;
// Test if cookie fields are defined
bool HasCommentUrl() const;
bool HasComment() const;
bool HasDiscard() const;
bool HasDomain() const;
bool HasMaxAge() const;
bool HasExpirationDate() const;
bool HasPath() const;
bool HasVersion() const;
bool HasName() const;
bool HasValue() const;
// Test if cookie could be deleted
bool ShouldDeleteAtExit() const;
bool ShouldDeleteNow() const;
// BArchivable members
virtual status_t Archive(BMessage* into,
bool deep = true) const;
static BArchivable* Instantiate(BMessage* archive);
// Overloaded operators
BNetworkCookie& operator=(const BNetworkCookie& other);
BNetworkCookie& operator=(const char* string);
bool operator==(const BNetworkCookie& other);
bool operator!=(const BNetworkCookie& other);
private:
void _Reset();
void _ExtractNameValuePair(
const BString& cookieString, int16* index,
bool parseField = false);
private:
mutable BString fRawCookie;
mutable bool fRawCookieValid;
mutable BString fRawFullCookie;
mutable bool fRawFullCookieValid;
BString fComment;
BString fCommentUrl;
bool fDiscard;
BString fDomain;
BDateTime fExpiration;
mutable BString fExpirationString;
mutable bool fExpirationStringValid;
BString fPath;
bool fSecure;
int8 fVersion;
BString fName;
BString fValue;
bool fHasDiscard;
bool fHasExpirationDate;
bool fSessionCookie;
bool fHasVersion;
};
#endif // _B_NETWORK_COOKIE_H_
+141
View File
@@ -0,0 +1,141 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_NETWORK_COOKIE_JAR_H_
#define _B_NETWORK_COOKIE_JAR_H_
#include <Archivable.h>
#include <Flattenable.h>
#include <List.h>
#include <Message.h>
#include <NetworkCookie.h>
#include <String.h>
#include <Url.h>
typedef BList BNetworkCookieList;
class BNetworkCookieJar : public BArchivable, public BFlattenable {
public:
// Nested types
class Iterator;
class UrlIterator;
struct PrivateIterator;
struct PrivateHashMap;
public:
BNetworkCookieJar();
BNetworkCookieJar(
const BNetworkCookieJar& other);
BNetworkCookieJar(
const BNetworkCookieList& otherList);
BNetworkCookieJar(BMessage* archive);
virtual ~BNetworkCookieJar();
bool AddCookie(const BNetworkCookie& cookie);
bool AddCookie(BNetworkCookie* cookie);
bool AddCookies(
const BNetworkCookieList& cookies);
uint32 DeleteOutdatedCookies();
uint32 PurgeForExit();
// BArchivable members
virtual status_t Archive(BMessage* into,
bool deep = true) const;
static BArchivable* Instantiate(BMessage* archive);
// BFlattenable members
virtual bool IsFixedSize() const;
virtual type_code TypeCode() const;
virtual ssize_t FlattenedSize() const;
virtual status_t Flatten(void* buffer, ssize_t size)
const;
virtual bool AllowsTypeCode(type_code code) const;
virtual status_t Unflatten(type_code code,
const void* buffer, ssize_t size);
// Iterators
Iterator GetIterator() const;
UrlIterator GetUrlIterator(const BUrl& url) const;
private:
void _DoFlatten() const;
private:
friend class Iterator;
friend class UrlIterator;
PrivateHashMap* fCookieHashMap;
mutable BString fFlattened;
};
class BNetworkCookieJar::Iterator {
public:
Iterator(const Iterator& other);
~Iterator();
bool HasNext() const;
BNetworkCookie* Next();
BNetworkCookie* NextDomain();
BNetworkCookie* Remove();
void RemoveDomain();
Iterator& operator=(const Iterator& other);
private:
Iterator(const BNetworkCookieJar* map);
void _FindNext();
private:
friend class BNetworkCookieJar;
BNetworkCookieJar* fCookieJar;
PrivateIterator* fIterator;
BNetworkCookieList* fLastList;
BNetworkCookieList* fList;
BNetworkCookie* fElement;
BNetworkCookie* fLastElement;
int32 fIndex;
};
class BNetworkCookieJar::UrlIterator {
public:
UrlIterator(const UrlIterator& other);
~UrlIterator();
bool HasNext() const;
BNetworkCookie* Next();
BNetworkCookie* Remove();
UrlIterator& operator=(const UrlIterator& other);
private:
UrlIterator(const BNetworkCookieJar* map,
const BUrl& url);
bool _SupDomain();
void _FindNext();
void _FindDomain();
bool _FindPath();
private:
friend class BNetworkCookieJar;
BNetworkCookieJar* fCookieJar;
PrivateIterator* fIterator;
BNetworkCookieList* fList;
BNetworkCookieList* fLastList;
BNetworkCookie* fElement;
BNetworkCookie* fLastElement;
int32 fIndex;
int32 fLastIndex;
BUrl fUrl;
};
#endif // _B_NETWORK_COOKIE_JAR_
+145
View File
@@ -0,0 +1,145 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_H_
#define _B_URL_H_
#include <Archivable.h>
#include <Message.h>
#include <String.h>
class BUrl : public BArchivable {
public:
BUrl(const char* url);
BUrl(BMessage* archive);
BUrl(const BUrl& other);
BUrl();
virtual ~BUrl();
// URL fields modifiers
BUrl& SetUrlString(const BString& url);
BUrl& SetProtocol(const BString& scheme);
BUrl& SetUserName(const BString& user);
BUrl& SetPassword(const BString& password);
BUrl& SetHost(const BString& host);
BUrl& SetPort(int port);
BUrl& SetPath(const BString& path);
BUrl& SetRequest(const BString& request);
BUrl& SetFragment(const BString& fragment);
// URL fields access
const BString& UrlString() const;
const BString& Protocol() const;
const BString& UserName() const;
const BString& Password() const;
const BString& UserInfo() const;
const BString& Host() const;
int Port() const;
const BString& Authority() const;
const BString& Path() const;
const BString& Request() const;
const BString& Fragment() const;
// URL fields tests
bool IsValid() const;
bool HasProtocol() const;
bool HasUserName() const;
bool HasPassword() const;
bool HasUserInfo() const;
bool HasHost() const;
bool HasPort() const;
bool HasAuthority() const;
bool HasPath() const;
bool HasRequest() const;
bool HasFragment() const;
// Url encoding/decoding of needed fields
void UrlEncode(bool strict = false);
void UrlDecode(bool strict = false);
// Url encoding/decoding of strings
static BString UrlEncode(const BString& url,
bool strict = false,
bool directory = false);
static BString UrlDecode(const BString& url,
bool strict = false);
// BArchivable members
virtual status_t Archive(BMessage* into,
bool deep = true) const;
static BArchivable* Instantiate(BMessage* archive);
// URL comparison
bool operator==(BUrl& other) const;
bool operator!=(BUrl& other) const;
// URL assignment
const BUrl& operator=(const BUrl& other);
const BUrl& operator=(const BString& string);
const BUrl& operator=(const char* string);
// URL to string conversion
operator const char*() const;
private:
void _ResetFields();
void _ExplodeUrlString(const BString& urlString);
void _ExtractProtocol(const BString& urlString,
int16* origin);
void _ExtractAuthority(const BString& urlString,
int16* origin);
void _ExtractPath(const BString& urlString,
int16* origin);
void _ExtractRequestAndFragment(
const BString& urlString, int16* origin);
static BString _DoUrlEncodeChunk(const BString& chunk,
bool strict, bool directory = false);
static BString _DoUrlDecodeChunk(const BString& chunk,
bool strict);
bool _IsProtocolValid();
static bool _IsAuthorityTerminator(char c);
static bool _IsPathTerminator(char c);
static bool _IsRequestTerminator(char c);
static bool _IsUnreserved(char c);
static bool _IsGenDelim(char c);
static bool _IsSubDelim(char c);
private:
mutable BString fUrlString;
mutable BString fAuthority;
mutable BString fUserInfo;
BString fProtocol;
BString fUser;
BString fPassword;
BString fHost;
int fPort;
BString fPath;
BString fRequest;
BString fFragment;
mutable bool fUrlStringValid : 1;
mutable bool fAuthorityValid : 1;
mutable bool fUserInfoValid : 1;
bool fBasicUri : 1;
bool fHasProtocol : 1;
bool fHasUserName : 1;
bool fHasPassword : 1;
bool fHasUserInfo : 1;
bool fHasHost : 1;
bool fHasPort : 1;
bool fHasAuthority : 1;
bool fHasPath : 1;
bool fHasRequest : 1;
bool fHasFragment : 1;
};
#endif // _B_URL_H_
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_CONTEXT_H_
#define _B_URL_CONTEXT_H_
#include <NetworkCookieJar.h>
class BUrlContext {
public:
BUrlContext();
// Context modifiers
void SetCookieJar(
const BNetworkCookieJar& cookieJar);
// Context accessors
BNetworkCookieJar& GetCookieJar();
private:
BNetworkCookieJar fCookieJar;
};
#endif // _B_URL_CONTEXT_H_
+139
View File
@@ -0,0 +1,139 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_PROTOCOL_H_
#define _B_URL_PROTOCOL_H_
#include <Url.h>
#include <UrlResult.h>
#include <UrlContext.h>
#include <UrlProtocolListener.h>
#include <OS.h>
class BUrlProtocol {
public:
BUrlProtocol(const BUrl& url,
BUrlProtocolListener* listener,
BUrlContext* context,
BUrlResult* result,
const char* threadName,
const char* protocolName);
// URL protocol required members
// TODO: (stippi) I know it's sometimes appealing to have these
// "simplistic" methods that can do anything, but they remove
// type-safety. Why not overload SetOption with all possible types?
// Like:
// SetOption(uint32 option, bool value);
// SetOption(uint32 option, int8 value);
// SetOption(uint32 option, int16 value);
// SetOption(uint32 option, int32 value);
// ...
// This keeps the calling side even more simple, since it
// don't need to do pointer stuff with the parameters. Also, since
// this method is forced to be implemented in derived classes, why
// is it here at all? Why not have non-virtual setters for specific
// things, where the setter is properly named?
virtual status_t SetOption(uint32 name, void* value) = 0;
// URL protocol thread management
virtual thread_id Run();
virtual status_t Pause();
virtual status_t Resume();
virtual status_t Stop();
// URL protocol parameters modification
status_t SetUrl(const BUrl& url);
status_t SetResult(BUrlResult* result);
status_t SetContext(BUrlContext* context);
status_t SetListener(BUrlProtocolListener* listener);
// URL protocol parameters access
const BUrl& Url() const;
BUrlResult* Result() const;
BUrlContext* Context() const;
BUrlProtocolListener* Listener() const;
const BString& Protocol() const;
// TODO: Does not belong here.
BHttpHeaders& Headers() { return fRequestHeaders; }
// URL protocol informations
bool IsRunning() const;
status_t Status() const;
virtual const char* StatusString(status_t threadStatus)
const;
protected:
static int32 _ThreadEntry(void* arg);
virtual status_t _ProtocolLoop();
virtual void _EmitDebug(BUrlProtocolDebugMessage type,
const char* format, ...);
// URL result parameters access
BMallocIO& _ResultRawData();
BHttpHeaders& _ResultHeaders();
void _SetResultStatusCode(int32 statusCode);
BString& _ResultStatusText();
protected:
BUrl fUrl;
BHttpHeaders fRequestHeaders;
// TODO: Does not belong here.
BUrlResult* fResult;
BUrlContext* fContext;
BUrlProtocolListener* fListener;
bool fQuit;
bool fRunning;
status_t fThreadStatus;
thread_id fThreadId;
BString fThreadName;
BString fProtocol;
};
// TODO: Rename, this is in the global namespace.
enum {
B_PROT_THREAD_STATUS__BASE = 0,
B_PROT_SUCCESS = B_PROT_THREAD_STATUS__BASE,
B_PROT_RUNNING,
B_PROT_PAUSED,
B_PROT_ABORTED,
B_PROT_SOCKET_ERROR,
B_PROT_CONNECTION_FAILED,
B_PROT_CANT_RESOLVE_HOSTNAME,
B_PROT_WRITE_FAILED,
B_PROT_READ_FAILED,
B_PROT_NO_MEMORY,
B_PROT_PROTOCOL_ERROR,
// Thread status over this one are guaranteed to be
// errors
B_PROT_THREAD_STATUS__END
};
namespace BPrivate {
class BUrlProtocolOption {
public:
BUrlProtocolOption(void* value) : fValuePtr(value) { }
bool Bool() const { return *reinterpret_cast<bool*>(fValuePtr); }
int8 Int8() const { return *reinterpret_cast<int8*>(fValuePtr); }
int16 Int16() const { return *reinterpret_cast<int16*>(fValuePtr); }
int32 Int32() const { return *reinterpret_cast<int32*>(fValuePtr); }
char* String() const { return reinterpret_cast<char*>(fValuePtr); }
void* Pointer() const { return fValuePtr; }
private:
void* fValuePtr;
};
} // namespace BPrivate
#endif // _B_URL_PROTOCOL_H_
@@ -0,0 +1,46 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_PROTOCOL_ASYNCHRONOUS_LISTENER_H_
#define _B_URL_PROTOCOL_ASYNCHRONOUS_LISTENER_H_
#include <Handler.h>
#include <Message.h>
#include <UrlProtocolDispatchingListener.h>
class BUrlProtocolAsynchronousListener : public BHandler {
public:
BUrlProtocolAsynchronousListener(
bool transparent = false);
virtual ~BUrlProtocolAsynchronousListener();
virtual void ConnectionOpened(BUrlProtocol* caller);
virtual void HostnameResolved(BUrlProtocol* caller,
const char* ip);
virtual void ResponseStarted(BUrlProtocol* caller);
virtual void HeadersReceived(BUrlProtocol* caller);
virtual void DataReceived(BUrlProtocol* caller,
const char* data, ssize_t size);
virtual void DownloadProgress(BUrlProtocol* caller,
ssize_t bytesReceived, ssize_t bytesTotal);
virtual void UploadProgress(BUrlProtocol* caller,
ssize_t bytesSent, ssize_t bytesTotal);
virtual void RequestCompleted(BUrlProtocol* caller,
bool success);
// Synchronous listener access
BUrlProtocolListener* SynchronousListener();
// BHandler interface
virtual void MessageReceived(BMessage* message);
private:
BUrlProtocolDispatchingListener*
fSynchronousListener;
};
#endif // _B_URL_PROTOCOL_ASYNCHRONOUS_LISTENER_H_
@@ -0,0 +1,66 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_PROTOCOL_DISPATCHING_LISTENER_H_
#define _B_URL_PROTOCOL_DISPATCHING_LISTENER_H_
#include <Messenger.h>
#include <Message.h>
#include <UrlProtocolListener.h>
//! To be in AppTypes.h
enum {
B_URL_PROTOCOL_NOTIFICATION = '_UPN'
};
// Notification types
enum {
B_URL_PROTOCOL_CONNECTION_OPENED,
B_URL_PROTOCOL_HOSTNAME_RESOLVED,
B_URL_PROTOCOL_RESPONSE_STARTED,
B_URL_PROTOCOL_HEADERS_RECEIVED,
B_URL_PROTOCOL_DATA_RECEIVED,
B_URL_PROTOCOL_DOWNLOAD_PROGRESS,
B_URL_PROTOCOL_UPLOAD_PROGRESS,
B_URL_PROTOCOL_REQUEST_COMPLETED
};
class BUrlProtocolDispatchingListener : public BUrlProtocolListener {
public:
BUrlProtocolDispatchingListener(
BHandler* handler);
BUrlProtocolDispatchingListener(
const BMessenger& messenger);
virtual void ConnectionOpened(BUrlProtocol* caller);
virtual void HostnameResolved(BUrlProtocol* caller,
const char* ip);
virtual void ResponseStarted(BUrlProtocol* caller);
virtual void HeadersReceived(BUrlProtocol* caller);
virtual void DataReceived(BUrlProtocol* caller,
const char* data, ssize_t size);
virtual void DownloadProgress(BUrlProtocol* caller,
ssize_t bytesReceived, ssize_t bytesTotal);
virtual void UploadProgress(BUrlProtocol* caller,
ssize_t bytesSent, ssize_t bytesTotal);
virtual void RequestCompleted(BUrlProtocol* caller,
bool success);
virtual void DebugMessage(BUrlProtocol*,
BUrlProtocolDebugMessage,
const char*) { }
private:
void _SendMessage(BMessage* message,
int8 notification, BUrlProtocol* caller);
private:
BMessenger fMessenger;
};
#endif // _B_URL_PROTOCOL_DISPATCHING_LISTENER_H_
+235
View File
@@ -0,0 +1,235 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_PROTOCOL_HTTP_H_
#define _B_URL_PROTOCOL_HTTP_H_
#include <deque>
#include <HttpAuthentication.h>
#include <HttpForm.h>
#include <HttpHeaders.h>
#include <NetEndpoint.h>
#include <NetBuffer.h>
#include <UrlProtocol.h>
class BUrlProtocolHttp : public BUrlProtocol {
public:
BUrlProtocolHttp(BUrl& url,
BUrlProtocolListener* listener = NULL,
BUrlContext* context = NULL,
BUrlResult* result = NULL);
virtual status_t SetOption(uint32 option, void* value);
static bool IsInformationalStatusCode(int16 code);
static bool IsSuccessStatusCode(int16 code);
static bool IsRedirectionStatusCode(int16 code);
static bool IsClientErrorStatusCode(int16 code);
static bool IsServerErrorStatusCode(int16 code);
static int16 StatusCodeClass(int16 code);
virtual const char* StatusString(status_t threadStatus) const;
private:
void _ResetOptions();
status_t _ProtocolLoop();
bool _ResolveHostName();
status_t _MakeRequest();
void _CreateRequest();
void _AddHeaders();
status_t _GetLine(BString& destString);
void _ParseStatus();
void _ParseHeaders();
void _CopyChunkInBuffer(char** buffer,
ssize_t* bytesReceived);
void _AddOutputBufferLine(const char* line);
private:
BNetEndpoint fSocket;
BNetAddress fRemoteAddr;
int8 fRequestMethod;
int8 fHttpVersion;
BString fOutputBuffer;
BNetBuffer fInputBuffer;
BHttpHeaders fHeaders;
BHttpAuthentication fAuthentication;
// Request status
BHttpHeaders fOutputHeaders;
bool fStatusReceived;
bool fHeadersReceived;
bool fContentReceived;
bool fTrailingHeadersReceived;
// Protocol options
uint8 fOptMaxRedirs;
BString fOptReferer;
BString fOptUserAgent;
BString fOptUsername;
BString fOptPassword;
uint32 fOptAuthMethods;
BHttpHeaders* fOptHeaders;
BHttpForm* fOptPostFields;
BDataIO* fOptInputData;
bool fOptSetCookies : 1;
bool fOptFollowLocation : 1;
bool fOptDiscardData : 1;
bool fOptDisableListener : 1;
bool fOptAutoReferer : 1;
};
// ProtocolLoop return status
enum {
B_PROT_HTTP_NOT_FOUND = B_PROT_THREAD_STATUS__END,
B_PROT_HTTP_THREAD_STATUS__END
};
// Request method
enum {
B_HTTP_GET = 1,
B_HTTP_POST,
B_HTTP_PUT,
B_HTTP_HEAD,
B_HTTP_DELETE,
B_HTTP_OPTIONS
};
// HTTP Version
enum {
B_HTTP_10 = 1,
B_HTTP_11
};
// HTTP Protocol options
enum {
B_HTTPOPT_METHOD = 0,
// (int) Request method (see B_HTTP_GET, ...)
B_HTTPOPT_FOLLOWLOCATION,
// (bool) Follow Location: headers
B_HTTPOPT_MAXREDIRS,
// (int) Max relocation
B_HTTPOPT_HEADERS,
// (BHttpHeaders*) Headers to be sent
B_HTTPOPT_REFERER,
// (string) Referer
B_HTTPOPT_USERAGENT,
// (string) User-Agent
B_HTTPOPT_SETCOOKIES,
// (bool) Send cookies from context
B_HTTPOPT_DISCARD_DATA,
// (bool) Discard incoming data (still notified)
B_HTTPOPT_DISABLE_LISTENER,
// (bool) Don't send notification to the listener
B_HTTPOPT_AUTOREFERER,
// (bool) Automatically set the Referer header
B_HTTPOPT_POSTFIELDS,
// (BHttpForm*) POST data to be sent
B_HTTPOPT_INPUTDATA,
// (BDataIO*) Input data to be sent (POST, PUT)
B_HTTPOPT_AUTHUSERNAME,
// (string) Authentication username
B_HTTPOPT_AUTHPASSWORD,
// (string) Authentication password
B_HTTPOPT_AUTHMETHOD,
// (int) Allowed authentication methods (see BHttpAuthenticationMethod)
B_HTTPOPT__OPT_NUM
};
// HTTP status classes
enum http_status_code_class {
B_HTTP_STATUS_CLASS_INVALID = 000,
B_HTTP_STATUS_CLASS_INFORMATIONAL = 100,
B_HTTP_STATUS_CLASS_SUCCESS = 200,
B_HTTP_STATUS_CLASS_REDIRECTION = 300,
B_HTTP_STATUS_CLASS_CLIENT_ERROR = 400,
B_HTTP_STATUS_CLASS_SERVER_ERROR = 500
};
// Known HTTP status codes
enum http_status_code {
// Informational status codes
B_HTTP_STATUS__INFORMATIONAL_BASE = 100,
B_HTTP_STATUS_CONTINUE = B_HTTP_STATUS__INFORMATIONAL_BASE,
B_HTTP_STATUS_SWITCHING_PROTOCOLS,
B_HTTP_STATUS__INFORMATIONAL_END,
// Success status codes
B_HTTP_STATUS__SUCCESS_BASE = 200,
B_HTTP_STATUS_OK = B_HTTP_STATUS__SUCCESS_BASE,
B_HTTP_STATUS_CREATED,
B_HTTP_STATUS_ACCEPTED,
B_HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION,
B_HTTP_STATUS_NO_CONTENT,
B_HTTP_STATUS_RESET_CONTENT,
B_HTTP_STATUS_PARTIAL_CONTENT,
B_HTTP_STATUS__SUCCESS_END,
// Redirection status codes
B_HTTP_STATUS__REDIRECTION_BASE = 300,
B_HTTP_STATUS_MULTIPLE_CHOICE = B_HTTP_STATUS__REDIRECTION_BASE,
B_HTTP_STATUS_MOVED_PERMANENTLY,
B_HTTP_STATUS_FOUND,
B_HTTP_STATUS_SEE_OTHER,
B_HTTP_STATUS_NOT_MODIFIED,
B_HTTP_STATUS_USE_PROXY,
B_HTTP_STATUS_TEMPORARY_REDIRECT,
B_HTTP_STATUS__REDIRECTION_END,
// Client error status codes
B_HTTP_STATUS__CLIENT_ERROR_BASE = 400,
B_HTTP_STATUS_BAD_REQUEST = B_HTTP_STATUS__CLIENT_ERROR_BASE,
B_HTTP_STATUS_UNAUTHORIZED,
B_HTTP_STATUS_PAYMENT_REQUIRED,
B_HTTP_STATUS_FORBIDDEN,
B_HTTP_STATUS_NOT_FOUND,
B_HTTP_STATUS_METHOD_NOT_ALLOWED,
B_HTTP_STATUS_NOT_ACCEPTABLE,
B_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED,
B_HTTP_STATUS_REQUEST_TIMEOUT,
B_HTTP_STATUS_CONFLICT,
B_HTTP_STATUS_GONE,
B_HTTP_STATUS_LENGTH_REQUIRED,
B_HTTP_STATUS_PRECONDITION_FAILED,
B_HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE,
B_HTTP_STATUS_REQUEST_URI_TOO_LARGE,
B_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE,
B_HTTP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE,
B_HTTP_STATUS_EXPECTATION_FAILED,
B_HTTP_STATUS__CLIENT_ERROR_END,
// Server error status codes
B_HTTP_STATUS__SERVER_ERROR_BASE = 500,
B_HTTP_STATUS_INTERNAL_SERVER_ERROR = B_HTTP_STATUS__SERVER_ERROR_BASE,
B_HTTP_STATUS_NOT_IMPLEMENTED,
B_HTTP_STATUS_BAD_GATEWAY,
B_HTTP_STATUS_SERVICE_UNAVAILABLE,
B_HTTP_STATUS_GATEWAY_TIMEOUT,
B_HTTP_STATUS__SERVER_ERROR_END
};
// HTTP default User-Agent
#define B_HTTP_PROTOCOL_USER_AGENT_FORMAT "ServicesKit (%s)"
#endif // _B_URL_PROTOCOL_HTTP_H_
+120
View File
@@ -0,0 +1,120 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_PROTOCOL_LISTENER_H_
#define _B_URL_PROTOCOL_LISTENER_H_
#include <stddef.h>
#include <cstdlib>
class BUrlProtocol;
enum BUrlProtocolDebugMessage {
B_URL_PROTOCOL_DEBUG_TEXT,
B_URL_PROTOCOL_DEBUG_ERROR,
B_URL_PROTOCOL_DEBUG_HEADER_IN,
B_URL_PROTOCOL_DEBUG_HEADER_OUT,
B_URL_PROTOCOL_DEBUG_TRANSFER_IN,
B_URL_PROTOCOL_DEBUG_TRANSFER_OUT
};
class BUrlProtocolListener {
public:
/*
ConnectionOpened()
Frequency: Once
Called when the socket is opened.
*/
virtual void ConnectionOpened(BUrlProtocol* caller);
/*
HostnameResolved(ip)
Frequency: Once
Parameters: ip String representing the IP address of the resource
host.
Called when the final IP is discovered
*/
virtual void HostnameResolved(BUrlProtocol* caller,
const char* ip);
/*
ReponseStarted()
Frequency: Once
Called when the request has been emitted and the server begins to
reply. Typically when the HTTP status code is received.
*/
virtual void ResponseStarted(BUrlProtocol* caller);
/*
HeadersReceived()
Frequency: Once
Called when all the server response metadata (such as headers) have
been read and parsed.
*/
virtual void HeadersReceived(BUrlProtocol* caller);
/*
DataReceived(data, size)
Frequency: Zero or more
Parameters: data Pointer to the data block in memory
size Size of the data block
Called each time a full block of data is received.
*/
virtual void DataReceived(BUrlProtocol* caller,
const char* data, ssize_t size);
/*
DownloadProgress(bytesReceived, bytesTotal)
Frequency: Once or more
Parameters: bytesReceived Number of data bytes received
bytesTotal Total number of data bytes expected
Called each time a data block is received.
*/
virtual void DownloadProgress(BUrlProtocol* caller,
ssize_t bytesReceived, ssize_t bytesTotal);
/*
UploadProgress(bytesSent, bytesTotal)
Frequency: Once or more
Parameters: bytesSent Number of data bytes sent
bytesTotal Total number of data bytes expected
Called each time a data block is emitted.
*/
virtual void UploadProgress(BUrlProtocol* caller,
ssize_t bytesSent, ssize_t bytesTotal);
/*
RequestCompleted(success)
Frequency: Once
Parameters: success true if the resource have been successfully
false if not
Called once the request is complete.
*/
virtual void RequestCompleted(BUrlProtocol* caller,
bool success);
/*
DebugMessage(type, text)
Frequency: zero or more
Parameters: type Type of the verbose message (see BUrlProtocolDebug)
Called each time a debug message is emitted
*/
virtual void DebugMessage(BUrlProtocol* caller,
BUrlProtocolDebugMessage type,
const char* text);
};
#endif // _B_URL_PROTOCOL_LISTENER_H_
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_REQUEST_H_
#define _B_URL_REQUEST_H_
#include <HttpHeaders.h>
#include <Url.h>
#include <UrlResult.h>
#include <UrlContext.h>
#include <UrlProtocol.h>
#include <UrlProtocolListener.h>
enum {
B_NO_HANDLER_FOR_PROTOCOL = B_ERROR
};
class BUrlRequest {
public:
BUrlRequest(const BUrl& url,
BUrlProtocolListener* listener);
BUrlRequest(const BUrl& url);
BUrlRequest(const BUrlRequest& other);
virtual ~BUrlRequest() { };
// Request parameters modification
status_t SetUrl(const BUrl& url);
void SetContext(BUrlContext* context);
void SetProtocolListener(
BUrlProtocolListener* listener);
bool SetProtocolOption(int32 option,
void* value);
// Request parameters access
const BUrlProtocol* Protocol();
const BUrlResult& Result();
const BUrl& Url();
// Request control
status_t Identify();
virtual status_t Perform();
// TODO: Rename to Run() perhaps? "Perform" is used for FBC stuff.
virtual status_t Pause();
virtual status_t Resume();
virtual status_t Abort();
// Request informations
virtual bool InitCheck() const;
bool IsRunning() const;
status_t Status() const;
// Overloaded members
BUrlRequest& operator=(const BUrlRequest& other);
protected:
BUrlProtocolListener* fListener;
BUrlProtocol* fUrlProtocol;
BUrlResult fResult;
BUrlContext* fContext;
BUrl fUrl;
bool fReady;
};
#endif // _B_URL_REQUEST_H_
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_RESULT_H_
#define _B_URL_RESULT_H_
#include <iostream>
#include <DataIO.h>
#include <HttpHeaders.h>
#include <String.h>
#include <Url.h>
class BUrlProtocol;
class BUrlResult {
friend class BUrlProtocol;
public:
BUrlResult(const BUrl& url);
BUrlResult(const BUrlResult& other);
// Result parameters modifications
void SetUrl(const BUrl& url);
// Result parameters access
const BUrl& Url() const;
const BMallocIO& RawData() const;
const BHttpHeaders& Headers() const;
const BString& StatusText() const;
int32 StatusCode() const;
// Result tests
bool HasHeaders() const;
// Overloaded members
BUrlResult& operator=(const BUrlResult& other);
friend std::ostream& operator<<(std::ostream& out,
const BUrlResult& result);
private:
BUrl fUrl;
BMallocIO fRawData;
BHttpHeaders fHeaders;
// TODO: HTTP specific stuff should not live here.
int32 fStatusCode;
BString fStatusString;
};
#endif // _B_URL_RESULT_H_
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_URL_SYNCHRONOUS_REQUEST_H_
#define _B_URL_SYNCHRONOUS_REQUEST_H_
#include <UrlRequest.h>
#include <UrlProtocolListener.h>
class BUrlSynchronousRequest : public BUrlRequest, public BUrlProtocolListener {
public:
BUrlSynchronousRequest(BUrl& url);
virtual ~BUrlSynchronousRequest() { };
// Synchronous wait
virtual status_t Perform();
virtual status_t WaitUntilCompletion();
// Protocol hooks
virtual void ConnectionOpened(BUrlProtocol* caller);
virtual void HostnameResolved(BUrlProtocol* caller,
const char* ip);
virtual void ResponseStarted(BUrlProtocol* caller);
virtual void HeadersReceived(BUrlProtocol* caller);
virtual void DataReceived(BUrlProtocol* caller,
const char* data, ssize_t size);
virtual void DownloadProgress(BUrlProtocol* caller,
ssize_t bytesReceived, ssize_t bytesTotal);
virtual void UploadProgress(BUrlProtocol* caller,
ssize_t bytesSent, ssize_t bytesTotal);
virtual void RequestCompleted(BUrlProtocol* caller,
bool success);
protected:
bool fRequestComplete;
};
#endif // _B_URL_SYNCHRONOUS_REQUEST_H_
+46
View File
@@ -0,0 +1,46 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_HTTP_TIME_H_
#define _B_HTTP_TIME_H_
#include <ctime>
#include <String.h>
namespace BPrivate {
enum {
B_HTTP_TIME_FORMAT_PARSED = -1,
B_HTTP_TIME_FORMAT_RFC1123 = 0,
B_HTTP_TIME_FORMAT_PREFERRED = B_HTTP_TIME_FORMAT_RFC1123,
B_HTTP_TIME_FORMAT_COOKIE,
B_HTTP_TIME_FORMAT_RFC1036,
B_HTTP_TIME_FORMAT_ASCTIME
};
class BHttpTime {
public:
BHttpTime();
BHttpTime(time_t date);
BHttpTime(const BString& dateString);
// Date modification
void SetString(const BString& string);
void SetDate(time_t date);
// Date conversion
time_t Parse();
BString ToString(int8 format = B_HTTP_TIME_FORMAT_PARSED);
private:
BString fDateString;
time_t fDate;
int8 fDateFormat;
};
}
#endif // _B_HTTP_TIME_H_
@@ -0,0 +1,395 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <HttpAuthentication.h>
#include <cstdlib>
#include <openssl/md5.h>
#include <cstdio>
#define PRINT(x) printf x
static const char* kBase64Symbols
= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
BHttpAuthentication::BHttpAuthentication()
:
fAuthenticationMethod(B_HTTP_AUTHENTICATION_NONE)
{
}
BHttpAuthentication::BHttpAuthentication(const BString& username, const BString& password)
:
fAuthenticationMethod(B_HTTP_AUTHENTICATION_NONE),
fUserName(username),
fPassword(password)
{
}
// #pragma mark Field modification
void
BHttpAuthentication::SetUserName(const BString& username)
{
fUserName = username;
}
void
BHttpAuthentication::SetPassword(const BString& password)
{
fPassword = password;
}
void
BHttpAuthentication::SetMethod(BHttpAuthenticationMethod method)
{
fAuthenticationMethod = method;
}
status_t
BHttpAuthentication::Initialize(const BString& wwwAuthenticate)
{
fAuthenticationMethod = B_HTTP_AUTHENTICATION_NONE;
fDigestQop = B_HTTP_QOP_NONE;
if (wwwAuthenticate.Length() == 0)
return B_BAD_VALUE;
BString authRequired;
BString additionalData;
int32 firstSpace = wwwAuthenticate.FindFirst(' ');
if (firstSpace == -1)
wwwAuthenticate.CopyInto(authRequired, 0, wwwAuthenticate.Length());
else {
wwwAuthenticate.CopyInto(authRequired, 0, firstSpace);
wwwAuthenticate.CopyInto(additionalData, firstSpace+1,
wwwAuthenticate.Length());
}
authRequired.ToLower();
if (authRequired == "basic")
fAuthenticationMethod = B_HTTP_AUTHENTICATION_BASIC;
else if (authRequired == "digest") {
fAuthenticationMethod = B_HTTP_AUTHENTICATION_DIGEST;
fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
} else
return B_ERROR;
while (additionalData.Length()) {
int32 firstColon = additionalData.FindFirst(',');
if (firstColon == -1)
firstColon = additionalData.Length();
BString value;
additionalData.MoveInto(value, 0, firstColon);
additionalData.Remove(0, 1);
additionalData.Trim();
int32 equal = value.FindFirst('=');
if (equal == -1)
continue;
BString name;
value.MoveInto(name, 0, equal);
value.Remove(0, 1);
name.ToLower();
if (value[0] == '"') {
value.Remove(0, 1);
value.Remove(value.Length()-1, 1);
}
PRINT(("HttpAuth: name=%s, value=%s\n", name.String(),
value.String()));
if (name == "realm")
fRealm = value;
else if (name == "nonce")
fDigestNonce = value;
else if (name == "opaque")
fDigestOpaque = value;
else if (name == "stale") {
value.ToLower();
fDigestStale = (value == "true");
} else if (name == "algorithm") {
value.ToLower();
if (value == "md5")
fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5;
else if (value == "md5-sess")
fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS;
else
fDigestAlgorithm = B_HTTP_AUTHENTICATION_ALGORITHM_NONE;
} else if (name == "qop")
fDigestQop = B_HTTP_QOP_AUTH;
}
if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_BASIC)
return B_OK;
else if (fAuthenticationMethod == B_HTTP_AUTHENTICATION_DIGEST
&& fDigestNonce.Length() > 0
&& fDigestAlgorithm != B_HTTP_AUTHENTICATION_ALGORITHM_NONE)
return B_OK;
else
return B_ERROR;
}
// #pragma mark Field access
const BString&
BHttpAuthentication::UserName() const
{
return fUserName;
}
const BString&
BHttpAuthentication::Password() const
{
return fPassword;
}
BHttpAuthenticationMethod
BHttpAuthentication::Method() const
{
return fAuthenticationMethod;
}
BString
BHttpAuthentication::Authorization(const BUrl& url, const BString& method) const
{
BString authorizationString;
switch (fAuthenticationMethod) {
case B_HTTP_AUTHENTICATION_NONE:
break;
case B_HTTP_AUTHENTICATION_BASIC:
{
BString basicEncode;
basicEncode << fUserName << ':' << fPassword;
authorizationString << "Basic " << Base64Encode(basicEncode);
break;
}
case B_HTTP_AUTHENTICATION_DIGEST:
case B_HTTP_AUTHENTICATION_IE_DIGEST:
authorizationString << "Digest " << "username=\"" << fUserName
<< "\", realm=\"" << fRealm << "\", nonce=\"" << fDigestNonce
<< "\", algorithm=";
if (fDigestAlgorithm == B_HTTP_AUTHENTICATION_ALGORITHM_MD5)
authorizationString << "MD5";
else
authorizationString << "MD5-sess";
if (fDigestOpaque.Length() > 0)
authorizationString << ", opaque=\"" << fDigestOpaque << "\"";
if (fDigestQop != B_HTTP_QOP_NONE) {
if (fDigestCnonce.Length() == 0) {
fDigestCnonce = _H(fDigestOpaque);
//fDigestCnonce = "03c6790a055cbbac";
fDigestNc = 0;
}
authorizationString << ", uri=\"" << url.Path() << "\"";
authorizationString << ", qop=auth, cnonce=\"" << fDigestCnonce
<< "\"";
char strNc[9];
snprintf(strNc, 9, "%08x", ++fDigestNc);
authorizationString << ", nc=" << strNc;
}
authorizationString << ", response=\""
<< _DigestResponse(url.Path(), method) << "\"";
break;
}
return authorizationString;
}
// #pragma mark Base64 encoding
/*static*/ BString
BHttpAuthentication::Base64Encode(const BString& string)
{
BString result;
BString tmpString = string;
while (tmpString.Length()) {
char in[3] = { 0, 0, 0 };
char out[4] = { 0, 0, 0, 0 };
int8 remaining = tmpString.Length();
tmpString.MoveInto(in, 0, 3);
out[0] = (in[0] & 0xFC) >> 2;
out[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4);
out[2] = ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6);
out[3] = in[2] & 0x3F;
for (int i = 0; i < 4; i++)
out[i] = kBase64Symbols[(int)out[i]];
// Add padding if the input length is not a multiple
// of 3
switch (remaining) {
case 1:
out[2] = '=';
// Fall through
case 2:
out[3] = '=';
break;
}
result.Append(out, 4);
}
return result;
}
/*static*/ BString
BHttpAuthentication::Base64Decode(const BString& string)
{
BString base64Reverse(kBase64Symbols);
BString result;
// Invalid input
if (string.Length() % 4 != 0)
return BString("");
BString tmpString(string);
while (tmpString.Length()) {
char in[4] = { 0, 0, 0, 0 };
char out[3] = { 0, 0, 0 };
tmpString.MoveInto(in, 0, 4);
for (int i = 0; i < 4; i++) {
if (in[i] == '=')
in[i] = 0;
else
in[i] = base64Reverse.FindFirst(in[i], 0);
}
out[0] = (in[0] << 2) | ((in[1] & 0x30) >> 4);
out[1] = ((in[1] & 0x0F) << 4) | ((in[2] & 0x3C) >> 2);
out[2] = ((in[2] & 0x03) << 6) | in[3];
result.Append(out, 3);
}
return result;
}
BString
BHttpAuthentication::_DigestResponse(const BString& uri, const BString& method) const
{
PRINT(("HttpAuth: Computing digest response: \n"));
PRINT(("HttpAuth: > username = %s\n", fUserName.String()));
PRINT(("HttpAuth: > password = %s\n", fPassword.String()));
PRINT(("HttpAuth: > realm = %s\n", fRealm.String()));
PRINT(("HttpAuth: > nonce = %s\n", fDigestNonce.String()));
PRINT(("HttpAuth: > cnonce = %s\n", fDigestCnonce.String()));
PRINT(("HttpAuth: > nc = %08x\n", fDigestNc));
PRINT(("HttpAuth: > uri = %s\n", uri.String()));
PRINT(("HttpAuth: > method = %s\n", method.String()));
PRINT(("HttpAuth: > algorithm = %d (MD5:%d, MD5-sess:%d)\n",
fDigestAlgorithm, B_HTTP_AUTHENTICATION_ALGORITHM_MD5,
B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS));
BString A1;
A1 << fUserName << ':' << fRealm << ':' << fPassword;
if (fDigestAlgorithm == B_HTTP_AUTHENTICATION_ALGORITHM_MD5_SESS) {
A1 = _H(A1);
A1 << ':' << fDigestNonce << ':' << fDigestCnonce;
}
BString A2;
A2 << method << ':' << uri;
PRINT(("HttpAuth: > A1 = %s\n", A1.String()));
PRINT(("HttpAuth: > A2 = %s\n", A2.String()));
PRINT(("HttpAuth: > H(A1) = %s\n", _H(A1).String()));
PRINT(("HttpAuth: > H(A2) = %s\n", _H(A2).String()));
char strNc[9];
snprintf(strNc, 9, "%08x", fDigestNc);
BString secretResp;
secretResp << fDigestNonce << ':' << strNc << ':' << fDigestCnonce
<< ":auth:" << _H(A2);
PRINT(("HttpAuth: > R2 = %s\n", secretResp.String()));
BString response = _KD(_H(A1), secretResp);
PRINT(("HttpAuth: > response = %s\n", response.String()));
return response;
}
BString
BHttpAuthentication::_H(const BString& value) const
{
unsigned char* hashResult
= MD5(reinterpret_cast<const unsigned char*>(value.String()),
value.Length(), NULL);
BString result;
// TODO: This is slower than it needs to be. If we already know the
// final hash string length, we can use
// BString::LockBuffer(MD5_DIGEST_LENGTH * 2) to preallocate it.
// I am not making the change since I can not test it right now (stippi).
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
char c = ((hashResult[i] & 0xF0) >> 4);
c += (c > 9) ? 'a' - 10 : '0';
result << c;
c = hashResult[i] & 0x0F;
c += (c > 9) ? 'a' - 10 : '0';
result << c;
}
return result;
}
BString
BHttpAuthentication::_KD(const BString& secret, const BString& data) const
{
BString encode;
encode << secret << ':' << data;
return _H(encode);
}
+781
View File
@@ -0,0 +1,781 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <File.h>
#include <HttpForm.h>
#include <NodeInfo.h>
#include <TypeConstants.h>
#include <Url.h>
static int32 kBoundaryRandomSize = 16;
using namespace std;
// #pragma mark -- BHttpFormData
BHttpFormData::BHttpFormData()
:
fDataType(B_HTTPFORM_STRING),
fCopiedBuffer(false),
fFileMark(false),
fBufferValue(NULL),
fBufferSize(0)
{
}
BHttpFormData::BHttpFormData(const BString& name, const BString& value)
:
fDataType(B_HTTPFORM_STRING),
fCopiedBuffer(false),
fFileMark(false),
fName(name),
fStringValue(value),
fBufferValue(NULL),
fBufferSize(0)
{
}
BHttpFormData::BHttpFormData(const BString& name, const BPath& file)
:
fDataType(B_HTTPFORM_FILE),
fCopiedBuffer(false),
fFileMark(false),
fName(name),
fPathValue(file),
fBufferValue(NULL),
fBufferSize(0)
{
}
BHttpFormData::BHttpFormData(const BString& name, const void* buffer,
ssize_t size)
:
fDataType(B_HTTPFORM_BUFFER),
fCopiedBuffer(false),
fFileMark(false),
fName(name),
fBufferValue(buffer),
fBufferSize(size)
{
}
BHttpFormData::BHttpFormData(const BHttpFormData& other)
:
fCopiedBuffer(false),
fFileMark(false),
fBufferValue(NULL),
fBufferSize(0)
{
*this = other;
}
BHttpFormData::~BHttpFormData()
{
if (fCopiedBuffer)
delete[] reinterpret_cast<const char*>(fBufferValue);
}
// #pragma mark Retrieve data informations
bool
BHttpFormData::InitCheck() const
{
if (fDataType == B_HTTPFORM_BUFFER)
return fBufferValue != NULL;
return true;
}
const BString&
BHttpFormData::Name() const
{
return fName;
}
const BString&
BHttpFormData::String() const
{
return fStringValue;
}
const BPath&
BHttpFormData::File() const
{
return fPathValue;
}
const void*
BHttpFormData::Buffer() const
{
return fBufferValue;
}
ssize_t
BHttpFormData::BufferSize() const
{
return fBufferSize;
}
bool
BHttpFormData::IsFile() const
{
return fFileMark;
}
const BString&
BHttpFormData::Filename() const
{
return fFilename;
}
const BString&
BHttpFormData::MimeType() const
{
return fMimeType;
}
form_content_type
BHttpFormData::Type() const
{
return fDataType;
}
// #pragma mark Change behavior
status_t
BHttpFormData::MarkAsFile(const BString& filename, const BString& mimeType)
{
if (fDataType == B_HTTPFORM_UNKNOWN || fDataType == B_HTTPFORM_FILE)
return B_ERROR;
fFilename = filename;
fMimeType = mimeType;
fFileMark = true;
return B_OK;
}
status_t
BHttpFormData::MarkAsFile(const BString& filename)
{
return MarkAsFile(filename, "");
}
void
BHttpFormData::UnmarkAsFile()
{
fFilename.Truncate(0, true);
fMimeType.Truncate(0, true);
fFileMark = false;
}
status_t
BHttpFormData::CopyBuffer()
{
if (fDataType != B_HTTPFORM_BUFFER)
return B_ERROR;
char* copiedBuffer = new char[fBufferSize];
if (copiedBuffer == NULL)
return B_NO_MEMORY;
memcpy(copiedBuffer, fBufferValue, fBufferSize);
fBufferValue = copiedBuffer;
fCopiedBuffer = true;
return B_OK;
}
BHttpFormData&
BHttpFormData::operator=(const BHttpFormData& other)
{
fDataType = other.fDataType;
fCopiedBuffer = false;
fFileMark = other.fFileMark;
fName = other.fName;
fStringValue = other.fStringValue;
fPathValue = other.fPathValue;
fBufferValue = other.fBufferValue;
fBufferSize = other.fBufferSize;
fFilename = other.fFilename;
fMimeType = other.fMimeType;
if (other.fCopiedBuffer)
CopyBuffer();
return *this;
}
// #pragma mark -- BHttpForm
BHttpForm::BHttpForm()
: fType(B_HTTP_FORM_URL_ENCODED)
{
}
BHttpForm::BHttpForm(const BHttpForm&)
: fType(B_HTTP_FORM_URL_ENCODED)
{
}
BHttpForm::BHttpForm(const BString& formString)
: fType(B_HTTP_FORM_URL_ENCODED)
{
ParseString(formString);
}
BHttpForm::~BHttpForm()
{
Clear();
}
// #pragma mark Form string parsing
void
BHttpForm::ParseString(const BString& formString)
{
int32 index = 0;
while (index < formString.Length()) {
_ExtractNameValuePair(formString, &index);
}
}
BString
BHttpForm::RawData() const
{
BString result;
if (fType == B_HTTP_FORM_URL_ENCODED) {
for (FormStorage::const_iterator it = fFields.begin();
it != fFields.end(); it++) {
const BHttpFormData* currentField = &it->second;
switch (currentField->Type()) {
case B_HTTPFORM_UNKNOWN:
break;
case B_HTTPFORM_STRING:
result << '&' << BUrl::UrlEncode(currentField->Name())
<< '=' << BUrl::UrlEncode(currentField->String());
break;
case B_HTTPFORM_FILE:
break;
case B_HTTPFORM_BUFFER:
// Send the buffer only if its not marked as a file
if (!currentField->IsFile()) {
result << '&' << BUrl::UrlEncode(currentField->Name())
<< '=';
result.Append(
reinterpret_cast<const char*>(currentField->Buffer()),
currentField->BufferSize());
}
break;
}
}
result.Remove(0, 1);
} else if (fType == B_HTTP_FORM_MULTIPART) {
// Very slow and memory consuming method since we're caching the
// file content, this should be preferably handled by the protocol
for (FormStorage::const_iterator it = fFields.begin();
it != fFields.end(); it++) {
const BHttpFormData* currentField = &it->second;
result << _GetMultipartHeader(currentField);
switch (currentField->Type()) {
case B_HTTPFORM_UNKNOWN:
break;
case B_HTTPFORM_STRING:
result << currentField->String();
break;
case B_HTTPFORM_FILE:
{
BFile upFile(currentField->File().Path(), B_READ_ONLY);
char readBuffer[1024];
ssize_t readSize;
readSize = upFile.Read(readBuffer, 1024);
while (readSize > 0) {
result.Append(readBuffer, readSize);
readSize = upFile.Read(readBuffer, 1024);
}
}
break;
case B_HTTPFORM_BUFFER:
result.Append(
reinterpret_cast<const char*>(currentField->Buffer()),
currentField->BufferSize());
break;
}
result << "\r\n";
}
result << "--" << fMultipartBoundary << "--\r\n";
}
return result;
}
// #pragma mark Form add
status_t
BHttpForm::AddString(const BString& fieldName, const BString& value)
{
BHttpFormData formData(fieldName, value);
if (!formData.InitCheck())
return B_ERROR;
fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
return B_OK;
}
status_t
BHttpForm::AddInt(const BString& fieldName, int32 value)
{
BString strValue;
strValue << value;
return AddString(fieldName, strValue);
}
status_t
BHttpForm::AddFile(const BString& fieldName, const BPath& file)
{
BHttpFormData formData(fieldName, file);
if (!formData.InitCheck())
return B_ERROR;
fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
if (fType != B_HTTP_FORM_MULTIPART)
SetFormType(B_HTTP_FORM_MULTIPART);
return B_OK;
}
status_t
BHttpForm::AddBuffer(const BString& fieldName, const void* buffer,
ssize_t size)
{
BHttpFormData formData(fieldName, buffer, size);
if (!formData.InitCheck())
return B_ERROR;
fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
return B_OK;
}
status_t
BHttpForm::AddBufferCopy(const BString& fieldName, const void* buffer,
ssize_t size)
{
BHttpFormData formData(fieldName, buffer, size);
if (!formData.InitCheck())
return B_ERROR;
// Copy the buffer of the inserted form data copy to
// avoid an unneeded copy of the buffer upon insertion
pair<FormStorage::iterator, bool> insertResult
= fFields.insert(pair<BString, BHttpFormData>(fieldName, formData));
return insertResult.first->second.CopyBuffer();
}
// #pragma mark Mark a field as a filename
void
BHttpForm::MarkAsFile(const BString& fieldName, const BString& filename,
const BString& mimeType)
{
FormStorage::iterator it = fFields.find(fieldName);
if (it == fFields.end())
return;
it->second.MarkAsFile(filename, mimeType);
if (fType != B_HTTP_FORM_MULTIPART)
SetFormType(B_HTTP_FORM_MULTIPART);
}
void
BHttpForm::MarkAsFile(const BString& fieldName, const BString& filename)
{
MarkAsFile(fieldName, filename, "");
}
void
BHttpForm::UnmarkAsFile(const BString& fieldName)
{
FormStorage::iterator it = fFields.find(fieldName);
if (it == fFields.end())
return;
it->second.UnmarkAsFile();
}
// #pragma mark Change form type
void
BHttpForm::SetFormType(form_type type)
{
fType = type;
if (fType == B_HTTP_FORM_MULTIPART)
_GenerateMultipartBoundary();
}
// #pragma mark Form test
bool
BHttpForm::HasField(const BString& name) const
{
return (fFields.find(name) != fFields.end());
}
// #pragma mark Form retrieve
BString
BHttpForm::GetMultipartHeader(const BString& fieldName) const
{
FormStorage::const_iterator it = fFields.find(fieldName);
if (it == fFields.end())
return BString("");
return _GetMultipartHeader(&it->second);
}
form_type
BHttpForm::GetFormType() const
{
return fType;
}
const BString&
BHttpForm::GetMultipartBoundary() const
{
return fMultipartBoundary;
}
BString
BHttpForm::GetMultipartFooter() const
{
BString result = "--";
result << fMultipartBoundary << "--\r\n";
return result;
}
ssize_t
BHttpForm::ContentLength() const
{
if (fType == B_HTTP_FORM_URL_ENCODED)
return RawData().Length();
ssize_t contentLength = 0;
for (FormStorage::const_iterator it = fFields.begin();
it != fFields.end(); it++) {
const BHttpFormData* c = &it->second;
contentLength += _GetMultipartHeader(c).Length();
switch (c->Type()) {
case B_HTTPFORM_UNKNOWN:
break;
case B_HTTPFORM_STRING:
contentLength += c->String().Length();
break;
case B_HTTPFORM_FILE:
{
BFile upFile(c->File().Path(), B_READ_ONLY);
upFile.Seek(0, SEEK_END);
contentLength += upFile.Position();
}
break;
case B_HTTPFORM_BUFFER:
contentLength += c->BufferSize();
break;
}
contentLength += 2;
}
contentLength += fMultipartBoundary.Length() + 6;
return contentLength;
}
// #pragma mark Form iterator
BHttpForm::Iterator
BHttpForm::GetIterator()
{
return BHttpForm::Iterator(this);
}
// #pragma mark Form clear
void
BHttpForm::Clear()
{
fFields.clear();
}
// #pragma mark Overloaded operators
BHttpFormData&
BHttpForm::operator[](const BString& name)
{
if (!HasField(name))
AddString(name, "");
return fFields[name];
}
void
BHttpForm::_ExtractNameValuePair(const BString& formString, int32* index)
{
// Look for a name=value pair
int16 firstAmpersand = formString.FindFirst("&", *index);
int16 firstEqual = formString.FindFirst("=", *index);
BString name;
BString value;
if (firstAmpersand == -1) {
if (firstEqual != -1) {
formString.CopyInto(name, *index, firstEqual - *index);
formString.CopyInto(value, firstEqual + 1,
formString.Length() - firstEqual - 1);
} else
formString.CopyInto(value, *index,
formString.Length() - *index);
*index = formString.Length() + 1;
} else {
if (firstEqual != -1 && firstEqual < firstAmpersand) {
formString.CopyInto(name, *index, firstEqual - *index);
formString.CopyInto(value, firstEqual + 1,
firstAmpersand - firstEqual - 1);
} else
formString.CopyInto(value, *index, firstAmpersand - *index);
*index = firstAmpersand + 1;
}
AddString(name, value);
}
void
BHttpForm::_GenerateMultipartBoundary()
{
fMultipartBoundary = "----------------------------";
srand(time(NULL));
// TODO: Maybe a more robust way to seed the random number
// generator is needed?
for (int32 i = 0; i < kBoundaryRandomSize; i++)
fMultipartBoundary << (char)(rand() % 10 + '0');
}
// #pragma mark Field information access by std iterator
BString
BHttpForm::_GetMultipartHeader(const BHttpFormData* element) const
{
BString result;
result << "--" << fMultipartBoundary << "\r\n";
result << "Content-Disposition: form-data; name=\"" << element->Name()
<< '"';
switch (element->Type()) {
case B_HTTPFORM_UNKNOWN:
break;
case B_HTTPFORM_FILE:
{
result << "; filename=\"" << element->File().Leaf() << '"';
BNode fileNode(element->File().Path());
BNodeInfo fileInfo(&fileNode);
result << "\r\nContent-Type: ";
char tempMime[128];
if (fileInfo.GetType(tempMime) == B_OK)
result << tempMime;
else
result << "application/octet-stream";
}
break;
case B_HTTPFORM_STRING:
case B_HTTPFORM_BUFFER:
if (element->IsFile()) {
result << "; filename=\"" << element->Filename() << '"';
if (element->MimeType().Length() > 0)
result << "\r\nContent-Type: " << element->MimeType();
else
result << "\r\nContent-Type: text/plain";
}
break;
}
result << "\r\n\r\n";
return result;
}
// #pragma mark -- Iterator
BHttpForm::Iterator::Iterator(BHttpForm* form)
{
fForm = form;
fStdIterator = form->fFields.begin();
_FindNext();
}
BHttpForm::Iterator::Iterator(const Iterator& other)
{
*this = other;
}
bool
BHttpForm::Iterator::HasNext() const
{
return fStdIterator != fForm->fFields.end();
}
BHttpFormData*
BHttpForm::Iterator::Next()
{
BHttpFormData* element = fElement;
_FindNext();
return element;
}
void
BHttpForm::Iterator::Remove()
{
fForm->fFields.erase(fStdIterator);
fElement = NULL;
}
BString
BHttpForm::Iterator::MultipartHeader()
{
return fForm->_GetMultipartHeader(fPrevElement);
}
BHttpForm::Iterator&
BHttpForm::Iterator::operator=(const Iterator& other)
{
fForm = other.fForm;
fStdIterator = other.fStdIterator;
fElement = other.fElement;
fPrevElement = other.fPrevElement;
return *this;
}
void
BHttpForm::Iterator::_FindNext()
{
fPrevElement = fElement;
if (fStdIterator != fForm->fFields.end()) {
fElement = &fStdIterator->second;
fStdIterator++;
}
else
fElement = NULL;
}
+315
View File
@@ -0,0 +1,315 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <cstring>
#include <new>
#include <String.h>
#include <HttpHeaders.h>
// #pragma mark -- BHttpHeader
BHttpHeader::BHttpHeader()
:
fName(),
fValue(),
fRawHeader(),
fRawHeaderValid(true)
{
}
BHttpHeader::BHttpHeader(const char* string)
:
fRawHeaderValid(true)
{
SetHeader(string);
}
BHttpHeader::BHttpHeader(const char* name, const char* value)
:
fName(name),
fValue(value),
fRawHeaderValid(false)
{
}
BHttpHeader::BHttpHeader(const BHttpHeader& copy)
:
fName(copy.fName),
fValue(copy.fValue),
fRawHeaderValid(false)
{
}
void
BHttpHeader::SetName(const char* name)
{
fRawHeaderValid = false;
fName = name;
}
void
BHttpHeader::SetValue(const char* value)
{
fRawHeaderValid = false;
fValue = value;
}
bool
BHttpHeader::SetHeader(const char* string)
{
BString strLine(string);
fRawHeaderValid = false;
fName.Truncate(0);
fValue.Truncate(0);
int32 separatorLocation = strLine.FindFirst(": ");
if (separatorLocation == B_ERROR)
return false;
strLine.MoveInto(fName, 0, separatorLocation);
strLine.Remove(0, 2);
fValue = strLine;
return true;
}
const char*
BHttpHeader::Name() const
{
return fName.String();
}
const char*
BHttpHeader::Value() const
{
return fValue.String();
}
const char*
BHttpHeader::Header() const
{
if (!fRawHeaderValid) {
fRawHeaderValid = true;
fRawHeader.Truncate(0);
fRawHeader << fName << ": " << fValue;
}
return fRawHeader.String();
}
bool
BHttpHeader::NameIs(const char* name) const
{
return fName == BString(name);
}
BHttpHeader&
BHttpHeader::operator=(const BHttpHeader& other)
{
fName = other.fName;
fValue = other.fValue;
fRawHeaderValid = false;
return *this;
}
// #pragma mark -- BHttpHeaders
BHttpHeaders::BHttpHeaders()
:
fHeaderList()
{
}
BHttpHeaders::BHttpHeaders(const BHttpHeaders& copy)
:
fHeaderList()
{
for (int32 i = 0; i < copy.CountHeaders(); i++)
AddHeader(copy.HeaderAt(i).Name(), copy.HeaderAt(i).Value());
}
BHttpHeaders::~BHttpHeaders()
{
_EraseData();
}
// #pragma mark Header access
const char*
BHttpHeaders::HeaderValue(const char* name) const
{
for (int32 i = 0; i < fHeaderList.CountItems(); i++) {
BHttpHeader* header
= reinterpret_cast<BHttpHeader*>(fHeaderList.ItemAtFast(i));
if (header->NameIs(name))
return header->Value();
}
return NULL;
}
BHttpHeader&
BHttpHeaders::HeaderAt(int32 index) const
{
//! Note: index _must_ be in-bounds
BHttpHeader* header
= reinterpret_cast<BHttpHeader*>(fHeaderList.ItemAtFast(index));
return *header;
}
// #pragma mark Header count
int32
BHttpHeaders::CountHeaders() const
{
return fHeaderList.CountItems();
}
// #pragma Header tests
int32
BHttpHeaders::HasHeader(const char* name) const
{
for (int32 i = 0; i < fHeaderList.CountItems(); i++) {
BHttpHeader* header
= reinterpret_cast<BHttpHeader*>(fHeaderList.ItemAt(i));
if (header->NameIs(name))
return i;
}
return B_ERROR;
}
// #pragma mark Header add/replace
bool
BHttpHeaders::AddHeader(const char* line)
{
BHttpHeader* heapHeader = new(std::nothrow) BHttpHeader(line);
if (heapHeader != NULL) {
fHeaderList.AddItem(heapHeader);
return true;
}
return false;
}
bool
BHttpHeaders::AddHeader(const char* name, const char* value)
{
BHttpHeader* heapHeader = new(std::nothrow) BHttpHeader(name, value);
if (heapHeader != NULL) {
fHeaderList.AddItem(heapHeader);
return true;
}
return false;
}
bool
BHttpHeaders::AddHeader(const char* name, int32 value)
{
BString strValue;
strValue << value;
return AddHeader(name, strValue);
}
// #pragma mark Header deletion
void
BHttpHeaders::Clear()
{
_EraseData();
fHeaderList.MakeEmpty();
}
// #pragma mark Overloaded operators
BHttpHeaders&
BHttpHeaders::operator=(const BHttpHeaders& other)
{
for (int32 i = 0; i < other.CountHeaders(); i++)
AddHeader(other.HeaderAt(i).Name(), other.HeaderAt(i).Value());
return *this;
}
BHttpHeader&
BHttpHeaders::operator[](int32 index) const
{
//! Note: Index _must_ be in-bounds
BHttpHeader* header
= reinterpret_cast<BHttpHeader*>(fHeaderList.ItemAtFast(index));
return *header;
}
const char*
BHttpHeaders::operator[](const char* name) const
{
return HeaderValue(name);
}
void
BHttpHeaders::_EraseData()
{
// Free allocated data;
for (int32 i = 0; i < fHeaderList.CountItems(); i++) {
BHttpHeader* header
= reinterpret_cast<BHttpHeader*>(fHeaderList.ItemAtFast(i));
delete header;
}
}
+132
View File
@@ -0,0 +1,132 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <HttpTime.h>
#include <new>
#include <cstdio>
#define PRINT(x) printf x
static const char* kRfc1123Format = "%a, %d %b %Y %H:%M:%S GMT";
static const char* kCookieFormat = "%a, %d-%b-%Y %H:%M:%S GMT";
static const char* kRfc1036Format = "%A, %d-%b-%y %H:%M:%S GMT";
static const char* kAscTimeFormat = "%a %d %b %H:%M:%S %Y";
static const uint16 kTimetToStringMaxLength = 128;
using namespace BPrivate;
BHttpTime::BHttpTime()
:
fDate(0),
fDateFormat(B_HTTP_TIME_FORMAT_PREFERRED)
{
}
BHttpTime::BHttpTime(time_t date)
:
fDate(date),
fDateFormat(B_HTTP_TIME_FORMAT_PREFERRED)
{
}
BHttpTime::BHttpTime(const BString& dateString)
:
fDateString(dateString),
fDate(0),
fDateFormat(B_HTTP_TIME_FORMAT_PREFERRED)
{
}
// #pragma mark Date modification
void
BHttpTime::SetString(const BString& string)
{
fDateString = string;
}
void
BHttpTime::SetDate(time_t date)
{
fDate = date;
}
// #pragma mark Date conversion
time_t
BHttpTime::Parse()
{
struct tm expireTime;
if (fDateString.Length() < 4)
return 0;
if (fDateString[3] == ',') {
if (strptime(fDateString.String(), kRfc1123Format, &expireTime)
== NULL) {
strptime(fDateString.String(), kCookieFormat, &expireTime);
fDateFormat = B_HTTP_TIME_FORMAT_COOKIE;
} else
fDateFormat = B_HTTP_TIME_FORMAT_RFC1123;
} else if (fDateString[3] == ' ') {
strptime(fDateString.String(), kRfc1036Format, &expireTime);
fDateFormat = B_HTTP_TIME_FORMAT_RFC1036;
} else {
strptime(fDateString.String(), kAscTimeFormat, &expireTime);
fDateFormat = B_HTTP_TIME_FORMAT_ASCTIME;
}
// return timegm(&expireTime);
// TODO: The above was used initially. See http://en.wikipedia.org/wiki/Time.h
// stippi: I don't know how Christophe had this code compiling initially,
// since Haiku does not appear to implement timegm().
return mktime(&expireTime);
}
BString
BHttpTime::ToString(int8 format)
{
BString expirationFinal;
struct tm* expirationTm = localtime(&fDate);
char expirationString[kTimetToStringMaxLength + 1];
size_t strLength;
switch ((format == B_HTTP_TIME_FORMAT_PARSED)?fDateFormat:format) {
default:
case B_HTTP_TIME_FORMAT_RFC1123:
strLength = strftime(expirationString, kTimetToStringMaxLength,
kRfc1123Format, expirationTm);
break;
case B_HTTP_TIME_FORMAT_RFC1036:
strLength = strftime(expirationString, kTimetToStringMaxLength,
kRfc1036Format, expirationTm);
break;
case B_HTTP_TIME_FORMAT_ASCTIME:
strLength = strftime(expirationString, kTimetToStringMaxLength,
kAscTimeFormat, expirationTm);
break;
}
expirationFinal.SetTo(expirationString, strLength);
return expirationFinal;
}
+24 -1
View File
@@ -12,8 +12,31 @@ SharedLibrary libbnetapi.so :
NetworkAddress.cpp
NetworkAddressResolver.cpp
NetworkCookie.cpp
NetworkCookieJar.cpp
NetworkInterface.cpp
NetworkRoster.cpp
: be $(TARGET_NETWORK_LIBS) $(TARGET_LIBSUPC++)
# TODO: The HTTP stuff should all go into an add-on. It needs linking
# against libcrypto.so and only the add-on should link against it.
# Building of the commented out files has not been completely tested after
# integrating the code from the GSoC 2010 "Services Kit" project and doing
# some renaming of types, constants and methods.
# HttpAuthentication.cpp
# HttpHeaders.cpp
# HttpForm.cpp
HttpTime.cpp
Url.cpp
UrlContext.cpp
# UrlProtocol.cpp
# UrlProtocolAsynchronousListener.cpp
# UrlProtocolDispatchingListener.cpp
# UrlProtocolHttp.cpp # TODO: -> add-on, See above.
# UrlProtocolListener.cpp
# UrlRequest.cpp
# UrlResult.cpp
# UrlSynchronousRequest.cpp
: be $(TARGET_NETWORK_LIBS) $(TARGET_LIBSUPC++) libshared.a
;
@@ -0,0 +1,813 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <cstdlib>
#include <ctime>
#include <new>
#include <HttpTime.h>
#include <NetworkCookie.h>
#include <cstdio>
#define PRINT(x) printf x;
using BPrivate::BHttpTime;
static const char* kArchivedCookieComment = "be:cookie.comment";
static const char* kArchivedCookieCommentUrl = "be:cookie.commenturl";
static const char* kArchivedCookieDiscard = "be:cookie.discard";
static const char* kArchivedCookieDomain = "be:cookie.domain";
static const char* kArchivedCookieExpirationDate = "be:cookie.expiredate";
static const char* kArchivedCookiePath = "be:cookie.path";
static const char* kArchivedCookieSecure = "be:cookie.secure";
static const char* kArchivedCookieVersion = "be:cookie.version";
static const char* kArchivedCookieName = "be:cookie.name";
static const char* kArchivedCookieValue = "be:cookie.value";
BNetworkCookie::BNetworkCookie(const char* name, const char* value)
:
fDiscard(false),
fExpiration(BDateTime::CurrentDateTime(B_GMT_TIME)),
fVersion(0),
fName(name),
fValue(value),
fSessionCookie(true)
{
_Reset();
}
BNetworkCookie::BNetworkCookie(const BNetworkCookie& other)
:
BArchivable(),
fDiscard(false),
fExpiration(BDateTime::CurrentDateTime(B_GMT_TIME)),
fVersion(0),
fSessionCookie(true)
{
_Reset();
*this = other;
}
BNetworkCookie::BNetworkCookie(const BString& cookieString)
:
fDiscard(false),
fExpiration(BDateTime::CurrentDateTime(B_GMT_TIME)),
fVersion(0),
fSessionCookie(true)
{
_Reset();
ParseCookieString(cookieString);
}
BNetworkCookie::BNetworkCookie(const BString& cookieString,
const BUrl& url)
:
fDiscard(false),
fExpiration(BDateTime::CurrentDateTime(B_GMT_TIME)),
fVersion(0),
fSessionCookie(true)
{
_Reset();
ParseCookieStringFromUrl(cookieString, url);
}
BNetworkCookie::BNetworkCookie(BMessage* archive)
:
fDiscard(false),
fExpiration(BDateTime::CurrentDateTime(B_GMT_TIME)),
fVersion(0),
fSessionCookie(true)
{
_Reset();
archive->FindString(kArchivedCookieName, &fName);
archive->FindString(kArchivedCookieValue, &fValue);
archive->FindString(kArchivedCookieComment, &fComment);
archive->FindString(kArchivedCookieCommentUrl, &fCommentUrl);
archive->FindString(kArchivedCookieDomain, &fDomain);
archive->FindString(kArchivedCookiePath, &fPath);
archive->FindBool(kArchivedCookieSecure, &fSecure);
if (archive->FindBool(kArchivedCookieDiscard, &fDiscard) == B_OK)
fHasDiscard = true;
if (archive->FindInt8(kArchivedCookieVersion, &fVersion) == B_OK)
fHasVersion = true;
int32 expiration;
if (archive->FindInt32(kArchivedCookieExpirationDate, &expiration)
== B_OK) {
SetExpirationDate((time_t)expiration);
}
}
BNetworkCookie::BNetworkCookie()
:
fDiscard(false),
fExpiration(BDateTime::CurrentDateTime(B_GMT_TIME)),
fPath("/"),
fVersion(0),
fSessionCookie(true)
{
_Reset();
}
BNetworkCookie::~BNetworkCookie()
{
}
// #pragma mark String to cookie fields
BNetworkCookie&
BNetworkCookie::ParseCookieStringFromUrl(const BString& string,
const BUrl& url)
{
BString cookieString(string);
int16 index = 0;
_Reset();
// Default values from url
SetDomain(url.Host());
SetPath(url.Path());
_ExtractNameValuePair(cookieString, &index);
while (index < cookieString.Length())
_ExtractNameValuePair(cookieString, &index, true);
return *this;
}
BNetworkCookie&
BNetworkCookie::ParseCookieString(const BString& string)
{
BUrl url;
ParseCookieStringFromUrl(string, url);
return *this;
}
// #pragma mark Cookie fields modification
BNetworkCookie&
BNetworkCookie::SetComment(const BString& comment)
{
fComment = comment;
fRawFullCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetCommentUrl(const BString& commentUrl)
{
fCommentUrl = commentUrl;
fRawFullCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetDiscard(bool discard)
{
fDiscard = discard;
fHasDiscard = true;
fRawFullCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetDomain(const BString& domain)
{
fDomain = domain;
// We always use pre-dotted domains for tail matching
if (fDomain.ByteAt(0) != '.')
fDomain.Prepend(".");
fRawFullCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetMaxAge(int32 maxAge)
{
BDateTime expiration = BDateTime::CurrentDateTime(B_GMT_TIME);
expiration.Time().AddSeconds(maxAge);
return SetExpirationDate(expiration);
}
BNetworkCookie&
BNetworkCookie::SetExpirationDate(time_t expireDate)
{
BDateTime expiration;
expiration.SetTime_t(expireDate);
return SetExpirationDate(expiration);
}
BNetworkCookie&
BNetworkCookie::SetExpirationDate(BDateTime& expireDate)
{
if (expireDate.Time_t() <= 0) {
fExpiration.SetTime_t(0);
fSessionCookie = true;
fExpirationStringValid = false;
fRawFullCookieValid = false;
fHasExpirationDate = false;
} else {
fExpiration = expireDate;
fSessionCookie = false;
fExpirationStringValid = false;
fRawFullCookieValid = false;
fHasExpirationDate = true;
}
return *this;
}
BNetworkCookie&
BNetworkCookie::SetPath(const BString& path)
{
fPath = path;
fRawFullCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetSecure(bool secure)
{
fSecure = secure;
fRawFullCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetVersion(int8 version)
{
fVersion = version;
fHasVersion = true;
fRawCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetName(const BString& name)
{
fName = name;
fRawFullCookieValid = false;
fRawCookieValid = false;
return *this;
}
BNetworkCookie&
BNetworkCookie::SetValue(const BString& value)
{
fValue = value;
fRawFullCookieValid = false;
fRawCookieValid = false;
return *this;
}
// #pragma mark Cookie fields access
const BString&
BNetworkCookie::Comment() const
{
return fComment;
}
const BString&
BNetworkCookie::CommentUrl() const
{
return fCommentUrl;
}
bool
BNetworkCookie::Discard() const
{
return fDiscard;
}
const BString&
BNetworkCookie::Domain() const
{
return fDomain;
}
int32
BNetworkCookie::MaxAge() const
{
return fExpiration.Time_t() - BDateTime::CurrentDateTime(B_GMT_TIME).Time_t();
}
time_t
BNetworkCookie::ExpirationDate() const
{
return fExpiration.Time_t();
}
const BString&
BNetworkCookie::ExpirationString() const
{
BHttpTime date(ExpirationDate());
if (!fExpirationStringValid) {
fExpirationString = date.ToString(BPrivate::B_HTTP_TIME_FORMAT_COOKIE);
fExpirationStringValid = true;
}
return fExpirationString;
}
const BString&
BNetworkCookie::Path() const
{
return fPath;
}
bool
BNetworkCookie::Secure() const
{
return fSecure;
}
int8
BNetworkCookie::Version() const
{
return fVersion;
}
const BString&
BNetworkCookie::Name() const
{
return fName;
}
const BString&
BNetworkCookie::Value() const
{
return fValue;
}
const BString&
BNetworkCookie::RawCookie(bool full) const
{
if (full && !fRawFullCookieValid) {
fRawFullCookie.Truncate(0);
fRawFullCookieValid = true;
fRawFullCookie << fName << "=" << fValue;
if (HasCommentUrl())
fRawFullCookie << "; Comment-Url=" << fCommentUrl;
if (HasComment())
fRawFullCookie << "; Comment=" << fComment;
if (HasDiscard())
fRawFullCookie << "; Discard=" << (fDiscard?"true":"false");
if (HasDomain())
fRawFullCookie << "; Domain=" << fDomain;
if (HasExpirationDate())
fRawFullCookie << "; Max-Age=" << MaxAge();
// fRawFullCookie << "; Expires=" << ExpirationString();
if (HasPath())
fRawFullCookie << "; Path=" << fPath;
if (Secure() && fSecure)
fRawFullCookie << "; Secure=" << (fSecure?"true":"false");
if (HasVersion())
fRawFullCookie << ", Version=" << fVersion;
} else if (!full && !fRawCookieValid) {
fRawCookie.Truncate(0);
fRawCookieValid = true;
fRawCookie << fName << "=" << fValue;
}
return full?fRawFullCookie:fRawCookie;
}
// #pragma mark Cookie test
bool
BNetworkCookie::IsSessionCookie() const
{
return fSessionCookie;
}
bool
BNetworkCookie::IsValid(bool strict) const
{
return HasName() && HasValue() && (!strict || HasVersion());
}
bool
BNetworkCookie::IsValidForUrl(const BUrl& url) const
{
BString urlHost = url.Host();
BString urlPath = url.Path();
return IsValidForDomain(urlHost) && IsValidForPath(urlPath);
}
bool
BNetworkCookie::IsValidForDomain(const BString& domain) const
{
if (fDomain.Length() > domain.Length())
return false;
return domain.FindLast(fDomain) == (domain.Length() - fDomain.Length());
}
bool
BNetworkCookie::IsValidForPath(const BString& path) const
{
if (fPath.Length() > path.Length())
return false;
return path.FindFirst(fPath) == 0;
}
// #pragma mark Cookie fields existence tests
bool
BNetworkCookie::HasCommentUrl() const
{
return fCommentUrl.Length() > 0;
}
bool
BNetworkCookie::HasComment() const
{
return fComment.Length() > 0;
}
bool
BNetworkCookie::HasDiscard() const
{
return fHasDiscard;
}
bool
BNetworkCookie::HasDomain() const
{
return fDomain.Length() > 0;
}
bool
BNetworkCookie::HasPath() const
{
return fPath.Length() > 0;
}
bool
BNetworkCookie::HasVersion() const
{
return fHasVersion;
}
bool
BNetworkCookie::HasName() const
{
return fName.Length() > 0;
}
bool
BNetworkCookie::HasValue() const
{
return fValue.Length() > 0;
}
bool
BNetworkCookie::HasExpirationDate() const
{
return fHasExpirationDate;
}
// #pragma mark Cookie delete test
bool
BNetworkCookie::ShouldDeleteAtExit() const
{
return (HasDiscard() && Discard())
|| (!IsSessionCookie() && ShouldDeleteNow())
|| IsSessionCookie();
}
bool
BNetworkCookie::ShouldDeleteNow() const
{
if (!IsSessionCookie() && HasExpirationDate())
return (BDateTime::CurrentDateTime(B_GMT_TIME) > fExpiration);
return false;
}
// #pragma mark BArchivable members
status_t
BNetworkCookie::Archive(BMessage* into, bool deep) const
{
status_t error = BArchivable::Archive(into, deep);
if (error != B_OK)
return error;
error = into->AddString(kArchivedCookieName, fName);
if (error != B_OK)
return error;
error = into->AddString(kArchivedCookieValue, fValue);
if (error != B_OK)
return error;
// We add optional fields only if they're defined
if (HasComment()) {
error = into->AddString(kArchivedCookieComment, fComment);
if (error != B_OK)
return error;
}
if (HasCommentUrl()) {
error = into->AddString(kArchivedCookieCommentUrl, fCommentUrl);
if (error != B_OK)
return error;
}
if (HasDiscard()) {
error = into->AddBool(kArchivedCookieDiscard, fDiscard);
if (error != B_OK)
return error;
}
if (HasDomain()) {
error = into->AddString(kArchivedCookieDomain, fDomain);
if (error != B_OK)
return error;
}
if (fHasExpirationDate) {
error = into->AddInt32(kArchivedCookieExpirationDate,
fExpiration.Time_t());
if (error != B_OK)
return error;
}
if (HasPath()) {
error = into->AddString(kArchivedCookiePath, fPath);
if (error != B_OK)
return error;
}
if (Secure()) {
error = into->AddBool(kArchivedCookieSecure, fSecure);
if (error != B_OK)
return error;
}
if (HasVersion()) {
error = into->AddInt8(kArchivedCookieVersion, fVersion);
if (error != B_OK)
return error;
}
return B_OK;
}
/*static*/ BArchivable*
BNetworkCookie::Instantiate(BMessage* archive)
{
if (archive->HasString(kArchivedCookieName)
&& archive->HasString(kArchivedCookieValue))
return new(std::nothrow) BNetworkCookie(archive);
return NULL;
}
// #pragma mark Overloaded operators
BNetworkCookie&
BNetworkCookie::operator=(const BNetworkCookie& other)
{
// Should we prefer to discard the cache ?
fRawCookie = other.fRawCookie;
fRawCookieValid = other.fRawCookieValid;
fRawFullCookie = other.fRawFullCookie;
fRawFullCookieValid = other.fRawFullCookieValid;
fExpirationString = other.fExpirationString;
fExpirationStringValid = other.fExpirationStringValid;
fComment = other.fComment;
fCommentUrl = other.fCommentUrl;
fDiscard = other.fDiscard;
fDomain = other.fDomain;
fExpiration = other.fExpiration;
fPath = other.fPath;
fSecure = other.fSecure;
fVersion = other.fVersion;
fName = other.fName;
fValue = other.fValue;
fHasDiscard = other.fHasDiscard;
fHasExpirationDate = other.fHasExpirationDate;
fSessionCookie = other.fSessionCookie;
fHasVersion = other.fHasVersion;
return *this;
}
BNetworkCookie&
BNetworkCookie::operator=(const char* string)
{
return ParseCookieString(string);
}
bool
BNetworkCookie::operator==(const BNetworkCookie& other)
{
// Equality : name and values equals
return fName == other.fName && fValue == other.fValue;
}
bool
BNetworkCookie::operator!=(const BNetworkCookie& other)
{
return !(*this == other);
}
void
BNetworkCookie::_Reset()
{
fComment.Truncate(0);
fCommentUrl.Truncate(0);
fDomain.Truncate(0);
fPath.Truncate(0);
fName.Truncate(0);
fValue.Truncate(0);
fDiscard = false;
fSecure = false;
fVersion = 0;
fExpiration = 0;
fHasDiscard = false;
fHasExpirationDate = false;
fSessionCookie = true;
fHasVersion = false;
fRawCookieValid = false;
fRawFullCookieValid = false;
fExpirationStringValid = false;
}
void
BNetworkCookie::_ExtractNameValuePair(const BString& cookieString,
int16* index, bool parseField)
{
// Skip whitespaces
while (cookieString.ByteAt(*index) == ' '
&& *index < cookieString.Length())
(*index)++;
if (*index >= cookieString.Length())
return;
// Look for a name=value pair
int16 firstSemiColon = cookieString.FindFirst(";", *index);
int16 firstEqual = cookieString.FindFirst("=", *index);
BString name;
BString value;
if (firstSemiColon == -1) {
if (firstEqual != -1) {
cookieString.CopyInto(name, *index, firstEqual - *index);
cookieString.CopyInto(value, firstEqual + 1,
cookieString.Length() - firstEqual - 1);
} else
cookieString.CopyInto(value, *index,
cookieString.Length() - *index);
*index = cookieString.Length() + 1;
} else {
if (firstEqual != -1 && firstEqual < firstSemiColon) {
cookieString.CopyInto(name, *index, firstEqual - *index);
cookieString.CopyInto(value, firstEqual + 1,
firstSemiColon - firstEqual - 1);
} else
cookieString.CopyInto(value, *index, firstSemiColon - *index);
*index = firstSemiColon + 1;
}
// Cookie name/value pair
if (!parseField) {
SetName(name);
SetValue(value);
return;
}
name.ToLower();
name.Trim();
value.Trim();
// Cookie comment
if (name == "comment")
SetComment(value);
// Cookie comment URL
else if (name == "comment-url")
SetCommentUrl(value);
// Cookie discard flag
else if (name == "discard")
SetDiscard(value.Length() == 0 || value.ToLower() == "true");
// Cookie max-age
else if (name == "maxage")
SetMaxAge(atoi(value.String()));
// Cookie expiration date
else if (name == "expires") {
BHttpTime date(value);
SetExpirationDate(date.Parse());
// Cookie valid domain
} else if (name == "domain")
SetDomain(value);
// Cookie valid path
else if (name == "path")
SetPath(value);
// Cookie secure flag
else if (name == "secure")
SetSecure(value.Length() == 0 || value.ToLower() == "true");
// Cookie version
else if (name == "version")
SetVersion(atoi(value.String()));
}
@@ -0,0 +1,683 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <new>
#include <Debug.h>
#include <HashMap.h>
#include <HashString.h>
#include <Message.h>
#include <NetworkCookieJar.h>
#include "NetworkCookieJarPrivate.h"
const char* kArchivedCookieMessageName = "be:cookie";
BNetworkCookieJar::BNetworkCookieJar()
:
fCookieHashMap(new PrivateHashMap)
{
}
BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieJar&)
:
BArchivable(),
fCookieHashMap(new PrivateHashMap)
{
// TODO
}
BNetworkCookieJar::BNetworkCookieJar(const BNetworkCookieList& otherList)
:
fCookieHashMap(new PrivateHashMap)
{
AddCookies(otherList);
}
BNetworkCookieJar::BNetworkCookieJar(BMessage* archive)
:
fCookieHashMap(new PrivateHashMap)
{
BMessage extractedCookie;
for (int32 i = 0;
archive->FindMessage(kArchivedCookieMessageName, i, &extractedCookie)
== B_OK;
i++) {
BNetworkCookie* heapCookie
= new(std::nothrow) BNetworkCookie(&extractedCookie);
if (heapCookie == NULL || !AddCookie(heapCookie))
break;
}
}
BNetworkCookieJar::~BNetworkCookieJar()
{
BNetworkCookie* cookiePtr;
for (Iterator it(GetIterator()); (cookiePtr = it.Next()); )
delete it.Remove();
}
// #pragma mark Add cookie to cookie jar
bool
BNetworkCookieJar::AddCookie(const BNetworkCookie& cookie)
{
BNetworkCookie* heapCookie = new(std::nothrow) BNetworkCookie(cookie);
if (!AddCookie(heapCookie)) {
delete heapCookie;
return false;
}
return true;
}
bool
BNetworkCookieJar::AddCookie(BNetworkCookie* cookie)
{
if (cookie != NULL) {
HashString key(cookie->Domain());
if (!fCookieHashMap->fHashMap.ContainsKey(key))
fCookieHashMap->fHashMap.Put(key, new BList);
BNetworkCookieList* list = fCookieHashMap->fHashMap.Get(key);
for (int32 i = 0; i < list->CountItems(); i++) {
BNetworkCookie* c
= reinterpret_cast<BNetworkCookie*>(list->ItemAt(i));
if (c->Name() == cookie->Name()) {
list->RemoveItem(i);
break;
}
}
// Discard the cookie if it's to be deleted
if (!cookie->ShouldDeleteNow())
list->AddItem(cookie);
}
return true;
}
bool
BNetworkCookieJar::AddCookies(const BNetworkCookieList& cookies)
{
for (int32 i = 0; i < cookies.CountItems(); i++) {
BNetworkCookie* cookiePtr
= reinterpret_cast<BNetworkCookie*>(cookies.ItemAt(i));
// Using AddCookie by reference in order to avoid multiple
// cookie jar share the same cookie pointers
if (!AddCookie(*cookiePtr))
return false;
}
return true;
}
// #pragma mark Purge useless cookies
uint32
BNetworkCookieJar::DeleteOutdatedCookies()
{
int32 deleteCount = 0;
BNetworkCookie* cookiePtr;
for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) {
if (cookiePtr->ShouldDeleteNow()) {
delete it.Remove();
deleteCount++;
}
}
return deleteCount;
}
uint32
BNetworkCookieJar::PurgeForExit()
{
int32 deleteCount = 0;
BNetworkCookie* cookiePtr;
for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) {
if (cookiePtr->ShouldDeleteAtExit()) {
delete it.Remove();
deleteCount++;
}
}
return deleteCount;
}
// #pragma mark BArchivable interface
status_t
BNetworkCookieJar::Archive(BMessage* into, bool deep) const
{
status_t error = BArchivable::Archive(into, deep);
if (error == B_OK) {
BNetworkCookie* cookiePtr;
for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) {
BMessage subArchive;
error = cookiePtr->Archive(&subArchive, deep);
if (error != B_OK)
return error;
error = into->AddMessage(kArchivedCookieMessageName, &subArchive);
if (error != B_OK)
return error;
}
}
return error;
}
BArchivable*
BNetworkCookieJar::Instantiate(BMessage* archive)
{
if (archive->HasMessage(kArchivedCookieMessageName))
return new(std::nothrow) BNetworkCookieJar(archive);
return NULL;
}
// #pragma mark BFlattenable interface
bool
BNetworkCookieJar::IsFixedSize() const
{
// Flattened size vary
return false;
}
type_code
BNetworkCookieJar::TypeCode() const
{
// TODO: Add a B_COOKIEJAR_TYPE
return B_ANY_TYPE;
}
ssize_t
BNetworkCookieJar::FlattenedSize() const
{
_DoFlatten();
return fFlattened.Length() + 1;
}
status_t
BNetworkCookieJar::Flatten(void* buffer, ssize_t size) const
{
if (FlattenedSize() > size)
return B_ERROR;
fFlattened.CopyInto(reinterpret_cast<char*>(buffer), 0,
fFlattened.Length());
reinterpret_cast<char*>(buffer)[fFlattened.Length()] = 0;
return B_OK;
}
bool
BNetworkCookieJar::AllowsTypeCode(type_code) const
{
// TODO
return false;
}
status_t
BNetworkCookieJar::Unflatten(type_code, const void* buffer, ssize_t size)
{
BString flattenedCookies;
flattenedCookies.SetTo(reinterpret_cast<const char*>(buffer), size);
while (flattenedCookies.Length() > 0) {
BNetworkCookie tempCookie;
BString tempCookieLine;
int32 endOfLine = flattenedCookies.FindFirst('\n', 0);
if (endOfLine == -1)
tempCookieLine = flattenedCookies;
else {
flattenedCookies.MoveInto(tempCookieLine, 0, endOfLine);
flattenedCookies.Remove(0, 1);
}
if (tempCookieLine.Length() != 0 && tempCookieLine[0] != '#') {
for (int32 field = 0; field < 7; field++) {
BString tempString;
int32 endOfField = tempCookieLine.FindFirst('\t', 0);
if (endOfField == -1)
tempString = tempCookieLine;
else {
tempCookieLine.MoveInto(tempString, 0, endOfField);
tempCookieLine.Remove(0, 1);
}
switch (field) {
case 0:
tempCookie.SetDomain(tempString);
break;
case 1:
// TODO: Useless field ATM
break;
case 2:
tempCookie.SetPath(tempString);
break;
case 3:
tempCookie.SetSecure(tempString == "TRUE");
break;
case 4:
tempCookie.SetExpirationDate(atoi(tempString));
break;
case 5:
tempCookie.SetName(tempString);
break;
case 6:
tempCookie.SetValue(tempString);
break;
} // switch
} // for loop
AddCookie(tempCookie);
}
}
return B_OK;
}
// #pragma mark Iterators
BNetworkCookieJar::Iterator
BNetworkCookieJar::GetIterator() const
{
return BNetworkCookieJar::Iterator(this);
}
BNetworkCookieJar::UrlIterator
BNetworkCookieJar::GetUrlIterator(const BUrl& url) const
{
if (!url.HasPath()) {
BUrl copy(url);
copy.SetPath("/");
return BNetworkCookieJar::UrlIterator(this, copy);
}
return BNetworkCookieJar::UrlIterator(this, url);
}
void
BNetworkCookieJar::_DoFlatten() const
{
fFlattened.Truncate(0);
BNetworkCookie* cookiePtr;
for (Iterator it(GetIterator()); (cookiePtr = it.Next()); ) {
fFlattened << cookiePtr->Domain() << '\t' << "TRUE" << '\t'
<< cookiePtr->Path() << '\t'
<< (cookiePtr->Secure()?"TRUE":"FALSE") << '\t'
<< (int32)cookiePtr->ExpirationDate() << '\t'
<< cookiePtr->Name() << '\t' << cookiePtr->Value() << '\n';
}
}
// #pragma mark Iterator
BNetworkCookieJar::Iterator::Iterator(const Iterator& other)
:
fCookieJar(other.fCookieJar),
fIterator(other.fIterator),
fLastList(other.fLastList),
fList(other.fList),
fElement(other.fElement),
fLastElement(other.fLastElement),
fIndex(other.fIndex)
{
}
BNetworkCookieJar::Iterator::Iterator(const BNetworkCookieJar* cookieJar)
:
fCookieJar(const_cast<BNetworkCookieJar*>(cookieJar)),
fIterator(NULL),
fLastList(NULL),
fList(NULL),
fElement(NULL),
fLastElement(NULL),
fIndex(0)
{
fIterator = new(std::nothrow) PrivateIterator(
fCookieJar->fCookieHashMap->fHashMap.GetIterator());
// Locate first cookie
_FindNext();
}
BNetworkCookieJar::Iterator::~Iterator()
{
delete fIterator;
}
bool
BNetworkCookieJar::Iterator::HasNext() const
{
return fElement;
}
BNetworkCookie*
BNetworkCookieJar::Iterator::Next()
{
if (!fElement)
return NULL;
BNetworkCookie* result = fElement;
_FindNext();
return result;
}
BNetworkCookie*
BNetworkCookieJar::Iterator::NextDomain()
{
if (!fElement)
return NULL;
BNetworkCookie* result = fElement;
if (!fIterator->fCookieMapIterator.HasNext()) {
fElement = NULL;
return NULL;
}
fList = *(fIterator->fCookieMapIterator.NextValue());
fIndex = 0;
fElement = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex));
return result;
}
BNetworkCookie*
BNetworkCookieJar::Iterator::Remove()
{
if (!fLastElement)
return NULL;
BNetworkCookie* result = fLastElement;
if (fIndex == 0) {
if (fLastList->CountItems() == 1) {
fIterator->fCookieMapIterator.Remove();
delete fLastList;
}
else
fLastList->RemoveItem(fLastList->CountItems() - 1);
} else {
fList->RemoveItem(fIndex-1);
fIndex--;
}
fLastElement = NULL;
return result;
}
BNetworkCookieJar::Iterator&
BNetworkCookieJar::Iterator::operator=(const BNetworkCookieJar::Iterator& other)
{
fCookieJar = other.fCookieJar;
fIterator = other.fIterator;
fLastList = other.fLastList;
fList = other.fList;
fElement = other.fElement;
fLastElement = other.fLastElement;
fIndex = other.fIndex;
return *this;
}
void
BNetworkCookieJar::Iterator::_FindNext()
{
fLastElement = fElement;
fIndex++;
if (fList && fIndex < fList->CountItems()) {
fElement = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex));
return;
}
if (!fIterator->fCookieMapIterator.HasNext()) {
fElement = NULL;
return;
}
fLastList = fList;
fList = *(fIterator->fCookieMapIterator.NextValue());
fIndex = 0;
fElement = reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex));
}
// #pragma mark URL Iterator
BNetworkCookieJar::UrlIterator::UrlIterator(const UrlIterator& other)
{
*this = other;
}
BNetworkCookieJar::UrlIterator::UrlIterator(const BNetworkCookieJar* cookieJar,
const BUrl& url)
:
fCookieJar(const_cast<BNetworkCookieJar*>(cookieJar)),
fIterator(NULL),
fList(NULL),
fLastList(NULL),
fElement(NULL),
fLastElement(NULL),
fIndex(0),
fLastIndex(0),
fUrl(const_cast<BUrl&>(url))
{
BString domain(url.Host());
if (!domain.Length())
return;
if (domain[0] != '.')
domain.Prepend(".");
// Prepending another dot since _FindNext is going to
// call _SupDomain()
domain.Prepend(".");
fIterator = new(std::nothrow) PrivateIterator(
fCookieJar->fCookieHashMap->fHashMap.GetIterator());
fIterator->fKey.SetTo(domain, domain.Length());
_FindNext();
}
BNetworkCookieJar::UrlIterator::~UrlIterator()
{
delete fIterator;
}
bool
BNetworkCookieJar::UrlIterator::HasNext() const
{
return fElement;
}
BNetworkCookie*
BNetworkCookieJar::UrlIterator::Next()
{
if (!fElement)
return NULL;
BNetworkCookie* result = fElement;
_FindNext();
return result;
}
BNetworkCookie*
BNetworkCookieJar::UrlIterator::Remove()
{
if (!fLastElement)
return NULL;
BNetworkCookie* result = fLastElement;
fLastList->RemoveItem(fLastIndex);
if (fLastList->CountItems() == 0) {
HashString lastKey(fLastElement->Domain(),
fLastElement->Domain().Length());
delete fCookieJar->fCookieHashMap->fHashMap.Remove(lastKey);
}
fLastElement = NULL;
return result;
}
BNetworkCookieJar::UrlIterator&
BNetworkCookieJar::UrlIterator::operator=(
const BNetworkCookieJar::UrlIterator& other)
{
fCookieJar = other.fCookieJar;
fList = other.fList;
fLastList = other.fLastList;
fElement = other.fElement;
fLastElement = other.fLastElement;
fIndex = other.fIndex;
fLastIndex = other.fLastIndex;
fUrl = other.fUrl;
fIterator = other.fIterator;
return *this;
}
bool
BNetworkCookieJar::UrlIterator::_SupDomain()
{
BString domain(fIterator->fKey.GetString());
int32 nextDot = domain.FindFirst('.', 1);
if (nextDot == -1)
return false;
domain.Remove(0, nextDot);
fIterator->fKey.SetTo(domain.String(), domain.Length());
return true;
}
void
BNetworkCookieJar::UrlIterator::_FindNext()
{
fLastIndex = fIndex;
fLastElement = fElement;
if (_FindPath())
return;
fLastList = fList;
do {
if (!_SupDomain()) {
fElement = NULL;
return;
}
_FindDomain();
} while (!_FindPath());
}
void
BNetworkCookieJar::UrlIterator::_FindDomain()
{
fList = fCookieJar->fCookieHashMap->fHashMap.Get(fIterator->fKey);
if (fList == NULL)
fElement = NULL;
fIndex = -1;
}
bool
BNetworkCookieJar::UrlIterator::_FindPath()
{
fIndex++;
if (fList && fIndex < fList->CountItems()) {
do {
fElement
= reinterpret_cast<BNetworkCookie*>(fList->ItemAt(fIndex));
if (fElement->IsValidForPath(fUrl.Path()))
return true;
fIndex++;
} while (fList && fIndex < fList->CountItems());
}
return false;
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _B_NETWORK_COOKIE_JAR_PRIVATE_H_
#define _B_NETWORK_COOKIE_JAR_PRIVATE_H_
typedef BPrivate::HashMap<HashString, BNetworkCookieList*> BNetworkCookieHashMap;
struct BNetworkCookieJar::PrivateHashMap {
BNetworkCookieHashMap fHashMap;
};
struct BNetworkCookieJar::PrivateIterator {
PrivateIterator(
BNetworkCookieHashMap::Iterator it)
:
fCookieMapIterator(it)
{
}
HashString fKey;
BNetworkCookieHashMap::Iterator
fCookieMapIterator;
};
#endif // _B_NETWORK_COOKIE_JAR_PRIVATE_H_
+866
View File
@@ -0,0 +1,866 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <ctype.h>
#include <cstdio>
#include <cstdlib>
#include <new>
#include <Url.h>
static const char* kArchivedUrl = "be:url string";
BUrl::BUrl(const char* url)
:
fUrlString(),
fProtocol(),
fUser(),
fPassword(),
fHost(),
fPort(0),
fPath(),
fRequest(),
fHasAuthority(false)
{
SetUrlString(url);
}
BUrl::BUrl(BMessage* archive)
:
fUrlString(),
fProtocol(),
fUser(),
fPassword(),
fHost(),
fPort(0),
fPath(),
fRequest(),
fHasAuthority(false)
{
BString url;
if (archive->FindString(kArchivedUrl, &url) == B_OK)
SetUrlString(url);
}
BUrl::BUrl(const BUrl& other)
:
BArchivable(),
fUrlString(),
fProtocol(),
fUser(),
fPassword(),
fHost(),
fPort(0),
fPath(),
fRequest(),
fHasAuthority(false)
{
*this = other;
}
BUrl::BUrl()
:
fUrlString(),
fProtocol(),
fUser(),
fPassword(),
fHost(),
fPort(0),
fPath(),
fRequest(),
fHasAuthority(false)
{
}
BUrl::~BUrl()
{
}
// #pragma mark URL fields modifiers
BUrl&
BUrl::SetUrlString(const BString& url)
{
_ExplodeUrlString(url);
return *this;
}
BUrl&
BUrl::SetProtocol(const BString& protocol)
{
fProtocol = protocol;
fHasProtocol = true;
fUrlStringValid = false;
return *this;
}
BUrl&
BUrl::SetUserName(const BString& user)
{
fUser = user;
fHasUserName = true;
fUrlStringValid = false;
fAuthorityValid = false;
fUserInfoValid = false;
return *this;
}
BUrl&
BUrl::SetPassword(const BString& password)
{
fPassword = password;
fHasPassword = true;
fUrlStringValid = false;
fAuthorityValid = false;
fUserInfoValid = false;
return *this;
}
BUrl&
BUrl::SetHost(const BString& host)
{
fHost = host;
fHasHost = true;
fUrlStringValid = false;
fAuthorityValid = false;
return *this;
}
BUrl&
BUrl::SetPort(int port)
{
fPort = port;
fHasPort = true;
fUrlStringValid = false;
fAuthorityValid = false;
return *this;
}
BUrl&
BUrl::SetPath(const BString& path)
{
fPath = path;
fHasPath = true;
fUrlStringValid = false;
return *this;
}
BUrl&
BUrl::SetRequest(const BString& request)
{
fRequest = request;
fHasRequest = true;
fUrlStringValid = false;
return *this;
}
BUrl&
BUrl::SetFragment(const BString& fragment)
{
fFragment = fragment;
fHasFragment = true;
fUrlStringValid = false;
return *this;
}
// #pragma mark URL fields access
const BString&
BUrl::UrlString() const
{
if (!fUrlStringValid) {
fUrlString.Truncate(0);
if (HasProtocol()) {
fUrlString << fProtocol << ':';
if (HasAuthority())
fUrlString << "//";
}
fUrlString << Authority();
fUrlString << Path();
if (HasRequest())
fUrlString << '?' << fRequest;
if (HasFragment())
fUrlString << '#' << fFragment;
fUrlStringValid = true;
}
return fUrlString;
}
const BString&
BUrl::Protocol() const
{
return fProtocol;
}
const BString&
BUrl::UserName() const
{
return fUser;
}
const BString&
BUrl::Password() const
{
return fPassword;
}
const BString&
BUrl::UserInfo() const
{
if (!fUserInfoValid) {
fUserInfo = fUser;
if (HasPassword())
fUserInfo << ':' << fPassword;
fUserInfoValid = true;
}
return fUserInfo;
}
const BString&
BUrl::Host() const
{
return fHost;
}
int
BUrl::Port() const
{
return fPort;
}
const BString&
BUrl::Authority() const
{
if (!fAuthorityValid) {
fAuthority.Truncate(0);
if (HasUserInfo())
fAuthority << UserInfo() << '@';
fAuthority << Host();
if (HasPort())
fAuthority << ':' << fPort;
fAuthorityValid = true;
}
return fAuthority;
}
const BString&
BUrl::Path() const
{
return fPath;
}
const BString&
BUrl::Request() const
{
return fRequest;
}
const BString&
BUrl::Fragment() const
{
return fFragment;
}
// #pragma mark URL fields tests
bool
BUrl::IsValid() const
{
// TODO
return false;
}
bool
BUrl::HasProtocol() const
{
return fHasProtocol;
}
bool
BUrl::HasAuthority() const
{
return fHasAuthority;
}
bool
BUrl::HasUserName() const
{
return fHasUserName;
}
bool
BUrl::HasPassword() const
{
return fHasPassword;
}
bool
BUrl::HasUserInfo() const
{
return fHasUserInfo;
}
bool
BUrl::HasHost() const
{
return fHasHost;
}
bool
BUrl::HasPort() const
{
return fHasPort;
}
bool
BUrl::HasPath() const
{
return fHasPath;
}
bool
BUrl::HasRequest() const
{
return fHasRequest;
}
bool
BUrl::HasFragment() const
{
return fHasFragment;
}
// #pragma mark URL encoding/decoding of needed fields
void
BUrl::UrlEncode(bool strict)
{
fUser = _DoUrlEncodeChunk(fUser, strict);
fPassword = _DoUrlEncodeChunk(fPassword, strict);
fHost = _DoUrlEncodeChunk(fHost, strict);
fFragment = _DoUrlEncodeChunk(fFragment, strict);
fPath = _DoUrlEncodeChunk(fPath, strict, true);
}
void
BUrl::UrlDecode(bool strict)
{
fUser = _DoUrlDecodeChunk(fUser, strict);
fPassword = _DoUrlDecodeChunk(fPassword, strict);
fHost = _DoUrlDecodeChunk(fHost, strict);
fFragment = _DoUrlDecodeChunk(fFragment, strict);
fPath = _DoUrlDecodeChunk(fPath, strict);
}
// #pragma mark Url encoding/decoding of string
/*static*/ BString
BUrl::UrlEncode(const BString& url, bool strict, bool directory)
{
return _DoUrlEncodeChunk(url, strict, directory);
}
/*static*/ BString
BUrl::UrlDecode(const BString& url, bool strict)
{
return _DoUrlDecodeChunk(url, strict);
}
// #pragma mark BArchivable members
status_t
BUrl::Archive(BMessage* into, bool deep) const
{
status_t ret = BArchivable::Archive(into, deep);
if (ret == B_OK)
ret = into->AddString(kArchivedUrl, UrlString());
return ret;
}
/*static*/ BArchivable*
BUrl::Instantiate(BMessage* archive)
{
if (validate_instantiation(archive, "BUrl"))
return new(std::nothrow) BUrl(archive);
return NULL;
}
// #pragma mark URL comparison
bool
BUrl::operator==(BUrl& other) const
{
UrlString();
other.UrlString();
return fUrlString == other.fUrlString;
}
bool
BUrl::operator!=(BUrl& other) const
{
return !(*this == other);
}
// #pragma mark URL assignment
const BUrl&
BUrl::operator=(const BUrl& other)
{
fUrlStringValid = other.fUrlStringValid;
if (fUrlStringValid)
fUrlString = other.fUrlString;
fAuthorityValid = other.fAuthorityValid;
if (fAuthorityValid)
fAuthority = other.fAuthority;
fUserInfoValid = other.fUserInfoValid;
if (fUserInfoValid)
fUserInfo = other.fUserInfo;
fProtocol = other.fProtocol;
fUser = other.fUser;
fPassword = other.fPassword;
fHost = other.fHost;
fPort = other.fPort;
fPath = other.fPath;
fRequest = other.fRequest;
fFragment = other.fFragment;
fHasProtocol = other.fHasProtocol;
fHasUserName = other.fHasUserName;
fHasPassword = other.fHasPassword;
fHasUserInfo = other.fHasUserInfo;
fHasHost = other.fHasHost;
fHasPort = other.fHasPort;
fHasAuthority = other.fHasAuthority;
fHasPath = other.fHasPath;
fHasRequest = other.fHasRequest;
fHasFragment = other.fHasFragment;
return *this;
}
const BUrl&
BUrl::operator=(const BString& string)
{
SetUrlString(string);
return *this;
}
const BUrl&
BUrl::operator=(const char* string)
{
SetUrlString(string);
return *this;
}
// #pragma mark URL to string conversion
BUrl::operator const char*() const
{
return UrlString();
}
void
BUrl::_ResetFields()
{
fHasProtocol = false;
fHasUserName = false;
fHasPassword = false;
fHasUserInfo = false;
fHasHost = false;
fHasPort = false;
fHasAuthority = false;
fHasPath = false;
fHasRequest = false;
fHasFragment = false;
fProtocol.Truncate(0);
fUser.Truncate(0);
fPassword.Truncate(0);
fHost.Truncate(0);
fPort = 0;
fPath.Truncate(0);
fRequest.Truncate(0);
fFragment.Truncate(0);
// Force re-generation of these fields
fUrlStringValid = false;
fUserInfoValid = false;
fAuthorityValid = false;
}
void
BUrl::_ExplodeUrlString(const BString& url)
{
int16 urlIndex = 0;
_ResetFields();
_ExtractProtocol(url, &urlIndex);
_ExtractAuthority(url, &urlIndex);
_ExtractPath(url, &urlIndex);
_ExtractRequestAndFragment(url, &urlIndex);
}
void
BUrl::_ExtractProtocol(const BString& urlString, int16* origin)
{
int16 firstColon = urlString.FindFirst(':', *origin);
// If no colon is found, assume the protocol
// is not present
if (firstColon == -1)
return;
else {
urlString.CopyInto(fProtocol, *origin, firstColon - *origin);
*origin = firstColon + 1;
}
if (!_IsProtocolValid()) {
fHasProtocol = false;
fProtocol.Truncate(0);
} else
fHasProtocol = true;
}
void
BUrl::_ExtractAuthority(const BString& urlString, int16* origin)
{
// URI doesn't contain an authority field
if (urlString.FindFirst("//", *origin) != *origin)
return;
fHasAuthority = true;
// while (urlString.ByteAt(*origin) == '/')
// (*origin)++;
(*origin) += 2;
int16 userInfoEnd = urlString.FindFirst('@', *origin);
// URL contains userinfo field
if (userInfoEnd != -1) {
BString userInfo;
urlString.CopyInto(userInfo, *origin, userInfoEnd - *origin);
int16 colonDelimiter = userInfo.FindFirst(':', 0);
if (colonDelimiter == *origin) {
fHasPassword = true;
fPassword = userInfo;
} else if (colonDelimiter != -1) {
fHasUserName = true;
fHasPassword = true;
userInfo.CopyInto(fUser, 0, colonDelimiter);
userInfo.CopyInto(fPassword, colonDelimiter + 1,
userInfo.Length() - colonDelimiter);
} else {
fHasUserName = true;
fUser = userInfo;
}
fHasUserInfo = true;
*origin = userInfoEnd + 1;
}
// Extract the host part
int16 hostEnd = *origin;
while (hostEnd < urlString.Length()
&& !_IsAuthorityTerminator(urlString.ByteAt(hostEnd))
&& urlString.ByteAt(hostEnd) != ':') {
hostEnd++;
}
// The host is likely to be present if an authority is
// defined, but in some weird cases, it's not.
if (hostEnd != *origin) {
urlString.CopyInto(fHost, *origin, hostEnd - *origin);
*origin = hostEnd;
fHasHost = true;
}
// Extract the port part
fPort = 0;
if (urlString.ByteAt(*origin) == ':') {
int16 portEnd = ++(*origin);
while (portEnd < urlString.Length()
&& !_IsAuthorityTerminator(urlString.ByteAt(portEnd)))
portEnd++;
BString portString;
urlString.CopyInto(portString, *origin, portEnd - *origin);
fPort = atoi(portString.String());
// Even if the port is invalid, the URL is considered to
// have a port.
fHasPort = portString.Length() > 0;
*origin = portEnd;
}
}
void
BUrl::_ExtractPath(const BString& urlString, int16* origin)
{
// Extract path from URL
if (urlString.ByteAt(*origin) == '/' || !HasAuthority()) {
int16 pathEnd = *origin;
while (pathEnd < urlString.Length()
&& !_IsPathTerminator(urlString.ByteAt(pathEnd))) {
pathEnd++;
}
urlString.CopyInto(fPath, *origin, pathEnd - *origin);
*origin = pathEnd;
fHasPath = true;
}
}
void
BUrl::_ExtractRequestAndFragment(const BString& urlString, int16* origin)
{
// Extract request field from URL
if (urlString.ByteAt(*origin) == '?') {
(*origin)++;
int16 requestEnd = urlString.FindFirst('#', *origin);
fHasRequest = true;
if (requestEnd == -1) {
urlString.CopyInto(fRequest, *origin, urlString.Length() - *origin);
return;
} else {
urlString.CopyInto(fRequest, *origin, requestEnd - *origin);
*origin = requestEnd;
}
}
// Extract fragment field if needed
if (urlString.ByteAt(*origin) == '#') {
(*origin)++;
urlString.CopyInto(fFragment, *origin, urlString.Length() - *origin);
fHasFragment = true;
}
}
/*static*/ BString
BUrl::_DoUrlEncodeChunk(const BString& chunk, bool strict, bool directory)
{
BString result;
for (int32 i = 0; i < chunk.Length(); i++) {
if (_IsUnreserved(chunk[i])
|| (directory && (chunk[i] == '/' || chunk[i] == '\\')))
result << chunk[i];
else {
if (chunk[i] == ' ' && !strict) {
result << '+';
// In non-strict mode, spaces are encoded by a plus sign
} else {
char hexString[5];
snprintf(hexString, 5, "%X", chunk[i]);
result << '%' << hexString;
}
}
}
return result;
}
/*static*/ BString
BUrl::_DoUrlDecodeChunk(const BString& chunk, bool strict)
{
BString result;
for (int32 i = 0; i < chunk.Length(); i++) {
if (chunk[i] == '+' && !strict)
result << ' ';
else if (chunk[i] != '%')
result << chunk[i];
else {
char hexString[] = { chunk[i+1], chunk[i+2], 0 };
result << (char)strtol(hexString, NULL, 16);
i += 2;
}
}
return result;
}
bool
BUrl::_IsProtocolValid()
{
for (int8 index = 0; index < fProtocol.Length(); index++) {
char c = fProtocol[index];
if (index == 0 && !isalpha(c))
return false;
else if (!isalnum(c) && c != '+' && c != '-' && c != '.')
return false;
}
return true;
}
bool
BUrl::_IsAuthorityTerminator(char c)
{
if (c == '/' || _IsPathTerminator(c))
return true;
else
return false;
}
bool
BUrl::_IsPathTerminator(char c)
{
if (c == '?' || _IsRequestTerminator(c))
return true;
else
return false;
}
bool
BUrl::_IsRequestTerminator(char c)
{
if (c == '#')
return true;
else
return false;
}
bool
BUrl::_IsUnreserved(char c)
{
if (isalnum(c) || c == '-' || c == '.' || c == '_' || c == '~')
return true;
else
return false;
}
bool
BUrl::_IsGenDelim(char c)
{
if (c == ':' || c == '/' || c == '?' || c == '#' || c == '['
|| c == ']' || c == '@')
return true;
else
return false;
}
bool
BUrl::_IsSubDelim(char c)
{
if (c == '!' || c == '$' || c == '&' || c == '\'' || c == '('
|| c == ')' || c == '*' || c == '+' || c == ',' || c == ';'
|| c == '=')
return true;
else
return false;
}
+37
View File
@@ -0,0 +1,37 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <UrlContext.h>
BUrlContext::BUrlContext()
:
fCookieJar()
{
}
// #pragma mark Context modifiers
void
BUrlContext::SetCookieJar(const BNetworkCookieJar& cookieJar)
{
fCookieJar = cookieJar;
}
// #pragma mark Context accessors
BNetworkCookieJar&
BUrlContext::GetCookieJar()
{
return fCookieJar;
}
+298
View File
@@ -0,0 +1,298 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <UrlProtocol.h>
#include <Debug.h>
#include <stdio.h>
static const char* kProtocolThreadStrStatus[B_PROT_THREAD_STATUS__END+1]
= {
"Request successfully completed",
"Request running",
"Socket error",
"Connection failed",
"Hostname resolution failed",
"Network write failed",
"Network read failed",
"Out of memory",
"Protocol-specific error",
"Unknown error"
};
BUrlProtocol::BUrlProtocol(const BUrl& url, BUrlProtocolListener* listener,
BUrlContext* context, BUrlResult* result, const char* threadName,
const char* protocolName)
:
fUrl(url),
fResult(result),
fContext(context),
fListener(listener),
fQuit(false),
fRunning(false),
fThreadId(0),
fThreadName(threadName),
fProtocol(protocolName)
{
}
// #pragma mark URL protocol thread management
thread_id
BUrlProtocol::Run()
{
// Thread already running
if (fRunning) {
PRINT(("BUrlProtocol::Run() : Oops, already running ! "
"[urlProtocol=%p]!\n", this));
return fThreadId;
}
fThreadId = spawn_thread(BUrlProtocol::_ThreadEntry, fThreadName,
B_NORMAL_PRIORITY, this);
if (fThreadId < B_OK)
return fThreadId;
status_t launchErr = resume_thread(fThreadId);
if (launchErr < B_OK) {
PRINT(("BUrlProtocol::Run() : Failed to resume thread %ld\n",
fThreadId));
return launchErr;
}
fRunning = true;
return fThreadId;
}
status_t
BUrlProtocol::Pause()
{
// TODO
return B_ERROR;
}
status_t
BUrlProtocol::Resume()
{
// TODO
return B_ERROR;
}
status_t
BUrlProtocol::Stop()
{
if (!fRunning)
return B_ERROR;
status_t threadStatus = B_OK;
fQuit = true;
wait_for_thread(fThreadId, &threadStatus);
return threadStatus;
}
// #pragma mark URL protocol parameters modification
status_t
BUrlProtocol::SetUrl(const BUrl& url)
{
// We should avoid to change URL while the thread is running ...
if (IsRunning())
return B_ERROR;
fUrl = url;
return B_OK;
}
status_t
BUrlProtocol::SetResult(BUrlResult* result)
{
if (IsRunning())
return B_ERROR;
fResult = result;
return B_OK;
}
status_t
BUrlProtocol::SetContext(BUrlContext* context)
{
if (IsRunning())
return B_ERROR;
fContext = context;
return B_OK;
}
status_t
BUrlProtocol::SetListener(BUrlProtocolListener* listener)
{
if (IsRunning())
return B_ERROR;
fListener = listener;
return B_OK;
}
// #pragma mark URL protocol parameters access
const BUrl&
BUrlProtocol::Url() const
{
return fUrl;
}
BUrlResult*
BUrlProtocol::Result() const
{
return fResult;
}
BUrlContext*
BUrlProtocol::Context() const
{
return fContext;
}
BUrlProtocolListener*
BUrlProtocol::Listener() const
{
return fListener;
}
const BString&
BUrlProtocol::Protocol() const
{
return fProtocol;
}
// #pragma mark URL protocol informations
bool
BUrlProtocol::IsRunning() const
{
return fRunning;
}
status_t
BUrlProtocol::Status() const
{
return fThreadStatus;
}
const char*
BUrlProtocol::StatusString(status_t threadStatus) const
{
if (threadStatus < B_PROT_THREAD_STATUS__BASE)
threadStatus = B_PROT_THREAD_STATUS__END;
else if (threadStatus >= B_PROT_PROTOCOL_ERROR)
threadStatus = B_PROT_PROTOCOL_ERROR;
return kProtocolThreadStrStatus[threadStatus];
}
// #pragma mark Thread management
/*static*/ int32
BUrlProtocol::_ThreadEntry(void* arg)
{
BUrlProtocol* urlProtocol = reinterpret_cast<BUrlProtocol*>(arg);
urlProtocol->fThreadStatus = B_PROT_RUNNING;
status_t protocolLoopExitStatus = urlProtocol->_ProtocolLoop();
urlProtocol->fRunning = false;
urlProtocol->fThreadStatus = protocolLoopExitStatus;
if (urlProtocol->fListener != NULL)
urlProtocol->fListener->RequestCompleted(urlProtocol,
protocolLoopExitStatus == B_PROT_SUCCESS);
return B_OK;
}
status_t
BUrlProtocol::_ProtocolLoop()
{
// Dummy _ProtocolLoop
while (!fQuit)
snooze(1000);
return B_PROT_SUCCESS;
}
void
BUrlProtocol::_EmitDebug(BUrlProtocolDebugMessage type,
const char* format, ...)
{
if (fListener == NULL)
return;
va_list arguments;
va_start(arguments, format);
char debugMsg[256];
vsnprintf(debugMsg, 256, format, arguments);
fListener->DebugMessage(this, type, debugMsg);
va_end(arguments);
}
BMallocIO&
BUrlProtocol::_ResultRawData()
{
return fResult->fRawData;
}
BHttpHeaders&
BUrlProtocol::_ResultHeaders()
{
return fResult->fHeaders;
}
void
BUrlProtocol::_SetResultStatusCode(int32 statusCode)
{
fResult->fStatusCode = statusCode;
}
BString&
BUrlProtocol::_ResultStatusText()
{
return fResult->fStatusString;
}
@@ -0,0 +1,196 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <new>
#include <AppKit.h>
#include <UrlProtocolAsynchronousListener.h>
#include <Debug.h>
extern const char* kUrlProtocolMessageType;
extern const char* kUrlProtocolCaller;
BUrlProtocolAsynchronousListener::BUrlProtocolAsynchronousListener(
bool transparent)
:
BHandler("UrlProtocolAsynchronousListener"),
fSynchronousListener(NULL)
{
if (be_app->Lock()) {
be_app->AddHandler(this);
be_app->Unlock();
}
else
PRINT(("Cannot lock be_app\n"));
if (transparent)
fSynchronousListener
= new(std::nothrow) BUrlProtocolDispatchingListener(this);
}
BUrlProtocolAsynchronousListener::~BUrlProtocolAsynchronousListener()
{
if (be_app->Lock()) {
be_app->RemoveHandler(this);
be_app->Unlock();
}
delete fSynchronousListener;
}
void
BUrlProtocolAsynchronousListener::ConnectionOpened(BUrlProtocol*)
{
}
void
BUrlProtocolAsynchronousListener::HostnameResolved(BUrlProtocol*, const char*)
{
}
void
BUrlProtocolAsynchronousListener::ResponseStarted(BUrlProtocol*)
{
}
void
BUrlProtocolAsynchronousListener::HeadersReceived(BUrlProtocol*)
{
}
void
BUrlProtocolAsynchronousListener::DataReceived(BUrlProtocol*, const char*,
ssize_t)
{
}
void
BUrlProtocolAsynchronousListener::DownloadProgress(BUrlProtocol*, ssize_t,
ssize_t)
{
}
void
BUrlProtocolAsynchronousListener::UploadProgress(BUrlProtocol*, ssize_t,
ssize_t)
{
}
void
BUrlProtocolAsynchronousListener::RequestCompleted(BUrlProtocol*, bool)
{
}
// #pragma mark Synchronous listener access
BUrlProtocolListener*
BUrlProtocolAsynchronousListener::SynchronousListener()
{
return fSynchronousListener;
}
void
BUrlProtocolAsynchronousListener::MessageReceived(BMessage* message)
{
if (message->what != B_URL_PROTOCOL_NOTIFICATION) {
BHandler::MessageReceived(message);
return;
}
BUrlProtocol* caller;
if (message->FindPointer(kUrlProtocolCaller,
reinterpret_cast<void**>(&caller)) != B_OK)
return;
int8 notification;
if (message->FindInt8(kUrlProtocolMessageType, &notification)
!= B_OK)
return;
switch (notification) {
case B_URL_PROTOCOL_CONNECTION_OPENED:
ConnectionOpened(caller);
break;
case B_URL_PROTOCOL_HOSTNAME_RESOLVED:
{
const char* ip;
message->FindString("url:ip", &ip);
HostnameResolved(caller, ip);
}
break;
case B_URL_PROTOCOL_RESPONSE_STARTED:
ResponseStarted(caller);
break;
case B_URL_PROTOCOL_HEADERS_RECEIVED:
HeadersReceived(caller);
break;
case B_URL_PROTOCOL_DATA_RECEIVED:
{
const char* data;
ssize_t size;
message->FindData("url:data", B_STRING_TYPE,
reinterpret_cast<const void**>(&data), &size);
DataReceived(caller, data, size);
}
break;
case B_URL_PROTOCOL_DOWNLOAD_PROGRESS:
{
int32 bytesReceived;
int32 bytesTotal;
message->FindInt32("url:bytesReceived", &bytesReceived);
message->FindInt32("url:bytesTotal", &bytesTotal);
DownloadProgress(caller, bytesReceived, bytesTotal);
}
break;
case B_URL_PROTOCOL_UPLOAD_PROGRESS:
{
int32 bytesSent;
int32 bytesTotal;
message->FindInt32("url:bytesSent", &bytesSent);
message->FindInt32("url:bytesTotal", &bytesTotal);
UploadProgress(caller, bytesSent, bytesTotal);
}
break;
case B_URL_PROTOCOL_REQUEST_COMPLETED:
{
bool success;
message->FindBool("url:success", &success);
RequestCompleted(caller, success);
}
break;
default:
PRINT(("BUrlProtocolAsynchronousListener: Unknown notification %d\n",
notification));
break;
}
}
@@ -0,0 +1,126 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <UrlProtocolDispatchingListener.h>
#include <Debug.h>
const char* kUrlProtocolMessageType = "be:urlProtocolMessageType";
const char* kUrlProtocolCaller = "be:urlProtocolCaller";
BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener
(BHandler* handler)
:
fMessenger(handler)
{
}
BUrlProtocolDispatchingListener::BUrlProtocolDispatchingListener
(const BMessenger& messenger)
:
fMessenger(messenger)
{
}
void
BUrlProtocolDispatchingListener::ConnectionOpened(BUrlProtocol* caller)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
_SendMessage(&message, B_URL_PROTOCOL_CONNECTION_OPENED, caller);
}
void
BUrlProtocolDispatchingListener::HostnameResolved(BUrlProtocol* caller,
const char* ip)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
message.AddString("url:hostIp", ip);
_SendMessage(&message, B_URL_PROTOCOL_HOSTNAME_RESOLVED, caller);
}
void
BUrlProtocolDispatchingListener::ResponseStarted(BUrlProtocol* caller)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
_SendMessage(&message, B_URL_PROTOCOL_RESPONSE_STARTED, caller);
}
void
BUrlProtocolDispatchingListener::HeadersReceived(BUrlProtocol* caller)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
_SendMessage(&message, B_URL_PROTOCOL_HEADERS_RECEIVED, caller);
}
void
BUrlProtocolDispatchingListener::DataReceived(BUrlProtocol* caller,
const char* data, ssize_t size)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
message.AddData("url:data", B_STRING_TYPE, data, size, true, 1);
_SendMessage(&message, B_URL_PROTOCOL_DATA_RECEIVED, caller);
}
void
BUrlProtocolDispatchingListener::DownloadProgress(BUrlProtocol* caller,
ssize_t bytesReceived, ssize_t bytesTotal)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
message.AddInt32("url:bytesReceived", bytesReceived);
message.AddInt32("url:bytesTotal", bytesTotal);
_SendMessage(&message, B_URL_PROTOCOL_DOWNLOAD_PROGRESS, caller);
}
void
BUrlProtocolDispatchingListener::UploadProgress(BUrlProtocol* caller,
ssize_t bytesSent, ssize_t bytesTotal)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
message.AddInt32("url:bytesSent", bytesSent);
message.AddInt32("url:bytesTotal", bytesTotal);
_SendMessage(&message, B_URL_PROTOCOL_UPLOAD_PROGRESS, caller);
}
void
BUrlProtocolDispatchingListener::RequestCompleted(BUrlProtocol* caller,
bool success)
{
BMessage message(B_URL_PROTOCOL_NOTIFICATION);
message.AddBool("url:success", success);
_SendMessage(&message, B_URL_PROTOCOL_REQUEST_COMPLETED, caller);
}
void
BUrlProtocolDispatchingListener::_SendMessage(BMessage* message,
int8 notification, BUrlProtocol* caller)
{
ASSERT(message != NULL);
message->AddPointer(kUrlProtocolCaller, caller);
message->AddInt8(kUrlProtocolMessageType, notification);
ASSERT(fMessenger.SendMessage(message) == B_OK);
}
@@ -0,0 +1,868 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <cstdlib>
#include <deque>
#include <new>
#include <arpa/inet.h>
#include <Debug.h>
#include <File.h>
#include <UrlProtocolHttp.h>
using BPrivate::BUrlProtocolOption;
static const int32 kHttpProtocolReceiveBufferSize = 1024;
static const char* kHttpProtocolThreadStrStatus[
B_PROT_HTTP_THREAD_STATUS__END - B_PROT_THREAD_STATUS__END]
= {
"The remote server did not found the requested resource"
};
BUrlProtocolHttp::BUrlProtocolHttp(BUrl& url, BUrlProtocolListener* listener,
BUrlContext* context, BUrlResult* result)
:
BUrlProtocol(url, listener, context, result, "BUrlProtocol.HTTP", "HTTP"),
fRequestMethod(B_HTTP_GET),
fHttpVersion(B_HTTP_11)
{
_ResetOptions();
}
status_t
BUrlProtocolHttp::SetOption(uint32 name, void* value)
{
BUrlProtocolOption option(value);
switch (name) {
case B_HTTPOPT_METHOD:
fRequestMethod = option.Int8();
break;
case B_HTTPOPT_FOLLOWLOCATION:
fOptFollowLocation = option.Bool();
break;
case B_HTTPOPT_MAXREDIRS:
fOptMaxRedirs = option.Int8();
break;
case B_HTTPOPT_REFERER:
fOptReferer = option.String();
break;
case B_HTTPOPT_USERAGENT:
fOptUserAgent = option.String();
break;
case B_HTTPOPT_HEADERS:
fOptHeaders = reinterpret_cast<BHttpHeaders*>(option.Pointer());
break;
case B_HTTPOPT_DISCARD_DATA:
fOptDiscardData = option.Bool();
break;
case B_HTTPOPT_DISABLE_LISTENER:
fOptDisableListener = option.Bool();
break;
case B_HTTPOPT_AUTOREFERER:
fOptAutoReferer = option.Bool();
break;
case B_HTTPOPT_POSTFIELDS:
fOptPostFields = reinterpret_cast<BHttpForm*>(option.Pointer());
if (fOptPostFields != NULL)
fRequestMethod = B_HTTP_POST;
break;
case B_HTTPOPT_INPUTDATA:
fOptInputData = reinterpret_cast<BDataIO*>(option.Pointer());
break;
case B_HTTPOPT_AUTHUSERNAME:
fOptUsername = option.String();
break;
case B_HTTPOPT_AUTHPASSWORD:
fOptPassword = option.String();
break;
default:
return B_ERROR;
}
return B_OK;
}
/*static*/ bool
BUrlProtocolHttp::IsInformationalStatusCode(int16 code)
{
return (code >= B_HTTP_STATUS__INFORMATIONAL_BASE)
&& (code < B_HTTP_STATUS__INFORMATIONAL_END);
}
/*static*/ bool
BUrlProtocolHttp::IsSuccessStatusCode(int16 code)
{
return (code >= B_HTTP_STATUS__SUCCESS_BASE)
&& (code < B_HTTP_STATUS__SUCCESS_END);
}
/*static*/ bool
BUrlProtocolHttp::IsRedirectionStatusCode(int16 code)
{
return (code >= B_HTTP_STATUS__REDIRECTION_BASE)
&& (code < B_HTTP_STATUS__REDIRECTION_END);
}
/*static*/ bool
BUrlProtocolHttp::IsClientErrorStatusCode(int16 code)
{
return (code >= B_HTTP_STATUS__CLIENT_ERROR_BASE)
&& (code < B_HTTP_STATUS__CLIENT_ERROR_END);
}
/*static*/ bool
BUrlProtocolHttp::IsServerErrorStatusCode(int16 code)
{
return (code >= B_HTTP_STATUS__SERVER_ERROR_BASE)
&& (code < B_HTTP_STATUS__SERVER_ERROR_END);
}
/*static*/ int16
BUrlProtocolHttp::StatusCodeClass(int16 code)
{
if (BUrlProtocolHttp::IsInformationalStatusCode(code))
return B_HTTP_STATUS_CLASS_INFORMATIONAL;
else if (BUrlProtocolHttp::IsSuccessStatusCode(code))
return B_HTTP_STATUS_CLASS_SUCCESS;
else if (BUrlProtocolHttp::IsRedirectionStatusCode(code))
return B_HTTP_STATUS_CLASS_REDIRECTION;
else if (BUrlProtocolHttp::IsClientErrorStatusCode(code))
return B_HTTP_STATUS_CLASS_CLIENT_ERROR;
else if (BUrlProtocolHttp::IsServerErrorStatusCode(code))
return B_HTTP_STATUS_CLASS_SERVER_ERROR;
return B_HTTP_STATUS_CLASS_INVALID;
}
const char*
BUrlProtocolHttp::StatusString(status_t threadStatus) const
{
if (threadStatus < B_PROT_THREAD_STATUS__END)
return BUrlProtocol::StatusString(threadStatus);
else if (threadStatus >= B_PROT_HTTP_THREAD_STATUS__END)
return BUrlProtocol::StatusString(-1);
else
return kHttpProtocolThreadStrStatus[threadStatus
- B_PROT_THREAD_STATUS__END];
}
void
BUrlProtocolHttp::_ResetOptions()
{
fOptFollowLocation = true;
fOptMaxRedirs = 8;
fOptReferer = "";
fOptUserAgent = "Services Kit (Haiku)";
fOptUsername = "";
fOptPassword = "";
fOptAuthMethods = B_HTTP_AUTHENTICATION_BASIC | B_HTTP_AUTHENTICATION_DIGEST
| B_HTTP_AUTHENTICATION_IE_DIGEST;
fOptHeaders = NULL;
fOptPostFields = NULL;
fOptSetCookies = true;
fOptDiscardData = false;
fOptDisableListener = false;
fOptAutoReferer = true;
}
#include <stdio.h>
status_t
BUrlProtocolHttp::_ProtocolLoop()
{
printf("UHP[%p]::{Loop} %s\n", this, fUrl.UrlString().String());
// Socket initialization
fSocket = BNetEndpoint(SOCK_STREAM);
if (fSocket.InitCheck() != B_OK)
return B_PROT_SOCKET_ERROR;
// Initialize the request redirection loop
int8 maxRedirs = fOptMaxRedirs;
bool newRequest;
do {
newRequest = false;
// Result reset
fOutputBuffer.Truncate(0, true);
fOutputHeaders.Clear();
fHeaders.Clear();
_ResultHeaders().Clear();
_ResultRawData().Seek(SEEK_SET, 0);
_ResultRawData().SetSize(0);
if (!_ResolveHostName()) {
_EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR,
"Unable to resolve hostname, aborting.");
return B_PROT_CANT_RESOLVE_HOSTNAME;
}
_CreateRequest();
_AddHeaders();
_AddOutputBufferLine("");
status_t requestStatus = _MakeRequest();
if (requestStatus != B_PROT_SUCCESS)
return requestStatus;
// Prepare the referer for the next request if needed
if (fOptAutoReferer)
fOptReferer = fUrl.UrlString();
switch (StatusCodeClass(fResult->StatusCode())) {
case B_HTTP_STATUS_CLASS_INFORMATIONAL:
// Header 100:continue should have been
// handled in the _MakeRequest read loop
break;
case B_HTTP_STATUS_CLASS_SUCCESS:
break;
case B_HTTP_STATUS_CLASS_REDIRECTION:
// Redirection has been explicitly disabled
if (!fOptFollowLocation)
break;
// TODO: Some browsers seems to translate POST requests to
// GET when following a 302 redirection
if (fResult->StatusCode() == B_HTTP_STATUS_MOVED_PERMANENTLY) {
BString locationUrl = fHeaders["Location"];
// Absolute path
if (locationUrl[0] == '/')
fUrl.SetPath(locationUrl);
// URI
else
fUrl.SetUrlString(locationUrl);
if (--maxRedirs > 0) {
newRequest = true;
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
"Following: %s\n",
fUrl.UrlString().String());
}
}
break;
case B_HTTP_STATUS_CLASS_CLIENT_ERROR:
switch (fResult->StatusCode()) {
case B_HTTP_STATUS_UNAUTHORIZED:
if (fAuthentication.Method() != B_HTTP_AUTHENTICATION_NONE) {
newRequest = false;
break;
}
newRequest = false;
if (fOptUsername.Length() > 0
&& fAuthentication.Initialize(fHeaders["WWW-Authenticate"])
== B_OK) {
fAuthentication.SetUserName(fOptUsername);
fAuthentication.SetPassword(fOptPassword);
newRequest = true;
}
break;
}
break;
case B_HTTP_STATUS_CLASS_SERVER_ERROR:
break;
default:
case B_HTTP_STATUS_CLASS_INVALID:
break;
}
} while (newRequest);
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT,
"%ld headers and %ld bytes of data remaining",
fHeaders.CountHeaders(),
fInputBuffer.Size());
if (fResult->StatusCode() == 404)
return B_PROT_HTTP_NOT_FOUND;
return B_PROT_SUCCESS;
}
bool
BUrlProtocolHttp::_ResolveHostName()
{
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Resolving %s",
fUrl.UrlString().String());
if (fUrl.HasPort())
fRemoteAddr = BNetAddress(fUrl.Host(), fUrl.Port());
else
fRemoteAddr = BNetAddress(fUrl.Host(), 80);
if (fRemoteAddr.InitCheck() != B_OK)
return false;
char addr[15];
struct in_addr ip;
fRemoteAddr.GetAddr(ip);
inet_ntop(AF_INET, &ip, addr, 15);
//! ProtocolHook:HostnameResolved
if (fListener != NULL)
fListener->HostnameResolved(this, const_cast<const char*>(addr));
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Hostname resolved to: %s", addr);
return true;
}
status_t
BUrlProtocolHttp::_MakeRequest()
{
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection to %s.",
fUrl.Authority().String());
status_t connectError = fSocket.Connect(fRemoteAddr);
if (connectError != B_OK) {
_EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR, "Connection error: %s.",
fSocket.ErrorStr());
return B_PROT_CONNECTION_FAILED;
}
//! ProtocolHook:ConnectionOpened
if (fListener != NULL)
fListener->ConnectionOpened(this);
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Connection opened.");
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Sending request (size=%d)",
fOutputBuffer.Length());
fSocket.Send(fOutputBuffer.String(), fOutputBuffer.Length());
fOutputBuffer.Truncate(0);
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Request sent.");
if (fRequestMethod == B_HTTP_POST && fOptPostFields != NULL) {
if (fOptPostFields->GetFormType() != B_HTTP_FORM_MULTIPART) {
fOutputBuffer = fOptPostFields->RawData();
_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
fOutputBuffer.String());
fSocket.Send(fOutputBuffer.String(), fOutputBuffer.Length());
} else {
for (BHttpForm::Iterator it = fOptPostFields->GetIterator();
const BHttpFormData* currentField = it.Next();
) {
_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_OUT,
it.MultipartHeader().String());
fSocket.Send(it.MultipartHeader().String(),
it.MultipartHeader().Length());
switch (currentField->Type()) {
case B_HTTPFORM_UNKNOWN:
ASSERT(0);
break;
case B_HTTPFORM_STRING:
fSocket.Send(currentField->String().String(),
currentField->String().Length());
break;
case B_HTTPFORM_FILE:
{
BFile upFile(currentField->File().Path(),
B_READ_ONLY);
char readBuffer[1024];
ssize_t readSize;
readSize = upFile.Read(readBuffer, 1024);
while (readSize > 0) {
fSocket.Send(readBuffer, readSize);
readSize = upFile.Read(readBuffer, 1024);
}
}
break;
case B_HTTPFORM_BUFFER:
fSocket.Send(currentField->Buffer(),
currentField->BufferSize());
break;
}
fSocket.Send("\r\n", 2);
}
fSocket.Send(fOptPostFields->GetMultipartFooter().String(),
fOptPostFields->GetMultipartFooter().Length());
}
} else if ((fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT)
&& fOptInputData != NULL) {
char outputTempBuffer[1024];
ssize_t read = 0;
while (read != -1) {
read = fOptInputData->Read(outputTempBuffer, 1024);
if (read > 0) {
char hexSize[16];
size_t hexLength = sprintf(hexSize, "%ld", read);
fSocket.Send(hexSize, hexLength);
fSocket.Send("\r\n", 2);
fSocket.Send(outputTempBuffer, read);
fSocket.Send("\r\n", 2);
}
}
fSocket.Send("0\r\n\r\n", 5);
}
fOutputBuffer.Truncate(0, true);
fSocket.SetNonBlocking(false);
fStatusReceived = false;
fHeadersReceived = false;
// Receive loop
bool receiveEnd = false;
bool parseEnd = false;
bool readByChunks = false;
bool readError = false;
int32 receiveBufferSize = 32;
ssize_t bytesRead = 0;
ssize_t bytesReceived = 0;
ssize_t bytesTotal = 0;
char* inputTempBuffer = NULL;
fQuit = false;
while (!fQuit && !(receiveEnd && parseEnd)) {
if (!receiveEnd) {
bytesRead = fSocket.Receive(fInputBuffer, receiveBufferSize);
if (bytesRead < 0) {
readError = true;
fQuit = true;
continue;
} else if (bytesRead == 0)
receiveEnd = true;
}
else
bytesRead = 0;
if (!fStatusReceived) {
_ParseStatus();
//! ProtocolHook:ResponseStarted
if (fStatusReceived && fListener != NULL)
fListener->ResponseStarted(this);
} else if (!fHeadersReceived) {
_ParseHeaders();
if (fHeadersReceived) {
receiveBufferSize = kHttpProtocolReceiveBufferSize;
_ResultHeaders() = fHeaders;
//! ProtocolHook:HeadersReceived
if (fListener != NULL)
fListener->HeadersReceived(this);
// Parse received cookies
if ((fContext != NULL) && fHeaders.HasHeader("Set-Cookie")) {
for (int32 i = 0; i < fHeaders.CountHeaders(); i++) {
if (fHeaders.HeaderAt(i).NameIs("Set-Cookie")) {
}
}
}
if (BString(fHeaders["Transfer-Encoding"]) == "chunked")
readByChunks = true;
int32 index = fHeaders.HasHeader("Content-Length");
if (index != B_ERROR)
bytesTotal = atoi(fHeaders.HeaderAt(index).Value());
else
bytesTotal = 0;
}
} else {
// If Transfer-Encoding is chunked, we should read a complete
// chunk in buffer before handling it
if (readByChunks) {
_CopyChunkInBuffer(&inputTempBuffer, &bytesRead);
// A chunk of 0 bytes indicates the end of the chunked transfer
if (bytesRead == 0) {
receiveEnd = true;
}
}
else {
bytesRead = fInputBuffer.Size();
if (bytesRead > 0) {
inputTempBuffer = new char[bytesRead];
fInputBuffer.RemoveData(inputTempBuffer, bytesRead);
}
}
if (bytesRead > 0) {
bytesReceived += bytesRead;
_EmitDebug(B_URL_PROTOCOL_DEBUG_TRANSFER_IN, "%d bytes",
bytesRead);
if (fListener != NULL) {
fListener->DataReceived(this, inputTempBuffer, bytesRead);
fListener->DownloadProgress(this, bytesReceived,
bytesTotal);
}
ssize_t dataWrite = _ResultRawData().Write(inputTempBuffer,
bytesRead);
if (dataWrite != bytesRead) {
_EmitDebug(B_URL_PROTOCOL_DEBUG_ERROR,
"Unable to write %dbytes of data (%d).", bytesRead,
dataWrite);
return B_PROT_NO_MEMORY;
}
if (bytesTotal > 0 && bytesReceived >= bytesTotal)
receiveEnd = true;
delete[] inputTempBuffer;
}
}
parseEnd = (fInputBuffer.Size() == 0);
}
fSocket.Close();
if (readError)
return B_PROT_READ_FAILED;
return fQuit?B_PROT_ABORTED:B_PROT_SUCCESS;
}
status_t
BUrlProtocolHttp::_GetLine(BString& destString)
{
// Find a complete line in inputBuffer
uint32 characterIndex = 0;
while ((characterIndex < fInputBuffer.Size())
&& ((fInputBuffer.Data())[characterIndex] != '\n'))
characterIndex++;
if (characterIndex == fInputBuffer.Size())
return B_ERROR;
char* temporaryBuffer = new(std::nothrow) char[characterIndex + 1];
fInputBuffer.RemoveData(temporaryBuffer, characterIndex + 1);
// Strip end-of-line character(s)
if (temporaryBuffer[characterIndex-1] == '\r')
destString.SetTo(temporaryBuffer, characterIndex - 1);
else
destString.SetTo(temporaryBuffer, characterIndex);
delete[] temporaryBuffer;
return B_OK;
}
void
BUrlProtocolHttp::_ParseStatus()
{
// Status line should be formatted like: HTTP/M.m SSS ...
// With: M = Major version of the protocol
// m = Minor version of the protocol
// SSS = three-digit status code of the response
// ... = additional text info
BString statusLine;
if (_GetLine(statusLine) == B_ERROR)
return;
if (statusLine.CountChars() < 12)
return;
fStatusReceived = true;
BString statusCodeStr;
BString statusText;
statusLine.CopyInto(statusCodeStr, 9, 3);
_SetResultStatusCode(atoi(statusCodeStr.String()));
statusLine.CopyInto(_ResultStatusText(), 13, statusLine.Length() - 13);
_EmitDebug(B_URL_PROTOCOL_DEBUG_TEXT, "Status line received: Code %d (%s)",
atoi(statusCodeStr.String()), _ResultStatusText().String());
}
void
BUrlProtocolHttp::_ParseHeaders()
{
BString currentHeader;
if (_GetLine(currentHeader) == B_ERROR)
return;
// Empty line
if (currentHeader.Length() == 0) {
fHeadersReceived = true;
return;
}
_EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_IN, "%s", currentHeader.String());
fHeaders.AddHeader(currentHeader.String());
}
void
BUrlProtocolHttp::_CopyChunkInBuffer(char** buffer, ssize_t* bytesReceived)
{
static ssize_t chunkSize = -1;
BString chunkHeader;
if (chunkSize >= 0) {
if ((ssize_t)fInputBuffer.Size() >= chunkSize + 2) {
// 2 more bytes to handle the closing CR+LF
*bytesReceived = chunkSize;
*buffer = new char[chunkSize+2];
fInputBuffer.RemoveData(*buffer, chunkSize+2);
chunkSize = -1;
} else {
*bytesReceived = -1;
*buffer = NULL;
}
} else {
if (_GetLine(chunkHeader) == B_ERROR) {
chunkSize = -1;
*buffer = NULL;
*bytesReceived = -1;
return;
}
// Format of a chunk header:
// <chunk size in hex>[; optional data]
int32 semiColonIndex = chunkHeader.FindFirst(";", 0);
// Cut-off optional data if present
if (semiColonIndex != -1)
chunkHeader.Remove(semiColonIndex,
chunkHeader.Length() - semiColonIndex);
chunkSize = strtol(chunkHeader.String(), NULL, 16);
PRINT(("BHP[%p] Chunk %s=%d\n", this, chunkHeader.String(), chunkSize));
if (chunkSize == 0) {
fContentReceived = true;
}
*bytesReceived = -1;
*buffer = NULL;
}
}
void
BUrlProtocolHttp::_CreateRequest()
{
BString request;
switch (fRequestMethod) {
case B_HTTP_POST:
request << "POST";
break;
case B_HTTP_PUT:
request << "PUT";
break;
default:
case B_HTTP_GET:
request << "GET";
break;
}
if (Url().HasPath())
request << ' ' << Url().Path();
else
request << " /";
if (Url().HasRequest())
request << '?' << Url().Request();
if (Url().HasFragment())
request << '#' << Url().Fragment();
request << ' ';
switch (fHttpVersion) {
case B_HTTP_11:
request << "HTTP/1.1";
break;
default:
case B_HTTP_10:
request << "HTTP/1.0";
break;
}
_AddOutputBufferLine(request.String());
}
void
BUrlProtocolHttp::_AddHeaders()
{
// HTTP 1.1 additional headers
if (fHttpVersion == B_HTTP_11) {
fOutputHeaders.AddHeader("Host", Url().Host());
fOutputHeaders.AddHeader("Accept", "*/*");
fOutputHeaders.AddHeader("Accept-Encoding", "chunked");
// Allow the remote server to send dynamic content by chunks
// rather than waiting for the full content to be generated and
// sending us data.
fOutputHeaders.AddHeader("Connection", "close");
// Let the remote server close the connection after response since
// we don't handle multiple request on a single connection
}
// Classic HTTP headers
if (fOptUserAgent.CountChars() > 0)
fOutputHeaders.AddHeader("User-Agent", fOptUserAgent.String());
if (fOptReferer.CountChars() > 0)
fOutputHeaders.AddHeader("Referer", fOptReferer.String());
// Authentication
if (fAuthentication.Method() != B_HTTP_AUTHENTICATION_NONE) {
BString request;
switch (fRequestMethod) {
case B_HTTP_POST:
request = "POST";
break;
case B_HTTP_PUT:
request = "PUT";
break;
default:
case B_HTTP_GET:
request = "GET";
break;
}
fOutputHeaders.AddHeader("Authorization",
fAuthentication.Authorization(fUrl, request));
}
// Required headers for POST data
if (fOptPostFields != NULL && fRequestMethod == B_HTTP_POST) {
BString contentType;
switch (fOptPostFields->GetFormType()) {
case B_HTTP_FORM_MULTIPART:
contentType << "multipart/form-data; boundary="
<< fOptPostFields->GetMultipartBoundary() << "";
break;
case B_HTTP_FORM_URL_ENCODED:
contentType << "application/x-www-form-urlencoded";
break;
}
fOutputHeaders.AddHeader("Content-Type", contentType);
fOutputHeaders.AddHeader("Content-Length",
fOptPostFields->ContentLength());
} else if (fOptInputData != NULL
&& (fRequestMethod == B_HTTP_POST || fRequestMethod == B_HTTP_PUT))
fOutputHeaders.AddHeader("Transfer-Encoding", "chunked");
// Request headers
for (int32 headerIndex = 0;
headerIndex < fRequestHeaders.CountHeaders();
headerIndex++) {
BHttpHeader& optHeader = fRequestHeaders[headerIndex];
int32 replaceIndex = fOutputHeaders.HasHeader(optHeader.Name());
// Add or replace the current option header to the
// output header list
if (replaceIndex == -1)
fOutputHeaders.AddHeader(optHeader.Name(), optHeader.Value());
else
fOutputHeaders[replaceIndex].SetValue(optHeader.Value());
}
// Optional headers specified by the user
if (fOptHeaders != NULL) {
for (int32 headerIndex = 0;
headerIndex < fOptHeaders->CountHeaders();
headerIndex++) {
BHttpHeader& optHeader = (*fOptHeaders)[headerIndex];
int32 replaceIndex = fOutputHeaders.HasHeader(optHeader.Name());
// Add or replace the current option header to the
// output header list
if (replaceIndex == -1)
fOutputHeaders.AddHeader(optHeader.Name(), optHeader.Value());
else
fOutputHeaders[replaceIndex].SetValue(optHeader.Value());
}
}
// Context cookies
if (fOptSetCookies && (fContext != NULL)) {
BNetworkCookie* cookie;
for (BNetworkCookieJar::UrlIterator
it(fContext->GetCookieJar().GetUrlIterator(fUrl));
(cookie = it.Next()) != NULL;
)
fOutputHeaders.AddHeader("Cookie", cookie->RawCookie(false));
}
// Write output headers to output stream
for (int32 headerIndex = 0;
headerIndex < fOutputHeaders.CountHeaders();
headerIndex++)
_AddOutputBufferLine(fOutputHeaders.HeaderAt(headerIndex).Header());
}
void
BUrlProtocolHttp::_AddOutputBufferLine(const char* line)
{
_EmitDebug(B_URL_PROTOCOL_DEBUG_HEADER_OUT, "%s", line);
fOutputBuffer << line << "\r\n";
}
@@ -0,0 +1,91 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <iostream>
#include <cstdio>
#include <UrlProtocol.h>
#include <UrlProtocolListener.h>
using namespace std;
void
BUrlProtocolListener::ConnectionOpened(BUrlProtocol*)
{
}
void
BUrlProtocolListener::HostnameResolved(BUrlProtocol*, const char*)
{
}
void
BUrlProtocolListener::ResponseStarted(BUrlProtocol*)
{
}
void
BUrlProtocolListener::HeadersReceived(BUrlProtocol*)
{
}
void
BUrlProtocolListener::DataReceived(BUrlProtocol*, const char*, ssize_t)
{
}
void
BUrlProtocolListener::DownloadProgress(BUrlProtocol*, ssize_t, ssize_t)
{
}
void
BUrlProtocolListener::UploadProgress(BUrlProtocol*, ssize_t, ssize_t)
{
}
void
BUrlProtocolListener::RequestCompleted(BUrlProtocol*, bool)
{
}
void
BUrlProtocolListener::DebugMessage(BUrlProtocol* caller,
BUrlProtocolDebugMessage type, const char* text)
{
switch (type) {
case B_URL_PROTOCOL_DEBUG_TEXT:
cout << " ";
break;
case B_URL_PROTOCOL_DEBUG_ERROR:
cout << "!!!";
break;
case B_URL_PROTOCOL_DEBUG_TRANSFER_IN:
case B_URL_PROTOCOL_DEBUG_HEADER_IN:
cout << "<--";
break;
case B_URL_PROTOCOL_DEBUG_TRANSFER_OUT:
case B_URL_PROTOCOL_DEBUG_HEADER_OUT:
cout << "-->";
break;
}
cout << " " << caller->Protocol() << ": " << text << endl;
}
+245
View File
@@ -0,0 +1,245 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <new>
#include <UrlRequest.h>
#include <UrlHttpProtocol.h>
#include <Debug.h>
BUrlRequest::BUrlRequest(const BUrl& url, BUrlProtocolListener* listener)
:
fListener(listener),
fUrlProtocol(NULL),
fResult(url),
fContext(),
fUrl(),
fReady(false)
{
SetUrl(url);
}
BUrlRequest::BUrlRequest(const BUrl& url)
:
fListener(NULL),
fUrlProtocol(NULL),
fResult(url),
fContext(),
fUrl(),
fReady(false)
{
SetUrl(url);
}
BUrlRequest::BUrlRequest(const BUrlRequest& other)
:
fListener(NULL),
fUrlProtocol(NULL),
fResult(other.fUrl),
fContext(),
fUrl(),
fReady(false)
{
*this = other;
}
// #pragma mark Request parameters modification
status_t
BUrlRequest::SetUrl(const BUrl& url)
{
fUrl = url;
fResult.SetUrl(url);
if (fUrlProtocol != NULL && url.Protocol() == fUrl.Protocol())
fUrlProtocol->SetUrl(url);
else {
status_t err = Identify();
if (err != B_OK)
return err;
}
return B_OK;
}
void
BUrlRequest::SetContext(BUrlContext* context)
{
fContext = context;
}
void
BUrlRequest::SetProtocolListener(BUrlProtocolListener* listener)
{
fListener = listener;
if (fUrlProtocol != NULL)
fUrlProtocol->SetListener(listener);
}
bool
BUrlRequest::SetProtocolOption(int32 option, void* value)
{
if (fUrlProtocol == NULL)
return false;
fUrlProtocol->SetOption(option, value);
return true;
}
// #pragma mark Request parameters access
const BUrlProtocol*
BUrlRequest::Protocol()
{
return fUrlProtocol;
}
const BUrlResult&
BUrlRequest::Result()
{
return fResult;
}
const BUrl&
BUrlRequest::Url()
{
return fUrl;
}
// #pragma mark Request control
status_t
BUrlRequest::Identify()
{
// TODO: instanciate the correct BUrlProtocol w/ the services roster
delete fUrlProtocol;
fUrlProtocol = NULL;
if (fUrl.Protocol() == "http") {
fUrlProtocol = new(std::nothrow) BUrlHttpProtocol(fUrl, fListener, fContext, &fResult);
fReady = true;
return B_OK;
}
fReady = false;
return B_NO_HANDLER_FOR_PROTOCOL;
}
status_t
BUrlRequest::Perform()
{
if (fUrlProtocol == NULL) {
PRINT(("BUrlRequest::Perform() : Oops, no BUrlProtocol defined!\n"));
return B_ERROR;
}
thread_id protocolThread = fUrlProtocol->Run();
if (protocolThread < B_OK)
return protocolThread;
return B_OK;
}
status_t
BUrlRequest::Pause()
{
if (fUrlProtocol == NULL)
return B_ERROR;
return fUrlProtocol->Pause();
}
status_t
BUrlRequest::Resume()
{
if (fUrlProtocol == NULL)
return B_ERROR;
return fUrlProtocol->Resume();
}
status_t
BUrlRequest::Abort()
{
if (fUrlProtocol == NULL)
return B_ERROR;
status_t returnCode = fUrlProtocol->Stop();
delete fUrlProtocol;
fUrlProtocol = NULL;
return returnCode;
}
// #pragma mark Request informations
bool
BUrlRequest::InitCheck() const
{
return fReady;
}
bool
BUrlRequest::IsRunning() const
{
if (fUrlProtocol == NULL)
return false;
return fUrlProtocol->IsRunning();
}
status_t
BUrlRequest::Status() const
{
if (fUrlProtocol == NULL)
return B_ERROR;
return fUrlProtocol->Status();
}
// #pragma mark Overloaded members
BUrlRequest&
BUrlRequest::operator=(const BUrlRequest& other)
{
delete fUrlProtocol;
fUrlProtocol = NULL;
fUrl = other.fUrl;
fListener = other.fListener;
fContext = other.fContext;
fResult = BUrlResult(other.fUrl);
SetUrl(other.fUrl);
return *this;
}
+117
View File
@@ -0,0 +1,117 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <UrlResult.h>
#include <Debug.h>
using std::ostream;
BUrlResult::BUrlResult(const BUrl& url)
:
fUrl(url),
fRawData(),
fHeaders()
{
}
BUrlResult::BUrlResult(const BUrlResult& other)
:
fUrl(),
fRawData(),
fHeaders()
{
*this = other;
}
// #pragma mark Result parameters modifications
void
BUrlResult::SetUrl(const BUrl& url)
{
fUrl = url;
}
// #pragma mark Result parameters access
const BUrl&
BUrlResult::Url() const
{
return fUrl;
}
const BMallocIO&
BUrlResult::RawData() const
{
return fRawData;
}
const BHttpHeaders&
BUrlResult::Headers() const
{
return fHeaders;
}
int32
BUrlResult::StatusCode() const
{
return fStatusCode;
}
const BString&
BUrlResult::StatusText() const
{
return fStatusString;
}
// #pragma mark Result tests
bool
BUrlResult::HasHeaders() const
{
return (fHeaders.CountHeaders() > 0);
}
// #pragma mark Overloaded operators
BUrlResult&
BUrlResult::operator=(const BUrlResult& other)
{
fUrl = other.fUrl;
fHeaders = other.fHeaders;
fRawData.SetSize(other.fRawData.BufferLength());
fRawData.WriteAt(0, fRawData.Buffer(), fRawData.BufferLength());
return *this;
}
ostream&
operator<<(ostream& out, const BUrlResult& result)
{
out.write(reinterpret_cast<const char*>(result.fRawData.Buffer()),
result.fRawData.BufferLength());
return out;
}
@@ -0,0 +1,104 @@
/*
* Copyright 2010 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Christophe Huriaux, [email protected]
*/
#include <cstdio>
#include <UrlSynchronousRequest.h>
#define PRINT(x) printf x;
BUrlSynchronousRequest::BUrlSynchronousRequest(BUrl& url)
:
BUrlRequest(url, this),
fRequestComplete(false)
{
}
status_t
BUrlSynchronousRequest::Perform()
{
SetProtocolListener(this);
fRequestComplete = false;
return BUrlRequest::Perform();
}
status_t
BUrlSynchronousRequest::WaitUntilCompletion()
{
while (!fRequestComplete)
snooze(10000);
return B_OK;
}
void
BUrlSynchronousRequest::ConnectionOpened(BUrlProtocol*)
{
PRINT(("SynchronousRequest::ConnectionOpened()\n"));
}
void
BUrlSynchronousRequest::HostnameResolved(BUrlProtocol*, const char* ip)
{
PRINT(("SynchronousRequest::HostnameResolved(%s)\n", ip));
}
void
BUrlSynchronousRequest::ResponseStarted(BUrlProtocol*)
{
PRINT(("SynchronousRequest::ResponseStarted()\n"));
}
void
BUrlSynchronousRequest::HeadersReceived(BUrlProtocol*)
{
PRINT(("SynchronousRequest::HeadersReceived()\n"));
}
void
BUrlSynchronousRequest::DataReceived(BUrlProtocol*, const char*,
ssize_t size)
{
PRINT(("SynchronousRequest::DataReceived(%zd)\n", size));
}
void
BUrlSynchronousRequest::DownloadProgress(BUrlProtocol*,
ssize_t bytesReceived, ssize_t bytesTotal)
{
PRINT(("SynchronousRequest::DownloadProgress(%zd, %zd)\n", bytesReceived,
bytesTotal));
}
void
BUrlSynchronousRequest::UploadProgress(BUrlProtocol*, ssize_t bytesSent,
ssize_t bytesTotal)
{
PRINT(("SynchronousRequest::UploadProgress(%zd, %zd)\n", bytesSent,
bytesTotal));
}
void
BUrlSynchronousRequest::RequestCompleted(BUrlProtocol* caller, bool success)
{
PRINT(("SynchronousRequest::RequestCompleted(%s) : %s\n", (success?"true":"false"),
caller->StatusString(caller->Status())));
fRequestComplete = true;
}