Skip to content

Commit 80ebe4f

Browse files
committed
Several small changes
Don't panic if content-type header is missing. Allow downstream server to request HTTPS in urls. gofmt code
1 parent f9dab63 commit 80ebe4f

File tree

5 files changed

+140
-37
lines changed

5 files changed

+140
-37
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ POST /filedrop/screenshot.png?max-uses=5&store-secs=3600
4141
Following request will store file screenshot.png for one hour (3600 seconds)
4242
and allow it to be downloaded not more than 10 times.
4343

44+
**Note** To get `https` scheme in URLs downstream server should set header
45+
`X-HTTPS-Downstream` to `1` (or you can also set HTTPSDownstream config option)
4446

4547
### Authorization
4648

config.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ package filedrop
33
import "net/http"
44

55
type LimitsConfig struct {
6-
MaxUses uint
6+
MaxUses uint
77
MaxStoreSecs uint
8-
MaxFileSize uint
8+
MaxFileSize uint
99
}
1010

1111
type DBConfig struct {
1212
Driver string
13-
DSN string
13+
DSN string
1414
}
1515

1616
type AuthConfig struct {
@@ -21,12 +21,12 @@ type AuthConfig struct {
2121
}
2222

2323
type Config struct {
24-
Limits LimitsConfig
25-
DB DBConfig
26-
DownloadAuth AuthConfig
27-
UploadAuth AuthConfig
28-
StorageDir string
29-
HTTPSUpstream bool
24+
Limits LimitsConfig
25+
DB DBConfig
26+
DownloadAuth AuthConfig
27+
UploadAuth AuthConfig
28+
StorageDir string
29+
HTTPSDownstream bool
3030
}
3131

32-
var Default Config
32+
var Default Config

db.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ type db struct {
1313
remFile *sql.Stmt
1414
contentType *sql.Stmt
1515

16-
addUse *sql.Stmt
17-
shouldDelete *sql.Stmt
18-
cleanup *sql.Stmt
16+
addUse *sql.Stmt
17+
shouldDelete *sql.Stmt
18+
cleanup *sql.Stmt
1919
pendingCleanup *sql.Stmt
2020
}
2121

@@ -39,7 +39,6 @@ func openDB(driver, dsn string) (*db, error) {
3939
return nil, err
4040
}
4141

42-
4342
if driver == "sqlite3" {
4443
// Also some optimizations for SQLite to make it FAA-A-A-AST.
4544
db.Exec(`PRAGMA foreign_keys = ON`)
@@ -163,8 +162,8 @@ func (db *db) ContentType(tx *sql.Tx, fileUUID string) (string, error) {
163162
row = db.contentType.QueryRow(fileUUID)
164163
}
165164

166-
res := ""
167-
return res, row.Scan(&res)
165+
res := sql.NullString{}
166+
return res.String, row.Scan(&res)
168167
}
169168

170169
func (db *db) UnreachableFiles(tx *sql.Tx) ([]string, error) {
@@ -196,4 +195,4 @@ func (db *db) RemoveUnreachableFiles(tx *sql.Tx) error {
196195
_, err := db.cleanup.Exec(time.Now().Unix())
197196
return err
198197
}
199-
}
198+
}

server.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ var ErrFileDoesntExists = errors.New("file doesn't exists")
2020

2121
// filedrop server structure, implements http.Handler.
2222
type Server struct {
23-
DB *db
24-
Conf Config
23+
DB *db
24+
Conf Config
2525
Logger *log.Logger
2626

2727
fileCleanerStopChan chan bool
@@ -81,7 +81,7 @@ func (s *Server) removeFile(tx *sql.Tx, fileUUID string) error {
8181
return errors.Wrap(err, "uuid parse")
8282
}
8383

84-
if err := s.DB.RemoveFile(tx, fileUUID); err !=nil {
84+
if err := s.DB.RemoveFile(tx, fileUUID); err != nil {
8585
return errors.Wrap(err, "db remove")
8686
}
8787

@@ -131,7 +131,7 @@ func (s *Server) GetFile(fileUUID string) (r io.Reader, contentType string, err
131131

132132
if s.DB.ShouldDelete(tx, fileUUID) {
133133
if err := s.removeFile(tx, fileUUID); err != nil {
134-
s.Logger.Println("Error while trying to remove file", fileUUID + ":", err)
134+
s.Logger.Println("Error while trying to remove file", fileUUID+":", err)
135135

136136
}
137137
if err := tx.Commit(); err != nil {
@@ -226,7 +226,11 @@ func (s *Server) acceptFile(w http.ResponseWriter, r *http.Request) {
226226

227227
// Smart logic to convert request's URL into absolute result URL.
228228
resURL := url.URL{}
229-
if s.Conf.HTTPSUpstream {
229+
if r.Header.Get("X-HTTPS-Downstream") == "1" {
230+
resURL.Scheme = "https"
231+
} else if r.Header.Get("X-HTTPS-Downstream") == "0" {
232+
resURL.Scheme = "http"
233+
} else if s.Conf.HTTPSDownstream {
230234
resURL.Scheme = "https"
231235
} else {
232236
resURL.Scheme = "http"
@@ -326,7 +330,7 @@ func (s *Server) cleanupFiles() {
326330
}
327331

328332
for _, fileUUID := range uuids {
329-
if err := os.Remove(filepath.Join(s.Conf.StorageDir, fileUUID)); err !=nil {
333+
if err := os.Remove(filepath.Join(s.Conf.StorageDir, fileUUID)); err != nil {
330334
s.Logger.Println("Failed to remove file during clean-up:", err)
331335
}
332336
}

server_test.go

Lines changed: 112 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import (
1919

2020
var TestDBConf = filedrop.DBConfig{
2121
Driver: "sqlite3",
22-
DSN: ":memory:",
22+
DSN: ":memory:",
2323
}
2424

2525
var file = `Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow Meow
@@ -43,6 +43,7 @@ func initServ(conf filedrop.Config) *filedrop.Server {
4343

4444
// Test for correct initialization of server.
4545
func TestNew(t *testing.T) {
46+
t.Parallel()
4647
conf := filedrop.Default
4748
conf.DB = TestDBConf
4849
tempDir, err := ioutil.TempDir("", "filedrop-tests")
@@ -74,7 +75,7 @@ func doPOST(t *testing.T, c *http.Client, url string, contentType string, reqBod
7475
t.Error("ioutil.ReadAll:", err)
7576
t.FailNow()
7677
}
77-
if resp.StatusCode / 100 != 2 {
78+
if resp.StatusCode/100 != 2 {
7879
t.Error("POST: HTTP", resp.StatusCode, resp.Status)
7980
t.Error("Body:", string(body))
8081
t.FailNow()
@@ -89,7 +90,7 @@ func doPOSTFail(t *testing.T, c *http.Client, url string, contentType string, re
8990
t.FailNow()
9091
}
9192
defer resp.Body.Close()
92-
if resp.StatusCode / 100 == 2 {
93+
if resp.StatusCode/100 == 2 {
9394
t.Error("POST: HTTP", resp.StatusCode, resp.Status)
9495
t.FailNow()
9596
}
@@ -108,7 +109,7 @@ func doGET(t *testing.T, c *http.Client, url string) []byte {
108109
t.Error("ioutil.ReadAll:", err)
109110
t.FailNow()
110111
}
111-
if resp.StatusCode / 100 != 2 {
112+
if resp.StatusCode/100 != 2 {
112113
t.Error("GET: HTTP", resp.Status)
113114
t.Error("Body:", string(body))
114115
t.FailNow()
@@ -123,7 +124,7 @@ func doGETFail(t *testing.T, c *http.Client, url string) int {
123124
t.FailNow()
124125
}
125126
defer resp.Body.Close()
126-
if resp.StatusCode / 100 == 2 {
127+
if resp.StatusCode/100 == 2 {
127128
t.Error("GET: HTTP", resp.StatusCode, resp.Status)
128129
t.FailNow()
129130
}
@@ -138,7 +139,7 @@ func TestBasicSubmit(t *testing.T) {
138139
defer ts.Close()
139140
c := ts.Client()
140141

141-
url := string(doPOST(t, c, ts.URL + "/filedrop/meow.txt", "text/plain", strings.NewReader(file)))
142+
url := string(doPOST(t, c, ts.URL+"/filedrop/meow.txt", "text/plain", strings.NewReader(file)))
142143

143144
t.Log("File URL:", url)
144145
if !strings.HasSuffix(url, "meow.txt") {
@@ -165,7 +166,7 @@ func TestDifferentFilename(t *testing.T) {
165166
defer ts.Close()
166167
c := ts.Client()
167168

168-
fileUrl := string(doPOST(t, c, ts.URL + "/filedrop/meow.txt", "text/plain", strings.NewReader(file)))
169+
fileUrl := string(doPOST(t, c, ts.URL+"/filedrop/meow.txt", "text/plain", strings.NewReader(file)))
169170

170171
t.Log("File URL:", fileUrl)
171172
if !strings.HasSuffix(fileUrl, "meow.txt") {
@@ -204,7 +205,7 @@ func TestNonExistent(t *testing.T) {
204205
defer ts.Close()
205206
c := ts.Client()
206207

207-
code := doGETFail(t, c, ts.URL + "/filedrop/AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA/meow2.txt")
208+
code := doGETFail(t, c, ts.URL+"/filedrop/AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA/meow2.txt")
208209
if code != 404 {
209210
t.Error("GET: HTTP", code)
210211
t.FailNow()
@@ -219,7 +220,7 @@ func TestContentTypePreserved(t *testing.T) {
219220
defer ts.Close()
220221
c := ts.Client()
221222

222-
url := string(doPOST(t, c, ts.URL + "/filedrop/meow.txt", "text/kitteh", strings.NewReader(file)))
223+
url := string(doPOST(t, c, ts.URL+"/filedrop/meow.txt", "text/kitteh", strings.NewReader(file)))
223224

224225
t.Log("File URL:", url)
225226

@@ -234,23 +235,120 @@ func TestContentTypePreserved(t *testing.T) {
234235
t.Error("ioutil.ReadAll:", err)
235236
t.FailNow()
236237
}
237-
if resp.StatusCode / 100 != 2 {
238+
if resp.StatusCode/100 != 2 {
238239
t.Error("GET: HTTP", resp.Status)
239240
t.Error("Body:", string(body))
240241
t.FailNow()
241242
}
242243
if resp.Header.Get("Content-Type") != "text/kitteh" {
243244
t.Log("Mismatched content type:")
244245
t.Log("\tWanted: 'text/kitteh'")
245-
t.Log("\tGot:", "'" + resp.Header.Get("Content-Type") + "'")
246+
t.Log("\tGot:", "'"+resp.Header.Get("Content-Type")+"'")
246247
t.Fail()
247248
}
248249
}
249250

251+
func TestNoContentType(t *testing.T) {
252+
serv := initServ(filedrop.Default)
253+
ts := httptest.NewServer(serv)
254+
defer os.RemoveAll(serv.Conf.StorageDir)
255+
defer serv.Close()
256+
defer ts.Close()
257+
c := ts.Client()
258+
259+
url := string(doPOST(t, c, ts.URL+"/filedrop/meow.txt", "", strings.NewReader(file)))
260+
261+
t.Log("File URL:", url)
262+
263+
resp, err := c.Get(url)
264+
if err != nil {
265+
t.Error("GET:", err)
266+
t.FailNow()
267+
}
268+
defer resp.Body.Close()
269+
body, err := ioutil.ReadAll(resp.Body)
270+
if err != nil {
271+
t.Error("ioutil.ReadAll:", err)
272+
t.FailNow()
273+
}
274+
if resp.StatusCode/100 != 2 {
275+
t.Error("GET: HTTP", resp.Status)
276+
t.Error("Body:", string(body))
277+
t.FailNow()
278+
}
279+
t.Log("Got:", "'"+resp.Header.Get("Content-Type")+"'")
280+
}
281+
282+
func TestHTTPSDownstream(t *testing.T) {
283+
serv := initServ(filedrop.Default)
284+
ts := httptest.NewServer(serv)
285+
defer os.RemoveAll(serv.Conf.StorageDir)
286+
defer serv.Close()
287+
defer ts.Close()
288+
c := ts.Client()
289+
290+
t.Run("X-HTTPS-Downstream=1", func(t *testing.T) {
291+
req, err := http.NewRequest("POST", ts.URL, strings.NewReader(file))
292+
if err != nil {
293+
t.Error(err)
294+
t.FailNow()
295+
}
296+
req.Header.Set("X-HTTPS-Downstream", "1")
297+
resp, err := c.Do(req)
298+
if err != nil {
299+
t.Error("POST:", err)
300+
t.FailNow()
301+
}
302+
defer resp.Body.Close()
303+
body, err := ioutil.ReadAll(resp.Body)
304+
if err != nil {
305+
t.Error("ioutil.ReadAll:", err)
306+
t.FailNow()
307+
}
308+
if resp.StatusCode/100 != 2 {
309+
t.Error("POST: HTTP", resp.StatusCode, resp.Status)
310+
t.Error("Body:", string(body))
311+
t.FailNow()
312+
}
313+
if !strings.HasPrefix(string(body), "https") {
314+
t.Error("Got non-HTTPS URl with X-HTTPS-Downstream=1")
315+
t.FailNow()
316+
}
317+
})
318+
t.Run("X-HTTPS-Downstream=0", func(t *testing.T) {
319+
req, err := http.NewRequest("POST", ts.URL, strings.NewReader(file))
320+
if err != nil {
321+
t.Error(err)
322+
t.FailNow()
323+
}
324+
req.Header.Set("X-HTTPS-Downstream", "0")
325+
resp, err := c.Do(req)
326+
if err != nil {
327+
t.Error("POST:", err)
328+
t.FailNow()
329+
}
330+
defer resp.Body.Close()
331+
body, err := ioutil.ReadAll(resp.Body)
332+
if err != nil {
333+
t.Error("ioutil.ReadAll:", err)
334+
t.FailNow()
335+
}
336+
if resp.StatusCode/100 != 2 {
337+
t.Error("POST: HTTP", resp.StatusCode, resp.Status)
338+
t.Error("Body:", string(body))
339+
t.FailNow()
340+
}
341+
if !strings.HasPrefix(string(body), "http") {
342+
t.Error("Got non-HTTP URL with X-HTTPS-Downstream=0")
343+
t.FailNow()
344+
}
345+
})
346+
}
347+
250348
func testWithPrefix(t *testing.T, ts *httptest.Server, c *http.Client, prefix string) {
251349
var URL string
252-
t.Run("submit with prefix " + prefix, func(t *testing.T) {
253-
URL = string(doPOST(t, c, ts.URL + prefix + "/meow.txt", "text/plain", strings.NewReader(file)))
350+
t.Run("submit with prefix "+prefix, func(t *testing.T) {
351+
URL = string(doPOST(t, c, ts.URL+prefix+"/meow.txt", "text/plain", strings.NewReader(file)))
254352
})
255353

256354
if !strings.Contains(URL, prefix) {
@@ -259,7 +357,7 @@ func testWithPrefix(t *testing.T, ts *httptest.Server, c *http.Client, prefix st
259357
}
260358

261359
if URL != "" {
262-
t.Run("get with " + prefix, func(t *testing.T) {
360+
t.Run("get with "+prefix, func(t *testing.T) {
263361
body := doGET(t, c, URL)
264362
if string(body) != file {
265363
t.Error("Got different file!")

0 commit comments

Comments
 (0)