Skip to content

Commit d19270e

Browse files
committed
Add docs and 404 in file PUT into non-existent dir
1 parent 4103944 commit d19270e

File tree

2 files changed

+113
-22
lines changed

2 files changed

+113
-22
lines changed

docs/workflows.md

Lines changed: 107 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,24 @@ MDNS is used to resolve [`circuitpython.local`](http://circuitpython.local) to a
7474
hostname of the form `cpy-XXXXXX.local`. The `XXXXXX` is based on network MAC address. The device
7575
also provides the MDNS service with service type `_circuitpython` and protocol `_tcp`.
7676

77+
### HTTP
7778
The web server is HTTP 1.1 and may use chunked responses so that it doesn't need to precompute
7879
content length.
7980

81+
The API generally consists of an HTTP method such as GET or PUT and a path. Requests and responses
82+
also have headers. Responses will contain a status code and status text such as `404 Not Found`.
83+
This API tries to use standard status codes to encode the status of the various operations. The
84+
[Mozilla Developer Network HTTP docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP)
85+
are a great reference.
86+
87+
#### Examples
88+
The examples use `curl`, a common command line program for issuing HTTP requests. The examples below
89+
use `circuitpython.local` as the easiest way to work. If you have multiple active devices, you'll
90+
want to use the specific `cpy-XXXXXX.local` version.
91+
92+
The examples also use `passw0rd` as the password placeholder. Replace it with your password before
93+
running the example.
94+
8095
### `/`
8196
The root welcome page links to the file system page and also displays other CircuitPython devices
8297
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.
101116
gzipped. If the `Accept: application/json` header is provided, then the JSON representation of the
102117
root will be returned.
103118

104-
#### OPTIONS
105-
When requested with the `OPTIONS` method, the server will respond with .
119+
##### OPTIONS
120+
When requested with the `OPTIONS` method, the server will respond with CORS related headers. Most
121+
aren't needed for API use. They are there for the web browser.
122+
123+
* `Access-Control-Allow-Methods` - Varies with USB state. `GET, OPTIONS` when USB is active. `GET, OPTIONS, PUT, DELETE` otherwise.
124+
125+
Example:
126+
127+
```sh
128+
curl -v -u :passw0rd -X OPTIONS -L --location-trusted http://circuitpython.local/fs/
129+
```
106130

107131
#### `/fs/<directory path>/`
108132
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.
111135
Returns a JSON representation of the directory.
112136

113137
* `200 OK` - Directory exists and JSON returned
138+
* `401 Unauthorized` - Incorrect password
139+
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
114140
* `404 Not Found` - Missing directory
115141

142+
Returns information about each file in the directory:
143+
144+
* `name` - File name. No trailing `/` on directory names
145+
* `directory` - `true` when a directory. `false` otherwise
146+
* `modified_ns` - File modification time in nanoseconds since January 1st, 1970. May not use full resolution
147+
* `file_size` - File size in bytes. `0` for directories
148+
149+
Example:
150+
151+
```sh
152+
curl -v -u :passw0rd -H "Accept: application/json" -L --location-trusted http://circuitpython.local/fs/lib/hello/
153+
```
154+
155+
```json
156+
[
157+
{
158+
"name": "world.txt",
159+
"directory": false,
160+
"modified_ns": 946934328000000000,
161+
"file_size": 12
162+
}
163+
]
164+
```
165+
116166
##### PUT
117-
Tries to make a directory at the given path. Request body is ignored. Returns:
167+
Tries to make a directory at the given path. Request body is ignored. The custom `X-Timestamp`
168+
header can provide a timestamp in milliseconds since January 1st, 1970 (to match JavaScript's file
169+
time resolution) used for the directories modification time. The RTC time will used otherwise.
170+
171+
Returns:
118172

119173
* `204 No Content` - Directory exists
120174
* `201 Created` - Directory created
175+
* `401 Unauthorized` - Incorrect password
176+
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
121177
* `409 Conflict` - USB is active and preventing file system modification
122178
* `404 Not Found` - Missing parent directory
123179
* `500 Server Error` - Other, unhandled error
124180

125181
Example:
126182

127-
``sh
183+
```sh
128184
curl -v -u :passw0rd -X PUT -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
129-
``
185+
```
130186

131187
##### DELETE
132188
Deletes the directory and all of its contents.
133189

134-
190+
* `204 No Content` - Directory and its contents deleted
191+
* `401 Unauthorized` - Incorrect password
192+
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
135193
* `404 Not Found` - No directory
136194
* `409 Conflict` - USB is active and preventing file system modification
137195

138196
Example:
139197

140-
``sh
198+
```sh
141199
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world/
142-
``
200+
```
143201

144202

145203
#### `/fs/<file path>`
146204

205+
##### PUT
206+
Stores the provided content to the file path.
207+
208+
The custom `X-Timestamp` header can provide a timestamp in milliseconds since January 1st, 1970
209+
(to match JavaScript's file time resolution) used for the directories modification time. The RTC
210+
time will used otherwise.
211+
212+
Returns:
213+
214+
* `201 Created` - File created and saved
215+
* `204 No Content` - File existed and overwritten
216+
* `401 Unauthorized` - Incorrect password
217+
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
218+
* `404 Not Found` - Missing parent directory
219+
* `409 Conflict` - USB is active and preventing file system modification
220+
* `413 Payload Too Large` - `Expect` header not sent and file is too large
221+
* `417 Expectation Failed` - `Expect` header sent and file is too large
222+
* `500 Server Error` - Other, unhandled error
223+
224+
If the client sends the `Expect` header, the server will reply with `100 Continue` when ok.
225+
226+
Example:
227+
228+
```sh
229+
echo "Hello world" >> test.txt
230+
curl -v -u :passw0rd -T test.txt -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
231+
```
232+
147233
##### GET
148234
Returns the raw file contents. `Content-Type` will be set based on extension:
149235

@@ -155,39 +241,39 @@ Returns the raw file contents. `Content-Type` will be set based on extension:
155241

156242
Will return:
157243
* `200 OK` - File exists and file returned
244+
* `401 Unauthorized` - Incorrect password
245+
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
158246
* `404 Not Found` - Missing file
159247

160-
##### PUT
161-
Stores the provided content to the file path. Returns:
248+
Example:
162249

163-
* `201 Created` - File created and saved
164-
* `204 No Content` - File existed and overwritten
165-
* `404 Not Found` - Missing parent directory
166-
* `409 Conflict` - USB is active and preventing file system modification
167-
* `413 Payload Too Large` - `Expect` header not sent and file is too large
168-
* `417 Expectation Failed` - `Expect` header sent and file is too large
169-
* `500 Server Error` - Other, unhandled error
250+
```sh
251+
curl -v -u :passw0rd -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
252+
```
170253

171-
If the client sends the `Expect` header, the server will reply with `100 Continue` when ok.
172254

173255
##### DELETE
174256
Deletes the file.
175257

258+
259+
* `204 No Content` - File existed and deleted
260+
* `401 Unauthorized` - Incorrect password
261+
* `403 Forbidden` - No `CIRCUITPY_WEB_API_PASSWORD` set
176262
* `404 Not Found` - File not found
177263
* `409 Conflict` - USB is active and preventing file system modification
178264

179265
Example:
180266

181-
``sh
267+
```sh
182268
curl -v -u :passw0rd -X DELETE -L --location-trusted http://circuitpython.local/fs/lib/hello/world.txt
183-
``
269+
```
184270

185271
### `/cp/`
186272

187273
`/cp/` serves basic info about the CircuitPython device and others discovered through MDNS. It is
188274
not protected by basic auth in case the device is someone elses.
189275

190-
Only `GET` requests are supported and will return `XXX Method Not Allowed` otherwise.
276+
Only `GET` requests are supported and will return `405 Method Not Allowed` otherwise.
191277

192278
#### `/cp/version.json`
193279

supervisor/shared/web_workflow/web_workflow.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -723,10 +723,16 @@ static void _write_file_and_reply(socketpool_socket_obj_t *socket, _request *req
723723
result = f_open(fs, &active_file, path, FA_WRITE | FA_OPEN_ALWAYS);
724724
}
725725

726+
if (result == FR_NO_PATH) {
727+
override_fattime(0);
728+
_reply_missing(socket, request);
729+
return;
730+
}
726731
if (result != FR_OK) {
727732
ESP_LOGE(TAG, "file write error %d %s", result, path);
728733
override_fattime(0);
729734
_reply_server_error(socket, request);
735+
return;
730736
} else if (request->expect) {
731737
_reply_continue(socket, request);
732738
}
@@ -928,7 +934,6 @@ static bool _reply(socketpool_socket_obj_t *socket, _request *request) {
928934
FRESULT result = f_open(fs, &active_file, path, FA_READ);
929935

930936
if (result != FR_OK) {
931-
// TODO: 404
932937
_reply_missing(socket, request);
933938
} else {
934939
_reply_with_file(socket, request, path, &active_file);

0 commit comments

Comments
 (0)