“This is the 28th day of my participation in the August Text Challenge
Net/HTTP standard library for Go
- The components of the NET/HTTP standard library:
- Go server processing requests:
Once an HTTP server is started, requests from clients can be received, processed and acted upon. The NET/HTTP standard library also provides an interface to connect to multiplexers and a multiplexer implemented by default. According to the client request path, the multiplexer finds the corresponding processor, and the processor will perform a series of operations. Finally, the processing result is displayed on the template through the template engine.
Second, use net/ HTTP standard library to build a simple Web server
Create a new server.go following code. Then run, visit http://localhost:8080 in your browser, and a blank page will appear in your browser. A simple Web server has been set up. Compared to The Java Web, you build with servlets, and then object oriented, which requires much more code than Go. So Go is more suitable for network applications.
package main
import "net/http"
func main(a) {
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: nil,
}
err := server.ListenAndServe()
iferr ! =nil {
panic(err)
}
}
Copy the code
Detailed configuration of http.Server
// Define the Http server running parameters
// The server's zero value is a valid configuration
type Server struct {
// Addr Specifies the TCP address listened by the server in the form of host: port. If empty, use: HTTP (port 80)
// For more information about address formats, see net.dial
Addr string
// Call handler, if nil, http.defaultServemux
Handler Handler
// TLSConfig optionally provides a TLS configuration for use
// by ServeTLS and ListenAndServeTLS. Note that this value is
// cloned by ServeTLS and ListenAndServeTLS, so it's not
// possible to modify the configuration with methods like
// tls.Config.SetSessionTicketKeys. To use
// SetSessionTicketKeys, use Server.Serve with a TLS Listener
// instead.
TLSConfig *tls.Config
// ReadTimeout is the maximum duration for reading the entire
// request, including the body.
//
// Because ReadTimeout does not let Handlers make per-request
// decisions on each request body's acceptable deadline or
// upload rate, most users will prefer to use
// ReadHeaderTimeout. It is valid to use them both.
ReadTimeout time.Duration
// ReadHeaderTimeout is the amount of time allowed to read
// request headers. The connection's read deadline is reset
// after reading the headers and the Handler can decide what
// is considered too slow for the body. If ReadHeaderTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, there is no timeout.
ReadHeaderTimeout time.Duration
// WriteTimeout is the maximum duration before timing out
// writes of the response. It is reset whenever a new
// request's header is read. Like ReadTimeout, it does not
// let Handlers make decisions on a per-request basis.
WriteTimeout time.Duration
// IdleTimeout is the maximum amount of time to wait for the
// next request when keep-alives are enabled. If IdleTimeout
// is zero, the value of ReadTimeout is used. If both are
// zero, there is no timeout.
IdleTimeout time.Duration
// MaxHeaderBytes controls the maximum number of bytes the
// server will read parsing the request header's keys and
// values, including the request line. It does not limit the
// size of the request body.
// If zero, DefaultMaxHeaderBytes is used.
MaxHeaderBytes int
// TLSNextProto optionally specifies a function to take over
// ownership of the provided TLS connection when an ALPN
// protocol upgrade has occurred. The map key is the protocol
// name negotiated. The Handler argument should be used to
// handle HTTP requests and will initialize the Request's TLS
// and RemoteAddr if not already set. The connection is
// automatically closed when the function returns.
// If TLSNextProto is not nil, HTTP/2 support is not enabled
// automatically.
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
// ConnState specifies an optional callback function that is
// called when a client connection changes state. See the
// ConnState type and associated constants for details.
ConnState func(net.Conn, ConnState)
// ErrorLog specifies an optional logger for errors accepting
// connections, unexpected behavior from handlers, and
// underlying FileSystem errors.
// If nil, logging is done via the log package's standard logger.
ErrorLog *log.Logger
// BaseContext optionally specifies a function that returns
// the base context for incoming requests on this server.
// The provided Listener is the specific Listener that's
// about to start accepting requests.
// If BaseContext is nil, the default is context.Background().
// If non-nil, it must return a non-nil context.
BaseContext func(net.Listener) context.Context
// ConnContext optionally specifies a function that modifies
// the context used for a new connection c. The provided ctx
// is derived from the base context and has a ServerContextKey
// value.
ConnContext func(ctx context.Context, c net.Conn) context.Context
disableKeepAlives int32 // accessed atomically.
inShutdown int32 // accessed atomically (non-zero means we're in Shutdown)
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
mu sync.Mutex
listeners map[*net.Listener]struct{}
activeConn map[*conn]struct{}
doneChan chan struct{}
onShutdown []func(a)
}
Copy the code
Set up a server to provide HTTPS services
-
Generate an SSL certificate and private key
package main import ( "crypto/rand" "crypto/rsa" "crypto/x509" "crypto/x509/pkix" "encoding/pem" "math/big" "net" "os" "time" ) func main(a) { max := new(big.Int).Lsh(big.NewInt(1), 128) serialNumber, _ := rand.Int(rand.Reader, max) subject := pkix.Name{ Organization: []string{"com.msr"}, OrganizationalUnit: []string{"better"}, CommonName: "go web", } template := x509.Certificate{ SerialNumber: serialNumber, Subject: subject, NotBefore: time.Now(), NotAfter: time.Now().Add(365 * 24 * time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, } pk, _ := rsa.GenerateKey(rand.Reader, 2048) derBytes, _ := x509.CreateCertificate(rand.Reader, &template, &template, &pk.PublicKey, pk) certOut, _ := os.Create("cert.pem") pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) certOut.Close() keyOut, _ := os.Create("key.pem") pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)}) keyOut.Close() } Copy the code
-
Start and run using HTTPS. Go to https://localhost:8080
package main import ( "fmt" "net/http" ) func main(a) { server := http.Server{ Addr: 127.0. 01.:8080", Handler: nil, } err := server.ListenAndServeTLS("cert.pem","key.pom") if err ! = nil { fmt.Println(err.Error()) } }Copy the code
Processors and processing functions
Processor:
In addition to receiving and processing requests from clients, a processor in a Web application calls a template engine, which generates HTML and fills the data into the response message to be sent back to the client. In the NET/HTTP library, a handler has a ServeHTTP method that takes the ResponseWriter interface as one argument and a pointer to the Request structure as the second argument. In other words, any interface that has a ServeHTTP method with the following signature is a processor.
ServeHTTP(w ResponseWriter, r *Request)
Copy the code
2.1 Customizing Handlers To process requests
A processor was created and bound to the server to replace the default multiplexer that was already in use. This means that instead of using URL matching to route requests to different processors, the server uses the same processor to process all requests, so that no matter what address the browser visits, the server returns the same Hello World! The response. Obviously, this is not the usual method.
This is also our in Web applications using multiplexer: for some special purpose server, use only one processor may can finish the work very well, but in most cases, we still hope that the server can according to the different response of different URL request returns, rather than an invariable only returns a response.
package main
import (
"fmt"
"net/http"
)
type MyHandler struct{}
func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")}func main(a) {
handler := MyHandler{}
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: &handler,
}
server.ListenAndServe()
}
Copy the code
2.2 Use multiple Handlers to handle different requests
Instead of specifying the processor in the Handler field of the Server structure, let the Server use the default DefaultServeMux as the processor, and then bind the processor to DefaultServeMux through the http.Handle function. Note that although the Handle function is derived from the HTTP package, it is actually a servemux-structured method: these functions are created for convenience, and calling them is equivalent to calling a method of DefaultServeMux. For example, calling http.Handle is actually calling the Handle method of DefaultServeMux.
package main
import "net/http"
type HelloHandler struct{}type WorldHandler struct{}func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello!!!"))}func (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("world!!!"))}func main(a) {
hello := HelloHandler{}
world := WorldHandler{}
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.Handle("/hello", &hello)
http.Handle("/world", &world)
server.ListenAndServe()
}
Copy the code
2.3 Processor Functions
The Go language has a HandlerFunc function type that converts a properly signed function F into a Handler with a method F
package main
import "net/http"
func hello(w http.ResponseWriter, r *http.Request){
w.Write([]byte("hello!!!"))}func world(w http.ResponseWriter, r *http.Request){
w.Write([]byte("hello!!!"))}func main(a) {
server := http.Server{
Addr: "127.0.0.1:8080",
}
http.HandleFunc("/hello", hello)
http.HandleFunc("/world", world)
server.ListenAndServe()
}
Copy the code
The Handle function binds a handler to a URL. The HandleFun function converts the function into a Handler and binds it to DefaultServeMux to simplify the task of creating and binding a Handler. In other words, processor functions are just a convenient way to create a processor.
2.4 Concatenate multiple processors and processing functions
Go is not a functional programming language, but it does have some of the features of a functional programming language, such as function types, anonymous functions, and closures. As the previous code shows, in Go, a program can pass a function to another function, or reference a named function through an identifier. This means that you can pass function F1 to another function F2, and then call F1 after function F2 has done something. For example, every time a processor is called, a call log is logged
Code examples:
Log returns an anonymous HandlerFunc function. Since hello is a function of type HandlerFunc, the code log(hello) actually sends hello to log. In other words, this code concatenates the log and Hello functions. The same is true for the Protect function.
package main
import (
"fmt"
"net/http"
)
type HelloHandler struct{}
func (h HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")}func log(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"Handler called - %T\n", h)
h.ServeHTTP(w, r)
})
}
func protect(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Assume a series of operations....
fmt.Fprintf(w,"Handler called - %T\n", h)
h.ServeHTTP(w, r)
})
}
func main(a) {
server := http.Server{
Addr: "127.0.0.1:8080",
}
hello := HelloHandler{}
http.Handle("/hello", protect(log(hello)))
server.ListenAndServe()
}
Copy the code
2.5 ServeMux and DefaultServeMux
A ServeMux is an HTTP request multiplexer that is responsible for receiving HTTP requests and redirecting them to the correct processor based on the URL in the request
The ServeMux structure contains a map that maps the URL to the appropriate processor. ServeMux also implements the ServeHTTP method and is therefore a processor. When the ServeHTTP method receives a request, it finds the URL in the structure map that best matches the request URL and invokes the corresponding processor’s ServeHTTP method.
ServeMux is a structure and not an interface, so DefaultServeMux is not an implementation of ServeMux. DefaultServeMux is an instance of ServeMux. And all programs that introduce the NET/HTTP standard library can use this instance. When the user does not specify a processor for the Server structure, the Server uses DefaultServeMux as the default instance of ServeMux.
Because ServeMux is also a processor, users can also perform processor concatenation on instances if needed.
If you bind the root URL (/), unsuccessful urls will be dropped based on the URL hierarchy and will eventually land above the root URL. When the browser visits/Random, the server passes the URL to the root URL handler because it cannot find the handler responsible for processing the URL. For example, if /hello is bound to a handler, when accessing /hello/ Mike, since /hello/ Mike is not bound to a handler, it will be relegated to/Hello’s handler.
Principle of least Surprise:
Also known as the Principle of least Accidents, it is a general rule for designing everything, including software. It states that we should do what makes sense when designing, so that the behavior of things is always obvious, consistent, and reasonable.
2.6 Using Other Multiplexers (HttpRouter)
The only thing you need to do to create a processor and multiplexer is implement the ServeHTTP method, so it is perfectly feasible to create a multiplexer instead of ServeMux under the NET/HTTP package.
One drawback of ServeMux is the inability to use variables for URL pattern matching. For example, if /user/123 is used to query the user whose ID is 123, it will be difficult to parse the URL to extract 123 for Go native ServeMux. Therefore, a lot of complex parsing must be done in the program, which brings additional complexity to the program.
HttpRouter Document: github.com/julienschmi…
A simple example:
package main
import (
"fmt"
"github.com/julienschmidt/httprouter"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
fmt.Fprintf(w, "hello, %s! \n", p.ByName("name"))}func main(a) {
mux := httprouter.New()
mux.GET("/hello/:name", hello)
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: mux,
}
server.ListenAndServe()
}
Copy the code
There are many Web frameworks that implement these routing comparisons and variable fetching. Create a multiplexer with Httprouter.new (). Instead of using the HandleFunc binding handler, you can specify the URI of the HTTP method and the corresponding handler as follows:
mux.GET(“/hello/:name”, hello)
Finally, instead of using DefaultServerMux, we use the multiplexer created from HttpRouter by passing HttpRouter to the Server structure.
Using the HTTP / 2.7 2
In Go1.6 and on, if you start the server in HTTPS mode, the server uses HTTP/2 by default.
package main
import (
"fmt"
"golang.org/x/net/http2"
"net/http"
)
type MyHandler struct{}func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello HTTP/2!")}func main(a) {
handler := MyHandler{}
server := http.Server{
Addr: "127.0.0.1:8080",
Handler: &handler,
}
http2.ConfigureServer(&server, &http2.Server{})
server.ListenAndServeTLS("cert.pem"."key.pem")}Copy the code
Run the go run server.go command. In Windows, you can run git bash to curl the curl command
Curl -i –http2 –insecure https://127.0.0.1:8080