1
0

Optimize requests

This commit is contained in:
Zef Hemel 2023-08-08 15:00:18 +02:00
parent 7498cc1ecb
commit b7b666ee1d
4 changed files with 34 additions and 13 deletions

View File

@ -140,9 +140,12 @@ export class HttpSpacePrimitives implements SpacePrimitives {
const res = await this.authenticatedFetch(
`${this.url}/${encodeURI(name)}`,
// This used to use HEAD, but it seems that Safari on iOS is blocking cookies/credentials to be sent along with HEAD requests
// so we'll use GET instead and not use the body. A bit wasteful, but it works.
// so we'll use GET instead with a magic header which the server may or may not use to omit the body.
{
method: "GET",
headers: {
"X-Get-Meta": "true",
},
},
);
if (res.status === 404) {
@ -154,7 +157,10 @@ export class HttpSpacePrimitives implements SpacePrimitives {
private responseToMeta(name: string, res: Response): FileMeta {
return {
name,
size: +res.headers.get("Content-Length")!,
// The server may set a custom X-Content-Length header in case a GET request was sent with X-Get-Meta, in which case the body may be omitted
size: res.headers.has("X-Content-Length")
? +res.headers.get("X-Content-Length")!
: +res.headers.get("Content-Length")!,
contentType: res.headers.get("Content-type")!,
lastModified: +(res.headers.get("X-Last-Modified") || "0"),
perm: (res.headers.get("X-Permission") as "rw" | "ro") || "rw",

View File

@ -257,7 +257,7 @@ export class HttpServer {
"/index.json",
// corsMiddleware,
async ({ request, response }) => {
if (request.headers.get("Accept") === "application/json") {
if (request.headers.has("X-Sync-Mode")) {
// Only handle direct requests for a JSON representation of the file list
response.headers.set("Content-type", "application/json");
response.headers.set("X-Space-Path", this.options.pagesPath);
@ -338,6 +338,12 @@ export class HttpServer {
async ({ params, response, request }) => {
const name = params[0];
console.log("Requested file", name);
if (!request.headers.has("X-Sync-Mode") && name.endsWith(".md")) {
// It can happen that during a sync, authentication expires
console.log("Request was without X-Sync-Mode, redirecting to page");
response.redirect(`/${name.slice(0, -3)}`);
return;
}
if (name.startsWith(".")) {
// Don't expose hidden files
response.status = 404;
@ -372,9 +378,15 @@ export class HttpServer {
return;
}
try {
const fileData = await spacePrimitives.readFile(
name,
);
if (request.headers.has("X-Get-Meta")) {
// Getting meta via GET request
const fileData = await spacePrimitives.getFileMeta(name);
response.status = 200;
this.fileMetaToHeaders(response.headers, fileData);
response.body = "";
return;
}
const fileData = await spacePrimitives.readFile(name);
const lastModifiedHeader = new Date(fileData.meta.lastModified)
.toUTCString();
if (
@ -388,8 +400,8 @@ export class HttpServer {
response.headers.set("Last-Modified", lastModifiedHeader);
response.body = fileData.data;
} catch {
// console.error("Error GETting of file", name, e);
} catch (e: any) {
console.error("Error GETting of file", name, e);
response.status = 404;
response.body = "Not found";
}
@ -454,6 +466,7 @@ export class HttpServer {
);
headers.set("Cache-Control", "no-cache");
headers.set("X-Permission", fileMeta.perm);
headers.set("X-Content-Length", "" + fileMeta.size);
}
stop() {

View File

@ -235,8 +235,7 @@ export class SyncService {
// Not present
}
try {
// This is wasteful, but Netlify (silverbullet.md) doesn't support OPTIONS call (404s) so we'll just fetch the whole file
remoteHash = (await this.remoteSpace!.readFile(name)).meta.lastModified;
remoteHash = (await this.remoteSpace!.getFileMeta(name)).lastModified;
} catch (e: any) {
if (e.message === "Not found") {
// File doesn't exist remotely, that's ok

View File

@ -1,11 +1,14 @@
The server API is relatively small. The client primarily communicates with the server for file “CRUD” (Create, Read, Update, Delete) style operations.
Heres an attempt to document this API:
All API requests from the client will always have the `X-Sync-Mode` header set to `true`. The server may use this fact to distinguish between requests coming from the client and regular e.g. `GET` requests from the browser (through navigation) and redirect appropriately (for instance to the UI URL associated with a specific `.md` file).
* `GET /index.json` (when sent with an `Accept: application/json` request header): will return a full listing of all files in your space including metadata like when the file was last modified, as well as permissions. This is primarily for sync purposes with the client. A request sent without the mentioned `Accept` header will redirect to `/` (to better support authentication layers like [Authelia](https://www.authelia.com/)).
* `GET /*.*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /index.md` you will receive the content of your `index` page. The `GET` response will have a few additional SB-specific headers:
The API:
* `GET /index.json` will return a full listing of all files in your space including metadata like when the file was last modified, as well as permissions. This is primarily used for sync purposes with the client.
* `GET /*.*`: _Reads_ and returns the content of the file at the given path. This means that if you `GET /index.md` you will receive the content of your `index` page. If the the optional `X-Get-Meta` _request header_ is set, the server does not _need to_ return the body of the file (but it can). The `GET` _response_ will have a few additional SB-specific headers:
* `X-Last-Modified` as a UNIX timestamp in ms (as coming from `Data.now()`)
* `X-Permission`: either `rw` or `ro` which will change whether the editor opens in read-only or regular mode.
* (optional) `X-Content-Length`: which will be the same as `Content-Length` except if the request was sent with a `X-Get-Meta` header and the body is not returned (then `Content-Length` will be `0` and `X-Content-Length` will be the size of the file)
* `PUT /*.*`: The same as `GET` except that it takes the body of the request and _writes_ it to a file.
* `DELETE /*.*`: Again the same, except this will _delete_ the given file.
* `GET /.client/*`: Retrieve files implementing the client