Skip to content

Commit 472c60c

Browse files
committed
WIP use a different approach to handle promise canceling
Add a new webpack generation config.
1 parent c556cfa commit 472c60c

File tree

10 files changed

+193
-67
lines changed

10 files changed

+193
-67
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ To get a browser bundle of GramJS, use the following command:
8080
NODE_ENV=production npx webpack
8181
```
8282
83+
You can also use the helpful script `generate_webpack.js`
84+
85+
```bash
86+
node generate_webpack.js
87+
```
88+
8389
## Calling the raw API
8490

8591
To use raw telegram API methods use [invoke function](https://gram.js.org/beta/classes/TelegramClient.html#invoke).

generate_webpack.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
const { exec } = require("child_process");
2+
const fs = require("fs");
3+
const path = require("path");
4+
const webpack = require("webpack");
5+
const webpackConfig = require("./webpack.config");
6+
webpackConfig.entry = path.resolve(__dirname, "browser/index.js");
7+
/**
8+
* Generates a webpack build and put it in browser folder
9+
*/
10+
11+
function addBuffer(dir) {
12+
fs.readdirSync(dir).forEach((file) => {
13+
let fullPath = path.join(dir, file);
14+
if (fs.lstatSync(fullPath).isDirectory()) {
15+
addBuffer(fullPath);
16+
} else {
17+
if (
18+
(fullPath.endsWith(".ts") || fullPath.endsWith(".js")) &&
19+
(!fullPath.endsWith(".d.ts") ||
20+
fullPath.endsWith("api.d.ts") ||
21+
fullPath.endsWith("define.d.ts"))
22+
) {
23+
const tsFile = fs.readFileSync(fullPath, "utf8");
24+
if (tsFile.includes("Buffer")) {
25+
const newTsFile = 'import { Buffer } from "buffer/";\n' + tsFile;
26+
fs.writeFileSync(fullPath, newTsFile, "utf8");
27+
}
28+
}
29+
}
30+
});
31+
}
32+
33+
function renameFiles(dir, action) {
34+
fs.readdirSync(dir).forEach((file) => {
35+
let fullPath = path.join(dir, file);
36+
if (fs.lstatSync(fullPath).isDirectory()) {
37+
renameFiles(fullPath, action);
38+
} else {
39+
if (fullPath.includes("example")) {
40+
fs.unlinkSync(fullPath);
41+
}
42+
43+
if (fullPath.includes("-BROWSER")) {
44+
console.log(action, fullPath);
45+
46+
if (action === "rename") {
47+
fs.renameSync(fullPath, fullPath.replace("-BROWSER", ""));
48+
} else if (action === "delete") {
49+
fs.unlinkSync(fullPath);
50+
}
51+
}
52+
}
53+
});
54+
}
55+
56+
function copyFolderSync(from, to) {
57+
fs.mkdirSync(to);
58+
fs.readdirSync(from).forEach((element) => {
59+
if (fs.lstatSync(path.join(from, element)).isFile()) {
60+
fs.copyFileSync(path.join(from, element), path.join(to, element));
61+
} else {
62+
copyFolderSync(path.join(from, element), path.join(to, element));
63+
}
64+
});
65+
}
66+
67+
fs.rmSync("browser", { recursive: true, force: true });
68+
fs.rmSync("tempBrowser", { recursive: true, force: true });
69+
copyFolderSync("gramjs", "tempBrowser");
70+
addBuffer("tempBrowser");
71+
renameFiles("tempBrowser", "rename");
72+
73+
const tsconfig = fs.readFileSync("tsconfig.json", "utf8");
74+
let newTsconfig = tsconfig.replace(/\.\/dist/g, "./browser");
75+
newTsconfig = newTsconfig.replace(/gramjs/g, "tempBrowser");
76+
fs.writeFileSync("tsconfig.json", newTsconfig, "utf8");
77+
const packageJSON = JSON.parse(fs.readFileSync("package.json", "utf8"));
78+
const oldValueStorage = packageJSON.dependencies["node-localstorage"];
79+
const oldValueSocks = packageJSON.dependencies["socks"];
80+
delete packageJSON.dependencies["node-localstorage"];
81+
delete packageJSON.dependencies["socks"];
82+
fs.writeFileSync(
83+
"package.json",
84+
JSON.stringify(packageJSON, null, " "),
85+
"utf8"
86+
);
87+
88+
const npmi = exec("npm i");
89+
npmi.on("close", (code) => {
90+
if (code !== 0) {
91+
throw new Error("Error happened " + code);
92+
}
93+
94+
const tsc = exec("tsc");
95+
tsc.stdout.on("data", function (data) {
96+
console.log("stdout: " + data.toString());
97+
});
98+
99+
tsc.stderr.on("data", function (data) {
100+
console.error("stderr: " + data.toString());
101+
});
102+
tsc.on("close", (code) => {
103+
if (code !== 0) {
104+
throw new Error("Error happened " + code);
105+
}
106+
107+
fs.copyFileSync("package.json", "browser/package.json");
108+
fs.copyFileSync("README.md", "browser/README.md");
109+
fs.copyFileSync("LICENSE", "browser/LICENSE");
110+
fs.copyFileSync("gramjs/tl/api.d.ts", "browser/tl/api.d.ts");
111+
fs.copyFileSync("gramjs/define.d.ts", "browser/define.d.ts");
112+
fs.rmSync("tempBrowser", { recursive: true, force: true });
113+
const tsconfig = fs.readFileSync("tsconfig.json", "utf8");
114+
let newTsconfig = tsconfig.replace(/\.\/browser/g, "./dist");
115+
newTsconfig = newTsconfig.replace(/tempBrowser/g, "gramjs");
116+
fs.writeFileSync("tsconfig.json", newTsconfig, "utf8");
117+
const packageJSON = JSON.parse(fs.readFileSync("package.json", "utf8"));
118+
packageJSON.dependencies["node-localstorage"] = oldValueStorage;
119+
packageJSON.dependencies["socks"] = oldValueSocks;
120+
fs.writeFileSync(
121+
"package.json",
122+
JSON.stringify(packageJSON, null, " "),
123+
"utf8"
124+
);
125+
126+
webpack(webpackConfig, (err, stats) => {
127+
if (err || stats.hasErrors()) {
128+
console.log("SOME ERROR HAPPENED");
129+
process.exit(0);
130+
}
131+
exec("npm i");
132+
console.log(
133+
"DONE!. File created at ",
134+
path.resolve(__dirname, "browser/telegram.js")
135+
);
136+
});
137+
});
138+
});

gramjs/Version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const version = "2.14.0";
1+
export const version = "2.14.9";

gramjs/extensions/CancelHelper.ts

Lines changed: 0 additions & 3 deletions
This file was deleted.

gramjs/extensions/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ export { PromisedWebSockets } from "./PromisedWebSockets";
55
export { PromisedNetSockets } from "./PromisedNetSockets";
66
export { MessagePacker } from "./MessagePacker";
77
export { AsyncQueue } from "./AsyncQueue";
8-
export { CancelHelper } from "./CancelHelper";

gramjs/network/MTProtoSender.ts

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import { AuthKey } from "../crypto/AuthKey";
1515
import { MTProtoState } from "./MTProtoState";
1616

17-
import { BinaryReader, CancelHelper, Logger } from "../extensions";
17+
import { BinaryReader, Logger } from "../extensions";
1818
import { MessagePacker } from "../extensions";
1919
import { GZIPPacked, MessageContainer, RPCResult, TLMessage } from "../tl/core";
2020
import { Api } from "../tl";
@@ -34,6 +34,11 @@ import { Connection, UpdateConnectionState } from "./";
3434
import type { TelegramClient } from "..";
3535
import { LogLevel } from "../extensions/Logger";
3636
import { Mutex } from "async-mutex";
37+
import {
38+
pseudoCancellable,
39+
CancellablePromise,
40+
Cancellation,
41+
} from "real-cancellable-promise";
3742

3843
interface DEFAULT_OPTIONS {
3944
logger: any;
@@ -101,11 +106,8 @@ export class MTProtoSender {
101106
_authenticated: boolean;
102107
private _securityChecks: boolean;
103108
private _connectMutex: Mutex;
104-
private _recvCancelPromise: Promise<CancelHelper>;
105-
private _recvCancelResolve?: (value: CancelHelper) => void;
106-
private _sendCancelPromise: Promise<CancelHelper>;
107-
private _sendCancelResolve?: (value: CancelHelper) => void;
108109
private _cancelSend: boolean;
110+
cancellableRecvLoopPromise?: CancellablePromise<any>;
109111

110112
/**
111113
* @param authKey
@@ -135,12 +137,6 @@ export class MTProtoSender {
135137

136138
this._connectMutex = new Mutex();
137139

138-
this._recvCancelPromise = new Promise((resolve) => {
139-
this._recvCancelResolve = resolve;
140-
});
141-
this._sendCancelPromise = new Promise((resolve) => {
142-
this._sendCancelResolve = resolve;
143-
});
144140
/**
145141
* whether we disconnected ourself or telegram did it.
146142
*/
@@ -438,14 +434,7 @@ export class MTProtoSender {
438434

439435
_cancelLoops() {
440436
this._cancelSend = true;
441-
this._recvCancelResolve!(new CancelHelper());
442-
this._sendCancelResolve!(new CancelHelper());
443-
this._recvCancelPromise = new Promise((resolve) => {
444-
this._recvCancelResolve = resolve;
445-
});
446-
this._sendCancelPromise = new Promise((resolve) => {
447-
this._sendCancelResolve = resolve;
448-
});
437+
this.cancellableRecvLoopPromise!.cancel();
449438
}
450439

451440
/**
@@ -526,14 +515,14 @@ export class MTProtoSender {
526515
while (this._userConnected && !this._reconnecting) {
527516
this._log.debug("Receiving items from the network...");
528517
try {
529-
body = await Promise.race([
530-
this._connection!.recv(),
531-
this._recvCancelPromise,
532-
]);
533-
if (body instanceof CancelHelper) {
518+
this.cancellableRecvLoopPromise = pseudoCancellable(
519+
this._connection!.recv()
520+
);
521+
body = await this.cancellableRecvLoopPromise;
522+
} catch (e: any) {
523+
if (e instanceof Cancellation) {
534524
return;
535525
}
536-
} catch (e: any) {
537526
this._log.error(e);
538527
this._log.warn("Connection closed while receiving data...");
539528
this._startReconnecting(e);

gramjs/network/connection/Connection.ts

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
CancelHelper,
32
Logger,
43
PromisedNetSockets,
54
PromisedWebSockets,
@@ -8,6 +7,11 @@ import { AsyncQueue } from "../../extensions";
87
import { AbridgedPacketCodec } from "./TCPAbridged";
98
import { FullPacketCodec } from "./TCPFull";
109
import { ProxyInterface } from "./TCPMTProxy";
10+
import {
11+
CancellablePromise,
12+
Cancellation,
13+
pseudoCancellable,
14+
} from "real-cancellable-promise";
1115

1216
interface ConnectionInterfaceParams {
1317
ip: string;
@@ -44,11 +48,8 @@ class Connection {
4448
protected _obfuscation: any;
4549
_sendArray: AsyncQueue;
4650
_recvArray: AsyncQueue;
47-
private _recvCancelPromise: Promise<CancelHelper>;
48-
private _recvCancelResolve?: (value: CancelHelper) => void;
49-
private _sendCancelPromise: Promise<CancelHelper>;
50-
private _sendCancelResolve?: (value: CancelHelper) => void;
51-
51+
recvCancel?: CancellablePromise<any>;
52+
sendCancel?: CancellablePromise<any>;
5253
socket: PromisedNetSockets | PromisedWebSockets;
5354
public _testServers: boolean;
5455

@@ -75,13 +76,6 @@ class Connection {
7576
this._recvArray = new AsyncQueue();
7677
this.socket = new socket(proxy);
7778
this._testServers = testServers;
78-
79-
this._recvCancelPromise = new Promise((resolve) => {
80-
this._recvCancelResolve = resolve;
81-
});
82-
this._sendCancelPromise = new Promise((resolve) => {
83-
this._sendCancelResolve = resolve;
84-
});
8579
}
8680

8781
async _connect() {
@@ -102,14 +96,8 @@ class Connection {
10296
}
10397

10498
_cancelLoops() {
105-
this._recvCancelResolve!(new CancelHelper());
106-
this._sendCancelResolve!(new CancelHelper());
107-
this._recvCancelPromise = new Promise((resolve) => {
108-
this._recvCancelResolve = resolve;
109-
});
110-
this._sendCancelPromise = new Promise((resolve) => {
111-
this._sendCancelResolve = resolve;
112-
});
99+
this.recvCancel!.cancel();
100+
this.sendCancel!.cancel();
113101
}
114102

115103
async disconnect() {
@@ -143,19 +131,17 @@ class Connection {
143131
async _sendLoop() {
144132
try {
145133
while (this._connected) {
146-
const data = await Promise.race([
147-
this._sendCancelPromise,
148-
this._sendArray.pop(),
149-
]);
150-
if (data instanceof CancelHelper) {
151-
break;
152-
}
134+
this.sendCancel = pseudoCancellable(this._sendArray.pop());
135+
const data = await this.sendCancel;
153136
if (!data) {
154137
continue;
155138
}
156139
await this._send(data);
157140
}
158141
} catch (e: any) {
142+
if (e instanceof Cancellation) {
143+
return;
144+
}
159145
this._log.info("The server closed the connection while sending");
160146
await this.disconnect();
161147
}
@@ -165,14 +151,12 @@ class Connection {
165151
let data;
166152
while (this._connected) {
167153
try {
168-
data = await Promise.race([
169-
this._recvCancelPromise,
170-
await this._recv(),
171-
]);
172-
if (data instanceof CancelHelper) {
154+
this.recvCancel = pseudoCancellable(this._recv());
155+
data = await this.recvCancel;
156+
} catch (e: any) {
157+
if (e instanceof Cancellation) {
173158
return;
174159
}
175-
} catch (e: any) {
176160
this._log.info("The server closed the connection");
177161
await this.disconnect();
178162
if (!this._recvArray._queue.length) {

0 commit comments

Comments
 (0)