English | 简体ä¸ć–‡
The goal is to have a simple REST protocol that does not rely on any external servers.
Because computer networks can be complicated, we cannot assume that every method is available. Some devices might not support multicast or might not be allowed to have an HTTP server running.
This is why this protocol tries to be "clever" and uses multiple methods to discover and to send files to other LocalSend members.
The protocol only needs one party to set up an HTTP server.
LocalSend does not require a specific port or multicast address but instead provides a default configuration.
Everything can be configured in the app settings if the port / address is somehow unavailable.
The default multicast group is 224.0.0.0/24 because some Android devices reject any other multicast group.
Multicast (UDP)
- Port: 53317
- Address: 224.0.0.167
HTTP (TCP)
- Port: 53317
The fingerprint is used to avoid self-discovery and to remember devices.
When encryption is on (HTTPS), then the fingerprint is the SHA-256 hash of the certificate.
When encryption is off (HTTP), then the fingerprint is a random generated string.
Announcement
At the start of the app, the following message will be sent to the multicast group:
{
"alias": "Nice Orange",
"version": "2.0", // protocol version (major.minor)
"deviceModel": "Samsung", // nullable
"deviceType": "mobile", // mobile | desktop | web | headless | server, nullable
"fingerprint": "random string",
"port": 53317,
"protocol": "https", // http | https
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
"announce": true
}
Response
Other LocalSend members will notice this message and will reply with their respective information.
First, an HTTP/TCP request is sent to the origin:
POST /api/localsend/v2/register
{
"alias": "Secret Banana",
"version": "2.0",
"deviceModel": "Windows",
"deviceType": "desktop",
"fingerprint": "random string", // ignored in HTTPS mode
"port": 53317,
"protocol": "https",
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
}
As fallback, members can also respond with a Multicast/UDP message.
{
"alias": "Secret Banana",
"version": "2.0",
"deviceModel": "Windows",
"deviceType": "desktop",
"fingerprint": "random string",
"port": 53317,
"protocol": "https",
"download": true,
"announce": false,
}
The fingerprint
is only used to avoid self-discovering.
A response is only triggered when announce
is true
.
This method should be used when multicast was unsuccessful.
Devices are discovered by sending this request to all local IP addresses.
POST /api/localsend/v2/register
Request
{
"alias": "Secret Banana",
"version": "2.0", // protocol version (major.minor)
"deviceModel": "Windows",
"deviceType": "desktop",
"fingerprint": "random string", // ignored in HTTPS mode
"port": 53317,
"protocol": "https", // http | https
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
}
Response
{
"alias": "Nice Orange",
"version": "2.0",
"deviceModel": "Samsung",
"deviceType": "mobile",
"fingerprint": "random string", // ignored in HTTPS mode
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
}
This is the default method.
The receiver setups the HTTP server.
The sender (i.e. HTTP client) sends files to the HTTP server.
Sends only the metadata to the receiver.
The receiver will decide if this request gets accepted, partially accepted or rejected.
If a PIN is required, the query parameter ?pin=123456
should be added.
POST /api/localsend/v2/prepare-upload
Request
{
"info": {
"alias": "Nice Orange",
"version": "2.0", // protocol version (major.minor)
"deviceModel": "Samsung", // nullable
"deviceType": "mobile", // mobile | desktop | web | headless | server, nullable
"fingerprint": "random string", // ignored in HTTPS mode
"port": 53317,
"protocol": "https", // http | https
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
},
"files": {
"some file id": {
"id": "some file id",
"fileName": "my image.png",
"size": 324242, // bytes
"fileType": "image/jpeg",
"sha256": "*sha256 hash*", // nullable
"preview": "*preview data*", // nullable
"metadata": { // nullable
"modified": "2021-01-01T12:34:56Z", // nullable
"accessed": "2021-01-01T12:34:56Z", // nullable
}
},
"another file id": {
"id": "another file id",
"fileName": "another image.jpg",
"size": 1234,
"fileType": "image/jpeg",
"sha256": "*sha256 hash*",
"preview": "*preview data*"
}
}
}
Response
{
"sessionId": "mySessionId",
"files": {
"someFileId": "someFileToken",
"someOtherFileId": "someOtherFileToken"
}
}
Errors
HTTP code | Message |
---|---|
204 | Finished (No file transfer needed) |
400 | Invalid body |
401 | PIN required / Invalid PIN |
403 | Rejected |
409 | Blocked by another session |
429 | Too many requests |
500 | Unknown error by receiver |
The file transfer.
Use the sessionId
, the fileId
and its file-specific token
from /prepare-upload
.
This route can be called in parallel.
POST /api/localsend/v2/upload?sessionId=mySessionId&fileId=someFileId&token=someFileToken
Request
Binary data
Response
No body
Errors
HTTP code | Message |
---|---|
400 | Missing parameters |
403 | Invalid token or IP address |
409 | Blocked by another session |
500 | Unknown error by receiver |
This route will be called when the sender wants to cancel the session.
Use the sessionId
from /prepare-upload
.
POST /api/localsend/v2/cancel?sessionId=mySessionId
Response
No body
This is an alternative method which should be used when LocalSend is not available on the receiver.
The sender setups an HTTP server to send files to other members by providing a URL.
The receiver then opens the browser with the given URL and downloads the file.
It is important to note that the unencrypted HTTP protocol is used because browsers reject self-signed certificates.
The receiver can open the following URL in the browser to download the file.
http://<sender-ip>:<sender-port>
Send to the sender a request to get a list of file metadata.
The downloader may add ?sessionId=mySessionId
. In this case, the request should be accepted if it is the same session.
This is needed if the user refreshes the browser page.
If a PIN is required, the query parameter ?pin=123456
should be added.
POST /api/localsend/v2/prepare-download
Request
No body
Response
{
"info": {
"alias": "Nice Orange",
"version": "2.0",
"deviceModel": "Samsung", // nullable
"deviceType": "mobile", // mobile | desktop | web | headless | server, nullable
"fingerprint": "random string", // ignored in HTTPS mode
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
},
"sessionId": "mySessionId",
"files": {
"some file id": {
"id": "some file id",
"fileName": "my image.png",
"size": 324242, // bytes
"fileType": "image/jpeg",
"sha256": "*sha256 hash*", // nullable
"preview": "*preview data*" // nullable
},
"another file id": {
"id": "another file id",
"fileName": "another image.jpg",
"size": 1234,
"fileType": "image/jpeg",
"sha256": "*sha256 hash*",
"preview": "*preview data*"
}
}
}
Errors
HTTP code | Message |
---|---|
401 | PIN required / Invalid PIN |
403 | Rejected |
429 | Too many requests |
500 | Unknown error by sender |
The file transfer.
Use the sessionId
, the fileId
from /prepare-download
.
This route can be called in parallel.
GET /api/localsend/v2/download?sessionId=mySessionId&fileId=someFileId
Request
No body
Response
Binary data
This was an old route previously used for discovery. This has been replaced with /register
which is a two-way discovery.
Now this route should be only used for debugging purposes.
GET /api/localsend/v2/info
Response
{
"alias": "Nice Orange",
"version": "2.0",
"deviceModel": "Samsung", // nullable
"deviceType": "mobile", // mobile | desktop | web | headless | server, nullable
"fingerprint": "random string",
"download": true, // if download API (section 5.2, 5.3) is active (optional, default: false)
}
In this project, enums are used to define the possible values of some fields.
Device types are only used for UI purposes like showing an icon.
There is no difference in the protocol between the different device types.
Value | Description |
---|---|
mobile | mobile device (Android, iOS, FireOS) |
desktop | desktop (Windows, macOS, Linux) |
web | web browser (Firefox, Chrome) |
headless | program without GUI running on a terminal |
server | (self-hosted) cloud service running 24/7 |
The implementation handle unknown values. The official implementation falls back to desktop
.