Skip to content

Commit 1d731d8

Browse files
author
Sergio Andres Virviescas Santana
committed
Add ServeFiles with custom settings
1 parent 3406fea commit 1d731d8

File tree

2 files changed

+142
-147
lines changed

2 files changed

+142
-147
lines changed

router.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,14 +302,48 @@ func (r *Router) ServeFiles(path string, rootPath string) {
302302
}
303303

304304
prefix := path[:len(path)-10]
305-
306305
fileHandler := fasthttp.FSHandler(rootPath, strings.Count(prefix, "/"))
307306

308307
r.GET(path, func(ctx *fasthttp.RequestCtx) {
309308
fileHandler(ctx)
310309
})
311310
}
312311

312+
// ServeFilesCustom serves files from the given file system settings.
313+
// The path must end with "/*filepath", files are then served from the local
314+
// path /defined/root/dir/*filepath.
315+
// For example if root is "/etc" and *filepath is "passwd", the local file
316+
// "/etc/passwd" would be served.
317+
// Internally a http.FileServer is used, therefore http.NotFound is used instead
318+
// of the Router's NotFound handler.
319+
// router.ServeFilesCustom("/src/*filepath", *customFS)
320+
func (r *Router) ServeFilesCustom(path string, fs *fasthttp.FS) {
321+
if len(path) < 10 || path[len(path)-10:] != "/*filepath" {
322+
panic("path must end with /*filepath in path '" + path + "'")
323+
}
324+
325+
if r.beginPath != "/" {
326+
path = r.beginPath + path
327+
}
328+
329+
if r.parent != nil {
330+
r.parent.ServeFilesCustom(path, fs)
331+
return
332+
}
333+
334+
prefix := path[:len(path)-10]
335+
stripSlashes := strings.Count(prefix, "/")
336+
337+
if fs.PathRewrite == nil && stripSlashes > 0 {
338+
fs.PathRewrite = fasthttp.NewPathSlashesStripper(stripSlashes)
339+
}
340+
fileHandler := fs.NewRequestHandler()
341+
342+
r.GET(path, func(ctx *fasthttp.RequestCtx) {
343+
fileHandler(ctx)
344+
})
345+
}
346+
313347
func (r *Router) recv(ctx *fasthttp.RequestCtx) {
314348
if rcv := recover(); rcv != nil {
315349
r.PanicHandler(ctx, rcv)

router_test.go

Lines changed: 107 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"net"
1313
"net/http"
1414
"os"
15+
"strings"
1516
"testing"
1617
"time"
1718

@@ -343,34 +344,35 @@ func TestRouterGroup(t *testing.T) {
343344
r4 := r1.Group("/moo")
344345
r5 := r4.Group("/foo")
345346
r6 := r5.Group("/foo")
346-
fooHit := false
347+
348+
hit := false
349+
347350
r1.POST("/foo", func(ctx *fasthttp.RequestCtx) {
348-
fooHit = true
351+
hit = true
349352
ctx.SetStatusCode(fasthttp.StatusOK)
350353
})
351-
352-
barHit := false
353354
r2.POST("/bar", func(ctx *fasthttp.RequestCtx) {
354-
barHit = true
355+
hit = true
355356
ctx.SetStatusCode(fasthttp.StatusOK)
356357
})
357358
r3.POST("/bar", func(ctx *fasthttp.RequestCtx) {
358-
barHit = true
359+
hit = true
359360
ctx.SetStatusCode(fasthttp.StatusOK)
360361
})
361362
r4.POST("/bar", func(ctx *fasthttp.RequestCtx) {
362-
barHit = true
363+
hit = true
363364
ctx.SetStatusCode(fasthttp.StatusOK)
364365
})
365366
r5.POST("/bar", func(ctx *fasthttp.RequestCtx) {
366-
barHit = true
367+
hit = true
367368
ctx.SetStatusCode(fasthttp.StatusOK)
368369
})
369370
r6.POST("/bar", func(ctx *fasthttp.RequestCtx) {
370-
barHit = true
371+
hit = true
371372
ctx.SetStatusCode(fasthttp.StatusOK)
372373
})
373374
r6.ServeFiles("/static/*filepath", "./")
375+
r6.ServeFilesCustom("/custom/static/*filepath", &fasthttp.FS{Root: "./"})
374376

375377
s := &fasthttp.Server{
376378
Handler: r1.Handler,
@@ -379,149 +381,53 @@ func TestRouterGroup(t *testing.T) {
379381
rw := &readWriter{}
380382
ch := make(chan error)
381383

382-
rw.r.WriteString("POST /foo HTTP/1.1\r\n\r\n")
383-
go func() {
384-
ch <- s.ServeConn(rw)
385-
}()
386-
select {
387-
case err := <-ch:
388-
if err != nil {
389-
t.Fatalf("return error %s", err)
390-
}
391-
case <-time.After(100 * time.Millisecond):
392-
t.Fatalf("timeout")
393-
}
394-
br := bufio.NewReader(&rw.w)
395-
var resp fasthttp.Response
396-
if err := resp.Read(br); err != nil {
397-
t.Fatalf("Unexpected error when reading response: %s", err)
398-
}
399-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && fooHit) {
400-
t.Errorf("Regular routing failed with router chaining.")
401-
t.FailNow()
402-
}
403-
// testing router group - r2 (grouped from r1)
404-
rw.r.WriteString("POST /boo/bar HTTP/1.1\r\n\r\n")
405-
go func() {
406-
ch <- s.ServeConn(rw)
407-
}()
408-
select {
409-
case err := <-ch:
410-
if err != nil {
411-
t.Fatalf("return error %s", err)
412-
}
413-
case <-time.After(100 * time.Millisecond):
414-
t.Fatalf("timeout")
415-
}
416-
if err := resp.Read(br); err != nil {
417-
t.Fatalf("Unexpected error when reading response: %s", err)
418-
}
419-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) {
420-
t.Errorf("Chained routing failed with router grouping.")
421-
t.FailNow()
422-
}
423-
// testing multiple router group - r3 (grouped from r1)
424-
rw.r.WriteString("POST /goo/bar HTTP/1.1\r\n\r\n")
425-
go func() {
426-
ch <- s.ServeConn(rw)
427-
}()
428-
select {
429-
case err := <-ch:
430-
if err != nil {
431-
t.Fatalf("return error %s", err)
432-
}
433-
case <-time.After(100 * time.Millisecond):
434-
t.Fatalf("timeout")
435-
}
436-
if err := resp.Read(br); err != nil {
437-
t.Fatalf("Unexpected error when reading response: %s", err)
438-
}
439-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) {
440-
t.Errorf("Chained routing failed with router grouping.")
441-
t.FailNow()
442-
}
443-
// testing multiple router group - r4 (grouped from r1)
444-
rw.r.WriteString("POST /moo/bar HTTP/1.1\r\n\r\n")
445-
go func() {
446-
ch <- s.ServeConn(rw)
447-
}()
448-
select {
449-
case err := <-ch:
450-
if err != nil {
451-
t.Fatalf("return error %s", err)
384+
requests := []string{
385+
"POST /foo HTTP/1.1\r\n\r\n",
386+
// testing router group - r2 (grouped from r1)
387+
"POST /boo/bar HTTP/1.1\r\n\r\n",
388+
// testing multiple router group - r3 (grouped from r1)
389+
"POST /goo/bar HTTP/1.1\r\n\r\n",
390+
// testing multiple router group - r4 (grouped from r1)
391+
"POST /moo/bar HTTP/1.1\r\n\r\n",
392+
// testing sub-router group - r5 (grouped from r4)
393+
"POST /moo/foo/bar HTTP/1.1\r\n\r\n",
394+
// testing multiple sub-router group - r6 (grouped from r5)
395+
"POST /moo/foo/foo/bar HTTP/1.1\r\n\r\n",
396+
// testing multiple sub-router group - r6 (grouped from r5) to serve files
397+
"GET /moo/foo/foo/static/router.go HTTP/1.1\r\n\r\n",
398+
// testing multiple sub-router group - r6 (grouped from r5) to serve files with custom settings
399+
"GET /moo/foo/foo/custom/static/router.go HTTP/1.1\r\n\r\n",
400+
}
401+
402+
for _, req := range requests {
403+
hit = false
404+
405+
rw.r.WriteString(req)
406+
go func() {
407+
ch <- s.ServeConn(rw)
408+
}()
409+
select {
410+
case err := <-ch:
411+
if err != nil {
412+
t.Fatalf("return error %s", err)
413+
}
414+
case <-time.After(100 * time.Millisecond):
415+
t.Fatalf("timeout")
452416
}
453-
case <-time.After(100 * time.Millisecond):
454-
t.Fatalf("timeout")
455-
}
456-
if err := resp.Read(br); err != nil {
457-
t.Fatalf("Unexpected error when reading response: %s", err)
458-
}
459-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) {
460-
t.Errorf("Chained routing failed with router grouping.")
461-
t.FailNow()
462-
}
463-
// testing sub-router group - r5 (grouped from r4)
464-
rw.r.WriteString("POST /moo/foo/bar HTTP/1.1\r\n\r\n")
465-
go func() {
466-
ch <- s.ServeConn(rw)
467-
}()
468-
select {
469-
case err := <-ch:
470-
if err != nil {
471-
t.Fatalf("return error %s", err)
417+
br := bufio.NewReader(&rw.w)
418+
var resp fasthttp.Response
419+
if err := resp.Read(br); err != nil {
420+
t.Fatalf("Unexpected error when reading response: %s", err)
472421
}
473-
case <-time.After(100 * time.Millisecond):
474-
t.Fatalf("timeout")
475-
}
476-
if err := resp.Read(br); err != nil {
477-
t.Fatalf("Unexpected error when reading response: %s", err)
478-
}
479-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) {
480-
t.Errorf("Chained routing failed with subrouter grouping.")
481-
t.FailNow()
482-
}
483-
// testing multiple sub-router group - r6 (grouped from r5)
484-
rw.r.WriteString("POST /moo/foo/foo/bar HTTP/1.1\r\n\r\n")
485-
go func() {
486-
ch <- s.ServeConn(rw)
487-
}()
488-
select {
489-
case err := <-ch:
490-
if err != nil {
491-
t.Fatalf("return error %s", err)
422+
if !(resp.Header.StatusCode() == fasthttp.StatusOK) {
423+
t.Fatalf("Status code %d, want %d", resp.Header.StatusCode(), fasthttp.StatusOK)
492424
}
493-
case <-time.After(100 * time.Millisecond):
494-
t.Fatalf("timeout")
495-
}
496-
if err := resp.Read(br); err != nil {
497-
t.Fatalf("Unexpected error when reading response: %s", err)
498-
}
499-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) {
500-
t.Errorf("Chained routing failed with subrouter grouping.")
501-
t.FailNow()
502-
}
503-
504-
// testing multiple sub-router group - r6 (grouped from r5) to serve files
505-
rw.r.WriteString("GET /moo/foo/foo/static/router.go HTTP/1.1\r\n\r\n")
506-
go func() {
507-
ch <- s.ServeConn(rw)
508-
}()
509-
select {
510-
case err := <-ch:
511-
if err != nil {
512-
t.Fatalf("return error %s", err)
425+
if !strings.Contains(req, "static") && !hit {
426+
t.Fatalf("Regular routing failed with router chaining. %s", req)
513427
}
514-
case <-time.After(100 * time.Millisecond):
515-
t.Fatalf("timeout")
516-
}
517-
if err := resp.Read(br); err != nil {
518-
t.Fatalf("Unexpected error when reading response: %s", err)
519-
}
520-
if !(resp.Header.StatusCode() == fasthttp.StatusOK && barHit) {
521-
t.Errorf("Chained routing failed with subrouter grouping.")
522-
t.FailNow()
523428
}
524429

430+
// Not found
525431
rw.r.WriteString("POST /qax HTTP/1.1\r\n\r\n")
526432
go func() {
527433
ch <- s.ServeConn(rw)
@@ -534,6 +440,8 @@ func TestRouterGroup(t *testing.T) {
534440
case <-time.After(100 * time.Millisecond):
535441
t.Fatalf("timeout")
536442
}
443+
br := bufio.NewReader(&rw.w)
444+
var resp fasthttp.Response
537445
if err := resp.Read(br); err != nil {
538446
t.Fatalf("Unexpected error when reading response: %s", err)
539447
}
@@ -1095,6 +1003,59 @@ func TestRouterServeFiles(t *testing.T) {
10951003
}
10961004
}
10971005

1006+
func TestRouterServeFilesCustom(t *testing.T) {
1007+
r := New()
1008+
1009+
root := os.TempDir()
1010+
1011+
fs := &fasthttp.FS{
1012+
Root: root,
1013+
}
1014+
1015+
recv := catchPanic(func() {
1016+
r.ServeFilesCustom("/noFilepath", fs)
1017+
})
1018+
if recv == nil {
1019+
t.Fatal("registering path not ending with '*filepath' did not panic")
1020+
}
1021+
body := []byte("fake ico")
1022+
ioutil.WriteFile(root+"/favicon.ico", body, 0644)
1023+
1024+
r.ServeFilesCustom("/*filepath", fs)
1025+
1026+
s := &fasthttp.Server{
1027+
Handler: r.Handler,
1028+
}
1029+
1030+
rw := &readWriter{}
1031+
ch := make(chan error)
1032+
1033+
rw.r.WriteString(string("GET /favicon.ico HTTP/1.1\r\n\r\n"))
1034+
go func() {
1035+
ch <- s.ServeConn(rw)
1036+
}()
1037+
select {
1038+
case err := <-ch:
1039+
if err != nil {
1040+
t.Fatalf("return error %s", err)
1041+
}
1042+
case <-time.After(500 * time.Millisecond):
1043+
t.Fatalf("timeout")
1044+
}
1045+
1046+
br := bufio.NewReader(&rw.w)
1047+
var resp fasthttp.Response
1048+
if err := resp.Read(br); err != nil {
1049+
t.Fatalf("Unexpected error when reading response: %s", err)
1050+
}
1051+
if resp.Header.StatusCode() != 200 {
1052+
t.Fatalf("Unexpected status code %d. Expected %d", resp.Header.StatusCode(), 200)
1053+
}
1054+
if !bytes.Equal(resp.Body(), body) {
1055+
t.Fatalf("Unexpected body %q. Expected %q", resp.Body(), string(body))
1056+
}
1057+
}
1058+
10981059
type readWriter struct {
10991060
net.Conn
11001061
r bytes.Buffer

0 commit comments

Comments
 (0)