Skip to content

Commit 830c6e5

Browse files
committed
refs #4565 fixed WebSocket PING/PONG handling
refs #4564 added WebSocketClientDataProvider, FileDataProvider, FtpDataProvider, and FtpPollerDataProvider functionality, fixed tests
1 parent f88c663 commit 830c6e5

33 files changed

+715
-180
lines changed

bin/qdp

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,13 @@ class QdpCmd {
183183
}
184184

185185
static listEvents(AbstractDataProvider provider) {
186-
hash<string, AbstractDataProviderType> events = provider.getEventTypes();
186+
hash<string, hash<DataProviderMessageInfo>> events = provider.getEventTypes();
187187
map printf(" - %s\n", $1), keys events;
188188
}
189189

190190
static event(AbstractDataProvider provider) {
191191
string event = QdpCmd::getString("event", True);
192-
QdpCmd::showType(provider.getEventType(event));
192+
QdpCmd::showType(event, provider.getEventInfo(event));
193193
}
194194

195195
static errors(AbstractDataProvider provider) {
@@ -343,13 +343,13 @@ class QdpCmd {
343343
}
344344

345345
static listMessages(AbstractDataProvider provider) {
346-
hash<string, AbstractDataProviderType> messages = provider.getMessageTypes();
346+
hash<string, hash<DataProviderMessageInfo>> messages = provider.getMessageTypes();
347347
map printf(" - %s\n", $1), keys messages;
348348
}
349349

350350
static message(AbstractDataProvider provider) {
351351
string message = QdpCmd::getString("message", True);
352-
QdpCmd::showType(provider.getMessageType(message));
352+
QdpCmd::showType(message, provider.getMessageInfo(message));
353353
}
354354

355355
static listen(AbstractDataProvider provider) {
@@ -523,12 +523,17 @@ class QdpCmd {
523523
# this method intentionally left empty
524524
}
525525

526+
static showType(string msg, hash<DataProviderMessageInfo> info) {
527+
printf("%s: %s\n", msg, info.desc);
528+
QdpCmd::showType(info.type);
529+
}
530+
526531
static showType(AbstractDataProviderType type, *string offset) {
527532
*hash<string, AbstractDataField> fields = type.getFields();
528533
if (fields) {
529534
QdpCmd::showRecord(fields, offset);
530535
} else {
531-
printf("%s%s%s\n", offset, type.getName());
536+
printf("%s%s%s (%s)\n", offset, type.getName());
532537
}
533538
}
534539

@@ -800,7 +805,7 @@ class ConsoleAppender inherits LoggerAppenderWithLayout {
800805
class InputHelper {
801806
public {
802807
# message map
803-
hash<string, AbstractDataProviderType> mmap;
808+
hash<string, hash<DataProviderMessageInfo>> mmap;
804809

805810
const Prompt = "> ";
806811
const HistoryFile = ".qdp";

doxygen/lang/900_release_notes.dox.tmpl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@
1313
(<a href="https://github.com/qorelanguage/qore/issues/4557">issue 4557</a>)
1414
- <a href="../../modules/FileDataProvider/html/index.html">FileDataProvider</a> module
1515
- new module
16+
(<a href="https://github.com/qorelanguage/qore/issues/4564">issue 4564</a>)
1617
- <a href="../../modules/FtpDataProvider/html/index.html">FtpDataProvider</a> module
1718
- new module
19+
(<a href="https://github.com/qorelanguage/qore/issues/4564">issue 4564</a>)
1820
- <a href="../../modules/FtpPoller/html/index.html">FtpPoller</a> module
1921
- added support for the event-based DataProvider API
2022
(<a href="https://github.com/qorelanguage/qore/issues/4557">issue 4557</a>)
2123
- <a href="../../modules/FilePoller/html/index.html">FilePoller</a> module
2224
- added support for the event-based DataProvider API
2325
(<a href="https://github.com/qorelanguage/qore/issues/4557">issue 4557</a>)
26+
- <a href="../../modules/WebSocketClient/html/index.html">WebSocketClient</a> module
27+
- added support for the event-based DataProvider API
28+
(<a href="https://github.com/qorelanguage/qore/issues/4557">issue 4557</a>)
2429

2530
@subsection qore_1_10_0_bug_fixes Bug Fixes in Qore
2631
- <a href="../../modules/FsUtil/html/index.html">FsUtil</a> module

examples/test/qlib/FileDataProvider/FileDataProvider.qtest

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ public class FileDataProviderTest inherits QUnit::Test {
2626

2727
fileDataProviderTest() {
2828
# create test file
29-
TmpFile tmp("FileDataProviderTest-");
29+
TmpDir tmpdir("FileDataProviderTest-");
30+
TmpFile tmp("FileDataProviderTest-", NOTHING, tmpdir.path);
3031
string str = get_random_string();
3132
tmp.file.write(str);
3233

@@ -40,7 +41,7 @@ public class FileDataProviderTest inherits QUnit::Test {
4041
assertEq("REGULAR", h.type);
4142

4243
prov = fdp.getChildProviderEx("copy");
43-
string targ = join_paths(tmp_location(), get_random_string());
44+
string targ = join_paths(tmpdir.path, get_random_string());
4445
h = prov.doRequest({
4546
"source": tmp.path,
4647
"target": targ,
@@ -49,7 +50,7 @@ public class FileDataProviderTest inherits QUnit::Test {
4950
assertTrue(is_file(targ));
5051

5152
prov = fdp.getChildProviderEx("move");
52-
string new_targ = join_paths(tmp_location(), get_random_string());
53+
string new_targ = join_paths(tmpdir.path, get_random_string());
5354
assertNeq(new_targ, targ);
5455
h = prov.doRequest({
5556
"source": targ,
@@ -84,8 +85,22 @@ public class FileDataProviderTest inherits QUnit::Test {
8485

8586
prov = fdp.getChildProviderEx("list");
8687
h = prov.doRequest({
87-
"path": dirname(tmp.path),
88+
"path": tmpdir.path,
8889
})[0];
8990
assertEq(basename(new_targ), h.name);
91+
92+
string dir = get_random_string();
93+
prov = fdp.getChildProviderEx("mkdir");
94+
h = prov.doRequest({
95+
"path": dir,
96+
});
97+
assertEq(dir, h.path);
98+
99+
prov = fdp.getChildProviderEx("stat");
100+
h = prov.doRequest({
101+
"path": dir,
102+
});
103+
assertEq(dir, h.name);
104+
assertEq("DIRECTORY", h.type);
90105
}
91106
}

examples/test/qlib/FtpDataProvider/FtpDataProvider.qtest

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public class FtpDataProviderTest inherits QUnit::Test {
6868
"size": 15,
6969
}, h - "mtime");
7070

71-
/* test server doesn't support RNFR yet
71+
/* test server doesn't support RNFR / RNTO yet
7272
string newpath = "/test1.txt";
7373
prov = root.getChildProviderEx("move");
7474
h = prov.doRequest({
@@ -89,6 +89,19 @@ public class FtpDataProviderTest inherits QUnit::Test {
8989
assertThrows("FTP-STAT-ERROR", \prov.doRequest(), {
9090
"path": path,
9191
});
92+
93+
string dir = get_random_string();
94+
prov = root.getChildProviderEx("mkdir");
95+
h = prov.doRequest({
96+
"path": dir,
97+
});
98+
assertEq("/" + dir, h.path);
99+
100+
prov = root.getChildProviderEx("stat");
101+
h = prov.doRequest({
102+
"path": dir,
103+
});
104+
assertEq("DIRECTORY", h.type);
92105
}
93106

94107
private softint getPort() {

examples/test/qlib/FtpPoller/FtpPoller.qtest

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ class FtpPollerTest inherits QUnit::Test {
100100

101101
# printf("files: %y\n", file_paths);
102102

103-
MyFtpPoller poller(ftp_client, {"path": dir});
103+
MyFtpPoller poller(ftp_client, {"path": tmp_dir.path});
104104
assertTrue(poller.runOnce());
105105

106106
foreach hash<FtpPollerFileEventInfo> file in (poller.files) {
107+
on_error printf("file: %y file_paths: %y\n", file - "data", keys file_paths);
107108
assertTrue(file.name != NOTHING);
108109
assertTrue(file_paths.hasKey(file.filepath));
109110
}

examples/test/qlib/WebSocketClient/WebSocketClient.qtest

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
class MyWsConnection inherits WebSocketConnection {
2222
constructor(WebSocketHandler handler) : WebSocketConnection(handler) {
23+
}
24+
25+
sendData() {
2326
map send($1), WebSocketClientTest::Data;
2427
}
2528

@@ -34,8 +37,12 @@ class MyWsHandler inherits WebSocketHandler {
3437
bool debug = False;
3538
}
3639

37-
WebSocketConnection getConnectionImpl(hash cx, hash hdr, string cid) {
38-
return new MyWsConnection(self);
40+
WebSocketConnection getConnectionImpl(hash<auto> cx, hash<auto> hdr, string cid) {
41+
MyWsConnection conn(self);
42+
if (hdr."send-data") {
43+
conn.sendData();
44+
}
45+
return conn;
3946
}
4047

4148
logError(string fmt) {
@@ -64,6 +71,8 @@ class WebSocketClientTest inherits QUnit::Test {
6471
int port;
6572
int responseSize;
6673

74+
Logger logger;
75+
6776
const Data = (
6877
"test1",
6978
<beeffeed>,
@@ -72,10 +81,11 @@ class WebSocketClientTest inherits QUnit::Test {
7281
}
7382

7483
constructor() : Test("WebSocketClientTest", "1.0") {
84+
addTestCase("data provider test", \dataProviderTest());
7585
addTestCase("WebSocketClient tests", \webSocketClientTests());
7686
addTestCase("connection tests", \connectionTests());
7787

78-
Logger logger("test", LoggerLevel::getLevelInfo());
88+
logger = new Logger("test", LoggerLevel::getLevelInfo());
7989
if (m_options.verbose > 2) {
8090
logger.addAppender(new TestAppender());
8191
}
@@ -93,6 +103,21 @@ class WebSocketClientTest inherits QUnit::Test {
93103
mServer.stop();
94104
}
95105

106+
dataProviderTest() {
107+
MyObserver observer();
108+
WebSocketClientDelayedDataProvider prov({
109+
"url": "ws://localhost:" + port,
110+
});
111+
prov.setLogger(logger);
112+
prov.registerObserver(observer);
113+
prov.observersReady();
114+
115+
prov.sendMessage(MESSAGE_WS_PING, "hb");
116+
117+
observer.wait();
118+
assertTrue(True);
119+
}
120+
96121
webSocketClientTests() {
97122
list<auto> l = ();
98123
Counter c(1);
@@ -118,15 +143,15 @@ class WebSocketClientTest inherits QUnit::Test {
118143
"debuglog": log("DEBUG"),
119144
};
120145
WebSocketClient wsc(cb, opts);
121-
wsc.connect();
146+
wsc.connect({"hdr": {"Send-Data": 1}});
122147
c.waitForZero();
123148
assertEq(Data, l);
124149
c.inc();
125150
l = ();
126151
responseSize = 1;
127152
# issue #2566: send a unidirectional PONG to make sure that the WebSocketHandler can handle it
128153
# https://tools.ietf.org/html/rfc6455#section-5.5.3: unsolicited PONG messages must be ignored
129-
wsc.pong();
154+
wsc.pong("heartbeat");
130155
wsc.send("REQUEST");
131156
c.waitForZero();
132157
assertEq(list("RESPONSE:REQUEST"), l);
@@ -164,3 +189,43 @@ class TestAppender inherits LoggerAppenderWithLayout {
164189
}
165190
}
166191
}
192+
193+
class MyObserver inherits Observer {
194+
public {
195+
list<hash<auto>> l();
196+
}
197+
198+
private {
199+
Mutex m();
200+
Condition cond();
201+
int waiting;
202+
}
203+
204+
update(string event_id, hash<auto> data_) {
205+
m.lock();
206+
on_exit m.unlock();
207+
208+
l += {
209+
"id": event_id,
210+
"data": data_,
211+
};
212+
213+
if (waiting) {
214+
cond.broadcast();
215+
}
216+
}
217+
218+
wait() {
219+
date start = now_us();
220+
221+
m.lock();
222+
on_exit m.unlock();
223+
224+
while (!l) {
225+
cond.wait(m, 1s);
226+
if ((now_us() - start) > 5s) {
227+
throw "ERROR", "no data received in timeout";
228+
}
229+
}
230+
}
231+
}

examples/test/qore/classes/FtpServer.qc

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -283,35 +283,43 @@ class FtpServer {
283283
}
284284

285285
if (cmd =~ /^PWD/i) {
286-
ftpSend(ns, 257, pwd);
286+
ftpSend(ns, 257, pwd ?? "/");
287287
continue;
288288
}
289289

290290
if (cmd =~ /^CWD/i) {
291291
string dir = (cmd =~ x/^CWD (.+)$/i)[0];
292-
293-
*string path = t ? t.path : pwd;
294-
path += DirSep + dir;
292+
string path = getPath(dir);
295293

296294
if (!is_dir(path)) {
297-
ftpSend(ns, 550, "Failed to change directory to " + dir);
295+
ftpSend(ns, 550, "Failed to change directory to %y: %s", path, strerror());
298296
} else {
299297
ftpSend(ns, 250, "Okay.");
300298
pwd = dir;
301299
}
302300
continue;
303301
}
304302

303+
if (cmd =~ /^MKD/i) {
304+
string dir = (cmd =~ x/^MKD (.+)$/i)[0];
305+
string path = getPath(dir);
306+
307+
if (mkdir(path, 0755, True)) {
308+
ftpSend(ns, 550, "Failed to make directory " + dir);
309+
} else {
310+
ftpSend(ns, 250, "Okay.");
311+
}
312+
continue;
313+
}
314+
305315
if (cmd =~ /^MDTM/i) {
306316
string file_name = (cmd =~ x/^MDTM (.+)$/i)[0];
307317
# printf("FILE_NAME: %y\n", file_name);
308-
309-
*string path = t ? t.path : pwd;
310-
path += DirSep + file_name;
318+
string path = getPath(file_name);
311319

312320
*hash<StatInfo> info = hstat(path);
313321
if (!info) {
314-
ftpSend(ns, 550, "Failed to retrieve modified date for " + file_name);
322+
ftpSend(ns, 550, "Failed to retrieve modified date for %s: %s", path, strerror());
315323
} else {
316324
ftpSend(ns, 250, info.mtime.toString());
317325
}
@@ -321,9 +329,7 @@ class FtpServer {
321329
if (cmd =~ /^SIZE/i) {
322330
string file_name = (cmd =~ x/^SIZE (.+)$/i)[0];
323331
# printf("FILE_NAME: %y\n", file_name);
324-
325-
*string path = t ? t.path : pwd;
326-
path += DirSep + file_name;
332+
string path = getPath(file_name);
327333

328334
*hash<StatInfo> info = hstat(path);
329335
if (!info) {
@@ -337,9 +343,7 @@ class FtpServer {
337343
if (cmd =~ /^DELE/i) {
338344
string file_name = (cmd =~ x/^DELE (.+)$/i)[0];
339345
# printf("FILE_NAME: %y\n", file_name);
340-
341-
*string path = t ? t.path : pwd;
342-
path += DirSep + file_name;
346+
string path = getPath(file_name);
343347

344348
int rc = unlink(path);
345349
if (rc) {
@@ -357,6 +361,14 @@ class FtpServer {
357361
termWaitData(data, dcnt, \dataquit);
358362
}
359363

364+
#! Returns the target path
365+
private string getPath(string p) {
366+
if (absolute_path(p)) {
367+
return t ? t.path + DirSep + p : p;
368+
}
369+
return (t ? t.path : pwd) + DirSep + p;
370+
}
371+
360372
private bool doBroken(string cmd, Socket data) {
361373
if (broken{cmd}) {
362374
usleep(broken{cmd});

0 commit comments

Comments
 (0)