Golang实现静态服务器详解

来源:互联网 发布:2016全国人口流动数据 编辑:程序博客网 时间:2024/05/22 18:50

Golang实现静态服务器详解


Go的http库已经做了很好的底层封装,所以用该库可以用很简单的几十行代码编写一个http静态服务器。下面我们就具体分析一下http库对静态服务器的具体实现。

package mainimport ("net/http")func StaticServer(w http.ResponseWriter, r *http.Request) {http.StripPrefix("/", http.FileServer(http.Dir("./static/"))).ServeHTTP(w, r)}func main() {http.HandleFunc("/", StaticServer)http.ListenAndServe(":8080", nil)}
    以上代码,在本地8080端口上打开一个http服务,可以访问当前目录下的static文件夹下的内容。具体实现都封装在http库里面了,好在go语言的库都是提供源代码的,我们可以一窥其全貌。
   http服务实现部分我将会用另外一篇文章做说明,这里只讲http静态文件部分,即 以下这句代码的实现细节:
http.StripPrefix("/", http.FileServer(http.Dir("./static/"))).ServeHTTP(w, r)

  首先 http.Dir("./static/") 这个结构体在“\Go\src\net\http\fs.go” 文件中实现,代码如下:

// A Dir implements FileSystem using the native file system restricted to a// specific directory tree.//// While the FileSystem.Open method takes '/'-separated paths, a Dir's string// value is a filename on the native file system, not a URL, so it is separated// by filepath.Separator, which isn't necessarily '/'.//// An empty Dir is treated as ".".type Dir stringfunc (d Dir) Open(name string) (File, error) {if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||strings.Contains(name, "\x00") {return nil, errors.New("http: invalid character in file path")}dir := string(d)if dir == "" {dir = "."}f, err := os.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))if err != nil {return nil, err}return f, nil}
  该结构体实现了一个叫做FileSystem  的接口,代码如下:

// A FileSystem implements access to a collection of named files.// The elements in a file path are separated by slash ('/', U+002F)// characters, regardless of host operating system convention.type FileSystem interface {Open(name string) (File, error)}

 http.FileServer<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 函数接受一个 <span style="color: rgb(0, 0, 128); background-color: rgb(240, 240, 240);">FileSystem</span> 接口,返回一个</span><span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 0, 128);">fileHandler</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 结构体,</span><span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 0, 128);">fileHandler</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 实现了http.</span><span style="font-family: Arial, Helvetica, sans-serif; color: rgb(0, 0, 128);">Handler</span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> 接口 ,具体代码如下:  </span>

// FileServer returns a handler that serves HTTP requests// with the contents of the file system rooted at root.//// To use the operating system's file system implementation,// use http.Dir:////     http.Handle("/", http.FileServer(http.Dir("/tmp")))//// As a special case, the returned file server redirects any request// ending in "/index.html" to the same path, without the final// "index.html".func FileServer(root FileSystem) Handler {return &fileHandler{root}}

type fileHandler struct {root FileSystem}func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {upath := r.URL.Pathif !strings.HasPrefix(upath, "/") {upath = "/" + upathr.URL.Path = upath}serveFile(w, r, f.root, path.Clean(upath), true)}

// A Handler responds to an HTTP request.//// ServeHTTP should write reply headers and data to the ResponseWriter// and then return. Returning signals that the request is finished; it// is not valid to use the ResponseWriter or read from the// Request.Body after or concurrently with the completion of the// ServeHTTP call.//// Depending on the HTTP client software, HTTP protocol version, and// any intermediaries between the client and the Go server, it may not// be possible to read from the Request.Body after writing to the// ResponseWriter. Cautious handlers should read the Request.Body// first, and then reply.//// If ServeHTTP panics, the server (the caller of ServeHTTP) assumes// that the effect of the panic was isolated to the active request.// It recovers the panic, logs a stack trace to the server error log,// and hangs up the connection.type Handler interface {ServeHTTP(ResponseWriter, *Request)}

最后,http.StripPrefix 函数在“\Go\src\net\http\server.go” 中实现,它接受一个string和一个http.Handler 接口同时返回一个http.Handler 接口,代码如下:

// StripPrefix returns a handler that serves HTTP requests// by removing the given prefix from the request URL's Path// and invoking the handler h. StripPrefix handles a// request for a path that doesn't begin with prefix by// replying with an HTTP 404 not found error.func StripPrefix(prefix string, h Handler) Handler {if prefix == "" {return h}return HandlerFunc(func(w ResponseWriter, r *Request) {if p := strings.TrimPrefix(r.URL.Path, prefix); len(p) < len(r.URL.Path) {r.URL.Path = ph.ServeHTTP(w, r)} else {NotFound(w, r)}})}
// The HandlerFunc type is an adapter to allow the use of// ordinary functions as HTTP handlers.  If f is a function// with the appropriate signature, HandlerFunc(f) is a// Handler that calls f.type HandlerFunc func(ResponseWriter, *Request)// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {f(w, r)}
这里值得注意的是,HandlerFunc 是一个类似C++的仿函数(或者叫函数对象,即实现了函数调符的类对象)  的东西,当HandlerFunc 类型的结构体(其本身是一个函数,可以把同类型的函数强制转换为该结构体) 调用ServeHTTP(w,r) 的时候,就会调用该函数本身,是一个很精妙的实现。

接下来的实现有点繁琐,限于个人水平,不能用文字简单的表达清楚,所以制作了一个xls文件,大家可以下载来看看,连接如下:

Go静态HTTP服务器.xlsx

0 0