mirror of
https://github.com/ading2210/libcurl.js.git
synced 2025-05-13 14:30:02 -04:00
ftp works but it blocks the thread
This commit is contained in:
parent
0a5ace96fb
commit
2a072ecee0
12 changed files with 165 additions and 184 deletions
|
@ -1,44 +1,55 @@
|
|||
//unfinished!
|
||||
|
||||
class FTPSession {
|
||||
class FTPSession extends CurlSession {
|
||||
constructor(url, options={}) {
|
||||
if (!url.startsWith("ftp://") || !url.startsWith("ftps://")) {
|
||||
if (!url.startsWith("ftp://") && !url.startsWith("ftps://")) {
|
||||
throw "invalid url protocol";
|
||||
}
|
||||
super();
|
||||
|
||||
this.url = url;
|
||||
this.cwd = new URL(url).pathname;
|
||||
this.options = options;
|
||||
this.http_handle = null;
|
||||
}
|
||||
|
||||
do_request(url) {
|
||||
send_cmd(cmd) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let http_handle;
|
||||
let data_callback = (data) => {this.data_callback(data)};
|
||||
let finish_callback = (error) => {
|
||||
_cleanup_handle(http_handle);
|
||||
if (error) {
|
||||
reject();
|
||||
}
|
||||
else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
let headers_callback = () => {this.headers_callback()};
|
||||
let request_ptr;
|
||||
let chunks = [];
|
||||
|
||||
http_handle = create_request(url, data_callback, finish_callback, headers_callback);
|
||||
_ftp_set_options(http_handle, url, 1);
|
||||
start_request(http_handle);
|
||||
let data_callback = () => {};
|
||||
let finish_callback = (error) => {
|
||||
this.remove_request(request_ptr);
|
||||
if (error) {
|
||||
reject(`Sending FTP command failed with error ${error}: ${get_error_str(error)}`);
|
||||
}
|
||||
}
|
||||
let headers_callback = (chunk) => {
|
||||
chunks.push(chunk);
|
||||
console.log(chunk);
|
||||
}
|
||||
|
||||
request_ptr = this.create_request(this.url, data_callback, finish_callback, headers_callback);
|
||||
c_func(_ftp_set_cmd, [request_ptr, cmd]);
|
||||
});
|
||||
}
|
||||
|
||||
async download(path) {
|
||||
let url = new URL(path, this.url);
|
||||
_ftp_set_options(this.http_handle, url, 0);
|
||||
}
|
||||
download(path) {
|
||||
let url = new URL(path, this.url).href;
|
||||
console.log(url);
|
||||
|
||||
cleanup() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request_ptr;
|
||||
let finish_callback = (error) => {
|
||||
this.remove_request(request_ptr);
|
||||
if (error) {
|
||||
reject(`FTP request failed with error ${error}: ${get_error_str(error)}`);
|
||||
}
|
||||
};
|
||||
let headers_callback = (stream) => {
|
||||
resolve(stream);
|
||||
};
|
||||
|
||||
request_ptr = this.stream_response(url, headers_callback, finish_callback);
|
||||
_ftp_set_options(request_ptr);
|
||||
this.start_request(request_ptr);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,72 +1,42 @@
|
|||
class HTTPSession extends CurlSession {
|
||||
constructor() {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.options = options;
|
||||
this.set_connections(50, 40);
|
||||
}
|
||||
|
||||
request_async(url, params, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stream_controller;
|
||||
let http_handle;
|
||||
let response_obj;
|
||||
let aborted = false;
|
||||
|
||||
//handle abort signals
|
||||
if (params.signal instanceof AbortSignal) {
|
||||
params.signal.addEventListener("abort", () => {
|
||||
if (aborted) return;
|
||||
aborted = true;
|
||||
_cleanup_handle(http_handle);
|
||||
if (!response_obj) {
|
||||
reject(new DOMException("The operation was aborted."));
|
||||
}
|
||||
else {
|
||||
stream_controller.error("The operation was aborted.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let stream = new ReadableStream({
|
||||
start(controller) {
|
||||
stream_controller = controller;
|
||||
}
|
||||
});
|
||||
|
||||
let data_callback = (new_data) => {
|
||||
try {
|
||||
stream_controller.enqueue(new_data);
|
||||
}
|
||||
catch (e) {
|
||||
//the readable stream has been closed elsewhere, so cancel the request
|
||||
if (e instanceof TypeError) {
|
||||
_cleanup_handle(http_handle);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
let headers_callback = () => {
|
||||
let headers_callback = (stream) => {
|
||||
let response_json = c_func_str(_http_get_info, [http_handle]);
|
||||
response_obj = this.constructor.create_response(stream, JSON.parse(response_json));
|
||||
resolve(response_obj);
|
||||
let response = this.constructor.create_response(stream, JSON.parse(response_json));
|
||||
|
||||
if (params.redirect === "error" && response.status >= 300 && response.status < 400) {
|
||||
finish_callback(-2);
|
||||
return;
|
||||
}
|
||||
resolve(response);
|
||||
}
|
||||
let finish_callback = (error) => {
|
||||
if (error != 0) {
|
||||
if (error > 0) {
|
||||
error_msg(`Request "${url}" failed with error code ${error}: ${get_error_str(error)}`);
|
||||
reject(`Request failed with error code ${error}: ${get_error_str(error)}`);
|
||||
}
|
||||
try {
|
||||
stream_controller.close();
|
||||
} //this will only fail if the stream is already errored or closed, which isn't a problem
|
||||
catch {}
|
||||
else if (error === -1) {
|
||||
reject(new DOMException("The operation was aborted."));
|
||||
}
|
||||
else if (error === -2) {
|
||||
reject("Request failed because redirects were disallowed.");
|
||||
}
|
||||
this.remove_request(http_handle);
|
||||
}
|
||||
|
||||
let body_length = body ? body.length : 0;
|
||||
let params_json = JSON.stringify(params);
|
||||
|
||||
http_handle = this.create_request(url, data_callback, finish_callback, headers_callback);
|
||||
http_handle = this.stream_response(url, headers_callback, finish_callback, params.signal);
|
||||
c_func(_http_set_options, [http_handle, params_json, body, body_length]);
|
||||
this.start_request(http_handle);
|
||||
});
|
||||
|
|
|
@ -41,82 +41,6 @@ function check_loaded(check_websocket) {
|
|||
throw new Error("websocket proxy url not set, please call libcurl.set_websocket");
|
||||
}
|
||||
}
|
||||
|
||||
//wrap perform_request in a promise
|
||||
function perform_request_async(url, params, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let stream_controller;
|
||||
let http_handle;
|
||||
let response_obj;
|
||||
let aborted = false;
|
||||
|
||||
//handle abort signals
|
||||
if (params.signal instanceof AbortSignal) {
|
||||
params.signal.addEventListener("abort", () => {
|
||||
if (aborted) return;
|
||||
aborted = true;
|
||||
_cleanup_handle(http_handle);
|
||||
if (!response_obj) {
|
||||
reject(new DOMException("The operation was aborted."));
|
||||
}
|
||||
else {
|
||||
stream_controller.error("The operation was aborted.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let stream = new ReadableStream({
|
||||
start(controller) {
|
||||
stream_controller = controller;
|
||||
}
|
||||
});
|
||||
|
||||
function data_callback(new_data) {
|
||||
try {
|
||||
stream_controller.enqueue(new_data);
|
||||
}
|
||||
catch (e) {
|
||||
//the readable stream has been closed elsewhere, so cancel the request
|
||||
if (e instanceof TypeError) {
|
||||
_cleanup_handle(http_handle);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
function headers_callback() {
|
||||
let response_json = c_func_str(_http_get_info, [http_handle]);
|
||||
response_obj = create_response(stream, JSON.parse(response_json));
|
||||
resolve(response_obj);
|
||||
}
|
||||
function finish_callback(error) {
|
||||
if (error != 0) {
|
||||
error_msg(`Request "${url}" failed with error code ${error}: ${get_error_str(error)}`);
|
||||
reject(`Request failed with error code ${error}: ${get_error_str(error)}`);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stream_controller.close();
|
||||
} //this will only fail if the stream is already errored or closed, which isn't a problem
|
||||
catch {}
|
||||
}
|
||||
|
||||
let body_length = body ? body.length : 0;
|
||||
let params_json = JSON.stringify(params);
|
||||
|
||||
http_handle = create_request(url, data_callback, finish_callback, headers_callback);
|
||||
c_func(_http_set_options, [http_handle, params_json, body, body_length]);
|
||||
start_request(http_handle);
|
||||
});
|
||||
}
|
||||
|
||||
async function libcurl_fetch(url, params={}) {
|
||||
check_loaded(true);
|
||||
let body = await create_options(params);
|
||||
return await perform_request_async(url, params, body);
|
||||
}
|
||||
|
||||
function set_websocket_url(url) {
|
||||
websocket_url = url;
|
||||
if (Module.websocket) {
|
||||
|
@ -177,7 +101,9 @@ api = {
|
|||
WebSocket: WebSocket,
|
||||
CurlWebSocket: CurlWebSocket,
|
||||
TLSSocket: TLSSocket,
|
||||
fetch: () => {throw "not ready"},
|
||||
HTTPSession: HTTPSession,
|
||||
FTPSession: FTPSession,
|
||||
fetch() {throw "not ready"},
|
||||
|
||||
get copyright() {return copyright_notice},
|
||||
get version() {return get_version()},
|
||||
|
|
|
@ -29,7 +29,6 @@ class CurlSession {
|
|||
let end_callback = (error) => {
|
||||
Module.removeFunction(end_callback_ptr);
|
||||
Module.removeFunction(data_callback_ptr);
|
||||
Module.removeFunction(headers_callback_ptr);
|
||||
|
||||
this.active_requests--;
|
||||
js_end_callback(error);
|
||||
|
@ -41,12 +40,14 @@ class CurlSession {
|
|||
js_data_callback(chunk);
|
||||
}
|
||||
|
||||
let headers_callback = () => {
|
||||
js_headers_callback();
|
||||
let headers_callback = (chunk_ptr, chunk_size) => {
|
||||
let data = Module.HEAPU8.subarray(chunk_ptr, chunk_ptr + chunk_size);
|
||||
let chunk = new Uint8Array(data);
|
||||
js_headers_callback(chunk);
|
||||
}
|
||||
|
||||
end_callback_ptr = Module.addFunction(end_callback, "vi");
|
||||
headers_callback_ptr = Module.addFunction(headers_callback, "v");
|
||||
headers_callback_ptr = Module.addFunction(headers_callback, "vii");
|
||||
data_callback_ptr = Module.addFunction(data_callback, "vii");
|
||||
let request_ptr = c_func(_create_request, [url, data_callback_ptr, end_callback_ptr, headers_callback_ptr]);
|
||||
|
||||
|
@ -78,6 +79,7 @@ class CurlSession {
|
|||
this.event_loop = setInterval(() => {
|
||||
let libcurl_active = _session_get_active(this.session_ptr);
|
||||
if (libcurl_active || this.active_requests) {
|
||||
console.log("test");
|
||||
_session_perform(this.session_ptr);
|
||||
}
|
||||
else {
|
||||
|
@ -95,4 +97,65 @@ class CurlSession {
|
|||
_session_cleanup(this.session_ptr);
|
||||
this.session_ptr = null;
|
||||
}
|
||||
|
||||
//wrap request callbacks using a readable stream and return the new callbacks
|
||||
stream_response(url, headers_callback, end_callback, abort_signal) {
|
||||
let stream_controller;
|
||||
let aborted = false;
|
||||
let headers_received = false;
|
||||
|
||||
let stream = new ReadableStream({
|
||||
start(controller) {
|
||||
stream_controller = controller;
|
||||
}
|
||||
});
|
||||
|
||||
if (abort_signal instanceof AbortSignal) {
|
||||
abort_signal.addEventListener("abort", () => {
|
||||
if (aborted) return;
|
||||
aborted = true;
|
||||
if (headers_received) {
|
||||
stream_controller.error("The operation was aborted.");
|
||||
}
|
||||
real_abort_callback();
|
||||
});
|
||||
}
|
||||
|
||||
let real_data_callback = (new_data) => {
|
||||
if (!headers_received) {
|
||||
headers_received = true;
|
||||
headers_callback(stream);
|
||||
}
|
||||
|
||||
try {
|
||||
stream_controller.enqueue(new_data);
|
||||
}
|
||||
catch (e) {
|
||||
//the readable stream has been closed elsewhere, so cancel the request
|
||||
if (e instanceof TypeError) {
|
||||
finish_callback(-1);
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let real_end_callback = (error) => {
|
||||
if (!headers_received && error === 0) {
|
||||
headers_received = true;
|
||||
headers_callback(stream);
|
||||
}
|
||||
|
||||
if (error != 0) {
|
||||
try {
|
||||
stream_controller.close();
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
end_callback(error);
|
||||
}
|
||||
|
||||
return this.create_request(url, real_data_callback, real_end_callback, () => {});
|
||||
}
|
||||
}
|
|
@ -22,11 +22,8 @@ class TLSSocket extends CurlSession {
|
|||
}
|
||||
|
||||
connect() {
|
||||
let response_info;
|
||||
let data_callback = () => {};
|
||||
let headers_callback = (info) => {
|
||||
response_info = info;
|
||||
}
|
||||
let headers_callback = () => {};
|
||||
let finish_callback = (error) => {
|
||||
if (error === 0) {
|
||||
this.connected = true;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue