龙空技术网

golang源码分析:httptest

go算法架构leetcode 117

前言:

当前我们对“服务器测速源码”大概比较珍视,姐妹们都需要剖析一些“服务器测速源码”的相关文章。那么小编同时在网上网罗了一些关于“服务器测速源码””的相关文章,希望你们能喜欢,我们一起来了解一下吧!

httptest是golang官方源码自带的测试包,它可以非常方便获取http请求结构体,http返回值结构体,以及在本地启动一个loopback的server,方便我们做单测。对于go的web应用程序中往往需要与其他系统进行交互, 比如通过http访问其他系统, 此时就需要一种方法用于打桩来模拟Web服务端和客户端,httptest包即Go语言针对Web应用提供的解决方案。

1,获取返回值

rr := httptest.NewRecorder()

我们创建一个 ResponseRecorder (which satisfies http.ResponseWriter)来记录响应,主要利用httptest.NewRecorder()创建一个http.ResponseWriter,模拟了真实服务端的响应,这种响应时通过调用http.DefaultServeMux.ServeHTTP方法触发的。对应源码位于src/net/http/httptest/recorder.go

type ResponseRecorder struct {  // Code is the HTTP response code set by WriteHeader.  //  // Note that if a Handler never calls WriteHeader or Write,  // this might end up being 0, rather than the implicit  // http.StatusOK. To get the implicit value, use the Result  // method.  Code int  // HeaderMap contains the headers explicitly set by the Handler.  // It is an internal detail.  //  // Deprecated: HeaderMap exists for historical compatibility  // and should not be used. To access the headers returned by a handler,  // use the Response.Header map as returned by the Result method.  HeaderMap http.Header  // Body is the buffer to which the Handler's Write calls are sent.  // If nil, the Writes are silently discarded.  Body *bytes.Buffer  // Flushed is whether the Handler called Flush.  Flushed bool  result      *http.Response // cache of Result's return value  snapHeader  http.Header    // snapshot of HeaderMap at first Write  wroteHeader bool}
func NewRecorder() *ResponseRecorder {  return &ResponseRecorder{    HeaderMap: make(http.Header),    Body:      new(bytes.Buffer),    Code:      200,  }}

它对应的方法如下,可以根据实际需求进行调用。

func (rw *ResponseRecorder) Header() http.Header {func (rw *ResponseRecorder) writeHeader(b []byte, str string) {func (rw *ResponseRecorder) Write(buf []byte) (int, error) {func (rw *ResponseRecorder) WriteString(str string) (int, error) {func (rw *ResponseRecorder) WriteHeader(code int) {func (rw *ResponseRecorder) Flush() {func (rw *ResponseRecorder) Result() *http.Response {

2,获取http请求

req := httptest.NewRequest(    http.MethodPost,    "/health-check",    bytes.NewReader(reqBody),  )

在测试的时候可以把http.NewRequest替换为httptest.NewRequest。httptest.NewRequest的第三个参数可以用来传递body数据,必须实现io.Reader接口。httptest.NewRequest不会返回error,无需进行err!=nil检查。解析响应时没直接使用ResponseRecorder,而是调用了Result函数。源码位于src/net/http/httptest/httptest.go

func NewRequest(method, target string, body io.Reader) *http.Request {  req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(method + " " + target + " HTTP/1.0\r\n\r\n")))  if body != nil {    switch v := body.(type) {    case *bytes.Buffer:      req.ContentLength = int64(v.Len())    case *bytes.Reader:      req.ContentLength = int64(v.Len())    case *strings.Reader:      req.ContentLength = int64(v.Len())    default:      req.ContentLength = -1    }    if rc, ok := body.(io.ReadCloser); ok {      req.Body = rc    } else {      req.Body = io.NopCloser(body)    }  }

3,本地起模拟服务器

httptest.NewServer(http.HandlerFunc(healthHandler))

模拟服务器的创建使用的是httptest.NewServer函数,它接收一个http.Handler处理API请求的接口。代码示例中使用了Hander的适配器模式,http.HandlerFunc是一个函数类型,实现了http.Handler接口,这里是强制类型转换,不是函数的调用,源码位于:src/net/http/httptest/server.go

type Server struct {  URL      string // base URL of form  with no trailing slash  Listener net.Listener  // EnableHTTP2 controls whether HTTP/2 is enabled  // on the server. It must be set between calling  // NewUnstartedServer and calling Server.StartTLS.  EnableHTTP2 bool  // TLS is the optional TLS configuration, populated with a new config  // after TLS is started. If set on an unstarted server before StartTLS  // is called, existing fields are copied into the new config.  TLS *tls.Config  // Config may be changed after calling NewUnstartedServer and  // before Start or StartTLS.  Config *http.Server  // certificate is a parsed version of the TLS config certificate, if present.  certificate *x509.Certificate  // wg counts the number of outstanding HTTP requests on this server.  // Close blocks until all requests are finished.  wg sync.WaitGroup  mu     sync.Mutex // guards closed and conns  closed bool  conns  map[net.Conn]http.ConnState // except terminal states  // client is configured for use with the server.  // Its transport is automatically closed when Close is called.  client *http.Client}

默认会监听127.0.0.1:0

func newLocalListener() net.Listener {if serveFlag != "" {l, err := net.Listen("tcp", serveFlag)l, err := net.Listen("tcp", "127.0.0.1:0")if err != nil {if l, err = net.Listen("tcp6", "[::1]:0"); err != nil {
func NewServer(handler http.Handler) *Server {  ts := NewUnstartedServer(handler)  ts.Start()  return ts}
func NewUnstartedServer(handler http.Handler) *Server {  return &Server{    Listener: newLocalListener(),    Config:   &http.Server{Handler: handler},  }}
func (s *Server) Start() {          s.URL = "; + s.Listener.Addr().String()  s.wrap()  s.goServe()
func (s *Server) StartTLS() { s.Listener = tls.NewListener(s.Listener, s.TLS)  s.URL = "; + s.Listener.Addr().String()  s.wrap()  s.goServe()
func NewTLSServer(handler http.Handler) *Server {  ts := NewUnstartedServer(handler)  ts.StartTLS()  return ts}
func (s *Server) Close() {        for c, st := range s.conns {        if st == http.StateIdle || st == http.StateNew {        s.closeConn(c)      }
func (s *Server) CloseClientConnections() { for c := range s.conns {    go s.closeConnChan(c, ch)  }
func (s *Server) goServe() {  s.wg.Add(1)  go func() {    defer s.wg.Done()    s.Config.Serve(s.Listener)  }()}

在协程里对不同http状态进行不同处理

func (s *Server) wrap() {  s.Config.ConnState = func(c net.Conn, cs http.ConnState) {    switch cs {    case http.StateNew:        case http.StateActive:        case http.StateIdle:        case http.StateHijacked, http.StateClosed:

标签: #服务器测速源码