Skip to content

Commit 1e9ed44

Browse files
committed
Added browser demos for package:fetch_api and package:fetch_client.
1 parent a23bc01 commit 1e9ed44

File tree

7 files changed

+651
-0
lines changed

7 files changed

+651
-0
lines changed

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<h2>HTTP request from inside WASM in Dart - list of demos</h2>
99

1010
<ol>
11+
<li><a href="package-fetch-api/">Demo using package:fetch_api</a></li>
12+
<li><a href="package-fetch-client/">Demo using package:fetch_client</a></li>
1113
<li><a href="package-http/">Demo using package:http</a></li>
1214
<li><a href="package-web/">Demo using package:web and fetch()</a></li>
1315
</ol>

package-fetch-api/index.html

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>HTTP request from WASM in Dart using package:fetch_api</title>
7+
<script type="module">
8+
import { instantiate, invoke } from "./main.mjs";
9+
10+
const module = await WebAssembly.compileStreaming(fetch('main.wasm'));
11+
const instance = await instantiate(module);
12+
invoke(instance);
13+
</script>
14+
</head>
15+
16+
<body>
17+
<h2>HTTP Request from inside WASM in Dart using package:fetch_api</h2>
18+
19+
<p>This example uses <a href="https://pub.dev/packages/fetch_api">packages/fetch_api</a>.</p>
20+
21+
<p>See the output in browser developer console.</p>
22+
23+
<p>Actual code:</p>
24+
<pre>
25+
26+
import 'package:fetch_api/fetch_api.dart';
27+
28+
final resp = await fetch('https://httpbin.org/anything');
29+
final txt = await resp.text();
30+
print('body: ${txt}');
31+
32+
</pre>
33+
<section>
34+
<h4>Info</h4>
35+
<ul>
36+
<li><a href="https://github.com/wasm-outbound-http-examples/dart/tree/main/browser-package-fetch-api">Demo
37+
source code</a></li>
38+
<li>Versions used on the moment of build:<small>
39+
Dart SDK: <a href="https://github.com/dart-lang/sdk/releases/tag/3.4.0">v3.4.0</a>,
40+
package:fetch_api: <a href="https://pub.dev/packages/fetch_api/versions/2.2.0">v2.2.0</a>.
41+
</small></li>
42+
</ul>
43+
</section>
44+
<footer><small>Created for (wannabe-awesome) <a href="https://github.com/vasilev/HTTP-request-from-inside-WASM">list</a></small></footer>
45+
</body>
46+
</html>

package-fetch-api/main.mjs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
let buildArgsList;
2+
3+
// `modulePromise` is a promise to the `WebAssembly.module` object to be
4+
// instantiated.
5+
// `importObjectPromise` is a promise to an object that contains any additional
6+
// imports needed by the module that aren't provided by the standard runtime.
7+
// The fields on this object will be merged into the importObject with which
8+
// the module will be instantiated.
9+
// This function returns a promise to the instantiated module.
10+
export const instantiate = async (modulePromise, importObjectPromise) => {
11+
let dartInstance;
12+
13+
function stringFromDartString(string) {
14+
const totalLength = dartInstance.exports.$stringLength(string);
15+
let result = '';
16+
let index = 0;
17+
while (index < totalLength) {
18+
let chunkLength = Math.min(totalLength - index, 0xFFFF);
19+
const array = new Array(chunkLength);
20+
for (let i = 0; i < chunkLength; i++) {
21+
array[i] = dartInstance.exports.$stringRead(string, index++);
22+
}
23+
result += String.fromCharCode(...array);
24+
}
25+
return result;
26+
}
27+
28+
function stringToDartString(string) {
29+
const length = string.length;
30+
let range = 0;
31+
for (let i = 0; i < length; i++) {
32+
range |= string.codePointAt(i);
33+
}
34+
if (range < 256) {
35+
const dartString = dartInstance.exports.$stringAllocate1(length);
36+
for (let i = 0; i < length; i++) {
37+
dartInstance.exports.$stringWrite1(dartString, i, string.codePointAt(i));
38+
}
39+
return dartString;
40+
} else {
41+
const dartString = dartInstance.exports.$stringAllocate2(length);
42+
for (let i = 0; i < length; i++) {
43+
dartInstance.exports.$stringWrite2(dartString, i, string.charCodeAt(i));
44+
}
45+
return dartString;
46+
}
47+
}
48+
49+
// Prints to the console
50+
function printToConsole(value) {
51+
if (typeof dartPrint == "function") {
52+
dartPrint(value);
53+
return;
54+
}
55+
if (typeof console == "object" && typeof console.log != "undefined") {
56+
console.log(value);
57+
return;
58+
}
59+
if (typeof print == "function") {
60+
print(value);
61+
return;
62+
}
63+
64+
throw "Unable to print message: " + js;
65+
}
66+
67+
// Converts a Dart List to a JS array. Any Dart objects will be converted, but
68+
// this will be cheap for JSValues.
69+
function arrayFromDartList(constructor, list) {
70+
const length = dartInstance.exports.$listLength(list);
71+
const array = new constructor(length);
72+
for (let i = 0; i < length; i++) {
73+
array[i] = dartInstance.exports.$listRead(list, i);
74+
}
75+
return array;
76+
}
77+
78+
buildArgsList = function(list) {
79+
const dartList = dartInstance.exports.$makeStringList();
80+
for (let i = 0; i < list.length; i++) {
81+
dartInstance.exports.$listAdd(dartList, stringToDartString(list[i]));
82+
}
83+
return dartList;
84+
}
85+
86+
// A special symbol attached to functions that wrap Dart functions.
87+
const jsWrappedDartFunctionSymbol = Symbol("JSWrappedDartFunction");
88+
89+
function finalizeWrapper(dartFunction, wrapped) {
90+
wrapped.dartFunction = dartFunction;
91+
wrapped[jsWrappedDartFunctionSymbol] = true;
92+
return wrapped;
93+
}
94+
95+
// Imports
96+
const dart2wasm = {
97+
98+
_18: f => finalizeWrapper(f,x0 => dartInstance.exports._18(f,x0)),
99+
_19: f => finalizeWrapper(f,x0 => dartInstance.exports._19(f,x0)),
100+
_101: x0 => x0.text(),
101+
_170: (x0,x1) => globalThis.fetch(x0,x1),
102+
_12887: v => stringToDartString(v.toString()),
103+
_12902: () => {
104+
let stackString = new Error().stack.toString();
105+
let frames = stackString.split('\n');
106+
let drop = 2;
107+
if (frames[0] === 'Error') {
108+
drop += 1;
109+
}
110+
return frames.slice(drop).join('\n');
111+
},
112+
_12911: s => stringToDartString(JSON.stringify(stringFromDartString(s))),
113+
_12912: s => printToConsole(stringFromDartString(s)),
114+
_12930: (c) =>
115+
queueMicrotask(() => dartInstance.exports.$invokeCallback(c)),
116+
_12932: (a, i) => a.push(i),
117+
_12943: a => a.length,
118+
_12945: (a, i) => a[i],
119+
_12946: (a, i, v) => a[i] = v,
120+
_12948: a => a.join(''),
121+
_12958: (s, p, i) => s.indexOf(p, i),
122+
_12961: (o, start, length) => new Uint8Array(o.buffer, o.byteOffset + start, length),
123+
_12962: (o, start, length) => new Int8Array(o.buffer, o.byteOffset + start, length),
124+
_12963: (o, start, length) => new Uint8ClampedArray(o.buffer, o.byteOffset + start, length),
125+
_12964: (o, start, length) => new Uint16Array(o.buffer, o.byteOffset + start, length),
126+
_12965: (o, start, length) => new Int16Array(o.buffer, o.byteOffset + start, length),
127+
_12966: (o, start, length) => new Uint32Array(o.buffer, o.byteOffset + start, length),
128+
_12967: (o, start, length) => new Int32Array(o.buffer, o.byteOffset + start, length),
129+
_12970: (o, start, length) => new Float32Array(o.buffer, o.byteOffset + start, length),
130+
_12971: (o, start, length) => new Float64Array(o.buffer, o.byteOffset + start, length),
131+
_12975: (o) => new DataView(o.buffer, o.byteOffset, o.byteLength),
132+
_12979: Function.prototype.call.bind(Object.getOwnPropertyDescriptor(DataView.prototype, 'byteLength').get),
133+
_12980: (b, o) => new DataView(b, o),
134+
_12982: Function.prototype.call.bind(DataView.prototype.getUint8),
135+
_12984: Function.prototype.call.bind(DataView.prototype.getInt8),
136+
_12986: Function.prototype.call.bind(DataView.prototype.getUint16),
137+
_12988: Function.prototype.call.bind(DataView.prototype.getInt16),
138+
_12990: Function.prototype.call.bind(DataView.prototype.getUint32),
139+
_12992: Function.prototype.call.bind(DataView.prototype.getInt32),
140+
_12998: Function.prototype.call.bind(DataView.prototype.getFloat32),
141+
_13000: Function.prototype.call.bind(DataView.prototype.getFloat64),
142+
_13021: o => o === undefined,
143+
_13022: o => typeof o === 'boolean',
144+
_13023: o => typeof o === 'number',
145+
_13025: o => typeof o === 'string',
146+
_13028: o => o instanceof Int8Array,
147+
_13029: o => o instanceof Uint8Array,
148+
_13030: o => o instanceof Uint8ClampedArray,
149+
_13031: o => o instanceof Int16Array,
150+
_13032: o => o instanceof Uint16Array,
151+
_13033: o => o instanceof Int32Array,
152+
_13034: o => o instanceof Uint32Array,
153+
_13035: o => o instanceof Float32Array,
154+
_13036: o => o instanceof Float64Array,
155+
_13037: o => o instanceof ArrayBuffer,
156+
_13038: o => o instanceof DataView,
157+
_13039: o => o instanceof Array,
158+
_13040: o => typeof o === 'function' && o[jsWrappedDartFunctionSymbol] === true,
159+
_13044: (l, r) => l === r,
160+
_13045: o => o,
161+
_13046: o => o,
162+
_13047: o => o,
163+
_13048: b => !!b,
164+
_13049: o => o.length,
165+
_13052: (o, i) => o[i],
166+
_13053: f => f.dartFunction,
167+
_13054: l => arrayFromDartList(Int8Array, l),
168+
_13055: l => arrayFromDartList(Uint8Array, l),
169+
_13056: l => arrayFromDartList(Uint8ClampedArray, l),
170+
_13057: l => arrayFromDartList(Int16Array, l),
171+
_13058: l => arrayFromDartList(Uint16Array, l),
172+
_13059: l => arrayFromDartList(Int32Array, l),
173+
_13060: l => arrayFromDartList(Uint32Array, l),
174+
_13061: l => arrayFromDartList(Float32Array, l),
175+
_13062: l => arrayFromDartList(Float64Array, l),
176+
_13063: (data, length) => {
177+
const view = new DataView(new ArrayBuffer(length));
178+
for (let i = 0; i < length; i++) {
179+
view.setUint8(i, dartInstance.exports.$byteDataGetUint8(data, i));
180+
}
181+
return view;
182+
},
183+
_13064: l => arrayFromDartList(Array, l),
184+
_13065: stringFromDartString,
185+
_13066: stringToDartString,
186+
_13069: l => new Array(l),
187+
_13073: (o, p) => o[p],
188+
_13077: o => String(o),
189+
_13078: (p, s, f) => p.then(s, f)
190+
};
191+
192+
const baseImports = {
193+
dart2wasm: dart2wasm,
194+
195+
196+
Math: Math,
197+
Date: Date,
198+
Object: Object,
199+
Array: Array,
200+
Reflect: Reflect,
201+
};
202+
203+
const jsStringPolyfill = {
204+
"charCodeAt": (s, i) => s.charCodeAt(i),
205+
"compare": (s1, s2) => {
206+
if (s1 < s2) return -1;
207+
if (s1 > s2) return 1;
208+
return 0;
209+
},
210+
"concat": (s1, s2) => s1 + s2,
211+
"equals": (s1, s2) => s1 === s2,
212+
"fromCharCode": (i) => String.fromCharCode(i),
213+
"length": (s) => s.length,
214+
"substring": (s, a, b) => s.substring(a, b),
215+
};
216+
217+
dartInstance = await WebAssembly.instantiate(await modulePromise, {
218+
...baseImports,
219+
...(await importObjectPromise),
220+
"wasm:js-string": jsStringPolyfill,
221+
});
222+
223+
return dartInstance;
224+
}
225+
226+
// Call the main function for the instantiated module
227+
// `moduleInstance` is the instantiated dart2wasm module
228+
// `args` are any arguments that should be passed into the main function.
229+
export const invoke = (moduleInstance, ...args) => {
230+
const dartMain = moduleInstance.exports.$getMain();
231+
const dartArgs = buildArgsList(args);
232+
moduleInstance.exports.$invokeMain(dartMain, dartArgs);
233+
}
234+

package-fetch-api/main.wasm

72 KB
Binary file not shown.

package-fetch-client/index.html

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>HTTP request from WASM in Dart using package:fetch_client</title>
7+
<script type="module">
8+
import { instantiate, invoke } from "./main.mjs";
9+
10+
const module = await WebAssembly.compileStreaming(fetch('main.wasm'));
11+
const instance = await instantiate(module);
12+
invoke(instance);
13+
</script>
14+
</head>
15+
16+
<body>
17+
<h2>HTTP Request from inside WASM in Dart using package:fetch_client</h2>
18+
19+
<p>This example uses <a href="https://pub.dev/packages/fetch_client">packages/fetch_client</a>.</p>
20+
21+
<p>See the output in browser developer console.</p>
22+
23+
<p>Actual code:</p>
24+
<pre>
25+
26+
import 'package:fetch_client/fetch_client.dart';
27+
28+
final client = FetchClient(mode: RequestMode.cors);
29+
final uri = Uri.parse('https://httpbin.org/anything');
30+
final resp = await client.get(uri);
31+
32+
print('${resp.body}');
33+
client.close();
34+
35+
</pre>
36+
<section>
37+
<h4>Info</h4>
38+
<ul>
39+
<li><a href="https://github.com/wasm-outbound-http-examples/dart/tree/main/browser-package-fetch-client">Demo
40+
source code</a></li>
41+
<li>Versions used on the moment of build:<small>
42+
Dart SDK: <a href="https://github.com/dart-lang/sdk/releases/tag/3.4.0">v3.4.0</a>,
43+
package:fetch_client: <a href="https://pub.dev/packages/fetch_client/versions/1.1.2">v1.1.2</a>.
44+
</small></li>
45+
</ul>
46+
</section>
47+
<footer><small>Created for (wannabe-awesome) <a href="https://github.com/vasilev/HTTP-request-from-inside-WASM">list</a></small></footer>
48+
</body>
49+
</html>

0 commit comments

Comments
 (0)