Add docs and 404 in file PUT into non-existent dir
This commit is contained in:
parent
41039445c9
commit
d19270e318
@ -74,9 +74,24 @@ MDNS is used to resolve [`circuitpython.local`](http://circuitpython.local) to a
|
|||||||
hostname of the form `cpy-XXXXXX.local`. The `XXXXXX` is based on network MAC address. The device
|
hostname of the form `cpy-XXXXXX.local`. The `XXXXXX` is based on network MAC address. The device
|
||||||
also provides the MDNS service with service type `_circuitpython` and protocol `_tcp`.
|
also provides the MDNS service with service type `_circuitpython` and protocol `_tcp`.
|
||||||
|
|
||||||
|
### HTTP
|
||||||
The web server is HTTP 1.1 and may use chunked responses so that it doesn't need to precompute
|
The web server is HTTP 1.1 and may use chunked responses so that it doesn't need to precompute
|
||||||
content length.
|
content length.
|
||||||
|
|
||||||
|
The API generally consists of an HTTP method such as GET or PUT and a path. Requests and responses
|
||||||
|
also have headers. Responses will contain a status code and status text such as `404 Not Found`.
|
||||||
|
This API tries to use standard status codes to encode the status of the various operations. The
|
||||||
|
[Mozilla Developer Network HTTP docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP)
|
||||||
|
are a great reference.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
The examples use `curl`, a common command line program for issuing HTTP requests. The examples below
|
||||||
|
use `circuitpython.local` as the easiest way to work. If you have multiple active devices, you'll
|
||||||
|
want to use the specific `cpy-XXXXXX.local` version.
|
||||||
|
|
||||||
|
The examples also use `passw0rd` as the password placeholder. Replace it with your password before
|
||||||
|
running the example.
|
||||||
|
|
||||||
### `/`
|
### `/`
|
||||||
The root welcome page links to the file system page and also displays other CircuitPython devices
|
The root welcome page links to the file system page and also displays other CircuitPython devices
|
||||||
found using MDNS service discovery. This allows web browsers to find other devices from one. (All
|
found using MDNS service discovery. This allows web browsers to find other devices from one. (All
|
||||||
@ -101,8 +116,17 @@ The `/fs/` page will respond with a directory browsing HTML once authenticated.
|
|||||||
gzipped. If the `Accept: application/json` header is provided, then the JSON representation of the
|
gzipped. If the `Accept: application/json` header is provided, then the JSON representation of the
|
||||||
root will be returned.
|
root will be returned.
|
||||||
|
|
||||||
#### OPTIONS
|
##### OPTIONS
|
||||||
When requested with the `OPTIONS` method, the server will respond with .
|
When requested with the `OPTIONS` method, the server will respond with CORS related headers. Most
|
||||||
|
aren't needed for API use. They are there for the web browser.
|
||||||
|
|
||||||
|
* `Access-Control-Allow-Methods` - Varies with USB state. `GET, OPTIONS` when USB is active. `GET, OPTIONS, PUT, DELETE` otherwise.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -v -u :passw0rd -X OPTIONS -L --location-trusted http://circuitpython.local/fs/
|
||||||
|
```
|
||||||
|
|
||||||
#### `/fs/<directory path>/`
|
#### `/fs/<directory path>/`
|
||||||
Directory paths must end with a /. Otherwise, the path is assumed to be a file.
|
Directory paths must end with a /. Otherwise, the path is assumed to be a file.
|
||||||
@ -111,39 +135,101 @@ Directory paths must end with a /. Otherwise, the path is assumed to be a file.
|
|||||||
Returns a JSON representation of the directory.
|
Returns a JSON representation of the directory.
|
||||||
|
|
||||||
* `200 OK` - Directory exists and JSON returned
|
* `200 OK` - Directory exists and JSON returned
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
* `404 Not Found` - Missing directory
|
* `404 Not Found` - Missing directory
|
||||||
|
|
||||||
|
Returns information about each file in the directory:
|
||||||
|
|
||||||
|
* `name` - File name. No trailing `/` on directory names
|
||||||
|
* `directory` - `true` when a directory. `false` otherwise
|
||||||
|
* `modified_ns` - File modification time in nanoseconds since January 1st, 1970. May not use full resolution
|
||||||
|
* `file_size` - File size in bytes. `0` for directories
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -v -u :passw0rd -H "Accept: application/json" -L --location-trusted http://circuitpython.local/fs/lib/hello/
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "world.txt",
|
||||||
|
"directory": false,
|
||||||
|
"modified_ns": 946934328000000000,
|
||||||
|
"file_size": 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
##### PUT
|
##### PUT
|
||||||
Tries to make a directory at the given path. Request body is ignored. Returns:
|
Tries to make a directory at the given path. Request body is ignored. The custom `X-Timestamp`
|
||||||
|
header can provide a timestamp in milliseconds since January 1st, 1970 (to match JavaScript's file
|
||||||
|
time resolution) used for the directories modification time. The RTC time will used otherwise.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
* `204 No Content` - Directory exists
|
* `204 No Content` - Directory exists
|
||||||
* `201 Created` - Directory created
|
* `201 Created` - Directory created
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
* `409 Conflict` - USB is active and preventing file system modification
|
* `409 Conflict` - USB is active and preventing file system modification
|
||||||
* `404 Not Found` - Missing parent directory
|
* `404 Not Found` - Missing parent directory
|
||||||
* `500 Server Error` - Other, unhandled error
|
* `500 Server Error` - Other, unhandled error
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
``sh
|
```sh
|
||||||
curl -v -u :passw0rd -X PUT -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
curl -v -u :passw0rd -X PUT -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
||||||
``
|
```
|
||||||
|
|
||||||
##### DELETE
|
##### DELETE
|
||||||
Deletes the directory and all of its contents.
|
Deletes the directory and all of its contents.
|
||||||
|
|
||||||
|
* `204 No Content` - Directory and its contents deleted
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
* `404 Not Found` - No directory
|
* `404 Not Found` - No directory
|
||||||
* `409 Conflict` - USB is active and preventing file system modification
|
* `409 Conflict` - USB is active and preventing file system modification
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
``sh
|
```sh
|
||||||
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
|
||||||
``
|
```
|
||||||
|
|
||||||
|
|
||||||
#### `/fs/<file path>`
|
#### `/fs/<file path>`
|
||||||
|
|
||||||
|
##### PUT
|
||||||
|
Stores the provided content to the file path.
|
||||||
|
|
||||||
|
The custom `X-Timestamp` header can provide a timestamp in milliseconds since January 1st, 1970
|
||||||
|
(to match JavaScript's file time resolution) used for the directories modification time. The RTC
|
||||||
|
time will used otherwise.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
* `201 Created` - File created and saved
|
||||||
|
* `204 No Content` - File existed and overwritten
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
|
* `404 Not Found` - Missing parent directory
|
||||||
|
* `409 Conflict` - USB is active and preventing file system modification
|
||||||
|
* `413 Payload Too Large` - `Expect` header not sent and file is too large
|
||||||
|
* `417 Expectation Failed` - `Expect` header sent and file is too large
|
||||||
|
* `500 Server Error` - Other, unhandled error
|
||||||
|
|
||||||
|
If the client sends the `Expect` header, the server will reply with `100 Continue` when ok.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
echo "Hello world" >> test.txt
|
||||||
|
curl -v -u :passw0rd -T test.txt -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
|
||||||
|
```
|
||||||
|
|
||||||
##### GET
|
##### GET
|
||||||
Returns the raw file contents. `Content-Type` will be set based on extension:
|
Returns the raw file contents. `Content-Type` will be set based on extension:
|
||||||
|
|
||||||
@ -155,39 +241,39 @@ Returns the raw file contents. `Content-Type` will be set based on extension:
|
|||||||
|
|
||||||
Will return:
|
Will return:
|
||||||
* `200 OK` - File exists and file returned
|
* `200 OK` - File exists and file returned
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
* `404 Not Found` - Missing file
|
* `404 Not Found` - Missing file
|
||||||
|
|
||||||
##### PUT
|
Example:
|
||||||
Stores the provided content to the file path. Returns:
|
|
||||||
|
|
||||||
* `201 Created` - File created and saved
|
```sh
|
||||||
* `204 No Content` - File existed and overwritten
|
curl -v -u :passw0rd -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
|
||||||
* `404 Not Found` - Missing parent directory
|
```
|
||||||
* `409 Conflict` - USB is active and preventing file system modification
|
|
||||||
* `413 Payload Too Large` - `Expect` header not sent and file is too large
|
|
||||||
* `417 Expectation Failed` - `Expect` header sent and file is too large
|
|
||||||
* `500 Server Error` - Other, unhandled error
|
|
||||||
|
|
||||||
If the client sends the `Expect` header, the server will reply with `100 Continue` when ok.
|
|
||||||
|
|
||||||
##### DELETE
|
##### DELETE
|
||||||
Deletes the file.
|
Deletes the file.
|
||||||
|
|
||||||
|
|
||||||
|
* `204 No Content` - File existed and deleted
|
||||||
|
* `401 Unauthorized` - Incorrect password
|
||||||
|
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
|
||||||
* `404 Not Found` - File not found
|
* `404 Not Found` - File not found
|
||||||
* `409 Conflict` - USB is active and preventing file system modification
|
* `409 Conflict` - USB is active and preventing file system modification
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
``sh
|
```sh
|
||||||
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
|
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
|
||||||
``
|
```
|
||||||
|
|
||||||
### `/cp/`
|
### `/cp/`
|
||||||
|
|
||||||
`/cp/` serves basic info about the CircuitPython device and others discovered through MDNS. It is
|
`/cp/` serves basic info about the CircuitPython device and others discovered through MDNS. It is
|
||||||
not protected by basic auth in case the device is someone elses.
|
not protected by basic auth in case the device is someone elses.
|
||||||
|
|
||||||
Only `GET` requests are supported and will return `XXX Method Not Allowed` otherwise.
|
Only `GET` requests are supported and will return `405 Method Not Allowed` otherwise.
|
||||||
|
|
||||||
#### `/cp/version.json`
|
#### `/cp/version.json`
|
||||||
|
|
||||||
|
@ -723,10 +723,16 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
|
|||||||
result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS);
|
result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result == FR_NO_PATH) {
|
||||||
|
override_fattime(0);
|
||||||
|
_reply_missing(socket, request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (result != FR_OK) {
|
if (result != FR_OK) {
|
||||||
ESP_LOGE(TAG, "file write error %d %s", result, path);
|
ESP_LOGE(TAG, "file write error %d %s", result, path);
|
||||||
override_fattime(0);
|
override_fattime(0);
|
||||||
_reply_server_error(socket, request);
|
_reply_server_error(socket, request);
|
||||||
|
return;
|
||||||
} else if (request->expect) {
|
} else if (request->expect) {
|
||||||
_reply_continue(socket, request);
|
_reply_continue(socket, request);
|
||||||
}
|
}
|
||||||
@ -928,7 +934,6 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
|
|||||||
FRESULT result = f_open(fs, &active_file, path, FA_READ);
|
FRESULT result = f_open(fs, &active_file, path, FA_READ);
|
||||||
|
|
||||||
if (result != FR_OK) {
|
if (result != FR_OK) {
|
||||||
// TODO: 404
|
|
||||||
_reply_missing(socket, request);
|
_reply_missing(socket, request);
|
||||||
} else {
|
} else {
|
||||||
_reply_with_file(socket, request, path, &active_file);
|
_reply_with_file(socket, request, path, &active_file);
|
||||||
|
Loading…
Reference in New Issue
Block a user