Skip to content

Commit

Permalink
Add HTTP handlers package
Browse files Browse the repository at this point in the history
  • Loading branch information
samherrmann committed Sep 4, 2020
1 parent 587fb8a commit cbc4112
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 20 deletions.
27 changes: 27 additions & 0 deletions handlers/file_handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package handlers

import (
"net/http"
"os"
)

// FileHandler returns a function to handle file requests. If the provided
// notFoundFile argument is a valid path to a file, then that file is served
// when the requested resource is not found. Set notFoundFile to an empty string
// to serve Go's default "404 page not found" response when the requested
// resource is not found.
func FileHandler(notFoundFile string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if !doesPathExist(r) && notFoundFile != "" {
w.WriteHeader(http.StatusNotFound)
http.ServeFile(w, r, notFoundFile)
return
}
http.ServeFile(w, r, r.URL.Path[1:])
}
}

func doesPathExist(r *http.Request) bool {
_, err := os.Stat("." + r.URL.Path)
return err == nil
}
78 changes: 78 additions & 0 deletions handlers/file_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package handlers

import (
"net/http"
"net/http/httptest"
"testing"
)

func TestFileHandler(t *testing.T) {
type Want struct {
status int
body string
}

type Test struct {
notFoundFile string
requestedFile string
want *Want
}

// Define test cases
tests := []Test{
{
notFoundFile: "",
requestedFile: "/test-files/request-1.txt",
want: &Want{
status: http.StatusOK,
body: "This file is used for testing.",
},
}, {
notFoundFile: "",
requestedFile: "/some-file-that-does-not-exist.txt",
want: &Want{
status: http.StatusNotFound,
body: "404 page not found\n",
},
}, {
notFoundFile: "./test-files/custom-404.txt",
requestedFile: "/some-file-that-does-not-exist.txt",
want: &Want{
status: http.StatusNotFound,
body: "Custom 404 page.",
},
},
}

// Loop over all test cases
for _, tc := range tests {
// Create a request to pass to the handler.
req, err := http.NewRequest("GET", tc.requestedFile, nil)
if err != nil {
t.Fatal(err)
}

// Setup handler with response recorder
rr := httptest.NewRecorder()
handler := http.HandlerFunc(FileHandler(tc.notFoundFile))
handler.ServeHTTP(rr, req)

// Check the status code is what we expect.
if status := rr.Code; status != tc.want.status {
t.Errorf(
"Handler returned wrong status code: want %v, but got %v.",
tc.want.status,
status,
)
}

// Check the response body is what we expect.
if body := rr.Body.String(); body != tc.want.body {
t.Errorf(
"Handler returned unexpected body: want \"%v\", but got \"%v\".",
tc.want.body,
body,
)
}
}
}
1 change: 1 addition & 0 deletions handlers/test-files/custom-404.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Custom 404 page.
1 change: 1 addition & 0 deletions handlers/test-files/request-1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This file is used for testing.
25 changes: 5 additions & 20 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,25 @@
package main

import (
"fmt"
"log"
"net/http"
"os"
"strconv"

"github.com/samherrmann/serveit/flag"
"github.com/samherrmann/serveit/handlers"
)

func main() {
// Parse command-line flags into a configuration object
config := flag.Parse()
// Register file handler
http.HandleFunc("/", fileHandler(config.NotFoundFile))
http.HandleFunc("/", handlers.FileHandler(config.NotFoundFile))
// Start HTTP server
listenAndServe(config.Port)
}

func fileHandler(notFoundFile string) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
if !doesPathExist(r) && notFoundFile != "" {
http.ServeFile(w, r, notFoundFile)
return
}
http.ServeFile(w, r, r.URL.Path[1:])
}
}

func doesPathExist(r *http.Request) bool {
_, err := os.Stat("." + r.URL.Path)
return err == nil
}

func listenAndServe(port int) {
addr := ":" + strconv.Itoa(port)
fmt.Println("Serving current directory on port " + addr)
panic(http.ListenAndServe(addr, nil))
log.Println("Serving current directory on port " + addr)
log.Fatalln(http.ListenAndServe(addr, nil))
}

0 comments on commit cbc4112

Please sign in to comment.