1/*
2 * Copyright 2022 Haiku Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Niels Sascha Reedijk, niels.reedijk@gmail.com
7 */
8
9#ifndef _HTTP_RESULT_PRIVATE_H_
10#define _HTTP_RESULT_PRIVATE_H_
11
12
13#include <memory>
14#include <optional>
15#include <string>
16
17#include <DataIO.h>
18#include <ExclusiveBorrow.h>
19#include <OS.h>
20#include <String.h>
21
22
23namespace BPrivate {
24
25namespace Network {
26
27struct HttpResultPrivate {
28	// Read-only properties (multi-thread safe)
29	const	int32				id;
30
31	// Locking and atomic variables
32	enum { kNoData = 0, kStatusReady, kHeadersReady, kBodyReady, kError };
33			int32				requestStatus = kNoData;
34			int32				canCancel = 0;
35			sem_id				data_wait;
36
37	// Data
38			std::optional<BHttpStatus> status;
39			std::optional<BHttpFields> fields;
40			std::optional<BHttpBody> body;
41			std::optional<std::exception_ptr> error;
42
43	// Interim body storage (used while the request is running)
44			BString				bodyString;
45			BBorrow<BDataIO>	bodyTarget;
46
47	// Utility functions
48								HttpResultPrivate(int32 identifier);
49			int32				GetStatusAtomic();
50			bool				CanCancel();
51			void				SetCancel();
52			void				SetError(std::exception_ptr e);
53			void				SetStatus(BHttpStatus&& s);
54			void				SetFields(BHttpFields&& f);
55			void				SetBody();
56			size_t				WriteToBody(const void* buffer, size_t size);
57};
58
59
60inline HttpResultPrivate::HttpResultPrivate(int32 identifier)
61	:
62	id(identifier)
63{
64	std::string name = "httpresult:" + std::to_string(identifier);
65	data_wait = create_sem(1, name.c_str());
66	if (data_wait < B_OK)
67		throw BRuntimeError(__PRETTY_FUNCTION__, "Cannot create internal sem for httpresult");
68}
69
70
71inline int32
72HttpResultPrivate::GetStatusAtomic()
73{
74	return atomic_get(&requestStatus);
75}
76
77
78inline bool
79HttpResultPrivate::CanCancel()
80{
81	return atomic_get(&canCancel) == 1;
82}
83
84
85inline void
86HttpResultPrivate::SetCancel()
87{
88	atomic_set(&canCancel, 1);
89}
90
91
92inline void
93HttpResultPrivate::SetError(std::exception_ptr e)
94{
95	// Release any held body target borrow
96	bodyTarget.Return();
97
98	error = e;
99	atomic_set(&requestStatus, kError);
100	release_sem(data_wait);
101}
102
103
104inline void
105HttpResultPrivate::SetStatus(BHttpStatus&& s)
106{
107	status = std::move(s);
108	atomic_set(&requestStatus, kStatusReady);
109	release_sem(data_wait);
110}
111
112
113inline void
114HttpResultPrivate::SetFields(BHttpFields&& f)
115{
116	fields = std::move(f);
117	atomic_set(&requestStatus, kHeadersReady);
118	release_sem(data_wait);
119}
120
121
122inline void
123HttpResultPrivate::SetBody()
124{
125	if (bodyTarget.HasValue()) {
126		body = BHttpBody{};
127		bodyTarget.Return();
128	} else
129		body = BHttpBody{std::move(bodyString)};
130
131	atomic_set(&requestStatus, kBodyReady);
132	release_sem(data_wait);
133}
134
135
136inline size_t
137HttpResultPrivate::WriteToBody(const void* buffer, size_t size)
138{
139	// TODO: when the support for a shared BMemoryRingIO is here, choose
140	// between one or the other depending on which one is available.
141	if (bodyTarget.HasValue()) {
142		auto result = bodyTarget->Write(buffer, size);
143		if (result < 0)
144			throw BSystemError("BDataIO::Write()", result);
145		return result;
146	} else {
147		bodyString.Append(reinterpret_cast<const char*>(buffer), size);
148		return size;
149	}
150}
151
152
153} // namespace Network
154
155} // namespace BPrivate
156
157#endif // _HTTP_RESULT_PRIVATE_H_
158