|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /** |
|
5 * Tests Curl Utils functionality. |
|
6 */ |
|
7 |
|
8 function test() { |
|
9 initNetMonitor(CURL_UTILS_URL).then(([aTab, aDebuggee, aMonitor]) => { |
|
10 info("Starting test... "); |
|
11 |
|
12 let { NetMonitorView, gNetwork } = aMonitor.panelWin; |
|
13 let { RequestsMenu } = NetMonitorView; |
|
14 |
|
15 RequestsMenu.lazyUpdate = false; |
|
16 |
|
17 waitForNetworkEvents(aMonitor, 1, 3).then(() => { |
|
18 let requests = { |
|
19 get: RequestsMenu.getItemAtIndex(0), |
|
20 post: RequestsMenu.getItemAtIndex(1), |
|
21 multipart: RequestsMenu.getItemAtIndex(2), |
|
22 multipartForm: RequestsMenu.getItemAtIndex(3) |
|
23 }; |
|
24 |
|
25 Task.spawn(function*() { |
|
26 yield createCurlData(requests.get.attachment, gNetwork).then((aData) => { |
|
27 test_findHeader(aData); |
|
28 }); |
|
29 |
|
30 yield createCurlData(requests.post.attachment, gNetwork).then((aData) => { |
|
31 test_isUrlEncodedRequest(aData); |
|
32 test_writePostDataTextParams(aData); |
|
33 }); |
|
34 |
|
35 yield createCurlData(requests.multipart.attachment, gNetwork).then((aData) => { |
|
36 test_isMultipartRequest(aData); |
|
37 test_getMultipartBoundary(aData); |
|
38 test_removeBinaryDataFromMultipartText(aData); |
|
39 }); |
|
40 |
|
41 yield createCurlData(requests.multipartForm.attachment, gNetwork).then((aData) => { |
|
42 test_getHeadersFromMultipartText(aData); |
|
43 }); |
|
44 |
|
45 if (Services.appinfo.OS != "WINNT") { |
|
46 test_escapeStringPosix(); |
|
47 } else { |
|
48 test_escapeStringWin(); |
|
49 } |
|
50 |
|
51 teardown(aMonitor).then(finish); |
|
52 }); |
|
53 }); |
|
54 |
|
55 aDebuggee.performRequests(SIMPLE_SJS); |
|
56 }); |
|
57 } |
|
58 |
|
59 function test_isUrlEncodedRequest(aData) { |
|
60 let isUrlEncoded = CurlUtils.isUrlEncodedRequest(aData); |
|
61 ok(isUrlEncoded, "Should return true for url encoded requests."); |
|
62 } |
|
63 |
|
64 function test_isMultipartRequest(aData) { |
|
65 let isMultipart = CurlUtils.isMultipartRequest(aData); |
|
66 ok(isMultipart, "Should return true for multipart/form-data requests."); |
|
67 } |
|
68 |
|
69 function test_findHeader(aData) { |
|
70 let headers = aData.headers; |
|
71 let hostName = CurlUtils.findHeader(headers, "Host"); |
|
72 let requestedWithLowerCased = CurlUtils.findHeader(headers, "x-requested-with"); |
|
73 let doesNotExist = CurlUtils.findHeader(headers, "X-Does-Not-Exist"); |
|
74 |
|
75 is(hostName, "example.com", |
|
76 "Header with name 'Host' should be found in the request array."); |
|
77 is(requestedWithLowerCased, "XMLHttpRequest", |
|
78 "The search should be case insensitive."); |
|
79 is(doesNotExist, null, |
|
80 "Should return null when a header is not found."); |
|
81 } |
|
82 |
|
83 function test_writePostDataTextParams(aData) { |
|
84 let params = CurlUtils.writePostDataTextParams(aData.postDataText); |
|
85 is(params, "param1=value1¶m2=value2¶m3=value3", |
|
86 "Should return a serialized representation of the request parameters"); |
|
87 } |
|
88 |
|
89 function test_getMultipartBoundary(aData) { |
|
90 let boundary = CurlUtils.getMultipartBoundary(aData); |
|
91 ok(/-{3,}\w+/.test(boundary), |
|
92 "A boundary string should be found in a multipart request."); |
|
93 } |
|
94 |
|
95 function test_removeBinaryDataFromMultipartText(aData) { |
|
96 let generatedBoundary = CurlUtils.getMultipartBoundary(aData); |
|
97 let text = aData.postDataText; |
|
98 let binaryRemoved = |
|
99 CurlUtils.removeBinaryDataFromMultipartText(text, generatedBoundary); |
|
100 let boundary = "--" + generatedBoundary; |
|
101 |
|
102 const EXPECTED_POSIX_RESULT = [ |
|
103 "$'", |
|
104 boundary, |
|
105 "\\r\\n\\r\\n", |
|
106 "Content-Disposition: form-data; name=\"param1\"", |
|
107 "\\r\\n\\r\\n", |
|
108 "value1", |
|
109 "\\r\\n", |
|
110 boundary, |
|
111 "\\r\\n\\r\\n", |
|
112 "Content-Disposition: form-data; name=\"file\"; filename=\"filename.png\"", |
|
113 "\\r\\n", |
|
114 "Content-Type: image/png", |
|
115 "\\r\\n\\r\\n", |
|
116 generatedBoundary, |
|
117 "--\\r\\n", |
|
118 "'" |
|
119 ].join(""); |
|
120 |
|
121 const EXPECTED_WIN_RESULT = [ |
|
122 '"' + boundary + '"^', |
|
123 '\u000d\u000A\u000d\u000A', |
|
124 '"Content-Disposition: form-data; name=""param1"""^', |
|
125 '\u000d\u000A\u000d\u000A', |
|
126 '"value1"^', |
|
127 '\u000d\u000A', |
|
128 '"' + boundary + '"^', |
|
129 '\u000d\u000A\u000d\u000A', |
|
130 '"Content-Disposition: form-data; name=""file""; filename=""filename.png"""^', |
|
131 '\u000d\u000A', |
|
132 '"Content-Type: image/png"^', |
|
133 '\u000d\u000A\u000d\u000A', |
|
134 '"' + generatedBoundary + '--"^', |
|
135 '\u000d\u000A', |
|
136 '""' |
|
137 ].join(""); |
|
138 |
|
139 if (Services.appinfo.OS != "WINNT") { |
|
140 is(CurlUtils.escapeStringPosix(binaryRemoved), EXPECTED_POSIX_RESULT, |
|
141 "The mulitpart request payload should not contain binary data."); |
|
142 } else { |
|
143 is(CurlUtils.escapeStringWin(binaryRemoved), EXPECTED_WIN_RESULT, |
|
144 "WinNT: The mulitpart request payload should not contain binary data."); |
|
145 } |
|
146 } |
|
147 |
|
148 function test_getHeadersFromMultipartText(aData) { |
|
149 let headers = CurlUtils.getHeadersFromMultipartText(aData.postDataText); |
|
150 |
|
151 ok(Array.isArray(headers), |
|
152 "Should return an array."); |
|
153 ok(headers.length > 0, |
|
154 "There should exist at least one request header."); |
|
155 is(headers[0].name, "Content-Type", |
|
156 "The first header name should be 'Content-Type'."); |
|
157 } |
|
158 |
|
159 function test_escapeStringPosix() { |
|
160 let surroundedWithQuotes = "A simple string"; |
|
161 is(CurlUtils.escapeStringPosix(surroundedWithQuotes), "'A simple string'", |
|
162 "The string should be surrounded with single quotes."); |
|
163 |
|
164 let singleQuotes = "It's unusual to put crickets in your coffee."; |
|
165 is(CurlUtils.escapeStringPosix(singleQuotes), |
|
166 "$'It\\'s unusual to put crickets in your coffee.'", |
|
167 "Single quotes should be escaped."); |
|
168 |
|
169 let newLines = "Line 1\r\nLine 2\u000d\u000ALine3"; |
|
170 is(CurlUtils.escapeStringPosix(newLines), "$'Line 1\\r\\nLine 2\\r\\nLine3'", |
|
171 "Newlines should be escaped."); |
|
172 |
|
173 let controlChars = "\u0007 \u0009 \u000C \u001B"; |
|
174 is(CurlUtils.escapeStringPosix(controlChars), "$'\\x07 \\x09 \\x0c \\x1b'", |
|
175 "Control characters should be escaped."); |
|
176 |
|
177 let extendedAsciiChars = "æ ø ü ß ö é"; |
|
178 is(CurlUtils.escapeStringPosix(extendedAsciiChars), |
|
179 "$'\\xc3\\xa6 \\xc3\\xb8 \\xc3\\xbc \\xc3\\x9f \\xc3\\xb6 \\xc3\\xa9'", |
|
180 "Character codes outside of the decimal range 32 - 126 should be escaped."); |
|
181 } |
|
182 |
|
183 function test_escapeStringWin() { |
|
184 let surroundedWithDoubleQuotes = "A simple string"; |
|
185 is(CurlUtils.escapeStringWin(surroundedWithDoubleQuotes), '"A simple string"', |
|
186 "The string should be surrounded with double quotes."); |
|
187 |
|
188 let doubleQuotes = "Quote: \"Time is an illusion. Lunchtime doubly so.\""; |
|
189 is(CurlUtils.escapeStringWin(doubleQuotes), |
|
190 '"Quote: ""Time is an illusion. Lunchtime doubly so."""', |
|
191 "Double quotes should be escaped."); |
|
192 |
|
193 let percentSigns = "%AppData%"; |
|
194 is(CurlUtils.escapeStringWin(percentSigns), '""%"AppData"%""', |
|
195 "Percent signs should be escaped."); |
|
196 |
|
197 let backslashes = "\\A simple string\\"; |
|
198 is(CurlUtils.escapeStringWin(backslashes), '"\\\\A simple string\\\\"', |
|
199 "Backslashes should be escaped."); |
|
200 |
|
201 let newLines = "line1\r\nline2\r\nline3"; |
|
202 is(CurlUtils.escapeStringWin(newLines), |
|
203 '"line1"^\u000d\u000A"line2"^\u000d\u000A"line3"', |
|
204 "Newlines should be escaped."); |
|
205 } |
|
206 |
|
207 function createCurlData(aSelected, aNetwork) { |
|
208 return Task.spawn(function*() { |
|
209 // Create a sanitized object for the Curl command generator. |
|
210 let data = { |
|
211 url: aSelected.url, |
|
212 method: aSelected.method, |
|
213 headers: [], |
|
214 httpVersion: aSelected.httpVersion, |
|
215 postDataText: null |
|
216 }; |
|
217 |
|
218 // Fetch header values. |
|
219 for (let { name, value } of aSelected.requestHeaders.headers) { |
|
220 let text = yield aNetwork.getString(value); |
|
221 data.headers.push({ name: name, value: text }); |
|
222 } |
|
223 |
|
224 // Fetch the request payload. |
|
225 if (aSelected.requestPostData) { |
|
226 let postData = aSelected.requestPostData.postData.text; |
|
227 data.postDataText = yield aNetwork.getString(postData); |
|
228 } |
|
229 |
|
230 return data; |
|
231 }); |
|
232 } |