UNPKG

3.05 kBJavaScriptView Raw
1/**
2 * Response.js
3 *
4 * Response class provides content decoding
5 */
6
7import Headers from './headers.js';
8import Body, {clone, extractContentType} from './body.js';
9import {isRedirect} from './utils/is-redirect.js';
10
11const INTERNALS = Symbol('Response internals');
12
13/**
14 * Response class
15 *
16 * Ref: https://fetch.spec.whatwg.org/#response-class
17 *
18 * @param Stream body Readable stream
19 * @param Object opts Response options
20 * @return Void
21 */
22export default class Response extends Body {
23 constructor(body = null, options = {}) {
24 super(body, options);
25
26 // eslint-disable-next-line no-eq-null, eqeqeq, no-negated-condition
27 const status = options.status != null ? options.status : 200;
28
29 const headers = new Headers(options.headers);
30
31 if (body !== null && !headers.has('Content-Type')) {
32 const contentType = extractContentType(body, this);
33 if (contentType) {
34 headers.append('Content-Type', contentType);
35 }
36 }
37
38 this[INTERNALS] = {
39 type: 'default',
40 url: options.url,
41 status,
42 statusText: options.statusText || '',
43 headers,
44 counter: options.counter,
45 highWaterMark: options.highWaterMark
46 };
47 }
48
49 get type() {
50 return this[INTERNALS].type;
51 }
52
53 get url() {
54 return this[INTERNALS].url || '';
55 }
56
57 get status() {
58 return this[INTERNALS].status;
59 }
60
61 /**
62 * Convenience property representing if the request ended normally
63 */
64 get ok() {
65 return this[INTERNALS].status >= 200 && this[INTERNALS].status < 300;
66 }
67
68 get redirected() {
69 return this[INTERNALS].counter > 0;
70 }
71
72 get statusText() {
73 return this[INTERNALS].statusText;
74 }
75
76 get headers() {
77 return this[INTERNALS].headers;
78 }
79
80 get highWaterMark() {
81 return this[INTERNALS].highWaterMark;
82 }
83
84 /**
85 * Clone this response
86 *
87 * @return Response
88 */
89 clone() {
90 return new Response(clone(this, this.highWaterMark), {
91 type: this.type,
92 url: this.url,
93 status: this.status,
94 statusText: this.statusText,
95 headers: this.headers,
96 ok: this.ok,
97 redirected: this.redirected,
98 size: this.size,
99 highWaterMark: this.highWaterMark
100 });
101 }
102
103 /**
104 * @param {string} url The URL that the new response is to originate from.
105 * @param {number} status An optional status code for the response (e.g., 302.)
106 * @returns {Response} A Response object.
107 */
108 static redirect(url, status = 302) {
109 if (!isRedirect(status)) {
110 throw new RangeError('Failed to execute "redirect" on "response": Invalid status code');
111 }
112
113 return new Response(null, {
114 headers: {
115 location: new URL(url).toString()
116 },
117 status
118 });
119 }
120
121 static error() {
122 const response = new Response(null, {status: 0, statusText: ''});
123 response[INTERNALS].type = 'error';
124 return response;
125 }
126
127 get [Symbol.toStringTag]() {
128 return 'Response';
129 }
130}
131
132Object.defineProperties(Response.prototype, {
133 type: {enumerable: true},
134 url: {enumerable: true},
135 status: {enumerable: true},
136 ok: {enumerable: true},
137 redirected: {enumerable: true},
138 statusText: {enumerable: true},
139 headers: {enumerable: true},
140 clone: {enumerable: true}
141});