├── .gitignore ├── CHANGELOG.md ├── v1.md ├── README-zh-CN.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to the protocol will be documented in this file. 4 | 5 | ## 2.1 6 | 7 | - add `pin` query parameter 8 | - add `401` and `429` error codes when PIN is required 9 | - add optional `metadata` object to each file 10 | 11 | ## 2.0 12 | 13 | - change `send-request` to `prepare-upload` 14 | - change `send` to `upload` 15 | - add `sessionId` to every request 16 | -------------------------------------------------------------------------------- /v1.md: -------------------------------------------------------------------------------- 1 | # LocalSend Protocol v1 2 | 3 | ## Table of Contents 4 | 5 | - [1. Defaults](#1-defaults) 6 | - [2. Discovery](#2-discovery) 7 | - [2.1 Multicast](#21-multicast-udp-default) 8 | - [2.2 HTTP](#22-http-legacy-mode) 9 | - [3. File transfer](#3-file-transfer-http) 10 | - [3.1 Send request](#31-send-request-metadata-only) 11 | - [3.2 Send file](#32-send-file) 12 | - [3.3 Cancel](#33-cancel) 13 | 14 | ## 1. Defaults 15 | 16 | LocalSend does not require a specific port or multicast address but instead provides a default configuration. 17 | 18 | Everything can be configured in the app settings if the port / address is somehow unavailable. 19 | 20 | The default multicast group is 224.0.0.0/24 because some Android devices reject any other multicast group. 21 | 22 | **Multicast (UDP)** 23 | - Port: 53317 24 | - Address: 224.0.0.167 25 | 26 | **HTTP (TCP)** 27 | - Port: 53317 28 | 29 | ## 2. Discovery 30 | 31 | ### 2.1 Multicast UDP (Default) 32 | 33 | **Announcement** 34 | 35 | At the start of the app, the following message will be sent to the multicast group: 36 | 37 | ```json5 38 | { 39 | "alias": "Nice Orange", 40 | "deviceModel": "Samsung", // nullable 41 | "deviceType": "mobile", // mobile | desktop | web 42 | "fingerprint": "random string", 43 | "announcement": true 44 | } 45 | ``` 46 | 47 | **Response** 48 | 49 | Other LocalSend members will notice this message and will reply with their respective information. 50 | 51 | First, an HTTP/TCP request is sent to the origin: 52 | 53 | `POST /api/localsend/v1/register` 54 | 55 | ```json5 56 | { 57 | "alias": "Secret Banana", 58 | "deviceModel": "Windows", 59 | "deviceType": "desktop", 60 | "fingerprint": "random string" 61 | } 62 | ``` 63 | 64 | As fallback, members can also respond with a Multicast/UDP message. 65 | 66 | ```json5 67 | { 68 | "alias": "Secret Banana", 69 | "deviceModel": "Windows", 70 | "deviceType": "desktop", 71 | "fingerprint": "random string", 72 | "announcement": false 73 | } 74 | ``` 75 | 76 | The `fingerprint` is only used to avoid self-discovering. 77 | 78 | A response is only triggered when `announcement` is `true`. 79 | 80 | ### 2.2 HTTP (Legacy Mode) 81 | 82 | This method should be used when multicast was unsuccessful. 83 | 84 | Devices are discovered by sending this request to all local IP addresses. 85 | 86 | `GET /api/localsend/v1/info?fingerprint=abc` 87 | 88 | Response 89 | 90 | ```json5 91 | { 92 | "alias": "Nice Orange", 93 | "deviceModel": "Samsung", // nullable 94 | "deviceType": "mobile" // mobile | desktop | web 95 | } 96 | ``` 97 | 98 | ## 3. File transfer (HTTP) 99 | 100 | ### 3.1 Send Request (Metadata only) 101 | 102 | Sends only the metadata to the receiver. 103 | 104 | The receiver will decide if this request gets accepted, partially accepted or rejected. 105 | 106 | Be aware that only one active session is allowed, i.e. the server will reject all requests when another session is ongoing. 107 | 108 | `POST /api/localsend/v1/send-request` 109 | 110 | Request 111 | 112 | ```json5 113 | { 114 | "info": { 115 | "alias": "Nice Orange", 116 | "deviceModel": "Samsung", // nullable 117 | "deviceType": "mobile" // mobile | desktop | web 118 | }, 119 | "files": { 120 | "some file id": { 121 | "id": "some file id", 122 | "fileName": "my image.png", 123 | "size": 324242, // bytes 124 | "fileType": "image", // image | video | pdf | text | other 125 | "preview": "*preview data*" // nullable 126 | }, 127 | "another file id": { 128 | "id": "another file id", 129 | "fileName": "another image.jpg", 130 | "size": 1234, 131 | "fileType": "image", 132 | "preview": "*preview data*" 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | Response 139 | 140 | ```json5 141 | { 142 | "some file id": "some token", 143 | "another file id": "some other token" 144 | } 145 | ``` 146 | 147 | ### 3.2 Send File 148 | 149 | The file transfer. 150 | 151 | Use the `fileId` and its file-specific `token` from `/send-request`. 152 | 153 | This route can be called in parallel. 154 | 155 | `POST /api/localsend/v1/send?fileId=some file id&token=some token` 156 | 157 | Request 158 | 159 | ```text 160 | Binary data 161 | ``` 162 | 163 | Response 164 | 165 | ```text 166 | No body 167 | ``` 168 | 169 | ### 3.3 Cancel 170 | 171 | This route will be called when the sender wants to cancel the session. 172 | 173 | The server will use the IP address to determine if the request is legitimate. 174 | 175 | `POST /api/localsend/v1/cancel` 176 | 177 | Response 178 | 179 | ```text 180 | No body 181 | ``` 182 | -------------------------------------------------------------------------------- /README-zh-CN.md: -------------------------------------------------------------------------------- 1 | # LocalSend 协议 v2 2 | 3 | [English](./README.md) | **简体中文** 4 | 5 | 主要为了实现一个不依赖于任何外部服务器的简单 REST 协议。 6 | 7 | 因为计算机网络比较复杂,因此我们不能假设每种方法都可用。某些设备可能不支持多点广播或不能运行 HTTP 服务器。 8 | 9 | 就是这个原因,所以这协议尝试用多种方式去发现其他 LocalSend 成员,从而执行发送文件。 10 | 11 | 该协议只需要一方建立 HTTP 服务器即可运行。 12 | 13 | ## 目录 14 | 15 | - [LocalSend 协议 v2](#localsend-协议-v2) 16 | - [目录](#目录) 17 | - [1. 默认配置](#1-默认配置) 18 | - [2. 指纹](#2-指纹) 19 | - [3. 搜寻发现](#3-搜寻发现) 20 | - [3.1 UDP 广播](#31-udp-广播) 21 | - [3.2 HTTP (传统模式)](#32-http-传统模式) 22 | - [4. 文件传输 (HTTP)](#4-文件传输-http) 23 | - [4.1 准备工作 (仅元数据)](#41-准备工作-仅元数据) 24 | - [4.2 发送文件](#42-发送文件) 25 | - [4.3 取消](#43-取消) 26 | - [5. 反向文件传输 (HTTP)](#5-反向文件传输-http) 27 | - [5.1 浏览器 URL](#51-浏览器-url) 28 | - [5.2 接收请求](#52-接收请求) 29 | - [5.3 接收文件](#53-接收文件) 30 | - [6. 其他 API](#6-其他-api) 31 | - [6.1 信息](#61-信息) 32 | - [7. 枚举](#7-枚举) 33 | - [7.1 设备类型](#71-设备类型) 34 | 35 | ## 1. 默认配置 36 | 37 | LocalSend 不需要特定的端口或广播地址,而是提供默认配置。 38 | 39 | 如果端口/地址不可用,可以在应用程序中修改配置。 40 | 41 | 默认广播地址是 224.0.0.0/24,因为某些 Android 设备禁止其他广播组。 42 | 43 | **UDP广播** 44 | 45 | - 端口: 53317 46 | - 地址: 224.0.0.167 47 | 48 | **HTTP (TCP)** 49 | 50 | - 端口: 53317 51 | 52 | ## 2. 指纹 53 | 54 | 指纹用于区分设备。 55 | 56 | 使用 HTTPS 加密时,证书的 SHA-256 哈希值作为指纹。 57 | 58 | 关闭 HTTPS 加密时,用随机字符串作为指纹。 59 | 60 | ## 3. 搜寻发现 61 | 62 | ### 3.1 UDP 广播 63 | 64 | **通知** 65 | 66 | 在应用程序启动时,会发送以下消息到广播地址: 67 | 68 | ```json5 69 | { 70 | "alias": "Nice Orange", 71 | "version": "2.0", // 协议版本(major.minor) 72 | "deviceModel": "Samsung", // nullable 73 | "deviceType": "mobile", // mobile | desktop | web | headless | server, nullable 74 | "fingerprint": "随机字符串", 75 | "port": 53317, 76 | "protocol": "https", // http | https 77 | "download": true, // 下载 API(5.2 和 5.3)是否激活(可选,默认为 false) 78 | "announce": true 79 | } 80 | ``` 81 | 82 | **返回** 83 | 84 | 其他 LocalSend 成员会收到到此消息并回复各自的设备信息。 85 | 86 | 首先,向原设备发送 HTTP/TCP 请求: 87 | 88 | `POST /api/localsend/v2/register` 89 | 90 | ```json5 91 | { 92 | "alias": "Secret Banana", 93 | "version": "2.0", 94 | "deviceModel": "Windows", 95 | "deviceType": "desktop", 96 | "fingerprint": "随机字符串", // 在 HTTPS 模式下被忽略 97 | "port": 53317, 98 | "protocol": "https", 99 | "download": true, // 下载 API(5.2 和 5.3)是否激活(可选,默认为 false) 100 | } 101 | ``` 102 | 103 | 另外,成员还可以使用 UDP 广播消息进行响应。 104 | 105 | ```json5 106 | { 107 | "alias": "Secret Banana", 108 | "version": "2.0", 109 | "deviceModel": "Windows", 110 | "deviceType": "desktop", 111 | "fingerprint": "随机字符串", 112 | "port": 53317, 113 | "protocol": "https", 114 | "download": true, 115 | "announce": false, 116 | } 117 | ``` 118 | 119 | `fingerprint` 仅用于区分自身设备。 120 | 121 | 只有当 `announce` 为 `true` 时,才会触发响应。 122 | 123 | ### 3.2 HTTP (传统模式) 124 | 125 | 当广播不成功时应使用此方法。 126 | 127 | 向本地所有 IP 地址发送此请求来发现设备。 128 | 129 | `POST /api/localsend/v2/register` 130 | 131 | 请求: 132 | 133 | ```json5 134 | { 135 | "alias": "Secret Banana", 136 | "version": "2.0", // 协议版本(major.minor) 137 | "deviceModel": "Windows", 138 | "deviceType": "desktop", 139 | "fingerprint": "随机字符串", // 在 HTTPS 模式下被忽略 140 | "port": 53317, 141 | "protocol": "https", // http | https 142 | "download": true, // 下载 API(5.2 和 5.3)是否激活(可选,默认为 false) 143 | } 144 | ``` 145 | 146 | 返回: 147 | 148 | ```json5 149 | { 150 | "alias": "Nice Orange", 151 | "version": "2.0", 152 | "deviceModel": "Samsung", 153 | "deviceType": "mobile", 154 | "fingerprint": "随机字符串", // 在 HTTPS 模式下被忽略 155 | "download": true, // 下载 API(5.2 和 5.3)是否激活(可选,默认为 false) 156 | } 157 | ``` 158 | 159 | ## 4. 文件传输 (HTTP) 160 | 161 | HTTP传输是默认方式。 162 | 163 | 接收方作为 HTTP 服务器。 164 | 165 | 发送方(即 HTTP 客户端)将文件发送到 HTTP 服务器。 166 | 167 | ### 4.1 准备工作 (仅元数据) 168 | 169 | 仅将元数据发送给接收方。 170 | 171 | 接收方将决定该请求是否被接受、部分接受或拒绝。 172 | 173 | `POST /api/localsend/v2/prepare-upload` 174 | 175 | 请求: 176 | 177 | ```json5 178 | { 179 | "info": { 180 | "alias": "Nice Orange", 181 | "version": "2.0", // 协议版本 (major.minor) 182 | "deviceModel": "Samsung", // 可为空 183 | "deviceType": "mobile", // mobile | desktop | web | headless | server, 可为空 184 | "fingerprint": "随机字符串", // 在 HTTPS 模式下被忽略 185 | "port": 53317, 186 | "protocol": "https", // http | https 187 | "download": true, // 下载 API (5.2 和 5.3) 是否激活 (可选,默认为 false) 188 | }, 189 | "files": { 190 | "文件ID": { 191 | "id": "文件ID", 192 | "fileName": "我的图片.png", 193 | "size": 324242, // bytes 194 | "fileType": "image/jpeg", 195 | "sha256": "*sha256哈希值*", // 可为空 196 | "preview": "*预览数据*" // 可为空 197 | }, 198 | "另一个文件ID": { 199 | "id": "另一个文件ID", 200 | "fileName": "另一个图片.jpg", 201 | "size": 1234, 202 | "fileType": "image/jpeg", 203 | "sha256": "*sha256哈希值*", 204 | "preview": "*预览数据*" 205 | } 206 | } 207 | } 208 | ``` 209 | 210 | 返回: 211 | 212 | ```json5 213 | { 214 | "sessionId": "我的会话ID", 215 | "files": { 216 | "文件ID": "文件Token", 217 | "另一个文件ID": "另一个文件Token" 218 | } 219 | } 220 | ``` 221 | 222 | Errors 错误代码 223 | 224 | | HTTP 代码 | 信息 | 225 | | --------- | ------------------- | 226 | | 204 | 完成 (无需传输文件) | 227 | | 400 | 无效请求体 | 228 | | 403 | 拒绝 | 229 | | 500 | 接收器未知错误 | 230 | 231 | ### 4.2 发送文件 232 | 233 | 文件传输。 234 | 235 | 使用来自 `/prepare-upload` 的 `sessionId`、`fileId` 及其特定于文件的 `token`。 236 | 237 | 这请求路径可以同时调用。 238 | 239 | `POST /api/localsend/v2/upload?sessionId=我的会话ID&fileId=文件ID&token=文件Token` 240 | 241 | 请求: 242 | 243 | ```text 244 | 二进制数据 245 | ``` 246 | 247 | 返回: 248 | 249 | ```text 250 | 无响应体 251 | ``` 252 | 253 | Errors 错误代码 254 | 255 | | HTTP 代码 | 信息 | 256 | | --------- | ------------------ | 257 | | 400 | 参数缺失 | 258 | | 403 | 无效令牌或 IP 地址 | 259 | | 409 | 被其他会话阻止 | 260 | | 500 | 接收器未知错误 | 261 | 262 | ### 4.3 取消 263 | 264 | 当发送方希望取消会话时,可用此请求路径。 265 | 266 | 使用 `/send-request` 中的 `sessionId`。 267 | 268 | `POST /api/localsend/v2/cancel?sessionId=我的会话ID` 269 | 270 | 返回: 271 | 272 | ```text 273 | 无响应体 274 | ``` 275 | 276 | ## 5. 反向文件传输 (HTTP) 277 | 278 | HTTP方式是当接收方无法使用 LocalSend 时应使用的备用方法。 279 | 280 | 发送方设置一个 HTTP 服务器,通过提供 URL 将文件发送给其他成员。 281 | 282 | 然后接收方使用给定的 URL 打开浏览器并下载文件。 283 | 284 | 需要注意的是,由于浏览器拒绝自签名证书,因此使用未加密的 HTTP 协议。 285 | 286 | ### 5.1 浏览器 URL 287 | 288 | 接收方可以在浏览器中打开以下网址来下载文件。 289 | 290 | ```text 291 | http://<发送者ip>:<发送者端口> 292 | ``` 293 | 294 | ### 5.2 接收请求 295 | 296 | 向发送方发送请求以获取文件元数据列表。 297 | 298 | 下载方可以添加 `?sessionId=我的会话ID`。此时,如果是同一个会话,则接受该请求。 299 | 300 | 如果用户刷新浏览器页面,则需要这样操作。 301 | 302 | `POST /api/localsend/v2/prepare-download` 303 | 304 | 请求: 305 | 306 | ```text 307 | 无请求体 308 | ``` 309 | 310 | 返回: 311 | 312 | ```json5 313 | { 314 | "info": { 315 | "alias": "Nice Orange", 316 | "version": "2.0", 317 | "deviceModel": "Samsung", // 可为空 318 | "deviceType": "mobile", // mobile | desktop | web | headless | server, 可为空 319 | "fingerprint": "随机字符串", // 在 HTTPS 模式下被忽略 320 | "download": true, // 下载 API (5.2 和 5.3) 是否激活 (可选,默认为 false) 321 | }, 322 | "sessionId": "mySessionId", 323 | "files": { 324 | "文件ID": { 325 | "id": "文件ID", 326 | "fileName": "我的图片.png", 327 | "size": 324242, // bytes 328 | "fileType": "image/jpeg", 329 | "sha256": "*sha256哈希值*", // 可为空 330 | "preview": "*预览数据*" // 可为空 331 | }, 332 | "另一个文件ID": { 333 | "id": "另一个文件ID", 334 | "fileName": "另一个图片.jpg", 335 | "size": 1234, 336 | "fileType": "image/jpeg", 337 | "sha256": "*sha256哈希值*", 338 | "preview": "*预览数据*" 339 | } 340 | } 341 | } 342 | ``` 343 | 344 | ### 5.3 接收文件 345 | 346 | 文件传输。 347 | 348 | 使用 `/receive-request` 中的 `sessionId` 和 `fileId`。 349 | 350 | 这请求路径可以同时调用。 351 | 352 | `GET /api/localsend/v2/download?sessionId=我的会话ID&fileId=文件ID` 353 | 354 | 请求: 355 | 356 | ```text 357 | 无请求体 358 | ``` 359 | 360 | 返回: 361 | 362 | ```text 363 | 二进制数据 364 | ``` 365 | 366 | ## 6. 其他 API 367 | 368 | ### 6.1 信息 369 | 370 | 这是一条以前用于发现的旧连接。 它已被替换为“/register”,这是一种双向发现。 371 | 372 | 现在该路由应该仅用于调试。 373 | 374 | `GET /api/localsend/v2/info` 375 | 376 | 返回: 377 | 378 | ```json5 379 | { 380 | "alias": "Nice Orange", 381 | "version": "2.0", 382 | "deviceModel": "Samsung", // 可为空 383 | "deviceType": "mobile", // mobile | desktop | web | headless | server, 可为空 384 | "fingerprint": "随机字符串", 385 | "download": true, // 下载 API (5.2 和 5.3) 是否激活 (可选,默认为 false) 386 | } 387 | ``` 388 | 389 | ## 7. 枚举 390 | 391 | 在这项目中,枚举用于定义某些字段的可能值。 392 | 393 | ### 7.1 设备类型 394 | 395 | 设备类型仅用于 UI 使用,例如显示图标。 396 | 397 | 各种设备类型的协议都一样。 398 | 399 | | 值 | 描述 | 400 | | -------- | ------------------------------- | 401 | | mobile | 移动设备 (Android, iOS, FireOS) | 402 | | desktop | 桌面 (Windows, macOS, Linux) | 403 | | web | 网页浏览器 (Firefox, Chrome) | 404 | | headless | 在终端运行的无图形用户界面程序 | 405 | | server | 全天候运行的(自托管)云服务 | 406 | 407 | 如有枚举外的值,都返回 `desktop` 类型。 408 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LocalSend Protocol v2.1 2 | 3 | **English** | [简体中文](./README-zh-CN.md) 4 | 5 | The goal is to have a simple REST protocol that does not rely on any external servers. 6 | 7 | Because computer networks can be complicated, we cannot assume that every method is available. 8 | Some devices might not support multicast or might not be allowed to have an HTTP server running. 9 | 10 | This is why this protocol tries to be "clever" and uses multiple methods to discover and to send files to other LocalSend members. 11 | 12 | The protocol only needs one party to set up an HTTP server. 13 | 14 | ## Table of Contents 15 | 16 | - [1. Defaults](#1-defaults) 17 | - [2. Fingerprint](#2-fingerprint) 18 | - [3. Discovery](#3-discovery) 19 | - [3.1 Multicast](#31-multicast-udp-default) 20 | - [3.2 HTTP](#32-http-legacy-mode) 21 | - [4. File Transfer](#4-file-transfer-http-aka-upload-api) 22 | - [4.1 Preparation](#41-preparation-metadata-only) 23 | - [4.2 Send File](#42-send-file) 24 | - [4.3 Cancel](#43-cancel) 25 | - [5. Reverse File Transfer](#5-reverse-file-transfer-http-aka-download-api) 26 | - [5.1 Browser URL](#51-browser-url) 27 | - [5.2 Receive Request](#52-receive-request-metadata-only) 28 | - [5.3 Receive File](#53-receive-file) 29 | - [6. Additional API](#6-additional-api) 30 | - [6.1 Info](#61-info) 31 | 32 | ## 1. Defaults 33 | 34 | LocalSend does not require a specific port or multicast address but instead provides a default configuration. 35 | 36 | Everything can be configured in the app settings if the port / address is somehow unavailable. 37 | 38 | The default multicast group is 224.0.0.0/24 because some Android devices reject any other multicast group. 39 | 40 | **Multicast (UDP)** 41 | 42 | - Port: 53317 43 | - Address: 224.0.0.167 44 | 45 | **HTTP (TCP)** 46 | 47 | - Port: 53317 48 | 49 | ## 2. Fingerprint 50 | 51 | The fingerprint is used to avoid self-discovery and to remember devices. 52 | 53 | When encryption is on (HTTPS), then the fingerprint is the SHA-256 hash of the certificate. 54 | 55 | When encryption is off (HTTP), then the fingerprint is a random generated string. 56 | 57 | ## 3. Discovery 58 | 59 | ### 3.1 Multicast UDP (Default) 60 | 61 | **Announcement** 62 | 63 | At the start of the app, the following message will be sent to the multicast group: 64 | 65 | ```json5 66 | { 67 | "alias": "Nice Orange", 68 | "version": "2.0", // protocol version (major.minor) 69 | "deviceModel": "Samsung", // nullable 70 | "deviceType": "mobile", // mobile | desktop | web | headless | server, nullable 71 | "fingerprint": "random string", 72 | "port": 53317, 73 | "protocol": "https", // http | https 74 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 75 | "announce": true 76 | } 77 | ``` 78 | 79 | **Response** 80 | 81 | Other LocalSend members will notice this message and will reply with their respective information. 82 | 83 | First, an HTTP/TCP request is sent to the origin: 84 | 85 | `POST /api/localsend/v2/register` 86 | 87 | ```json5 88 | { 89 | "alias": "Secret Banana", 90 | "version": "2.0", 91 | "deviceModel": "Windows", 92 | "deviceType": "desktop", 93 | "fingerprint": "random string", // ignored in HTTPS mode 94 | "port": 53317, 95 | "protocol": "https", 96 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 97 | } 98 | ``` 99 | 100 | As fallback, members can also respond with a Multicast/UDP message. 101 | 102 | ```json5 103 | { 104 | "alias": "Secret Banana", 105 | "version": "2.0", 106 | "deviceModel": "Windows", 107 | "deviceType": "desktop", 108 | "fingerprint": "random string", 109 | "port": 53317, 110 | "protocol": "https", 111 | "download": true, 112 | "announce": false, 113 | } 114 | ``` 115 | 116 | The `fingerprint` is only used to avoid self-discovering. 117 | 118 | A response is only triggered when `announce` is `true`. 119 | 120 | ### 3.2 HTTP (Legacy Mode) 121 | 122 | This method should be used when multicast was unsuccessful. 123 | 124 | Devices are discovered by sending this request to all local IP addresses. 125 | 126 | `POST /api/localsend/v2/register` 127 | 128 | Request 129 | 130 | ```json5 131 | { 132 | "alias": "Secret Banana", 133 | "version": "2.0", // protocol version (major.minor) 134 | "deviceModel": "Windows", 135 | "deviceType": "desktop", 136 | "fingerprint": "random string", // ignored in HTTPS mode 137 | "port": 53317, 138 | "protocol": "https", // http | https 139 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 140 | } 141 | ``` 142 | 143 | Response 144 | 145 | ```json5 146 | { 147 | "alias": "Nice Orange", 148 | "version": "2.0", 149 | "deviceModel": "Samsung", 150 | "deviceType": "mobile", 151 | "fingerprint": "random string", // ignored in HTTPS mode 152 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 153 | } 154 | ``` 155 | 156 | ## 4. File Transfer (HTTP) aka Upload API 157 | 158 | This is the default method. 159 | 160 | The receiver setups the HTTP server. 161 | 162 | The sender (i.e. HTTP client) sends files to the HTTP server. 163 | 164 | ### 4.1 Preparation (Metadata Only) 165 | 166 | Sends only the metadata to the receiver. 167 | 168 | The receiver will decide if this request gets accepted, partially accepted or rejected. 169 | 170 | If a PIN is required, the query parameter `?pin=123456` should be added. 171 | 172 | `POST /api/localsend/v2/prepare-upload` 173 | 174 | Request 175 | 176 | ```json5 177 | { 178 | "info": { 179 | "alias": "Nice Orange", 180 | "version": "2.0", // protocol version (major.minor) 181 | "deviceModel": "Samsung", // nullable 182 | "deviceType": "mobile", // mobile | desktop | web | headless | server, nullable 183 | "fingerprint": "random string", // ignored in HTTPS mode 184 | "port": 53317, 185 | "protocol": "https", // http | https 186 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 187 | }, 188 | "files": { 189 | "some file id": { 190 | "id": "some file id", 191 | "fileName": "my image.png", 192 | "size": 324242, // bytes 193 | "fileType": "image/jpeg", 194 | "sha256": "*sha256 hash*", // nullable 195 | "preview": "*preview data*", // nullable 196 | "metadata": { // nullable 197 | "modified": "2021-01-01T12:34:56Z", // nullable 198 | "accessed": "2021-01-01T12:34:56Z", // nullable 199 | } 200 | }, 201 | "another file id": { 202 | "id": "another file id", 203 | "fileName": "another image.jpg", 204 | "size": 1234, 205 | "fileType": "image/jpeg", 206 | "sha256": "*sha256 hash*", 207 | "preview": "*preview data*" 208 | } 209 | } 210 | } 211 | ``` 212 | 213 | Response 214 | 215 | ```json5 216 | { 217 | "sessionId": "mySessionId", 218 | "files": { 219 | "someFileId": "someFileToken", 220 | "someOtherFileId": "someOtherFileToken" 221 | } 222 | } 223 | ``` 224 | 225 | Errors 226 | 227 | | HTTP code | Message | 228 | |-----------|------------------------------------| 229 | | 204 | Finished (No file transfer needed) | 230 | | 400 | Invalid body | 231 | | 401 | PIN required / Invalid PIN | 232 | | 403 | Rejected | 233 | | 409 | Blocked by another session | 234 | | 429 | Too many requests | 235 | | 500 | Unknown error by receiver | 236 | 237 | ### 4.2 Send File 238 | 239 | The file transfer. 240 | 241 | Use the `sessionId`, the `fileId` and its file-specific `token` from [`/prepare-upload`](#41-preparation-metadata-only). 242 | 243 | This route can be called in parallel. 244 | 245 | `POST /api/localsend/v2/upload?sessionId=mySessionId&fileId=someFileId&token=someFileToken` 246 | 247 | Request 248 | 249 | ```text 250 | Binary data 251 | ``` 252 | 253 | Response 254 | 255 | ```text 256 | No body 257 | ``` 258 | 259 | Errors 260 | 261 | | HTTP code | Message | 262 | |-----------|-----------------------------| 263 | | 400 | Missing parameters | 264 | | 403 | Invalid token or IP address | 265 | | 409 | Blocked by another session | 266 | | 500 | Unknown error by receiver | 267 | 268 | ### 4.3 Cancel 269 | 270 | This route will be called when the sender wants to cancel the session. 271 | 272 | Use the `sessionId` from [`/prepare-upload`](#41-preparation-metadata-only). 273 | 274 | `POST /api/localsend/v2/cancel?sessionId=mySessionId` 275 | 276 | Response 277 | 278 | ```text 279 | No body 280 | ``` 281 | 282 | ## 5. Reverse File Transfer (HTTP) aka Download API 283 | 284 | This is an alternative method which should be used when LocalSend is not available on the receiver. 285 | 286 | The sender setups an HTTP server to send files to other members by providing a URL. 287 | 288 | The receiver then opens the browser with the given URL and downloads the file. 289 | 290 | It is important to note that the unencrypted HTTP protocol is used because browsers reject self-signed certificates. 291 | 292 | ### 5.1 Browser URL 293 | 294 | The receiver can open the following URL in the browser to download the file. 295 | 296 | ```text 297 | http://: 298 | ``` 299 | 300 | ### 5.2 Receive Request (Metadata Only) 301 | 302 | Send to the sender a request to get a list of file metadata. 303 | 304 | The downloader may add `?sessionId=mySessionId`. In this case, the request should be accepted if it is the same session. 305 | 306 | This is needed if the user refreshes the browser page. 307 | 308 | If a PIN is required, the query parameter `?pin=123456` should be added. 309 | 310 | `POST /api/localsend/v2/prepare-download` 311 | 312 | Request 313 | 314 | ```text 315 | No body 316 | ``` 317 | 318 | Response 319 | 320 | ```json5 321 | { 322 | "info": { 323 | "alias": "Nice Orange", 324 | "version": "2.0", 325 | "deviceModel": "Samsung", // nullable 326 | "deviceType": "mobile", // mobile | desktop | web | headless | server, nullable 327 | "fingerprint": "random string", // ignored in HTTPS mode 328 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 329 | }, 330 | "sessionId": "mySessionId", 331 | "files": { 332 | "some file id": { 333 | "id": "some file id", 334 | "fileName": "my image.png", 335 | "size": 324242, // bytes 336 | "fileType": "image/jpeg", 337 | "sha256": "*sha256 hash*", // nullable 338 | "preview": "*preview data*" // nullable 339 | }, 340 | "another file id": { 341 | "id": "another file id", 342 | "fileName": "another image.jpg", 343 | "size": 1234, 344 | "fileType": "image/jpeg", 345 | "sha256": "*sha256 hash*", 346 | "preview": "*preview data*" 347 | } 348 | } 349 | } 350 | ``` 351 | 352 | Errors 353 | 354 | | HTTP code | Message | 355 | |-----------|----------------------------| 356 | | 401 | PIN required / Invalid PIN | 357 | | 403 | Rejected | 358 | | 429 | Too many requests | 359 | | 500 | Unknown error by sender | 360 | 361 | ### 5.3 Receive File 362 | 363 | The file transfer. 364 | 365 | Use the `sessionId`, the `fileId` from [`/prepare-download`](#52-receive-request-metadata-only). 366 | 367 | This route can be called in parallel. 368 | 369 | `GET /api/localsend/v2/download?sessionId=mySessionId&fileId=someFileId` 370 | 371 | Request 372 | 373 | ```text 374 | No body 375 | ``` 376 | 377 | Response 378 | 379 | ```text 380 | Binary data 381 | ``` 382 | 383 | ## 6. Additional API 384 | 385 | ### 6.1 Info 386 | 387 | This was an old route previously used for discovery. This has been replaced with `/register` which is a two-way discovery. 388 | 389 | Now this route should be only used for debugging purposes. 390 | 391 | `GET /api/localsend/v2/info` 392 | 393 | Response 394 | 395 | ```json5 396 | { 397 | "alias": "Nice Orange", 398 | "version": "2.0", 399 | "deviceModel": "Samsung", // nullable 400 | "deviceType": "mobile", // mobile | desktop | web | headless | server, nullable 401 | "fingerprint": "random string", 402 | "download": true, // if download API (section 5.2, 5.3) is active (optional, default: false) 403 | } 404 | ``` 405 | 406 | ## 7. Enums 407 | 408 | In this project, enums are used to define the possible values of some fields. 409 | 410 | ### 7.1 Device Type 411 | 412 | Device types are only used for UI purposes like showing an icon. 413 | 414 | There is no difference in the protocol between the different device types. 415 | 416 | | Value | Description | 417 | |----------|-------------------------------------------| 418 | | mobile | mobile device (Android, iOS, FireOS) | 419 | | desktop | desktop (Windows, macOS, Linux) | 420 | | web | web browser (Firefox, Chrome) | 421 | | headless | program without GUI running on a terminal | 422 | | server | (self-hosted) cloud service running 24/7 | 423 | 424 | The implementation handle unknown values. The official implementation falls back to `desktop`. 425 | --------------------------------------------------------------------------------