Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reproduce path error on windows gorilla#588 #1

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3d1a52d
add `.github/workflows/pipeline.yml` file to run test cases on diff os
amustaque97 May 31, 2022
900686d
remove other os and keep windows-latest
amustaque97 May 31, 2022
5d6b912
remove `golint` from github actions
amustaque97 May 31, 2022
f0c49ad
add `linux` and `macos` as runners
amustaque97 May 31, 2022
ad2688d
remove `macos` as runner
amustaque97 May 31, 2022
b1f3adf
remove `ubuntu-latest` as runner
amustaque97 May 31, 2022
243c2c1
add `ubuntu-latest` as runner
amustaque97 May 31, 2022
f1ea8c7
remove `latest` from `ubuntu` runner name
amustaque97 May 31, 2022
cc017bc
add quotes to the runners array element
amustaque97 May 31, 2022
84776a4
fix pipeline to run on multiple os
amustaque97 May 31, 2022
35cd00a
add test case to reproduce `filepath.Abs` err on windows
amustaque97 May 31, 2022
4f0d6a3
add `concurrency` property to `pipeline.yml`
amustaque97 May 31, 2022
762ae15
replace `filepath.Abs` with `filepath.Join` inside `ServeHTTP`
amustaque97 May 31, 2022
1ef820e
comment 2nd occurance of filepath.join
amustaque97 May 31, 2022
fa25f03
add test case negative and positive for `SPA` filepath.Abs err on win…
amustaque97 Jun 1, 2022
eee2166
fix comments wrap-lines and uncomment code
amustaque97 Jun 1, 2022
5ce7865
Merge branch 'fix/update-readme-serve-spa' into develop
amustaque97 Jun 1, 2022
10803b5
fix condition of response check of `TestServeHttpFilepathAbs` testcase
amustaque97 Jun 1, 2022
77677fa
allow all branches and PR, run go >= 1.13
amustaque97 Jun 20, 2022
716948e
fix indentation and add tags to run pipeline
amustaque97 Jun 20, 2022
e2cb73f
add multiple golang versions
amustaque97 Jun 20, 2022
c6ffc02
replace `latest` version to `1.18` go version
amustaque97 Jun 20, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# This is a basic workflow to help you get started with Actions

name: Continous Integration

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
push:
branches:
- '*'
pull_request:
- '*'
tags:
- '*'

# Cancels all previous workflow runs for pull requests that have not completed.
concurrency:
# The concurrency group contains the workflow name and the branch name for pull requests
# or the commit hash for any other events.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# The "build" workflow
build:
# The type of runner that the job will run on
runs-on: ${{matrix.os}}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
golang: ["1.18", "1.17", "1.16"]

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2

# Setup Go
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: ${{matrix.golang}} # The Go version to download (if necessary) and use.

# Install all the dependencies
- name: Install dependencies
run: |
go version
go get -u golang.org/x/lint/golint

# Run build of the application
- name: Run build
run: go build .

# Run vet & lint on the code
- name: Run vet & lint
run: |
go vet .

# Run testing on the code
- name: Run test suites
run: go test -v

163 changes: 162 additions & 1 deletion mux_httpserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,96 @@ import (
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)

func TestSchemeMatchers(t *testing.T) {
// spaHandler implements the http.Handler interface, so we can use it
// to respond to HTTP requests. The path to the static directory and
// path to the index file within that static directory are used to
// serve the SPA in the given static directory.
type spaHandler struct {
staticPath string
indexPath string
}

// FilepathAbsServeHTTP inspects the URL path to locate a file within the static dir
// on the SPA handler. If a file is found, it will be served. If not, the
// file located at the index path on the SPA handler will be served. This
// is suitable behavior for serving an SPA (single page application).
// This is a negative test case where `filepath.Abs` will return path value like `D:\`
// if our route is `/`. As per docs: Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current working directory to turn
// it into an absolute path. The absolute path name for a given file is not guaranteed to
// be unique. Abs calls Clean on the result.
func (h spaHandler) FilepathAbsServeHTTP(w http.ResponseWriter, r *http.Request) { // {{{
// get the absolute path to prevent directory traversal
path, err := filepath.Abs(r.URL.Path)
if err != nil {
// if we failed to get the absolute path respond with a 400 bad request
// and stop
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// prepend the path with the path to the static directory
path = filepath.Join(h.staticPath, path)

// check whether a file exists at the given path
_, err = os.Stat(path)

if os.IsNotExist(err) {
// file does not exist, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
// file, return a 500 internal server error and stop
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// otherwise, use http.FileServer to serve the static dir
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
} // }}}

// ServeHTTP inspects the URL path to locate a file within the static dir
// on the SPA handler. If a file is found, it will be served. If not, the
// file located at the index path on the SPA handler will be served. This
// is suitable behavior for serving an SPA (single page application).
func (h spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var err error
// internally calls path.Clean path to prevent directory traversal
path := filepath.Join(h.staticPath, r.URL.Path)
if err != nil {
// if we failed to get the absolute path respond with a 400 bad request
// and stop
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// check whether a file exists at the given path
_, err = os.Stat(path)

if os.IsNotExist(err) {
// file does not exist, serve index.html
http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
return
} else if err != nil {
// if we got an error (that wasn't that the file doesn't exist) stating the
// file, return a 500 internal server error and stop
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// otherwise, use http.FileServer to serve the static dir
http.FileServer(http.Dir(h.staticPath)).ServeHTTP(w, r)
} // }}}

func TestSchemeMatchers(t *testing.T) { // {{{
router := NewRouter()
router.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
rw.Write([]byte("hello http world"))
Expand Down Expand Up @@ -47,4 +133,79 @@ func TestSchemeMatchers(t *testing.T) {
defer s.Close()
assertResponseBody(t, s, "hello https world")
})
} // }}}

func TestServeHttpFilepathAbs(t *testing.T) { // {{{
// create a diretory name `build`
os.Mkdir("build", 0700)

// create a file `index.html` and write below content
htmlContent := []byte(`<html><head><title>hello</title></head><body>world</body></html>`)
err := os.WriteFile("./build/index.html", htmlContent, 0700)
if err != nil {
t.Fatal(err)
}

// make new request
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
spa := spaHandler{staticPath: "./build", indexPath: "index.html"}
spa.FilepathAbsServeHTTP(rr, req)

status := rr.Code
if runtime.GOOS != "windows" && status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
} else if runtime.GOOS == "windows" && rr.Code != http.StatusInternalServerError {
t.Errorf("handler returned wrong status code in case of windows: got %v want %v",
status, http.StatusOK)
}

// Check the response body is what we expect.
if runtime.GOOS != "windows" && rr.Body.String() != string(htmlContent) {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), string(htmlContent))
} else if runtime.GOOS == "windows" && !strings.Contains(rr.Body.String(), "syntax is incorrect.") {
t.Errorf("handler returned unexpected body in case of windows: got %v want %v",
rr.Body.String(), string(htmlContent))
}
} // }}}

func TestServeHttpFilepathJoin(t *testing.T) {
// create a diretory name `build`
os.Mkdir("build", 0700)

// create a file `index.html` and write below content
htmlContent := []byte(`<html><head><title>hello</title></head><body>world</body></html>`)
err := os.WriteFile("./build/index.html", htmlContent, 0700)
if err != nil {
t.Fatal(err)
}

// make new request
req, err := http.NewRequest("GET", "/", nil)
if err != nil {
t.Fatal(err)
}

// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
spa := spaHandler{staticPath: "./build", indexPath: "index.html"}
spa.ServeHTTP(rr, req)

if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}

// Check the response body is what we expect.
if rr.Body.String() != string(htmlContent) {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), string(htmlContent))
}
}