Skip to content

Commit 59c2d11

Browse files
committed
Send ecards
1 parent dd02c51 commit 59c2d11

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

index.html

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<script src="vm/plopp-b3daccel-plugin.js"></script>
4040
<script src="vm/plopp-b3dengine-plugin.js"></script>
4141
<script src="vm/plopp-opengl.js"></script>
42+
<script src="vm/plopp-socket-plugin.js"></script>
4243
<script>
4344
// TODO:
4445
// [x] - implement primJPEGWriteImage, required for:

vm/plopp-socket-plugin.js

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Plopp Socket Plugin
3+
*
4+
* This plugin intercepts Plopp's "send ecard" http requests
5+
* to planet-plopp.com/ploppcardmailer and uses the fetch API
6+
* to send them to the server.
7+
*/
8+
9+
function SocketPlugin() {
10+
"use strict";
11+
12+
const Resolver_Ready = 1;
13+
const Unconnected = 0;
14+
const Connected = 2;
15+
16+
return {
17+
getModuleName: function() { return 'SocketPlugin (plopp)'; },
18+
interpreterProxy: null,
19+
primHandler: null,
20+
21+
setInterpreter: function(proxy) {
22+
this.interpreterProxy = proxy;
23+
this.vm = proxy.vm;
24+
this.primHandler = this.vm.primHandler;
25+
return true;
26+
},
27+
28+
primitiveResolverStatus: function(argCount) {
29+
const status = Resolver_Ready;
30+
return this.vm.popNandPush(argCount + 1, status);
31+
},
32+
33+
primitiveResolverStartNameLookup: function(argCount) {
34+
return this.vm.popN(argCount);
35+
},
36+
37+
primitiveResolverNameLookupResult: function(argCount) {
38+
const address = this.primHandler.makeStByteArray([1, 2, 3, 4]);
39+
return this.vm.popNandPush(argCount + 1, address);
40+
},
41+
42+
primitiveSocketCreate3Semaphores: function(argCount) {
43+
const socket = this.primHandler.makeStString('socket');
44+
socket.status = Unconnected;
45+
socket.readSema = this.interpreterProxy.stackIntegerValue(1);
46+
return this.vm.popNandPush(argCount + 1, socket);
47+
},
48+
49+
primitiveSocketConnectionStatus: function(argCount) {
50+
const socket = this.vm.top();
51+
return this.vm.popNandPush(argCount + 1, socket.status);
52+
},
53+
54+
primitiveSocketConnectToPort: function(argCount) {
55+
const socket = this.vm.stackValue(2);
56+
socket.status = Connected;
57+
socket.response = null;
58+
return this.vm.popN(argCount);
59+
},
60+
61+
primitiveSocketSendDataBufCount: function(argCount) {
62+
const socket = this.vm.stackValue(3);
63+
const data = this.vm.stackValue(2);
64+
this.sendRequestFromSqueak(socket, data);
65+
return this.vm.popNandPush(argCount + 1, data.bytesSize());
66+
},
67+
68+
primitiveSocketSendDone: function(argCount) {
69+
return this.vm.popNandPush(argCount + 1, this.vm.trueObj);
70+
},
71+
72+
primitiveSocketReceiveDataAvailable: function(argCount) {
73+
const socket = this.vm.top();
74+
const available = socket.response ? this.vm.trueObj : this.vm.falseObj;
75+
return this.vm.popNandPush(argCount + 1, available);
76+
},
77+
78+
primitiveSocketCloseConnection: function(argCount) {
79+
const socket = this.vm.top();
80+
socket.status = Unconnected;
81+
return this.vm.popN(argCount);
82+
},
83+
84+
primitiveSocketReceiveDataBufCount: function(argCount) {
85+
const socket = this.vm.stackValue(3);
86+
const response = socket.response;
87+
if (!response) return false;
88+
const target = this.interpreterProxy.stackObjectValue(2);
89+
target.bytes.set(response);
90+
const count = Math.min(target.bytesSize(), response.length);
91+
if (count < response.length) {
92+
socket.response = response.subarray(count);
93+
} else {
94+
socket.response = null;
95+
}
96+
return this.vm.popNandPush(argCount + 1, count);
97+
},
98+
99+
primitiveSocketDestroy: function(argCount) {
100+
const socket = this.vm.top();
101+
socket.status = Unconnected;
102+
return this.vm.popN(argCount);
103+
},
104+
105+
sendRequestFromSqueak: function(socket, data) {
106+
// extract request and body from data so we can use fetch
107+
const request = data.bytesAsString().split('\r\n\r\n', 1)[0];
108+
const body = data.bytes.subarray(request.length + 4);
109+
const requestLines = request.split('\r\n');
110+
const [method, path] = requestLines[0].split(' ', 2);
111+
let headers = {};
112+
let host;
113+
for (let i = 1; i < requestLines.length; i++) {
114+
const line = requestLines[i];
115+
const colon = line.indexOf(':');
116+
const key = line.substring(0, colon).trim();
117+
const value = line.substring(colon + 1).trim();
118+
headers[key] = value;
119+
if (key === 'Host') host = value;
120+
}
121+
const url = 'http://' + host + path;
122+
console.log(`Sending: ${method} to ${url}`, headers, body);
123+
fetch(url, { method, headers, body })
124+
.then(response => {
125+
console.log('ecard response:', response.status, response.statusText);
126+
response.text().then(body => {
127+
console.log(body);
128+
this.deliverResponseToSqueak(socket, response, body);
129+
});
130+
})
131+
.catch(error => {
132+
console.error('Send error:', error.message);
133+
const statusText = `SqueakJS HTTP ${method} Error (CORS?)`;
134+
this.deliverResponseToSqueak(socket, { status: 500, statusText }, statusText);
135+
});
136+
},
137+
138+
deliverResponseToSqueak: function(socket, response, body) {
139+
const headers = response.headers || new Map();
140+
const responseString = `HTTP/1.1 ${response.status} ${response.statusText}\r\n` +
141+
Array.from(headers.entries()).map(([key, value]) => `${key}: ${value}`).join('\r\n') +
142+
'\r\n\r\n' + body;
143+
socket.response = new Uint8Array(responseString.length);
144+
for (let i = 0; i < responseString.length; i++) {
145+
socket.response[i] = responseString.charCodeAt(i);
146+
}
147+
this.primHandler.signalSemaphoreWithIndex(socket.readSema);
148+
}
149+
}
150+
}
151+
152+
function registerSocketPlugin() {
153+
if (typeof Squeak === "object" && Squeak.registerExternalModule) {
154+
Squeak.registerExternalModule('SocketPlugin', SocketPlugin());
155+
} else self.setTimeout(registerSocketPlugin, 100);
156+
};
157+
158+
registerSocketPlugin();

0 commit comments

Comments
 (0)