summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Responses.h
blob: 20ff11081ee53af65b77273237e5b98560c4ec42 (plain)
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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
/*
 * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/ByteBuffer.h>
#include <AK/Error.h>
#include <AK/Forward.h>
#include <AK/Optional.h>
#include <AK/URL.h>
#include <AK/Vector.h>
#include <LibJS/Forward.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Heap/GCPtr.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Headers.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Statuses.h>

namespace Web::Fetch::Infrastructure {

// https://fetch.spec.whatwg.org/#concept-response
class Response : public JS::Cell {
    JS_CELL(Response, JS::Cell);

public:
    enum class CacheState {
        Local,
        Validated,
    };

    enum class Type {
        Basic,
        CORS,
        Default,
        Error,
        Opaque,
        OpaqueRedirect,
    };

    // https://fetch.spec.whatwg.org/#response-body-info
    struct BodyInfo {
        // https://fetch.spec.whatwg.org/#fetch-timing-info-encoded-body-size
        u64 encoded_size { 0 };

        // https://fetch.spec.whatwg.org/#fetch-timing-info-decoded-body-size
        u64 decoded_size { 0 };
    };

    [[nodiscard]] static JS::NonnullGCPtr<Response> create(JS::VM&);
    [[nodiscard]] static JS::NonnullGCPtr<Response> aborted_network_error(JS::VM&);
    [[nodiscard]] static JS::NonnullGCPtr<Response> network_error(JS::VM&, Variant<String, StringView> message);
    [[nodiscard]] static JS::NonnullGCPtr<Response> appropriate_network_error(JS::VM&, FetchParams const&);

    virtual ~Response() = default;

    [[nodiscard]] virtual Type type() const { return m_type; }
    void set_type(Type type) { m_type = type; }

    [[nodiscard]] virtual bool aborted() const { return m_aborted; }
    void set_aborted(bool aborted) { m_aborted = aborted; }

    [[nodiscard]] virtual Vector<AK::URL> const& url_list() const { return m_url_list; }
    [[nodiscard]] virtual Vector<AK::URL>& url_list() { return m_url_list; }
    void set_url_list(Vector<AK::URL> url_list) { m_url_list = move(url_list); }

    [[nodiscard]] virtual Status status() const { return m_status; }
    void set_status(Status status) { m_status = status; }

    [[nodiscard]] virtual ReadonlyBytes status_message() const { return m_status_message; }
    void set_status_message(ByteBuffer status_message) { m_status_message = move(status_message); }

    [[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const { return m_header_list; }
    void set_header_list(JS::NonnullGCPtr<HeaderList> header_list) { m_header_list = header_list; }

    [[nodiscard]] virtual Optional<Body> const& body() const { return m_body; }
    [[nodiscard]] virtual Optional<Body>& body() { return m_body; }
    void set_body(Optional<Body> body) { m_body = move(body); }

    [[nodiscard]] virtual Optional<CacheState> const& cache_state() const { return m_cache_state; }
    void set_cache_state(Optional<CacheState> cache_state) { m_cache_state = move(cache_state); }

    [[nodiscard]] virtual Vector<ByteBuffer> const& cors_exposed_header_name_list() const { return m_cors_exposed_header_name_list; }
    void set_cors_exposed_header_name_list(Vector<ByteBuffer> cors_exposed_header_name_list) { m_cors_exposed_header_name_list = move(cors_exposed_header_name_list); }

    [[nodiscard]] virtual bool range_requested() const { return m_range_requested; }
    void set_range_requested(bool range_requested) { m_range_requested = range_requested; }

    [[nodiscard]] virtual bool request_includes_credentials() const { return m_request_includes_credentials; }
    void set_request_includes_credentials(bool request_includes_credentials) { m_request_includes_credentials = request_includes_credentials; }

    [[nodiscard]] virtual bool timing_allow_passed() const { return m_timing_allow_passed; }
    void set_timing_allow_passed(bool timing_allow_passed) { m_timing_allow_passed = timing_allow_passed; }

    [[nodiscard]] virtual BodyInfo const& body_info() const { return m_body_info; }
    void set_body_info(BodyInfo body_info) { m_body_info = body_info; }

    [[nodiscard]] bool has_cross_origin_redirects() const { return m_has_cross_origin_redirects; }
    void set_has_cross_origin_redirects(bool has_cross_origin_redirects) { m_has_cross_origin_redirects = has_cross_origin_redirects; }

    [[nodiscard]] bool is_aborted_network_error() const;
    [[nodiscard]] bool is_network_error() const;

    [[nodiscard]] Optional<AK::URL const&> url() const;
    [[nodiscard]] ErrorOr<Optional<AK::URL>> location_url(Optional<String> const& request_fragment) const;

    [[nodiscard]] WebIDL::ExceptionOr<JS::NonnullGCPtr<Response>> clone(JS::Realm&) const;

    [[nodiscard]] bool is_cors_cross_origin() const;

    // Non-standard
    [[nodiscard]] Optional<StringView> network_error_message() const;

protected:
    explicit Response(JS::NonnullGCPtr<HeaderList>);

    virtual void visit_edges(JS::Cell::Visitor&) override;

private:
    // https://fetch.spec.whatwg.org/#concept-response-type
    // A response has an associated type which is "basic", "cors", "default", "error", "opaque", or "opaqueredirect". Unless stated otherwise, it is "default".
    Type m_type { Type::Default };

    // https://fetch.spec.whatwg.org/#concept-response-aborted
    // A response can have an associated aborted flag, which is initially unset.
    bool m_aborted { false };

    // https://fetch.spec.whatwg.org/#concept-response-url-list
    // A response has an associated URL list (a list of zero or more URLs). Unless stated otherwise, it is the empty list.
    Vector<AK::URL> m_url_list;

    // https://fetch.spec.whatwg.org/#concept-response-status
    // A response has an associated status, which is a status. Unless stated otherwise it is 200.
    Status m_status { 200 };

    // https://fetch.spec.whatwg.org/#concept-response-status-message
    // A response has an associated status message. Unless stated otherwise it is the empty byte sequence.
    ByteBuffer m_status_message;

    // https://fetch.spec.whatwg.org/#concept-response-header-list
    // A response has an associated header list (a header list). Unless stated otherwise it is empty.
    JS::NonnullGCPtr<HeaderList> m_header_list;

    // https://fetch.spec.whatwg.org/#concept-response-body
    // A response has an associated body (null or a body). Unless stated otherwise it is null.
    Optional<Body> m_body;

    // https://fetch.spec.whatwg.org/#concept-response-cache-state
    // A response has an associated cache state (the empty string, "local", or "validated"). Unless stated otherwise, it is the empty string.
    Optional<CacheState> m_cache_state;

    // https://fetch.spec.whatwg.org/#concept-response-cors-exposed-header-name-list
    // A response has an associated CORS-exposed header-name list (a list of zero or more header names). The list is empty unless otherwise specified.
    Vector<ByteBuffer> m_cors_exposed_header_name_list;

    // https://fetch.spec.whatwg.org/#concept-response-range-requested-flag
    // A response has an associated range-requested flag, which is initially unset.
    bool m_range_requested { false };

    // https://fetch.spec.whatwg.org/#response-request-includes-credentials
    // A response has an associated request-includes-credentials (a boolean), which is initially true.
    bool m_request_includes_credentials { true };

    // https://fetch.spec.whatwg.org/#concept-response-timing-allow-passed
    // A response has an associated timing allow passed flag, which is initially unset.
    bool m_timing_allow_passed { false };

    // https://fetch.spec.whatwg.org/#concept-response-body-info
    // A response has an associated body info (a response body info). Unless stated otherwise, it is a new response body info.
    BodyInfo m_body_info;

    // https://fetch.spec.whatwg.org/#response-service-worker-timing-info
    // FIXME: A response has an associated service worker timing info (null or a service worker timing info), which is initially null.

    // https://fetch.spec.whatwg.org/#response-has-cross-origin-redirects
    // A response has an associated has-cross-origin-redirects (a boolean), which is initially false.
    bool m_has_cross_origin_redirects { false };

    // Non-standard
    Optional<Variant<String, StringView>> m_network_error_message;
};

// https://fetch.spec.whatwg.org/#concept-filtered-response
class FilteredResponse : public Response {
    JS_CELL(FilteredResponse, Response);

public:
    FilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);
    virtual ~FilteredResponse() = 0;

    [[nodiscard]] virtual Type type() const override { return m_internal_response->type(); }
    [[nodiscard]] virtual bool aborted() const override { return m_internal_response->aborted(); }
    [[nodiscard]] virtual Vector<AK::URL> const& url_list() const override { return m_internal_response->url_list(); }
    [[nodiscard]] virtual Vector<AK::URL>& url_list() override { return m_internal_response->url_list(); }
    [[nodiscard]] virtual Status status() const override { return m_internal_response->status(); }
    [[nodiscard]] virtual ReadonlyBytes status_message() const override { return m_internal_response->status_message(); }
    [[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_internal_response->header_list(); }
    [[nodiscard]] virtual Optional<Body> const& body() const override { return m_internal_response->body(); }
    [[nodiscard]] virtual Optional<Body>& body() override { return m_internal_response->body(); }
    [[nodiscard]] virtual Optional<CacheState> const& cache_state() const override { return m_internal_response->cache_state(); }
    [[nodiscard]] virtual Vector<ByteBuffer> const& cors_exposed_header_name_list() const override { return m_internal_response->cors_exposed_header_name_list(); }
    [[nodiscard]] virtual bool range_requested() const override { return m_internal_response->range_requested(); }
    [[nodiscard]] virtual bool request_includes_credentials() const override { return m_internal_response->request_includes_credentials(); }
    [[nodiscard]] virtual bool timing_allow_passed() const override { return m_internal_response->timing_allow_passed(); }
    [[nodiscard]] virtual BodyInfo const& body_info() const override { return m_internal_response->body_info(); }

    [[nodiscard]] JS::NonnullGCPtr<Response> internal_response() const { return m_internal_response; }

protected:
    virtual void visit_edges(JS::Cell::Visitor&) override;

private:
    // https://fetch.spec.whatwg.org/#concept-internal-response
    JS::NonnullGCPtr<Response> m_internal_response;
};

// https://fetch.spec.whatwg.org/#concept-filtered-response-basic
class BasicFilteredResponse final : public FilteredResponse {
    JS_CELL(OpaqueRedirectFilteredResponse, FilteredResponse);

public:
    [[nodiscard]] static ErrorOr<JS::NonnullGCPtr<BasicFilteredResponse>> create(JS::VM&, JS::NonnullGCPtr<Response>);

    [[nodiscard]] virtual Type type() const override { return Type::Basic; }
    [[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_header_list; }

private:
    BasicFilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);

    virtual void visit_edges(JS::Cell::Visitor&) override;

    JS::NonnullGCPtr<HeaderList> m_header_list;
};

// https://fetch.spec.whatwg.org/#concept-filtered-response-cors
class CORSFilteredResponse final : public FilteredResponse {
    JS_CELL(CORSFilteredResponse, FilteredResponse);

public:
    [[nodiscard]] static ErrorOr<JS::NonnullGCPtr<CORSFilteredResponse>> create(JS::VM&, JS::NonnullGCPtr<Response>);

    [[nodiscard]] virtual Type type() const override { return Type::CORS; }
    [[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_header_list; }

private:
    CORSFilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);

    virtual void visit_edges(JS::Cell::Visitor&) override;

    JS::NonnullGCPtr<HeaderList> m_header_list;
};

// https://fetch.spec.whatwg.org/#concept-filtered-response-opaque
class OpaqueFilteredResponse final : public FilteredResponse {
    JS_CELL(OpaqueFilteredResponse, FilteredResponse);

public:
    [[nodiscard]] static JS::NonnullGCPtr<OpaqueFilteredResponse> create(JS::VM&, JS::NonnullGCPtr<Response>);

    [[nodiscard]] virtual Type type() const override { return Type::Opaque; }
    [[nodiscard]] virtual Vector<AK::URL> const& url_list() const override { return m_url_list; }
    [[nodiscard]] virtual Vector<AK::URL>& url_list() override { return m_url_list; }
    [[nodiscard]] virtual Status status() const override { return 0; }
    [[nodiscard]] virtual ReadonlyBytes status_message() const override { return {}; }
    [[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_header_list; }
    [[nodiscard]] virtual Optional<Body> const& body() const override { return m_body; }
    [[nodiscard]] virtual Optional<Body>& body() override { return m_body; }

private:
    OpaqueFilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);

    virtual void visit_edges(JS::Cell::Visitor&) override;

    Vector<AK::URL> m_url_list;
    JS::NonnullGCPtr<HeaderList> m_header_list;
    Optional<Body> m_body;
};

// https://fetch.spec.whatwg.org/#concept-filtered-response-opaque-redirect
class OpaqueRedirectFilteredResponse final : public FilteredResponse {
    JS_CELL(OpaqueRedirectFilteredResponse, FilteredResponse);

public:
    [[nodiscard]] static JS::NonnullGCPtr<OpaqueRedirectFilteredResponse> create(JS::VM&, JS::NonnullGCPtr<Response>);

    [[nodiscard]] virtual Type type() const override { return Type::OpaqueRedirect; }
    [[nodiscard]] virtual Status status() const override { return 0; }
    [[nodiscard]] virtual ReadonlyBytes status_message() const override { return {}; }
    [[nodiscard]] virtual JS::NonnullGCPtr<HeaderList> header_list() const override { return m_header_list; }
    [[nodiscard]] virtual Optional<Body> const& body() const override { return m_body; }
    [[nodiscard]] virtual Optional<Body>& body() override { return m_body; }

private:
    OpaqueRedirectFilteredResponse(JS::NonnullGCPtr<Response>, JS::NonnullGCPtr<HeaderList>);

    virtual void visit_edges(JS::Cell::Visitor&) override;

    JS::NonnullGCPtr<HeaderList> m_header_list;
    Optional<Body> m_body;
};
}