Simple HTTP server to save artifacts
-addr string
address to listen
-config string
path to config file
-document_root string
path to document root directory
-enable_auth
enable authentication
-enable_cors
enable CORS header
-file_naming_strategy string
File naming strategy
-max_upload_size int
max upload size in bytes
-read_only_tokens value
comma separated list of read only tokens
-read_timeout duration
read timeout. zero or negative value means no timeout. can be suffixed by the time units 'ns', 'us' (or 'µs'), 'ms', 's', 'm', 'h' (e.g. '1s', '500ms'). If no suffix is provided, it is interpreted as seconds.
-read_write_tokens value
comma separated list of read write tokens
-shutdown_timeout int
graceful shutdown timeout in milliseconds
-write_timeout duration
write timeout. zero or negative value means no timeout. same format as read_timeout.
Configurations via the arguments take precedence over those came from the config file.
This server does not require authentication by default. Anyone who can access the server can get/upload files.
The server implements a simple authentication mechanism using tokens.
- Configure the server to enable authentication:
"enable_auth": true
or-enable_auth=true
. - Prepare tokens. Any string value is valid as a token.
- Add them to the configuration. There are two keys in configuration:
read_only_tokens
andread_write_tokens
. If authentication is enabled but no tokens provided, the server generates a read-only token and a read-write token on its starting up. - Request with the token. Add Authorization header with value
Bearer <TOKEN>
ortoken=<TOKEN>
to the query parameter. Authorization header takes precedence.
Token Type | Allowed Operations |
---|---|
read-only | GET , HEAD |
read-write | POST , PUT in addition to read-only ops |
Note that OPTIONS
is always allowed without authentication.
Authentication is failed when:
- A request has no tokens.
- A request has a token but not registered to the server.
- A request has a token but not allowed to the requested operation.
In these cases, the server respond with 401 Unauthorized
with body like as: {"ok": false, "error": "unauthorized"}
.
No one can request write operations if you configures the server with read-only tokens only. As a result, the server operates like read-only mode.
v1 has TLS support but I decided to omit it from v2.
Please consider using a reverse proxy like nginx.
(Since v2.1.0)
There are 2 timeout configurations: read and write.
The terms "read" and "write" are from the server's perspective. From clients, they are "upload" (POST
/PUT
) and "download" (GET
) respectively.
Read timeout (-read_timeout
) is the maximum duration for the server reading the request.
Clients should finish sending request headers and the entire content within this timeout.
This is set to 15 seconds by default.
Write timeout (-write_timeout
) is the maximum duration for the server writing the response.
Clients should finish downloading the content within this timeout.
This timeout is not set by default. Before v2.1.0, this is set to 15 seconds.
Please consider changing these timeout if:
- the server or the clients are in a low-bandwidth network.
- you are working with large files.
Note that a longer timeout will result in more connections being maintained.
To run all tests, just run go test
as usual:
$ go test ./...
This includes end-to-end tests. By default, the server with on-memory FileSystem is created and it starts listening on the port chosen randomly. You can control this behavior by setting the environment variables.
If TEST_WITH_REAL_FS=${PATH_TO_DOCUMENT_ROOT}
is set, the test server uses the real filesystem. Make sure the document
root directory contains no files; otherwise, some tests might be failed. The directory will not be cleaned after testing.
If TEST_TARGET_ADDR-${HOST}:${PORT}
is set, the test program doesn't start a local test server and sends requests to
http://${HOST}:${PORT}
. Note that the target server's document root should be cleared prior to testing.
This repository has docker-compose.e2e.yml
to run the E2E test. To run tests using this:
$ docker compose -f docker-compose.e2e.yml run --rm test
$ docker compose -f docker-compose.e2e.yml down --rmi local --volumes
Uploads a new file. The name of the local (= server-side) file is taken from the uploading file.
Content-Type
: multipart/form-data
Parameters:
Name | Required? | Type | Description | Default |
---|---|---|---|---|
file |
x | Form Data | A content of the file. | |
overwrite |
boolean |
Allow overwriting the existing file on the server if true . |
false |
Status Code
: 201 Created
Content-Type
: application/json
Body:
Name | Type | Description |
---|---|---|
ok |
boolean |
true if successful. |
path |
string |
A path to access this file in this API. |
StatusCode | When |
---|---|
409 Conflict |
There is the file whose name is the same as the uploading file and overwriting is not allowed. |
$ echo 'Hello, world!' > sample.txt
$ curl -Ffile=@sample.txt http://localhost:25478/upload
{"ok":true,"path":"/files/sample.txt"}
$ cat $DOCROOT/sample.txt
Hello, world!
Uploads a file. The original file name is ignored and the name is taken from the path in the request URL.
Name | Required? | Type | Description | Default |
---|---|---|---|---|
:path |
x | string |
Path to the file. | |
file |
x | Form Data | A content of the file. | |
overwrite |
boolean |
Allow overwriting the existing file on the server. | false |
Status Code
: 201 Created
Content-Type
: application/json
Body:
Name | Type | Description |
---|---|---|
ok |
boolean |
true if successful. |
path |
string |
A path to access this file in this API. |
StatusCode | When |
---|---|
409 Conflict |
There is the file whose name is the same as the uploading file and overwriting is not allowed. |
$ curl -XPUT -Ffile=@sample.txt "http://localhost:25478/files/foobar.txt"
{"ok":true,"path":"/files/foobar.txt"}
$ cat $DOCROOT/foobar.txt
Hello, world!
Downloads a file.
Parameters:
Name | Required? | Type | Description | Default |
---|---|---|---|---|
path |
x | string |
A path to the file. |
Status Code
: 200 OK
Content-Type : Depends on the content.
Body : The content of the request file.
Content-Type
: application/json
StatusCode | When |
---|---|
404 Not Found |
There is no such file. |
$ curl http://localhost:25478/files/sample.txt
Hello, world!
Check existence of a file.
Parameters:
Name | Required? | Type | Description | Default |
---|---|---|---|---|
path |
x | string |
A path to the file. |
Status Code
: 200 OK
Body : Not Available
StatusCode | When |
---|---|
404 Not Found |
No such file on the server. |
$ curl -I http://localhost:25478/files/foobar.txt
CORS preflight request.
Parameters:
Name | Required? | Type | Description | Default |
---|---|---|---|---|
path |
x | string |
A path to the file. |
Status Code
: 204 No Content
TODO
- Requests using
*
as a path, like asOPTIONS * HTTP/1.1
, are not supported. - On sending
OPTIONS
request,token
parameter is not required. - For
/files/:path
request, server replies "204 No Content" even if the specified file does not exist.