github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/apiserver/static.go (about)

     1  // Copyright © 2021 Kaleido, Inc.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package apiserver
    18  
    19  import (
    20  	"encoding/json"
    21  	"net/http"
    22  	"os"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	"github.com/kaleido-io/firefly/internal/i18n"
    27  	"github.com/kaleido-io/firefly/internal/log"
    28  	"github.com/kaleido-io/firefly/pkg/fftypes"
    29  )
    30  
    31  type staticHandler struct {
    32  	staticPath string
    33  	indexPath  string
    34  	urlPrefix  string
    35  	os         osInterface
    36  }
    37  
    38  func newStaticHandler(staticPath, indexPath, urlPrefix string) *staticHandler {
    39  	return &staticHandler{
    40  		staticPath: staticPath,
    41  		indexPath:  indexPath,
    42  		urlPrefix:  urlPrefix,
    43  		os:         &realOS{},
    44  	}
    45  }
    46  
    47  func (h staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    48  	path, err := filepath.Rel(h.urlPrefix, r.URL.Path)
    49  	if err != nil {
    50  		// if we failed to get the path respond with a 404
    51  		http.Error(w, err.Error(), http.StatusNotFound)
    52  		return
    53  	}
    54  
    55  	// prepend the path with the path to the static directory
    56  	path = filepath.Join(h.staticPath, strings.ReplaceAll(path, "..", "_"), "/")
    57  
    58  	// check whether a file exists at the given path
    59  	_, err = h.os.Stat(path)
    60  	if os.IsNotExist(err) {
    61  		// file does not exist, serve index.html
    62  		http.ServeFile(w, r, filepath.Join(h.staticPath, h.indexPath))
    63  		return
    64  	} else if err != nil {
    65  		// if we got an error (that wasn't that the file doesn't exist) stating the
    66  		// file, return a 500 internal server error and stop
    67  		log.L(r.Context()).Errorf("Failed to serve file: %s", err)
    68  		w.WriteHeader(500)
    69  		_ = json.NewEncoder(w).Encode(&fftypes.RESTError{
    70  			Error: i18n.ExpandWithCode(r.Context(), i18n.MsgAPIServerStaticFail),
    71  		})
    72  		return
    73  	}
    74  
    75  	// otherwise, use http.FileServer to serve the static dir
    76  	http.StripPrefix(h.urlPrefix, http.FileServer(http.Dir(h.staticPath))).ServeHTTP(w, r)
    77  }
    78  
    79  type osInterface interface {
    80  	Stat(name string) (os.FileInfo, error)
    81  }
    82  
    83  type realOS struct{}
    84  
    85  func (r *realOS) Stat(name string) (os.FileInfo, error) {
    86  	return os.Stat(name)
    87  }