github.com/nuvolaris/nuv@v0.0.0-20240511174247-a74e3a52bfd8/serve.go (about)

     1  // Licensed to the Apache Software Foundation (ASF) under one
     2  // or more contributor license agreements.  See the NOTICE file
     3  // distributed with this work for additional information
     4  // regarding copyright ownership.  The ASF licenses this file
     5  // to you under the Apache License, Version 2.0 (the
     6  // "License"); you may not use this file except in compliance
     7  // with the License.  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,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package main
    19  
    20  import (
    21  	"flag"
    22  	"fmt"
    23  	"log"
    24  	"net"
    25  	"net/http"
    26  	"net/http/httputil"
    27  	"net/url"
    28  	"os"
    29  
    30  	"github.com/pkg/browser"
    31  )
    32  
    33  func Serve(olarisDir string, args []string) error {
    34  	flag := flag.NewFlagSet("serve", flag.ExitOnError)
    35  	flag.Usage = func() {
    36  		fmt.Println(`Serve a local directory on http://localhost:9768. You can change port with the NUV_PORT environment variable.
    37  
    38  Usage:
    39    nuv -serve [options] <dir>
    40  
    41  Options:
    42    -h, --help Print help message
    43    --no-open Do not open browser automatically
    44    --proxy <proxy> Use proxy server
    45  		`)
    46  	}
    47  	// Define command line flags
    48  
    49  	var helpFlag bool
    50  	var noBrowserFlag bool
    51  	var proxyFlag string
    52  
    53  	flag.BoolVar(&helpFlag, "h", false, "Print help message")
    54  	flag.BoolVar(&helpFlag, "help", false, "Print help message")
    55  	flag.BoolVar(&noBrowserFlag, "no-open", false, "Do not open browser")
    56  	flag.StringVar(&proxyFlag, "proxy", "", "Use proxy server")
    57  
    58  	// Parse command line flags
    59  	os.Args = args
    60  	err := flag.Parse(os.Args[1:])
    61  	if err != nil {
    62  		return err
    63  	}
    64  
    65  	// Print help message if requested
    66  	if flag.NArg() != 1 || helpFlag {
    67  		flag.Usage()
    68  		return nil
    69  	}
    70  
    71  	webDir := flag.Arg(0)
    72  
    73  	// run nuv server and open browser
    74  	port := getNuvPort()
    75  	webDirPath := joinpath(os.Getenv("NUV_PWD"), webDir)
    76  	log.Println("Serving directory: " + webDirPath)
    77  
    78  	if !noBrowserFlag {
    79  		if err := browser.OpenURL("http://localhost:" + port); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	localHandler := webFileServerHandler(webDirPath)
    85  
    86  	var proxy *httputil.ReverseProxy = nil
    87  	if proxyFlag != "" {
    88  		remoteUrl, err := url.Parse(proxyFlag)
    89  		if err != nil {
    90  			return err
    91  		}
    92  		proxy = httputil.NewSingleHostReverseProxy(remoteUrl)
    93  	}
    94  
    95  	customHandler := func(w http.ResponseWriter, r *http.Request) {
    96  		// Check if the file exists locally
    97  
    98  		_, err := http.Dir(webDirPath).Open(r.URL.Path)
    99  		if err == nil {
   100  			// Serve the file using the file server
   101  			localHandler.ServeHTTP(w, r)
   102  			return
   103  		}
   104  
   105  		// File not found locally, proxy the request to the remote server
   106  		log.Printf("not found locally %s\n", r.URL.Path)
   107  
   108  		if proxy != nil {
   109  			log.Printf("Proxying to %s\n", proxyFlag)
   110  			proxy.ServeHTTP(w, r)
   111  		}
   112  	}
   113  
   114  	handler := http.HandlerFunc(customHandler)
   115  
   116  	if checkPortAvailable(port) {
   117  		log.Println("Nuvolaris server started at http://localhost:" + port)
   118  		addr := fmt.Sprintf(":%s", port)
   119  		return http.ListenAndServe(addr, handler)
   120  	} else {
   121  		log.Println("Nuvolaris server failed to start. Port already in use?")
   122  		return nil
   123  	}
   124  }
   125  
   126  // Handler to serve the olaris/web directory
   127  func webFileServerHandler(webDir string) http.Handler {
   128  	return http.FileServer(http.Dir(webDir))
   129  }
   130  
   131  func checkPortAvailable(port string) bool {
   132  	ln, err := net.Listen("tcp", ":"+port)
   133  	if err != nil {
   134  		return false
   135  	}
   136  	//nolint:errcheck
   137  	ln.Close()
   138  	return true
   139  }