summaryrefslogtreecommitdiffstats
path: root/netwerk/test/unit/test_plaintext_sniff.js
blob: fb9620e04c1e76666051b099cfa38df3bd06869a (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
// Test the plaintext-or-binary sniffer

"use strict";

const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");

// List of Content-Type headers to test.  For each header we have an array.
// The first element in the array is the Content-Type header string.  The
// second element in the array is a boolean indicating whether we allow
// sniffing for that type.
var contentTypeHeaderList = [
  ["text/plain", true],
  ["text/plain; charset=ISO-8859-1", true],
  ["text/plain; charset=iso-8859-1", true],
  ["text/plain; charset=UTF-8", true],
  ["text/plain; charset=unknown", false],
  ["text/plain; param", false],
  ["text/plain; charset=ISO-8859-1; param", false],
  ["text/plain; charset=iso-8859-1; param", false],
  ["text/plain; charset=UTF-8; param", false],
  ["text/plain; charset=utf-8", false],
  ["text/plain; charset=utf8", false],
  ["text/plain; charset=UTF8", false],
  ["text/plain; charset=iSo-8859-1", false],
];

// List of response bodies to test.  For each response we have an array. The
// first element in the array is the body string.  The second element in the
// array is a boolean indicating whether that string should sniff as binary.
var bodyList = [["Plaintext", false]];

// List of possible BOMs
var BOMList = [
  "\xFE\xFF", // UTF-16BE
  "\xFF\xFE", // UTF-16LE
  "\xEF\xBB\xBF", // UTF-8
  "\x00\x00\xFE\xFF", // UCS-4BE
  "\x00\x00\xFF\xFE", // UCS-4LE
];

// Build up bodyList.  The things we treat as binary are ASCII codes 0-8,
// 14-26, 28-31.  That is, the control char range, except for tab, newline,
// vertical tab, form feed, carriage return, and ESC (this last being used by
// Shift_JIS, apparently).
function isBinaryChar(ch) {
  return (
    (0 <= ch && ch <= 8) || (14 <= ch && ch <= 26) || (28 <= ch && ch <= 31)
  );
}

// Test chars on their own
var i;
for (i = 0; i <= 127; ++i) {
  bodyList.push([String.fromCharCode(i), isBinaryChar(i)]);
}

// Test that having a BOM prevents plaintext sniffing
var j;
for (i = 0; i <= 127; ++i) {
  for (j = 0; j < BOMList.length; ++j) {
    bodyList.push([BOMList[j] + String.fromCharCode(i, i), false]);
  }
}

// Test that having a BOM requires at least 4 chars to kick in
for (i = 0; i <= 127; ++i) {
  for (j = 0; j < BOMList.length; ++j) {
    bodyList.push([
      BOMList[j] + String.fromCharCode(i),
      BOMList[j].length == 2 && isBinaryChar(i),
    ]);
  }
}

function makeChan(headerIdx, bodyIdx) {
  var chan = NetUtil.newChannel({
    uri:
      "http://localhost:" +
      httpserv.identity.primaryPort +
      "/" +
      headerIdx +
      "/" +
      bodyIdx,
    loadUsingSystemPrincipal: true,
  }).QueryInterface(Ci.nsIHttpChannel);

  chan.loadFlags |= Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;

  return chan;
}

function makeListener(headerIdx, bodyIdx) {
  var listener = {
    onStartRequest: function test_onStartR(request) {
      try {
        var chan = request.QueryInterface(Ci.nsIChannel);

        Assert.equal(chan.status, Cr.NS_OK);

        var type = chan.contentType;

        var expectedType =
          contentTypeHeaderList[headerIdx][1] && bodyList[bodyIdx][1]
            ? "application/x-vnd.mozilla.guess-from-ext"
            : "text/plain";
        if (expectedType != type) {
          do_throw(
            "Unexpected sniffed type '" +
              type +
              "'.  " +
              "Should be '" +
              expectedType +
              "'.  " +
              "Header is ['" +
              contentTypeHeaderList[headerIdx][0] +
              "', " +
              contentTypeHeaderList[headerIdx][1] +
              "].  " +
              "Body is ['" +
              bodyList[bodyIdx][0].toSource() +
              "', " +
              bodyList[bodyIdx][1] +
              "]."
          );
        }
        Assert.equal(expectedType, type);
      } catch (e) {
        do_throw("Unexpected exception: " + e);
      }

      throw Components.Exception("", Cr.NS_ERROR_ABORT);
    },

    onDataAvailable: function test_ODA() {
      do_throw("Should not get any data!");
    },

    onStopRequest: function test_onStopR(request, status) {
      // Advance to next test
      ++headerIdx;
      if (headerIdx == contentTypeHeaderList.length) {
        headerIdx = 0;
        ++bodyIdx;
      }

      if (bodyIdx == bodyList.length) {
        do_test_pending();
        httpserv.stop(do_test_finished);
      } else {
        doTest(headerIdx, bodyIdx);
      }

      do_test_finished();
    },
  };

  return listener;
}

function doTest(headerIdx, bodyIdx) {
  var chan = makeChan(headerIdx, bodyIdx);

  var listener = makeListener(headerIdx, bodyIdx);

  chan.asyncOpen(listener);

  do_test_pending();
}

function createResponse(headerIdx, bodyIdx, metadata, response) {
  response.setHeader(
    "Content-Type",
    contentTypeHeaderList[headerIdx][0],
    false
  );
  response.bodyOutputStream.write(
    bodyList[bodyIdx][0],
    bodyList[bodyIdx][0].length
  );
}

function makeHandler(headerIdx, bodyIdx) {
  var f = function handlerClosure(metadata, response) {
    return createResponse(headerIdx, bodyIdx, metadata, response);
  };
  return f;
}

var httpserv;
function run_test() {
  // disable on Windows for now, because it seems to leak sockets and die.
  // Silly operating system!
  // This is a really nasty way to detect Windows.  I wish we could do better.
  if (mozinfo.os == "win") {
    //failing eslint no-empty test
  }

  httpserv = new HttpServer();

  for (i = 0; i < contentTypeHeaderList.length; ++i) {
    for (j = 0; j < bodyList.length; ++j) {
      httpserv.registerPathHandler("/" + i + "/" + j, makeHandler(i, j));
    }
  }

  httpserv.start(-1);

  doTest(0, 0);
}