-
Notifications
You must be signed in to change notification settings - Fork 50
Expand file tree
/
Copy pathrequest-response.h
More file actions
276 lines (223 loc) · 10.5 KB
/
request-response.h
File metadata and controls
276 lines (223 loc) · 10.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#ifndef BUILTINS_WEB_FETCH_REQUEST_RESPONSE
#define BUILTINS_WEB_FETCH_REQUEST_RESPONSE
#include "fetch-errors.h"
#include "headers.h"
#include "host_api.h"
namespace builtins {
namespace web {
namespace fetch {
namespace request_response {
bool install(api::Engine *engine);
}
class RequestOrResponse final {
public:
enum class Slots {
RequestOrResponse,
BodyStream,
BodyAllPromise,
HasBody,
BodyUsed,
Headers,
URL,
Count,
};
static bool is_instance(JSObject *obj);
static bool is_incoming(JSObject *obj);
static host_api::HttpRequestResponseBase *handle(JSObject *obj);
static host_api::HttpRequestResponseBase *maybe_handle(JSObject *obj);
static host_api::HttpHeadersReadOnly *maybe_headers_handle(JSObject *obj);
static bool has_body(JSObject *obj);
static host_api::HttpIncomingBody *incoming_body_handle(JSObject *obj);
static host_api::HttpOutgoingBody *outgoing_body_handle(JSObject *obj);
static JSObject *body_stream(JSObject *obj);
static JSObject *body_source(JSContext *cx, JS::HandleObject obj);
static bool body_used(JSObject *obj);
static bool mark_body_used(JSContext *cx, JS::HandleObject obj);
static JS::Value url(JSObject *obj);
static void set_url(JSObject *obj, JS::Value url);
static bool body_unusable(JSContext *cx, JS::HandleObject body);
static bool extract_body(JSContext *cx, JS::HandleObject self, JS::HandleValue body_val);
/**
* Returns the RequestOrResponse's Headers if it has been reified, nullptr if
* not.
*/
static JSObject *maybe_headers(JSObject *obj);
/**
* Returns a handle to a clone of the RequestOrResponse's Headers.
*
* The main purposes for this function are use in sending outgoing requests/responses and
* in the constructor of request/response objects when a HeadersInit object is passed.
*
* The handle is guaranteed to be uniquely owned by the caller.
*/
static unique_ptr<host_api::HttpHeaders> headers_handle_clone(JSContext *, HandleObject self);
/**
* Returns the RequestOrResponse's Headers, reifying it if necessary.
*/
static JSObject *headers(JSContext *cx, JS::HandleObject obj);
static bool append_body(JSContext *cx, JS::HandleObject self, JS::HandleObject source,
api::TaskCompletionCallback callback, HandleObject callback_receiver);
using ParseBodyCB = bool(JSContext *cx, JS::HandleObject self, JS::UniqueChars buf, size_t len);
enum class BodyReadResult {
ArrayBuffer,
JSON,
Text,
};
template <BodyReadResult result_type>
static bool parse_body(JSContext *cx, JS::HandleObject self, JS::UniqueChars buf, size_t len);
static bool content_stream_read_then_handler(JSContext *cx, JS::HandleObject self,
JS::HandleValue extra, JS::CallArgs args);
static bool content_stream_read_catch_handler(JSContext *cx, JS::HandleObject self,
JS::HandleValue extra, JS::CallArgs args);
static bool consume_content_stream_for_bodyAll(JSContext *cx, JS::HandleObject self,
JS::HandleValue stream_val, JS::CallArgs args);
template <RequestOrResponse::BodyReadResult result_type>
static bool bodyAll(JSContext *cx, JS::CallArgs args, JS::HandleObject self);
static bool body_source_cancel_algorithm(JSContext *cx, JS::CallArgs args,
JS::HandleObject stream, JS::HandleObject owner,
JS::HandleValue reason);
static bool body_source_pull_algorithm(JSContext *cx, JS::CallArgs args, JS::HandleObject source,
JS::HandleObject body_owner, JS::HandleObject controller);
/**
* Ensures that the given |body_owner|'s body is properly streamed, if it
* requires streaming.
*
* If streaming is required, starts the process of reading from the
* ReadableStream representing the body and sets the |requires_streaming| bool
* to `true`.
*/
static bool maybe_stream_body(JSContext *cx, JS::HandleObject body_owner,
host_api::HttpOutgoingBodyOwner *destination,
bool *requires_streaming);
static JSObject *create_body_stream(JSContext *cx, JS::HandleObject owner);
static bool body_get(JSContext *cx, JS::CallArgs args, JS::HandleObject self,
bool create_if_undefined);
};
class Request final : public BuiltinImpl<Request> {
static bool method_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool headers_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool url_get(JSContext *cx, unsigned argc, JS::Value *vp);
template <RequestOrResponse::BodyReadResult result_type>
static bool bodyAll(JSContext *cx, unsigned argc, JS::Value *vp);
static bool body_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool clone(JSContext *cx, unsigned argc, JS::Value *vp);
public:
static constexpr const char *class_name = "Request";
enum class Slots {
Request = static_cast<int>(RequestOrResponse::Slots::RequestOrResponse),
BodyStream = static_cast<int>(RequestOrResponse::Slots::BodyStream),
HasBody = static_cast<int>(RequestOrResponse::Slots::HasBody),
BodyUsed = static_cast<int>(RequestOrResponse::Slots::BodyUsed),
Headers = static_cast<int>(RequestOrResponse::Slots::Headers),
URL = static_cast<int>(RequestOrResponse::Slots::URL),
Method = static_cast<int>(RequestOrResponse::Slots::Count),
ResponsePromise,
PendingResponseHandle,
Count,
};
static JSObject *response_promise(JSObject *obj);
static JSString *method(JS::HandleObject obj);
static const JSFunctionSpec static_methods[];
static const JSPropertySpec static_properties[];
static const JSFunctionSpec methods[];
static const JSPropertySpec properties[];
static const unsigned ctor_length = 1;
static bool init_class(JSContext *cx, JS::HandleObject global);
static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp);
static JSObject *create(JSContext *cx);
static bool initialize(JSContext *cx, JS::HandleObject requestInstance, JS::HandleValue input,
JS::HandleValue init_val, Headers::HeadersGuard guard);
static void init_slots(JSObject *requestInstance);
};
class Response final : public BuiltinImpl<Response> {
static bool waitUntil(JSContext *cx, unsigned argc, JS::Value *vp);
static bool ok_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool status_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool statusText_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool url_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool type_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool headers_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool redirected_get(JSContext *cx, unsigned argc, JS::Value *vp);
template <RequestOrResponse::BodyReadResult result_type>
static bool bodyAll(JSContext *cx, unsigned argc, JS::Value *vp);
static bool body_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp);
static bool redirect(JSContext *cx, unsigned argc, JS::Value *vp);
static bool json(JSContext *cx, unsigned argc, JS::Value *vp);
static bool clone(JSContext *cx, unsigned argc, JS::Value *vp);
public:
static constexpr const char *class_name = "Response";
enum class Slots {
Response = static_cast<int>(RequestOrResponse::Slots::RequestOrResponse),
BodyStream = static_cast<int>(RequestOrResponse::Slots::BodyStream),
HasBody = static_cast<int>(RequestOrResponse::Slots::HasBody),
BodyUsed = static_cast<int>(RequestOrResponse::Slots::BodyUsed),
Headers = static_cast<int>(RequestOrResponse::Slots::Headers),
URL = static_cast<int>(RequestOrResponse::Slots::URL),
Status = static_cast<int>(RequestOrResponse::Slots::Count),
StatusMessage,
Redirected,
Count,
};
static const JSFunctionSpec static_methods[];
static const JSPropertySpec static_properties[];
static const JSFunctionSpec methods[];
static const JSPropertySpec properties[];
static const unsigned ctor_length = 1;
static bool init_class(JSContext *cx, JS::HandleObject global);
static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp);
static JSObject *create(JSContext *cx);
static JSObject *init_slots(HandleObject response);
static JSObject *create_incoming(JSContext *cx, host_api::HttpIncomingResponse *response);
static host_api::HttpResponse *maybe_response_handle(JSObject *obj);
static uint16_t status(JSObject *obj);
static JSString *status_message(JSObject *obj);
static void set_status_message_from_code(JSContext *cx, JSObject *obj, uint16_t code);
};
class ResponseFutureTask final : public api::AsyncTask {
Heap<JSObject *> request_;
host_api::FutureHttpIncomingResponse *future_;
public:
explicit ResponseFutureTask(const HandleObject request,
host_api::FutureHttpIncomingResponse *future)
: request_(request), future_(future) {
auto res = future->subscribe();
MOZ_ASSERT(!res.is_err(), "Subscribing to a future should never fail");
handle_ = res.unwrap();
}
[[nodiscard]] bool run(api::Engine *engine) override {
// MOZ_ASSERT(ready());
JSContext *cx = engine->cx();
const RootedObject request(cx, request_);
RootedObject response_promise(cx, Request::response_promise(request));
auto res = future_->maybe_response();
if (res.is_err()) {
api::throw_error(cx, FetchErrors::FetchNetworkError);
return RejectPromiseWithPendingError(cx, response_promise);
}
auto maybe_response = res.unwrap();
MOZ_ASSERT(maybe_response.has_value());
auto response = maybe_response.value();
RootedObject response_obj(cx, Response::create_incoming(cx, response));
if (!response_obj) {
return false;
}
RequestOrResponse::set_url(response_obj, RequestOrResponse::url(request));
RootedValue response_val(cx, ObjectValue(*response_obj));
if (!ResolvePromise(cx, response_promise, response_val)) {
return false;
}
return cancel(engine);
}
[[nodiscard]] bool cancel(api::Engine *engine) override {
// TODO(TS): implement
handle_ = -1;
return true;
}
void trace(JSTracer *trc) override { TraceEdge(trc, &request_, "Request for response future"); }
};
} // namespace fetch
} // namespace web
} // namespace builtins
#endif