summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/har/har-utils.js
blob: fc7ae533f15e00d4bd1d18a51c5d34abc96d48ad (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
/* 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/. */

"use strict";

const { XPCOMUtils } = ChromeUtils.importESModule(
  "resource://gre/modules/XPCOMUtils.sys.mjs"
);

XPCOMUtils.defineLazyGetter(this, "ZipWriter", function () {
  return Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
});

const OPEN_FLAGS = {
  RDONLY: parseInt("0x01", 16),
  WRONLY: parseInt("0x02", 16),
  CREATE_FILE: parseInt("0x08", 16),
  APPEND: parseInt("0x10", 16),
  TRUNCATE: parseInt("0x20", 16),
  EXCL: parseInt("0x80", 16),
};

function formatDate(date) {
  const year = String(date.getFullYear() % 100).padStart(2, "0");
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hour = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");

  return `${year}-${month}-${day} ${hour}-${minutes}-${seconds}`;
}

/**
 * Helper API for HAR export features.
 */
var HarUtils = {
  getHarFileName(defaultFileName, jsonp, compress, hostname) {
    const extension = jsonp ? ".harp" : ".har";

    const now = new Date();
    let name = defaultFileName.replace(/%date/g, formatDate(now));
    name = name.replace(/%hostname/g, hostname);
    name = name.replace(/\:/gm, "-", "");
    name = name.replace(/\//gm, "_", "");

    let fileName = name + extension;

    // Default file extension is zip if compressing is on.
    if (compress) {
      fileName += ".zip";
    }

    return fileName;
  },

  /**
   * Save HAR string into a given file. The file might be compressed
   * if specified in the options.
   *
   * @param {File} file Target file where the HAR string (JSON)
   * should be stored.
   * @param {String} jsonString HAR data (JSON or JSONP)
   * @param {Boolean} compress The result file is zipped if set to true.
   */
  saveToFile(file, jsonString, compress) {
    const openFlags =
      OPEN_FLAGS.WRONLY | OPEN_FLAGS.CREATE_FILE | OPEN_FLAGS.TRUNCATE;

    try {
      const foStream = Cc[
        "@mozilla.org/network/file-output-stream;1"
      ].createInstance(Ci.nsIFileOutputStream);

      const permFlags = parseInt("0666", 8);
      foStream.init(file, openFlags, permFlags, 0);

      const convertor = Cc[
        "@mozilla.org/intl/converter-output-stream;1"
      ].createInstance(Ci.nsIConverterOutputStream);
      convertor.init(foStream, "UTF-8");

      // The entire jsonString can be huge so, write the data in chunks.
      const chunkLength = 1024 * 1024;
      for (let i = 0; i <= jsonString.length; i++) {
        const data = jsonString.substr(i, chunkLength + 1);
        if (data) {
          convertor.writeString(data);
        }

        i = i + chunkLength;
      }

      // this closes foStream
      convertor.close();
    } catch (err) {
      console.error(err);
      return false;
    }

    // If no compressing then bail out.
    if (!compress) {
      return true;
    }

    // Remember name of the original file, it'll be replaced by a zip file.
    const originalFilePath = file.path;
    const originalFileName = file.leafName;

    try {
      // Rename using unique name (the file is going to be removed).
      file.moveTo(null, "temp" + new Date().getTime() + "temphar");

      // Create compressed file with the original file path name.
      const zipFile = Cc["@mozilla.org/file/local;1"].createInstance(
        Ci.nsIFile
      );
      zipFile.initWithPath(originalFilePath);

      // The file within the zipped file doesn't use .zip extension.
      let fileName = originalFileName;
      if (fileName.indexOf(".zip") == fileName.length - 4) {
        fileName = fileName.substr(0, fileName.indexOf(".zip"));
      }

      const zip = new ZipWriter();
      zip.open(zipFile, openFlags);
      zip.addEntryFile(
        fileName,
        Ci.nsIZipWriter.COMPRESSION_DEFAULT,
        file,
        false
      );
      zip.close();

      // Remove the original file (now zipped).
      file.remove(true);
      return true;
    } catch (err) {
      console.error(err);

      // Something went wrong (disk space?) rename the original file back.
      file.moveTo(null, originalFileName);
    }

    return false;
  },

  getLocalDirectory(path) {
    let dir;

    if (!path) {
      dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
      dir.append("har");
      dir.append("logs");
    } else {
      dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
      dir.initWithPath(path);
    }

    return dir;
  },
};

// Exports from this module
exports.HarUtils = HarUtils;