Compare commits
23 Commits
v1.0.0-rc3
...
v1.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07d0882c65 | ||
|
|
016ffd9fa4 | ||
|
|
95c3ac899f | ||
|
|
3bc8446a6d | ||
|
|
aabe581148 | ||
|
|
7dff0209f5 | ||
|
|
65ca295289 | ||
|
|
3e06fb8d3d | ||
|
|
6e287dbe56 | ||
|
|
37234aadd5 | ||
|
|
75a5bf2316 | ||
|
|
669e4c89e0 | ||
|
|
6a2e8cde1f | ||
|
|
22c2c4f702 | ||
|
|
437df5266b | ||
|
|
30eca2478c | ||
|
|
1f4d6d89dc | ||
|
|
7db86c67b1 | ||
|
|
671ce83693 | ||
|
|
0ced49e323 | ||
|
|
b5dc800922 | ||
|
|
51ec5a44b6 | ||
|
|
4a5735c463 |
177
README.md
177
README.md
@@ -1,8 +1,10 @@
|
|||||||
# Prosody Filer
|
# Prosody Filer
|
||||||
|
|
||||||
A simple file server for handling XMPP http_upload requests. This server is meat to be used with the Prosody [mod_http_upload_external](https://modules.prosody.im/mod_http_upload_external.html) module.
|
A simple file server for handling XMPP http_upload requests. This server is meant to be used with the Prosody [mod_http_upload_external](https://modules.prosody.im/mod_http_upload_external.html) module.
|
||||||
|
|
||||||
*(This module can also be used with future versions of Ejabberd: https://github.com/processone/ejabberd/commit/fface33d54f24c777dbec96fda6bd00e665327fe)*
|
**Despite the name, this server is also compatible with Ejabberd and Ejabberd's http_upload module!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Why should I use this server?
|
## Why should I use this server?
|
||||||
|
|
||||||
@@ -18,7 +20,7 @@ A simple file server for handling XMPP http_upload requests. This server is meat
|
|||||||
> The GC will free the memory at some point, but the OS may still report that Prosody is using that memory due to the way the libc allocator works.
|
> The GC will free the memory at some point, but the OS may still report that Prosody is using that memory due to the way the libc allocator works.
|
||||||
> Most long lived processes behave this way (only increasing RAM, rarely decreasing)."
|
> Most long lived processes behave this way (only increasing RAM, rarely decreasing)."
|
||||||
* This server works without any script interpreters or additional dependencies. It is delivered as a binary.
|
* This server works without any script interpreters or additional dependencies. It is delivered as a binary.
|
||||||
* Go is very good at serving HTTP requests.
|
* Go is very good at serving HTTP requests and "made for this task".
|
||||||
|
|
||||||
|
|
||||||
## Download
|
## Download
|
||||||
@@ -34,19 +36,25 @@ To compile the server, you need a full Golang development environment. This can
|
|||||||
|
|
||||||
Then checkout this repo:
|
Then checkout this repo:
|
||||||
|
|
||||||
go get github.com/ThomasLeister/prosody-filer
|
```sh
|
||||||
|
go get github.com/ThomasLeister/prosody-filer
|
||||||
|
```
|
||||||
|
|
||||||
and switch to the new directory:
|
and switch to the new directory:
|
||||||
|
|
||||||
cd $GOPATH/src/github.com/ThomasLeister/prosody-filer
|
```sh
|
||||||
|
cd $GOPATH/src/github.com/ThomasLeister/prosody-filer
|
||||||
|
```
|
||||||
|
|
||||||
The application can now be build:
|
The application can now be build:
|
||||||
|
|
||||||
### Build static binary
|
```sh
|
||||||
./build.sh
|
### Build static binary
|
||||||
|
./build.sh
|
||||||
|
|
||||||
### OR regular Go build
|
### OR regular Go build
|
||||||
go build main.go
|
go build main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Set up / configuration
|
## Set up / configuration
|
||||||
@@ -56,11 +64,15 @@ The application can now be build:
|
|||||||
|
|
||||||
Create a new user for Prosody Filer to run as:
|
Create a new user for Prosody Filer to run as:
|
||||||
|
|
||||||
adduser --disabled-login --disabled-password prosody-filer
|
```sh
|
||||||
|
adduser --disabled-login --disabled-password prosody-filer
|
||||||
|
```
|
||||||
|
|
||||||
Switch to the new user:
|
Switch to the new user:
|
||||||
|
|
||||||
su - prosody-filer
|
```sh
|
||||||
|
su - prosody-filer
|
||||||
|
```
|
||||||
|
|
||||||
Copy
|
Copy
|
||||||
|
|
||||||
@@ -69,12 +81,18 @@ Copy
|
|||||||
|
|
||||||
to ```/home/prosody-filer/```. Rename the configuration to ```config.toml```.
|
to ```/home/prosody-filer/```. Rename the configuration to ```config.toml```.
|
||||||
|
|
||||||
|
Make sure the `prosody-filer` binary is executable:
|
||||||
|
|
||||||
|
```
|
||||||
|
chmod u+x prosody-filer
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Configure Prosody
|
### Configure Prosody
|
||||||
|
|
||||||
Back in your root shell make sure ```mod_http_upload``` is **dis**abled and ```mod_http_upload_external``` is **en**abled! Then configure the external upload module:
|
Back in your root shell make sure ```mod_http_upload``` is **dis**abled and ```mod_http_upload_external``` is **en**abled! Then configure the external upload module:
|
||||||
|
|
||||||
```
|
```lua
|
||||||
http_upload_external_base_url = "https://uploads.myserver.tld/upload/"
|
http_upload_external_base_url = "https://uploads.myserver.tld/upload/"
|
||||||
http_upload_external_secret = "mysecret"
|
http_upload_external_secret = "mysecret"
|
||||||
http_upload_external_file_size_limit = 50000000 -- 50 MB
|
http_upload_external_file_size_limit = 50000000 -- 50 MB
|
||||||
@@ -84,19 +102,32 @@ Restart Prosody when you are finished:
|
|||||||
|
|
||||||
systemctl restart prosody
|
systemctl restart prosody
|
||||||
|
|
||||||
|
|
||||||
|
### Alternative: Configure Ejabberd
|
||||||
|
|
||||||
|
Although this tool is named after Prosody, it can be used with Ejabberd, too! Make sure you have a Ejabberd configuration similar to this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mod_http_upload:
|
||||||
|
put_url: "https://uploads.@HOST@/upload"
|
||||||
|
external_secret: "mysecret"
|
||||||
|
max_size: 52428800
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Configure Prosody Filer
|
### Configure Prosody Filer
|
||||||
|
|
||||||
Prosody Filer configuration is done via the config.toml file in TOML syntax. There's not much to be configured:
|
Prosody Filer configuration is done via the config.toml file in TOML syntax. There's not much to be configured:
|
||||||
|
|
||||||
```
|
```toml
|
||||||
### IP address and port to listen to, e.g. "127.0.0.1:5050"
|
### IP address and port to listen to, e.g. "[::]:5050"
|
||||||
listenport = "127.0.0.1:5050"
|
listenport = "[::1]:5050"
|
||||||
|
|
||||||
### Secret (must match the one in prosody.conf.lua!)
|
### Secret (must match the one in prosody.conf.lua!)
|
||||||
secret = "mysecret"
|
secret = "mysecret"
|
||||||
|
|
||||||
### Where to store the uploaded files
|
### Where to store the uploaded files
|
||||||
storeDir = "./uploads/"
|
storeDir = "./upload/"
|
||||||
|
|
||||||
### Subdirectory for HTTP upload / download requests (usually "upload/")
|
### Subdirectory for HTTP upload / download requests (usually "upload/")
|
||||||
uploadSubDir = "upload/"
|
uploadSubDir = "upload/"
|
||||||
@@ -105,6 +136,10 @@ uploadSubDir = "upload/"
|
|||||||
Make sure ```mysecret``` matches the secret defined in your mod_http_upload_external settings!
|
Make sure ```mysecret``` matches the secret defined in your mod_http_upload_external settings!
|
||||||
|
|
||||||
|
|
||||||
|
In addition to that, make sure that the nginx user or group can read the files uploaded
|
||||||
|
via prosody-filer if you want to have them served by nginx directly.
|
||||||
|
|
||||||
|
|
||||||
### Systemd service file
|
### Systemd service file
|
||||||
|
|
||||||
Create a new Systemd service file: ```/etc/systemd/system/prosody-filer.service```
|
Create a new Systemd service file: ```/etc/systemd/system/prosody-filer.service```
|
||||||
@@ -119,6 +154,7 @@ Create a new Systemd service file: ```/etc/systemd/system/prosody-filer.service`
|
|||||||
WorkingDirectory=/home/prosody-filer
|
WorkingDirectory=/home/prosody-filer
|
||||||
User=prosody-filer
|
User=prosody-filer
|
||||||
Group=prosody-filer
|
Group=prosody-filer
|
||||||
|
# Group=nginx # if the files should get served by nginx directly:
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
@@ -137,7 +173,8 @@ Done! Prosody Filer is now listening on the specified port and waiting for reque
|
|||||||
|
|
||||||
Create a new config file ```/etc/nginx/sites-available/uploads.myserver.tld```:
|
Create a new config file ```/etc/nginx/sites-available/uploads.myserver.tld```:
|
||||||
|
|
||||||
server {
|
```nginx
|
||||||
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
listen [::]:80;
|
||||||
listen 443 ssl;
|
listen 443 ssl;
|
||||||
@@ -151,10 +188,21 @@ Create a new config file ```/etc/nginx/sites-available/uploads.myserver.tld```:
|
|||||||
client_max_body_size 50m;
|
client_max_body_size 50m;
|
||||||
|
|
||||||
location /upload/ {
|
location /upload/ {
|
||||||
proxy_pass http://127.0.0.1:5050/upload/;
|
if ( $request_method = OPTIONS ) {
|
||||||
|
add_header Access-Control-Allow-Origin '*';
|
||||||
|
add_header Access-Control-Allow-Methods 'PUT, GET, OPTIONS, HEAD';
|
||||||
|
add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
|
||||||
|
add_header Access-Control-Allow-Credentials 'true';
|
||||||
|
add_header Content-Length 0;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_pass http://[::]:5050/upload/;
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Enable the new config:
|
Enable the new config:
|
||||||
|
|
||||||
@@ -169,12 +217,99 @@ Reload Nginx:
|
|||||||
systemctl reload nginx
|
systemctl reload nginx
|
||||||
|
|
||||||
|
|
||||||
|
#### Alternative configuration for letting Nginx serve the uploaded files
|
||||||
|
|
||||||
|
*(not officially supported - user contribution!)*
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
|
listen 443 ssl;
|
||||||
|
listen [::]:443 ssl;
|
||||||
|
|
||||||
|
server_name uploads.myserver.tld;
|
||||||
|
|
||||||
|
ssl_certificate /etc/letsencrypt/live/uploads.myserver.tld/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/uploads.myserver.tld/privkey.pem;
|
||||||
|
|
||||||
|
location /upload/ {
|
||||||
|
if ( $request_method = OPTIONS ) {
|
||||||
|
add_header Access-Control-Allow-Origin '*';
|
||||||
|
add_header Access-Control-Allow-Methods 'PUT, GET, OPTIONS, HEAD';
|
||||||
|
add_header Access-Control-Allow-Headers 'Authorization, Content-Type';
|
||||||
|
add_header Access-Control-Allow-Credentials 'true';
|
||||||
|
add_header Content-Length 0;
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
root /home/prosody-filer;
|
||||||
|
autoindex off;
|
||||||
|
client_max_body_size 51m;
|
||||||
|
client_body_buffer_size 51m;
|
||||||
|
try_files $uri $uri/ @prosodyfiler;
|
||||||
|
}
|
||||||
|
location @prosodyfiler {
|
||||||
|
proxy_pass http://[::1]:5050;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-Forwarded-Host $host:$server_port;
|
||||||
|
proxy_set_header X-Forwarded-Server $host;
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## apache2 configuration (alternative to Nginx)
|
||||||
|
|
||||||
|
*(This configuration was provided by a user and has never been tested by the author of Prosody Filer. It might be outdated and might not work anymore)*
|
||||||
|
|
||||||
|
```
|
||||||
|
<VirtualHost *:80>
|
||||||
|
ServerName upload.example.eu
|
||||||
|
RedirectPermanent / https://upload.example.eu/
|
||||||
|
</VirtualHost>
|
||||||
|
|
||||||
|
<VirtualHost *:443>
|
||||||
|
ServerName upload.example.eu
|
||||||
|
SSLEngine on
|
||||||
|
|
||||||
|
SSLCertificateFile "Path to the ca file"
|
||||||
|
SSLCertificateKeyFile "Path to the key file"
|
||||||
|
|
||||||
|
Header always set Public-Key-Pins: ''
|
||||||
|
Header always set Strict-Transport-Security "max-age=63072000; includeSubdomains; preload"
|
||||||
|
H2Direct on
|
||||||
|
|
||||||
|
<Location /upload>
|
||||||
|
Header always set Access-Control-Allow-Origin "*"
|
||||||
|
Header always set Access-Control-Allow-Headers "Content-Type"
|
||||||
|
Header always set Access-Control-Allow-Methods "OPTIONS, PUT, GET"
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
RewriteCond %{REQUEST_METHOD} OPTIONS
|
||||||
|
RewriteRule ^(.*)$ $1 [R=200,L]
|
||||||
|
</Location>
|
||||||
|
|
||||||
|
SSLProxyEngine on
|
||||||
|
|
||||||
|
ProxyPreserveHost On
|
||||||
|
ProxyRequests Off
|
||||||
|
ProxyPass / http://localhost:5050/
|
||||||
|
ProxyPassReverse / http://localhost:5050/
|
||||||
|
</VirtualHost>
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Automatic purge
|
## Automatic purge
|
||||||
|
|
||||||
Prosody Filer has no immediate knowlegde over all the stored files and the time they were uploaded, since no database exists for that. Also Prosody is not capable to do auto deletion if *mod_http_upload_external* is used. Therefore the suggested way of purging the uploads directory is to execute a purge command via a cron job:
|
Prosody Filer has no immediate knowlegde over all the stored files and the time they were uploaded, since no database exists for that. Also Prosody is not capable to do auto deletion if *mod_http_upload_external* is used. Therefore the suggested way of purging the uploads directory is to execute a purge command via a cron job:
|
||||||
|
|
||||||
@daily find /home/prosody-filer/uploads -maxdepth 0 -type d -mtime +28 | xargs rm -rf
|
@daily find /home/prosody-filer/upload/ -type d -mtime +28 | xargs rm -rf
|
||||||
|
|
||||||
This will delete uploads older than 28 days.
|
This will delete uploads older than 28 days.
|
||||||
|
|
||||||
@@ -186,5 +321,3 @@ Get the log via
|
|||||||
journalctl -f -u prosody-filer
|
journalctl -f -u prosody-filer
|
||||||
|
|
||||||
If your XMPP clients uploads or downloads any file, there should be some log messages on the screen.
|
If your XMPP clients uploads or downloads any file, there should be some log messages on the screen.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
13
build.sh
13
build.sh
@@ -1,7 +1,10 @@
|
|||||||
#!/bin/sh
|
#!/bin/bash
|
||||||
|
|
||||||
##
|
### get VERSIONSTRING
|
||||||
## Builds static prosody-filer binary
|
VERSIONSTRING="$(git describe --tags --exact-match || git rev-parse --short HEAD)"
|
||||||
##
|
|
||||||
|
echo "Building version ${VERSIONSTRING} of Prosody-Filer ..."
|
||||||
|
|
||||||
|
### Compile and link statically
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -a -ldflags "-extldflags '-static' -w -s -X main.versionString=${VERSIONSTRING}" prosody-filer.go
|
||||||
|
|
||||||
CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' .
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
### Server configuration
|
### Server configuration
|
||||||
### (rename this file to "config.toml"!)
|
### (rename this file to "config.toml"!)
|
||||||
|
|
||||||
### IP address and port to listen to, e.g. "127.0.0.1:5050"
|
### IP address and port to listen to, e.g. "[::]:5050" to listen to ipv6 and ipv4 addresses
|
||||||
listenport = "127.0.0.1:5050"
|
listenport = "[::]:5050"
|
||||||
|
|
||||||
### Secret (must match the one in prosody.conf.lua!)
|
### Secret (must match the one in prosody.conf.lua!)
|
||||||
secret = "mysecret"
|
secret = "mysecret"
|
||||||
|
|||||||
@@ -36,6 +36,18 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var conf Config
|
var conf Config
|
||||||
|
var versionString string = "0.0.0"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets CORS headers
|
||||||
|
*/
|
||||||
|
func addCORSheaders(w http.ResponseWriter) {
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
w.Header().Set("Access-Control-Allow-Methods", "OPTIONS, HEAD, GET, PUT")
|
||||||
|
w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
|
||||||
|
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
w.Header().Set("Access-Control-Max-Age", "7200")
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Request handler
|
* Request handler
|
||||||
@@ -55,7 +67,10 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
log.Println("Failed to parse URL query params:", err)
|
log.Println("Failed to parse URL query params:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileStorePath := strings.TrimLeft(u.Path, conf.UploadSubDir)
|
fileStorePath := strings.TrimPrefix(u.Path, "/"+conf.UploadSubDir)
|
||||||
|
|
||||||
|
// Add CORS headers
|
||||||
|
addCORSheaders(w)
|
||||||
|
|
||||||
if r.Method == "PUT" {
|
if r.Method == "PUT" {
|
||||||
// Check if MAC is attached to URL
|
// Check if MAC is attached to URL
|
||||||
@@ -83,7 +98,7 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Make sure the path exists
|
// Make sure the path exists
|
||||||
os.MkdirAll(filepath.Dir(conf.Storedir+fileStorePath), os.ModePerm)
|
os.MkdirAll(filepath.Dir(conf.Storedir+fileStorePath), os.ModePerm)
|
||||||
|
|
||||||
file, err := os.Create(conf.Storedir + fileStorePath)
|
file, err := os.OpenFile(conf.Storedir+fileStorePath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0755)
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Creating new file failed:", err)
|
log.Println("Creating new file failed:", err)
|
||||||
@@ -94,11 +109,12 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
n, err := io.Copy(file, r.Body)
|
n, err := io.Copy(file, r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Writing to new file failed:", err)
|
log.Println("Writing to new file failed:", err)
|
||||||
http.Error(w, "409 Conflict", 409)
|
http.Error(w, "500 Internal Server Error", 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Successfully written", n, "bytes to file", fileStorePath)
|
log.Println("Successfully written", n, "bytes to file", fileStorePath)
|
||||||
|
w.WriteHeader(http.StatusCreated)
|
||||||
} else {
|
} else {
|
||||||
log.Println("Invalid MAC.")
|
log.Println("Invalid MAC.")
|
||||||
http.Error(w, "403 Forbidden", 403)
|
http.Error(w, "403 Forbidden", 403)
|
||||||
@@ -122,7 +138,8 @@ func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
} else if r.Method == "GET" {
|
} else if r.Method == "GET" {
|
||||||
contentType := mime.TypeByExtension(filepath.Ext(fileStorePath))
|
contentType := mime.TypeByExtension(filepath.Ext(fileStorePath))
|
||||||
if fileStorePath == "" {
|
if f, err := os.Stat(conf.Storedir + fileStorePath); err != nil || f.IsDir() {
|
||||||
|
log.Println("Directory listing forbidden!")
|
||||||
http.Error(w, "403 Forbidden", 403)
|
http.Error(w, "403 Forbidden", 403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -176,7 +193,7 @@ func main() {
|
|||||||
/*
|
/*
|
||||||
* Start HTTP server
|
* Start HTTP server
|
||||||
*/
|
*/
|
||||||
log.Println("Starting up XMPP HTTP upload server ...")
|
log.Println("Starting Prosody-Filer", versionString, "...")
|
||||||
http.HandleFunc("/"+conf.UploadSubDir, handleRequest)
|
http.HandleFunc("/"+conf.UploadSubDir, handleRequest)
|
||||||
log.Printf("Server started on port %s. Waiting for requests.\n", conf.Listenport)
|
log.Printf("Server started on port %s. Waiting for requests.\n", conf.Listenport)
|
||||||
http.ListenAndServe(conf.Listenport, nil)
|
http.ListenAndServe(conf.Listenport, nil)
|
||||||
@@ -1,13 +1,55 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Manual testing with CURL
|
||||||
|
* Send with:
|
||||||
|
* curl -X PUT "http://localhost:5050/upload/thomas/abc/catmetal.jpg?v=e17531b1e88bc9a5cbf816eca8a82fc09969c9245250f3e1b2e473bb564e4be0" --data-binary '@catmetal.jpg'
|
||||||
|
* HMAC: e17531b1e88bc9a5cbf816eca8a82fc09969c9245250f3e1b2e473bb564e4be0
|
||||||
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mockUpload() {
|
||||||
|
os.MkdirAll(filepath.Dir(conf.Storedir+"thomas/abc/"), os.ModePerm)
|
||||||
|
from, err := os.Open("./catmetal.jpg")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer from.Close()
|
||||||
|
|
||||||
|
to, err := os.OpenFile(conf.Storedir+"thomas/abc/catmetal.jpg", os.O_RDWR|os.O_CREATE, 0660)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer to.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(to, from)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanup() {
|
||||||
|
// Clean up
|
||||||
|
if _, err := os.Stat(conf.Storedir); err == nil {
|
||||||
|
// Delete existing catmetal picture
|
||||||
|
err := os.RemoveAll(conf.Storedir)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Error while cleaning up:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReadConfig(t *testing.T) {
|
func TestReadConfig(t *testing.T) {
|
||||||
// Set config
|
// Set config
|
||||||
err := readConfig("config.toml", &conf)
|
err := readConfig("config.toml", &conf)
|
||||||
@@ -46,6 +88,9 @@ func TestUploadValid(t *testing.T) {
|
|||||||
if status := rr.Code; status != http.StatusOK {
|
if status := rr.Code; status != http.StatusOK {
|
||||||
t.Errorf("handler returned wrong status code: got %v want %v. HTTP body: %s", status, http.StatusOK, rr.Body.String())
|
t.Errorf("handler returned wrong status code: got %v want %v. HTTP body: %s", status, http.StatusOK, rr.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUploadMissingMAC(t *testing.T) {
|
func TestUploadMissingMAC(t *testing.T) {
|
||||||
@@ -142,6 +187,9 @@ func TestDownloadHead(t *testing.T) {
|
|||||||
// Set config
|
// Set config
|
||||||
readConfig("config.toml", &conf)
|
readConfig("config.toml", &conf)
|
||||||
|
|
||||||
|
// Mock upload
|
||||||
|
mockUpload()
|
||||||
|
|
||||||
// Create request
|
// Create request
|
||||||
req, err := http.NewRequest("HEAD", "/upload/thomas/abc/catmetal.jpg", nil)
|
req, err := http.NewRequest("HEAD", "/upload/thomas/abc/catmetal.jpg", nil)
|
||||||
|
|
||||||
@@ -159,12 +207,18 @@ func TestDownloadHead(t *testing.T) {
|
|||||||
if status := rr.Code; status != http.StatusOK {
|
if status := rr.Code; status != http.StatusOK {
|
||||||
t.Errorf("handler returned wrong status code: got %v want %v. HTTP body: %s", status, http.StatusOK, rr.Body.String())
|
t.Errorf("handler returned wrong status code: got %v want %v. HTTP body: %s", status, http.StatusOK, rr.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDownloadGet(t *testing.T) {
|
func TestDownloadGet(t *testing.T) {
|
||||||
// Set config
|
// Set config
|
||||||
readConfig("config.toml", &conf)
|
readConfig("config.toml", &conf)
|
||||||
|
|
||||||
|
// moch upload
|
||||||
|
mockUpload()
|
||||||
|
|
||||||
// Create request
|
// Create request
|
||||||
req, err := http.NewRequest("GET", "/upload/thomas/abc/catmetal.jpg", nil)
|
req, err := http.NewRequest("GET", "/upload/thomas/abc/catmetal.jpg", nil)
|
||||||
|
|
||||||
@@ -182,6 +236,9 @@ func TestDownloadGet(t *testing.T) {
|
|||||||
if status := rr.Code; status != http.StatusOK {
|
if status := rr.Code; status != http.StatusOK {
|
||||||
t.Errorf("handler returned wrong status code: got %v want %v. HTTP body: %s", status, http.StatusOK, rr.Body.String())
|
t.Errorf("handler returned wrong status code: got %v want %v. HTTP body: %s", status, http.StatusOK, rr.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
cleanup()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyGet(t *testing.T) {
|
func TestEmptyGet(t *testing.T) {
|
||||||
Reference in New Issue
Block a user