Serving HTTP Requests and Files using same server in Go Lang - http

Hi I am trying to create a server in Go Lang which serves files and HTTP Requests at the same time.
I want /upload path to accept post requests and
/files path to serve static files in the fpath
I tried with the following code but i get a 404 error
func main() {
fpath, _ := filepath.Abs(filepath.Dir(os.Args[0]))
fpath+="/public"
fmt.Println(fpath)
http.HandleFunc("/upload",uploadFunc)
http.HandleFunc("/files",http.FileServer(http.Dir(fpath)))
panic(http.ListenAndServe(":8080", nil))
}

You need trailing slashes on your handle path if it's a directory. See http://golang.org/pkg/net/http/#ServeMux for more info.
Patterns name fixed, rooted paths, like "/favicon.ico", or rooted subtrees, like "/images/" (note the trailing slash).
Try
func main() {
fpath, _ := filepath.Abs(filepath.Dir(os.Args[0]))
fpath+="/public"
fmt.Println(fpath)
http.HandleFunc("/upload",uploadFunc)
http.Handle("/files/", http.StripPrefix("/files/", http.FileServer(http.Dir(fpath))))
panic(http.ListenAndServe(":8080", nil))
}

Related

How do I dynamically change static files directory of the http server?

I have the following piece of code, that serves static files from a static files directory (staticFilesDir):
for _, prefix := range []string{"css", "img", "js", "static"} {
prefix = "/" + prefix + "/"
fs := http.FileServer(http.Dir(staticFilesDir + prefix))
r.PathPrefix(prefix).Handler(http.StripPrefix(prefix, fs))
}
This directory changes from time to time, and currently I always need to restart the server process to use the new value.
How can I reconfigure/reload the FileServer without restarting the whole process?
Additional complication to that: other handlers of the http server are executing long-running jobs (incl. child processes etc.), that I would like to keep untouched during this reload.
What is the standard solution of this quite typical task?
You can add a level of indirection in between:
type MyFileServer struct {
sync.RWMutex
http.FileServer
}
func (f *MyFileServer) SetDir(dir string) {
f.Lock()
defer f.Unlock()
f.FileServer=http.FileServer(dir)
}
func (f *MyFileServer) ServeHTTP(w http.ResponseWriter,req *http.Request) {
f.RLock()
defer f.RUnlock()
f.FileServer.ServeHTTP(w,req)
}

Is it possible to run http.ListenAndServe() AND ReadFromUDP() concurrently?

I am trying to write a simple web app that will listen for UDP packets.
But I can either only listen for UDP packets, or run the web app...
I am not familiar with GoLang, but here's the code I'm using to...
listen for UDP:
ServerConn, _ := net.ListenUDP("udp", &net.UDPAddr{IP:[]byte{#,#,#,#},Port:####,Zone:""})
defer ServerConn.Close()
buf := make([]byte, 1024)
for {
n, addr, _ := ServerConn.ReadFromUDP(buf)
fmt.Println("Received ", string(buf[0:n]), " from ", addr)
}
Server logic:
package main
We import 4 important libraries
1. “net/http” to access the core go http functionality
2. “fmt” for formatting our text
3. “html/template” a library that allows us to interact with our html file.
4. "time" - a library for working with date and time.
import (
"net/http"
"fmt"
"time"
"html/template"
)
//Create a struct that holds information to be displayed in our HTML file
type Welcome struct {
Name string
Time string
}
//Go application entrypoint
func main() {
//Instantiate a Welcome struct object and pass in some random information.
//We shall get the name of the user as a query parameter from the URL
welcome := Welcome{"Anonymous", time.Now().Format(time.Stamp)}
//We tell Go exactly where we can find our html file. We ask Go to parse the html file (Notice
// the relative path). We wrap it in a call to template.Must() which handles any errors and halts if there are fatal errors
templates := template.Must(template.ParseFiles("templates/welcome-template.html"))
//Our HTML comes with CSS that go needs to provide when we run the app. Here we tell go to create
// a handle that looks in the static directory, go then uses the "/static/" as a url that our
//html can refer to when looking for our css and other files.
http.Handle("/static/", //final url can be anything
http.StripPrefix("/static/",
http.FileServer(http.Dir("static")))) //Go looks in the relative "static" directory first using http.FileServer(), then matches it to a
//url of our choice as shown in http.Handle("/static/"). This url is what we need when referencing our css files
//once the server begins. Our html code would therefore be <link rel="stylesheet" href="/static/stylesheet/...">
//It is important to note the url in http.Handle can be whatever we like, so long as we are consistent.
//This method takes in the URL path "/" and a function that takes in a response writer, and a http request.
http.HandleFunc("/" , func(w http.ResponseWriter, r *http.Request) {
//Takes the name from the URL query e.g ?name=Martin, will set welcome.Name = Martin.
if name := r.FormValue("name"); name != "" {
welcome.Name = name;
}
//If errors show an internal server error message
//I also pass the welcome struct to the welcome-template.html file.
if err := templates.ExecuteTemplate(w, "welcome-template.html", welcome); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
})
//Start the web server, set the port to listen to 8080. Without a path it assumes localhost
//Print any errors from starting the webserver using fmt
fmt.Println("Listening");
fmt.Println(http.ListenAndServe(":8080", nil));
}
taken from(https://medium.com/google-cloud/building-a-go-web-app-from-scratch-to-deploying-on-google-cloud-part-1-building-a-simple-go-aee452a2e654)
I tried putting both of these extracts in 1 file, as well as running 2 files at the same time using
go run *.go
Any help would be appreciated!
You're going to need to start looking into goroutines - since you're asking to do two things concurrently. I suggest doing some reading into channels, goroutines, and concurrency in general :)

Golang "301 Moved Permanently" if request path contains additional slash

I have been using golang's default http.ServeMux for http route handling.
wrap := func(h func(t *MyStruct, w http.ResponseWriter, r *http.Request)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
h(t, w, r)
}
}
// Register handlers with default mux
httpMux := http.NewServeMux()
httpMux.HandleFunc("/", wrap(payloadHandler))
Assume this server is accessible via http://example.com/
Very few of my client's requests were of path http://example.com/api//module (note the extra slash) which is redirected as 301 Moved Permanently. Exploring inside golang's http ServeMux.Handler(r *Request) function, seems it's intended.
path := cleanPath(r.URL.Path)
// if there is any change between actual and cleaned path, the request is 301 ed
if path != r.URL.Path {
_, pattern = mux.handler(host, path)
url := *r.URL
url.Path = path
return RedirectHandler(url.String(), StatusMovedPermanently), pattern
}
I've looked into other similar issue.
go-web-server-is-automatically-redirecting-post-requests
Above qn has problem with redundant / in register pattern itself, but my use case is not with register pattern (in some nested path which is irrelevent to register pattern)
Problem is, since my client's requests are POST, browsers handle 301 with new GET request with exact query params and POST body. But change in the HTTP method causes the request to fail.
I have already instructed client to fix the redundant / in url, but the fix might take few (?) weeks to be deployed in all client locations.
Also these redundant / are handled fine in Apache Tomcat, but fails only in golang server. So is this the intended behaviour in my use case (redundant / in nested path) with golang or possible bug?
I am thinking of way to override the Handler func of ServeMux, but it won't be useful since Handler calls are made internally. Looking to disable this 301 behaviour, help would be appreciated.
Relevant links
http-post-method-is-actally-sending-a-get
The clean and redirect is intended behavior.
Wrap the mux with a handler that removes the double slashes:
type slashFix struct {
mux http.Handler
}
func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "//", "/", -1)
h.mux.ServeHTTP(w, r)
}
Use it like this:
httpMux := http.NewServeMux()
httpMux.HandleFunc("/", wrap(payloadHandler))
http.ListenAndServe(addr, &slashFix{httpMux})
Accepeted answer solved the problem
One more way is to use Gorilla mux and setting SkipClean(true). But be sure to know about the side effects in its doc
SkipClean defines the path cleaning behaviour for new routes. The initial value is false. Users should be careful about which routes are not cleaned. When true, if the route path is "/path//to", it will remain with the double slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will become /fetch/http/xkcd.com/534
func (r *Router) SkipClean(value bool) *Router {
r.skipClean = value
return r
}

serving static files in different locations

I'm writing a Go Application that serves files in two different directories.
My project structure:
PROJECT_DIR
PROJECT_DIR/config
PROJECT_DIR/src
PROJECT_DIR/client
PROJECT_DIR/client/node_modules
PROJECT_DIR/client/www
in my main go file i start the file server using the following code:
func main() {
log.Print("started web server...");
httpsPortStr := ":" + strconv.FormatUint(config.CfgIni.HttpsPort, 10)
log.Printf("starting https web server at port %v", config.CfgIni.HttpsPort)
http.Handle("/", http.FileServer(http.Dir("client/www")))
http.Handle("/node_modules",http.FileServer(http.Dir(("client/node_modules"))))
err := http.ListenAndServeTLS(httpsPortStr, config.CfgIni.CertificateFile, config.CfgIni.PrivateKeyFile, nil)
if err != nil {
log.Fatalf("https server stopped with the following error: %v", err)
} else {
log.Print("https server stopped with no error")
}
}
as you can see I mapped / to client/www and /node_modules to client/node_modules.
when I try to access files on client/www for example https://host:port/test.html, it works great!
when I try to access files on client/node_modules for example: https://host:port/node_modules/test.html, I get 404 page not found.
test.html file exists in both location and is readable (no permission problems).
I'm probably configuring the routing wrong somehow.
any ideas?
thanks!
The FileServer is trying to route paths such as /node_modules/... to the file "client/node_modules/node_modules/..."
So use StripPrefix, eg:
http.Handle("/node_modules", http.StripPrefix("/node_modules", http.FileServer(http.Dir(("client/node_modules")))))
See another answer here.

Golang Modify HTTP Request Parameters Such As URL Path Before Routing

It's common practice in some cases to pass plain URIs as suffix of the path instead of a query parameter. Here is an example from Internet Archive's Wayback Machine.
https://web.archive.org/web/20150825082012/http://example.com/
In this example, user is requesting a copy of http://example.com/ as captured at 2015-08-25 08:20:12. If we were to implement similar service in Go, we probably would have a router as follows:
http.HandleFunc("/web/", returnArchivedCopy)
Then in the returnArchivedCopy handler function, we will split r.URL.Path (where r is the Request object) to extract the date-time and the target URL. However there is a problem in this style of URL scheme; Go's net/http package calls cleanPath function on the path portion to sanitize it. This sanitization process does various cleanup tasks such as eeliminating . and .. from the path and replace multiple slashes with a single one. This later operation makes sense when because in Unix systems // in the file path are same as /. However this causes an issue in the above described use case as http://example becomes http:/example and the server internally returns a redirect response to the client with the sanitized path.
I am wondering, what are my options in this case? Is there a way to ask HTTP not to sanitize the request path while still utilizing all the default behavior that is shipped with the default (or slightly modified) server, multiplexer, and handler? Or is there a way to modify the request parameters (path in this case) before it hits the multiplexer's routing patterns. If the later is possible, we might try to perform something like URL encoding to avoid the redirect and later decode the URL back in the handler function before extracting desired bits.
I have experimented with some custom handlers and multiplexers, but I am new to Go, hence I was not quite sure how to delegate the routing back to the default handlers after making changes in the request.
You can implement a wrapper mux, that falls back to the default one, here's a very simple example:
func main() {
http.HandleFunc("/blah", func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("w00t"))
})
http.ListenAndServe(":9090", http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
p := strings.SplitN(req.URL.RequestURI()[1:] /*trim the first slash*/, "/", 3)
if len(p) != 3 || p[0] != "web" {
http.DefaultServeMux.ServeHTTP(w, req)
return
}
t, err := time.Parse("20060102150405", p[1])
if err != nil {
http.Error(w, "invalid time", 400)
return
}
url := p[2]
fmt.Fprintf(w, "requested url %v # %v", url, t)
}))
}

Resources