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
23 namespace BPrivate {
24
25 namespace Network {
26
27 struct 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
HttpResultPrivate(int32 identifier)60 inline 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
71 inline int32
GetStatusAtomic()72 HttpResultPrivate::GetStatusAtomic()
73 {
74 return atomic_get(&requestStatus);
75 }
76
77
78 inline bool
CanCancel()79 HttpResultPrivate::CanCancel()
80 {
81 return atomic_get(&canCancel) == 1;
82 }
83
84
85 inline void
SetCancel()86 HttpResultPrivate::SetCancel()
87 {
88 atomic_set(&canCancel, 1);
89 }
90
91
92 inline void
SetError(std::exception_ptr e)93 HttpResultPrivate::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
104 inline void
SetStatus(BHttpStatus && s)105 HttpResultPrivate::SetStatus(BHttpStatus&& s)
106 {
107 status = std::move(s);
108 atomic_set(&requestStatus, kStatusReady);
109 release_sem(data_wait);
110 }
111
112
113 inline void
SetFields(BHttpFields && f)114 HttpResultPrivate::SetFields(BHttpFields&& f)
115 {
116 fields = std::move(f);
117 atomic_set(&requestStatus, kHeadersReady);
118 release_sem(data_wait);
119 }
120
121
122 inline void
SetBody()123 HttpResultPrivate::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
136 inline size_t
WriteToBody(const void * buffer,size_t size)137 HttpResultPrivate::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