-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: IPFS retrieval client #243
Changes from 14 commits
2bce5d3
1a30f47
5be5140
884f05a
93f2c31
f74672b
31a5b8c
3c5826a
f810edd
7e4d990
d6f4cdf
460b94d
19809a5
66b5837
ed48be7
12d2c7f
3078d7f
75f8239
00e396d
78f629a
7db4ee8
371b24a
b2e1cca
831b954
6093b21
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,8 @@ deno_fetch = "0.129.0" | |
deno_url = "0.105.0" | ||
deno_web = "0.136.0" | ||
deno_webidl = "0.105.0" | ||
lassie = "0.3.0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neat. I didn't know there is a Rust client! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a thin Rust wrapper embedding the original Go Lassie, I started the project three weeks ago :) https://github.com/filecoin-station/rusty-lassie |
||
# lassie = { git = "https://github.com/filecoin-station/rusty-lassie.git" } | ||
log.workspace = true | ||
once_cell = "1.18.0" | ||
serde.workspace = true | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { fetch as fetchImpl } from "ext:deno_fetch/26_fetch.js"; | ||
import { fromInnerResponse, toInnerResponse } from "ext:deno_fetch/23_response.js"; | ||
import { toInnerRequest, fromInnerRequest, Request } from "ext:deno_fetch/23_request.js"; | ||
import { guardFromHeaders } from "ext:deno_fetch/20_headers.js"; | ||
|
||
let ipfsScheme = "ipfs://"; | ||
bajtos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let ipfsBaseUrl = undefined; | ||
|
||
export function setLassieUrl(/** @type {string} */ value) { | ||
ipfsBaseUrl = value + "ipfs/"; | ||
} | ||
|
||
export function fetch(resource, options) { | ||
let request = new Request(resource, options); | ||
// Fortunately, Request#url is a string, not an instance of URL class | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is fortunate about that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great question! (The original answer got lost in the git history.) The
If What's fortunate: the conversion from "resource in one of the many supported formats" to "resource URL as a string" is already handled by the Request constructor. Can you suggest how to improve my code comment to make this matter easier to understand for future readers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, gotcha! What do you think about
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I decided to write a longer comment, see 7db4ee8 // The `resource` arg can be a string or any other object with a stringifier — including a URL
// object — that provides the URL of the resource you want to fetch; or a Request object.
// See https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters
// Fortunately, Request's constructor handles the conversions, and Request#url is always a string.
// See https://developer.mozilla.org/en-US/docs/Web/API/Request/url |
||
// See https://developer.mozilla.org/en-US/docs/Web/API/Request/url | ||
if (request.url.startsWith(ipfsScheme)) { | ||
return fetchFromIpfs(request); | ||
} else { | ||
return fetchImpl(request); | ||
} | ||
} | ||
|
||
async function fetchFromIpfs(request) { | ||
// Rewrite request URL to use Lassie | ||
request = buildIpfsRequest(request); | ||
|
||
// Call Deno's `fetch` using the rewritten URL to make the actual HTTP request | ||
const response = await fetchImpl(request); | ||
|
||
// Patch the response object to hide the fact that we are calling Lassie | ||
// We don't want to leak Lassie's URL | ||
return patchIpfsResponse(response); | ||
} | ||
|
||
// Deno's Fetch Request is a thin immutable wrapper around InnerRequest. In order to modify the | ||
// request URL, we must convert Request to InnerRequest first, make changes on the inner object, | ||
// and finally convert the InnerRequest back to a new Request instance. | ||
function buildIpfsRequest(request) { | ||
const inner = toInnerRequest(request); | ||
|
||
inner.urlList = /** @type {(() => string)[]}*/ (inner.urlList).map((urlFn) => { | ||
const url = urlFn(); | ||
if (!url.startsWith(ipfsScheme)) return urlFn; | ||
const newUrl = ipfsBaseUrl + url.slice(ipfsScheme.length); | ||
return () => newUrl; | ||
}); | ||
inner.urlListProcessed = /** @type {string[]} */ (inner.urlListProcessed).map((url) => | ||
url.startsWith(ipfsScheme) ? ipfsBaseUrl + url.slice(ipfsScheme.length) : url, | ||
); | ||
|
||
return fromInnerRequest(inner, request.signal, guardFromHeaders(request.headers)); | ||
} | ||
|
||
// Deno's Fetch Response is a thin immutable wrapper around InnerResponse. In order to modify the | ||
// response URL, we must convert Response to InnerResponse first, make changes on the inner object, | ||
// and finally convert the InnerResponse back to a new Response instance. | ||
function patchIpfsResponse(response) { | ||
const inner = toInnerResponse(response); | ||
|
||
inner.urlList = /** @type {string[])} */ (inner.urlList).map((url) => | ||
url.startsWith(ipfsBaseUrl) ? "ipfs://" + url.slice(ipfsBaseUrl.length) : url, | ||
); | ||
|
||
return fromInnerResponse(inner, guardFromHeaders(response.headers)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,4 +18,6 @@ mod reporter; | |
pub use console_reporter::*; | ||
pub use reporter::*; | ||
|
||
pub use lassie; | ||
|
||
mod ext; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
use std::sync::{Arc, OnceLock}; | ||
|
||
pub fn lassie_daemon() -> Arc<lassie::Daemon> { | ||
static LASSIE_DAEMON: OnceLock<Result<Arc<lassie::Daemon>, lassie::StartError>> = | ||
OnceLock::new(); | ||
|
||
let result = LASSIE_DAEMON | ||
.get_or_init(|| lassie::Daemon::start(lassie::DaemonConfig::default()).map(Arc::new)); | ||
|
||
match result { | ||
Ok(ptr) => Arc::clone(ptr), | ||
Err(err) => panic!("could not start Lassie daemon: {err}"), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Station Core will pass
$CACHE_ROOT
:https://github.com/filecoin-station/core/blob/7f07e5203c71366fa9bde713b0d28dee9ea0c51d/lib/zinnia.js#L48-L55.
Can we make Zinnia use that if it is set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am already using CACHE_ROOT in
zinniad
, see here:https://github.com/filecoin-station/zinnia/pull/243/files#diff-c74dac62db9c80f1be22978c93249f7b304e05ddb38131e7969efa13effaeb1eR45
The file
cli/main.js
implementszinnia
, the CLI people use locally when building Station modules. Let's explore together what a good developer experience would look like?IMO:
zinnia
users to always provideCACHE_ROOT
. We don't ask them forFIL_WALLET_ADDRESS
either. This way, users can typezinnia run main.js
, and all works out of the box.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll be implementing this cleanup in
zinnia
as part of #245There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't suggesting always needing to pass
CACHE_ROOT
, I thought if it's not passed then lassie shall pick its own temp dir, if it is passed (as in Station Core) it shall use that, just to keep all of the files together.The primary use case for changing lassie's temp dir to me is not CLI usage but inside Station.
That's a great point! What does Lassie use by default rn? If it uses an OS cleaned up dir, I'd suggest to leave it at that and add a comment summarizing our discussion here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quoting from Lassie comments:
https://github.com/filecoin-project/lassie/blob/afc2ee5a4bc6f5e22ef2cc69396cc9b25f57b854/pkg/lassie/lassie.go#L199-L201
I think that should be good enough for now, even if we may end up leaving some temporary files behind when
zinnia
exists unexpectedly.