summaryrefslogtreecommitdiffstats
path: root/remote/shared/NetworkResponse.sys.mjs
blob: 45a03fb44506ac52770faeccfe560c064a211cb8 (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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  NetworkUtils:
    "resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
});

/**
 * The NetworkResponse class is a wrapper around the internal channel which
 * provides getters and methods closer to fetch's response concept
 * (https://fetch.spec.whatwg.org/#concept-response).
 */
export class NetworkResponse {
  #channel;
  #decodedBodySize;
  #encodedBodySize;
  #fromCache;
  #headersTransmittedSize;
  #status;
  #statusMessage;
  #totalTransmittedSize;
  #wrappedChannel;

  /**
   *
   * @param {nsIChannel} channel
   *     The channel for the response.
   * @param {object} params
   * @param {boolean} params.fromCache
   *     Whether the response was read from the cache or not.
   * @param {string=} params.rawHeaders
   *     The response's raw (ie potentially compressed) headers
   */
  constructor(channel, params) {
    this.#channel = channel;
    const { fromCache, rawHeaders = "" } = params;
    this.#fromCache = fromCache;
    this.#wrappedChannel = ChannelWrapper.get(channel);

    this.#decodedBodySize = 0;
    this.#encodedBodySize = 0;
    this.#headersTransmittedSize = rawHeaders.length;
    this.#totalTransmittedSize = rawHeaders.length;

    // TODO: responseStatus and responseStatusText are sometimes inconsistent.
    // For instance, they might be (304, Not Modified) when retrieved during the
    // responseStarted event, and then (200, OK) during the responseCompleted
    // event.
    // For now consider them as immutable and store them on startup.
    this.#status = this.#channel.responseStatus;
    this.#statusMessage = this.#channel.responseStatusText;
  }

  get decodedBodySize() {
    return this.#decodedBodySize;
  }

  get encodedBodySize() {
    return this.#encodedBodySize;
  }

  get headersTransmittedSize() {
    return this.#headersTransmittedSize;
  }

  get fromCache() {
    return this.#fromCache;
  }

  get protocol() {
    return lazy.NetworkUtils.getProtocol(this.#channel);
  }

  get serializedURL() {
    return this.#channel.URI.spec;
  }

  get status() {
    return this.#status;
  }

  get statusMessage() {
    return this.#statusMessage;
  }

  get totalTransmittedSize() {
    return this.#totalTransmittedSize;
  }

  addResponseContent(responseContent) {
    this.#decodedBodySize = responseContent.decodedBodySize;
    this.#encodedBodySize = responseContent.bodySize;
    this.#totalTransmittedSize = responseContent.transferredSize;
  }

  getComputedMimeType() {
    // TODO: DevTools NetworkObserver is computing a similar value in
    // addResponseContent, but uses an inconsistent implementation in
    // addResponseStart. This approach can only be used as early as in
    // addResponseHeaders. We should move this logic to the NetworkObserver and
    // expose mimeType in addResponseStart. Bug 1809670.
    let mimeType = "";

    try {
      mimeType = this.#wrappedChannel.contentType;
      const contentCharset = this.#channel.contentCharset;
      if (contentCharset) {
        mimeType += `;charset=${contentCharset}`;
      }
    } catch (e) {
      // Ignore exceptions when reading contentType/contentCharset
    }

    return mimeType;
  }

  getHeadersList() {
    const headers = [];

    this.#channel.visitOriginalResponseHeaders({
      visitHeader(name, value) {
        headers.push([name, value]);
      },
    });

    return headers;
  }
}