Go语言的http.FileServer

看一下下面这段代码:

package main

import (
    "net/http"
)

func main() {
    // To serve a directory on disk (/tmp) under an alternate URL
    // path (/tmpfiles/), use StripPrefix to modify the request
    // URL's path before the FileServer sees it:
    http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp"))))
}

(1)http.Dir("/tmp")是利用本地tmp目录实现一个文件系统;
(2)http.FileServer(http.Dir("/tmp"))返回一个Handler,其用来处理访问本地"/tmp"文件夹的HTTP请求;
(3)http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp")))返回一个新的Handler,这个Handler用来处理HTTP请求(移除前缀是"/tmpfiles/"后的URL)。
总结一下,当要访问http://localhost/tmpfiles/a文件,实际访问的是本地/tmp/a文件。

Go语言DefaultClient没有设置请求超时

今天看到这篇文章:Don’t use Go’s default HTTP client。总结起来就是直接使用Go语言的http.Posthttp.Get等方法时,底层连接使用的是DefaultClient。而DefaultClient没有设置请求超时:

// DefaultClient is the default Client and is used by Get, Head, and Post.
var DefaultClient = &Client{}

因此,如果服务器端如果一直无响应的话,就会把当前发出请求的goroutine挂死。因此如果要使用DefaultClient,一定要留心这个陷阱。

Docker Swarm代码分析笔记(8)——创建Docker API router

上一篇提到的API Server需要设置HTTP处理函数:
......
server.SetHandler(api.NewPrimary(cl, tlsConfig, &statusHandler{cl, nil, nil}, c.GlobalBool("debug"), c.Bool("cors")))
......

api.Server结构体定义如下:

// Server is a Docker API server.
type Server struct {
    hosts  []string
    tlsConfig  *tls.Config
    dispatcher *dispatcher
}

其中处理HTTP请求的相关方法如下:

// Dispatcher is a meta http.Handler. It acts as an http.Handler and forwards
// requests to another http.Handler that can be changed at runtime.
type dispatcher struct {
    handler http.Handler
}

// SetHandler changes the underlying handler.
func (d *dispatcher) SetHandler(handler http.Handler) {
    d.handler = handler
}

// ServeHTTP forwards requests to the underlying handler.
func (d *dispatcher) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if d.handler == nil {
        httpError(w, "No dispatcher defined", http.StatusInternalServerError)
        return
    }
    d.handler.ServeHTTP(w, r)
}

// SetHandler is used to overwrite the HTTP handler for the API.
// This can be the api router or a reverse proxy.
func (s *Server) SetHandler(handler http.Handler) {
    s.dispatcher.SetHandler(handler)
}

Server.SetHandler所做的就是把handler赋给Server.dispatcher.handlerhandler类型是http.Handler:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

api.NewPrimary利用了mux这个project

// NewPrimary creates a new API router.
func NewPrimary(cluster cluster.Cluster, tlsConfig *tls.Config, status StatusHandler, debug, enableCors bool) *mux.Router {
    // Register the API events handler in the cluster.
    eventsHandler := newEventsHandler()
    cluster.RegisterEventHandler(eventsHandler)

    context := &context{
        cluster:       cluster,
        eventsHandler: eventsHandler,
        statusHandler: status,
        tlsConfig:     tlsConfig,
    }

    r := mux.NewRouter()
    setupPrimaryRouter(r, context, enableCors)

    if debug {
        profilerSetup(r, "/debug/")
    }

    return r
}

*mux.Router实现了ServeHTTP这个方法,所以符合http.Handler这个interface

// ServeHTTP dispatches the handler registered in the matched route.
//
// When there is a match, the route variables can be retrieved calling
// mux.Vars(request).
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    ......
}

api.ServerListenAndServe()函数定义了新的http.Server变量,利用上面实现的api.Server.dispatcherServeHTTP方法来响应HTTP请求:

server = &http.Server{
                Addr:    protoAddrParts[1],
                Handler: s.dispatcher,
            }

再看一下setupPrimaryRouter这个函数:

func setupPrimaryRouter(r *mux.Router, context *context, enableCors bool) {
    for method, mappings := range routes {
        for route, fct := range mappings {
            log.WithFields(log.Fields{"method": method, "route": route}).Debug("Registering HTTP route")

            localRoute := route
            localFct := fct

            wrap := func(w http.ResponseWriter, r *http.Request) {
                log.WithFields(log.Fields{"method": r.Method, "uri": r.RequestURI}).Debug("HTTP request received")
                if enableCors {
                    writeCorsHeaders(w, r)
                }
                context.apiVersion = mux.Vars(r)["version"]
                localFct(context, w, r)
            }
            localMethod := method

            r.Path("/v{version:[0-9]+.[0-9]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
            r.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)

            if enableCors {
                optionsMethod := "OPTIONS"
                optionsFct := optionsHandler

                wrap := func(w http.ResponseWriter, r *http.Request) {
                    log.WithFields(log.Fields{"method": optionsMethod, "uri": r.RequestURI}).
                        Debug("HTTP request received")
                    if enableCors {
                        writeCorsHeaders(w, r)
                    }
                    context.apiVersion = mux.Vars(r)["version"]
                    optionsFct(context, w, r)
                }

                r.Path("/v{version:[0-9]+.[0-9]+}" + localRoute).
                    Methods(optionsMethod).HandlerFunc(wrap)
                r.Path(localRoute).Methods(optionsMethod).
                    HandlerFunc(wrap)
            }
        }
    }
}

其中routes定义如下:

var routes = map[string]map[string]handler{
    "HEAD": {
        "/containers/{name:.*}/archive": proxyContainer,
    },
    "GET": {
        "/_ping":                          ping,
        "/events":                         getEvents,
        "/info":                           getInfo,
        ......
        }
    ......
}

"HEAD": {
        "/containers/{name:.*}/archive": proxyContainer,
}

为例:
methodlocalMethod"HEAD"
mappings是:

{
        "/containers/{name:.*}/archive": proxyContainer,
}

routelocalRoute"/containers/{name:.*}/archive"
fctlocalFctproxyContainer
所以

r.Path("/v{version:[0-9]+.[0-9]+}" + localRoute).Methods(localMethod).HandlerFunc(wrap)
r.Path(localRoute).Methods(localMethod).HandlerFunc(wrap)

就是为"/v*/containers/{name:.*}/archive""/containers/{name:.*}/archive"这个PATH"HEAD"操作注册了wrap函数,而wrap函数则封装了localFct,也就是proxyContainer函数。 另外,if enableCors部分与上述类似。

 

Docker Swarm代码分析笔记(7)——创建Docker API server

Docker Swarm manage函数的最后部分是创建Docker API server部分:

server := api.NewServer(hosts, tlsConfig)
if c.Bool("replication") {
    ......
    setupReplication(c, cl, server, discovery, addr, leaderTTL, tlsConfig)
} else {
    server.SetHandler(api.NewPrimary(cl, tlsConfig, &statusHandler{cl, nil, nil}, c.GlobalBool("debug"), c.Bool("cors")))
    cluster.NewWatchdog(cl)
}

log.Fatal(server.ListenAndServe())

Server结构体定义在api/server.go

type Server struct {
    hosts      []string
    tlsConfig  *tls.Config
    dispatcher *dispatcher
}

它的核心方法是ListenAndServe(),即为每个host起一个goroutine监听并处理Docker client的连接请求。

func (s *Server) ListenAndServe() error {
    chErrors := make(chan error, len(s.hosts))

    for _, host := range s.hosts {
        protoAddrParts := strings.SplitN(host, "://", 2)
        if len(protoAddrParts) == 1 {
            protoAddrParts = append([]string{"tcp"}, protoAddrParts...)
        }

        go func() {
            log.WithFields(log.Fields{"proto": protoAddrParts[0], "addr": protoAddrParts[1]}).Info("Listening for HTTP")

            var (
                l      net.Listener
                err    error
                server = &http.Server{
                    Addr:    protoAddrParts[1],
                    Handler: s.dispatcher,
                }
            )

            switch protoAddrParts[0] {
            case "unix":
                l, err = newUnixListener(protoAddrParts[1], s.tlsConfig)
            case "tcp":
                l, err = newListener("tcp", protoAddrParts[1], s.tlsConfig)
            default:
                err = fmt.Errorf("unsupported protocol: %q", protoAddrParts[0])
            }
            if err != nil {
                chErrors <- err
            } else {
                chErrors <- server.Serve(l)
            }

        }()
    }

    for i := 0; i < len(s.hosts); i++ {
        err := <-chErrors
        if err != nil {
            return err
        }
    }
    return nil
}

 

SSL,TLS和HTTPS

SSL(Secure Sockets Layer)是一个加密协议,TLS(Transport Layer Security)SSL的替代品,所以经常看见SSL/TLS字样。HTTPS是利用HTTP协议传输SSL/TLS数据。

SSL/TLSSSH类似:Client连接ServerServer需要返回一个认证的CA,其中包含public keyClient生成一个randome string,发回给ServerServerprivate key解开。双方生成同样的secret key,后续用这个key进行加密。(Server也可以要求ClientCA,这个步骤是可选的)

参考资料:
What’s the difference between SSL, TLS, and HTTPS?
Is it SSL, TLS or HTTPS?.

 

Go语言的“http.Handle”和“http.HandleFunc”

Go语言中http package包含HandleHandleFunc两个函数:

func Handle

func Handle(pattern string, handler Handler)
Handle registers the handler for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched.

func HandleFunc

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
HandleFunc registers the handler function for the given pattern in the DefaultServeMux. The documentation for ServeMux explains how patterns are matched.

Handle函数的handler参数是个interface

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

HandleFunchandler参数就是一个原型为func(ResponseWriter, *Request)的函数。

参考下例(使用Handle):

package main
import (
    "net/http"
    "log"
)

type httpServer struct {
}

func (server httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte(r.URL.Path))
}

func main() {
    var server httpServer
    http.Handle("/", server)
    log.Fatal(http.ListenAndServe("localhost:9000", nil))
}

使用HandleFunc

package main
import (
    "net/http"
    "log"
)

func main() {
    http.HandleFunc("/", func (w http.ResponseWriter, r *http.Request){
        w.Write([]byte(r.URL.Path))
    })
    log.Fatal(http.ListenAndServe("localhost:9000", nil))
}

根据The Go Programming Language

A handler pattern that ends with a slash matches any URL that has the pattern as a prefix. Behind the scenes, the server runs the handler for each incoming request in a separate goroutine so that it can serve multiple requests simultaneously.

因此,如果http.Handlehttp.HandleFunc所指定的handle pattern是“/”,则匹配所有的pattern;而“/foo/”则会匹配所有“/foo/*”。