How to upload a file using an iOS shortcut and receive it using a Deno web server?

1.3k views Asked by At

I have an http server written in deno like this

import { serve } from "https://deno.land/[email protected]/http/server.ts";

serve((_: Request) => {
  // do something with request
  // ...

  return new Response("hello", {
    status: 200,
    headers: new Headers({
      "content-type": "text/plain",
    }),
  });
});

I'm trying to upload an image to this server with an iOS shortcut (Get Content of URL). If I debug the server I can see the request come in with the image and I make it to response but the shortcut hangs and then errors with a timeout. It works if I use postman or if I simply change the content of the shortcut to json instead of an image. What could be the issue here? Is iOS expecting some special header I'm not aware of?

Here is a link to the shortcut. Replace the ip address with your machines ip address first https://www.icloud.com/shortcuts/b2de83c34e0448c081b0b38ba79bbc7a

Here is some code for nodejs that does not have the same issue.


const http = require('http');

const hostname = '0.0.0.0';
const port = 8000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

Machine info


10:20:11> lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

10:20:18> uname -r
5.15.0-60-generic

10:20:19> deno --version
deno 1.31.0 (release, x86_64-unknown-linux-gnu)
v8 11.0.226.13
typescript 4.9.4

1

There are 1 answers

6
jsejcksn On

It's not clear at which stage the problem occurs because your question doesn't include reproduction steps — so, in order to help you reproduce success, I'll provide a complete series of reproduction steps with everything needed:

  1. Create the following module file — it is an example of a stateful server which accepts a single uploaded file (POST) and responds with a file (GET) at the /file path. It expects a single uploaded file to be encoded as a FormData item:

server.ts:

// deno run --allow-net=0.0.0.0:8000 server.ts

import { serve } from "https://deno.land/[email protected]/http/server.ts";
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
import {
  Status,
  STATUS_TEXT,
} from "https://deno.land/[email protected]/http/http_status.ts";

function logFileInfo(file: File): void {
  console.log("\nFILE:", {
    name: file.name,
    type: file.type,
    size: file.size,
    lastModified: new Date(file.lastModified),
  });
}

function logRequestInfo(request: Request): void {
  console.log(`\n${request.method} ${request.url}`);
  console.log(`Headers:\n${
    [...request.headers]
      .map(([k, v]) => `  ${k}: ${v}`)
      .sort()
      .join("\n")
  }`);
}

function createResponseStatusInit(
  status: Status,
): Pick<ResponseInit, "status" | "statusText"> {
  return { status, statusText: STATUS_TEXT[status] };
}

async function getFileFromRequest(request: Request): Promise<File> {
  const fd = await request.formData();
  const entries = [...fd];
  // Assert only one uploaded file:
  assert(entries.length === 1);
  const [, file] = entries[0]!;
  // Assert that it's actually a file:
  assert(file instanceof File);
  return file;
}

async function handleFileUpload(request: Request): Promise<Response> {
  try {
    const file = await getFileFromRequest(request);
    state.file = file;
    logFileInfo(file);

    return new Response(
      `File received: ${JSON.stringify(file.name)}`,
      { headers: new Headers([["Content-Type", "text/plain"]]) },
    );
  } catch (ex) {
    console.error(ex);
    return new Response(null, createResponseStatusInit(Status.BadRequest));
  }
}

function handleFileDownload(): Response {
  const { file } = state;

  const headers = new Headers([
    ["Content-Type", file.type],
    ["Content-Length", String(file.size)],
  ]);

  return new Response(file, { headers });
}

const state = {
  file: new File([], "default.txt", {
    lastModified: Date.now(),
    type: "text/plain",
  }),
};

await serve(
  (request) => {
    logRequestInfo(request);

    const url = new URL(request.url);

    if (url.pathname === "/file") {
      switch (request.method) {
        case "GET":
          return handleFileDownload();
        case "POST":
          return handleFileUpload(request);
      }
    }

    return new Response(null, createResponseStatusInit(Status.BadRequest));
  },
  { port: 8000 },
);

  1. Run it in your terminal:
% deno --version
deno 1.31.1 (release, aarch64-apple-darwin)
v8 11.0.226.13
typescript 4.9.4

% deno run --allow-net=0.0.0.0:8000 server.ts
Listening on http://localhost:8000/
  1. Open a private browsing window/tab in your broswer (I'm using Chrome) and navigate to the server URL: http://localhost:8000/file

I see a blank page in my browser (expected) and these outputs in my terminal as a result:

GET http://localhost:8000/file
Headers:
  accept-encoding: gzip, deflate, br
  accept-language: en-US,en;q=0.9
  accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
  connection: keep-alive
  host: localhost:8000
  sec-ch-ua-mobile: ?0
  sec-ch-ua-platform: "macOS"
  sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"
  sec-fetch-dest: document
  sec-fetch-mode: navigate
  sec-fetch-site: none
  sec-fetch-user: ?1
  upgrade-insecure-requests: 1
  user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36

GET http://localhost:8000/favicon.ico
Headers:
  accept-encoding: gzip, deflate, br
  accept-language: en-US,en;q=0.9
  accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
  connection: keep-alive
  host: localhost:8000
  referer: http://localhost:8000/file
  sec-ch-ua-mobile: ?0
  sec-ch-ua-platform: "macOS"
  sec-ch-ua: "Chromium";v="110", "Not A(Brand";v="24", "Google Chrome";v="110"
  sec-fetch-dest: image
  sec-fetch-mode: no-cors
  sec-fetch-site: same-origin
  user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36
  1. The following is a data URL which represents a zip-compressed Shortcut (because SO doesn't allow uploading files). Copy + paste it into a new tab to download it and add it to your Shortcuts on your iOS device:
data:application/zip;base64,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

This screenshot depicts the details of the shortcut in case you don't want to download the data file or you would like to re-create it from scrach:

shortcut details

  1. After installing/creating the shortcut, open this page on your iOS device and take a screenshot of it. This will save an image to your device's photos app.

  2. Open the photos app and find the screenshot from the previous step, and share the image (this opens the share sheet).

  3. Find the share sheet item that corresponds to the shortcut (if you installed it, the share item will be named so_75583257), and tap that item in the list.

  4. The shortcut will run. You might encounter a permission request to allow connecting to the server address that you defined when importing/creating the shortcut — accept the permission request. After a short time, you should see the shortcut complete — it will look something like this:

shortcut result

Looking back to the terminal, you should also see some new output similar to the following:

POST http://192.168.1.10:8000/file
Headers:
  accept-encoding: gzip, deflate
  accept-language: en-US,en;q=0.9
  accept: */*
  connection: keep-alive
  content-length: 870965
  content-type: multipart/form-data; boundary=4EDDBE49-3BB0-468A-93DF-DCD9D13A5E02-8189-0000078E118034CD
  host: 192.168.1.10:8000
  user-agent: BackgroundShortcutRunner/1355.1 CFNetwork/1404.0.5 Darwin/22.3.0

FILE: {
  name: "IMG_3870.jpeg",
  type: "image/jpeg",
  size: 870734,
  lastModified: 2023-02-28T01:15:38.962Z
}
  1. Return to your initial browser tab from step 3 and refresh the page. You should now see the screenshot image that you uploaded from your iOS device and more ouptut in the terminal:
GET http://localhost:8000/file
<identical to previous>

GET http://localhost:8000/favicon.ico
<identical to previous>
^C

At this point, you can terminate the deno process in your terminal using ctrl + c.


After following the steps above, I'm confident that you'll be able to determine the cause of your issue. If anything is unclear, feel free to leave a comment on this answer.


Update in response to your comment:

Do you know a way to get it to work with a file body instead of form data?

FormData is the correct mechanism for sending one or more files. Otherwise, you'll have to invent your own encoding and decoding schema — a "file" body is just an unstructured payload of binary data. One way to get binary data from such a request is to use the method Request.blob().

Here's an example of applying the information above to modify the getFileFromRequest function:

async function getFileFromRequest(request: Request): Promise<File> {
  // Transmission of file metadata is something that FormData handles for you,
  // but when dealing with transmission of arbitrary binary data,
  // you'll have to manually encode and send the file metadata in HTTP headers,
  // then decode and validate them here:
  const fileMeta: FilePropertyBag = {};

  // Shortcuts seems to send this one for you:
  const type = request.headers.get("Content-Type");
  if (type) fileMeta.type = type;

  // This becomes your responsibility:
  fileMeta.lastModified = Date.now();
  // As does this:
  const fileName = "unknown";

  const file = new File([await request.blob()], fileName, fileMeta);
  return file;
}