github.com/haraldrudell/parl@v0.4.176/phttp/http_test.go (about)

     1  /*
     2  © 2021–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package phttp
     7  
     8  import (
     9  	"fmt"
    10  	"net/http"
    11  	"net/netip"
    12  	"sync/atomic"
    13  	"testing"
    14  
    15  	"github.com/haraldrudell/parl"
    16  	"github.com/haraldrudell/parl/perrors"
    17  	"github.com/haraldrudell/parl/pnet"
    18  )
    19  
    20  func TestNewHttp(t *testing.T) {
    21  	var nearSocketInvalid0 = netip.AddrPort{}
    22  	var expAddr1 = ":http"
    23  	var nearSocket0 = netip.MustParseAddrPort("1.2.3.4:0")
    24  	var expAddr2 = "1.2.3.4:http"
    25  	var nearSocket6 = netip.MustParseAddrPort("[::1]:1024")
    26  	var expAddr3 = "[::1]:1024"
    27  
    28  	// HandleFunc() Listen() SendErr() Shutdown() WaitForUp()
    29  	var pnetHttp *Http
    30  
    31  	// nearSocket invalid 0
    32  	pnetHttp = NewHttp(nearSocketInvalid0, pnet.NetworkDefault)
    33  	// addr: ":http" network: tcp
    34  	t.Logf("addr: %q network: %s", pnetHttp.Server.Addr, pnetHttp.Network)
    35  	if pnetHttp.Network != pnet.NetworkTCP {
    36  		t.Errorf("New1 bad network %s exp %s", pnetHttp.Network, pnet.NetworkTCP)
    37  	}
    38  	if pnetHttp.Server.Addr != expAddr1 {
    39  		t.Errorf("New1 bad addr %q exp %q", pnetHttp.Server.Addr, expAddr1)
    40  	}
    41  
    42  	// port 0
    43  	pnetHttp = NewHttp(nearSocket0, pnet.NetworkTCP)
    44  	if pnetHttp.Network != pnet.NetworkTCP {
    45  		t.Errorf("New bad network %s exp %s", pnetHttp.Network, pnet.NetworkTCP)
    46  	}
    47  	if pnetHttp.Server.Addr != expAddr2 {
    48  		t.Errorf("New1 bad addr %q exp %q", pnetHttp.Server.Addr, expAddr1)
    49  	}
    50  
    51  	// IPv6
    52  	pnetHttp = NewHttp(nearSocket6, pnet.NetworkTCP)
    53  	if pnetHttp.Network != pnet.NetworkTCP {
    54  		t.Errorf("New bad network %s exp %s", pnetHttp.Network, pnet.NetworkTCP)
    55  	}
    56  	if pnetHttp.Server.Addr != expAddr3 {
    57  		t.Errorf("New1 bad addr %q exp %q", pnetHttp.Server.Addr, expAddr1)
    58  	}
    59  }
    60  
    61  func TestHttp(t *testing.T) {
    62  	var nearSocket netip.AddrPort
    63  	var network pnet.Network
    64  	// "/" matches everything
    65  	var URIPattern = "/"
    66  
    67  	var protocol = "http://"
    68  	var handler *sHandler
    69  	var near, respS string
    70  	var resp *http.Response
    71  	var err error
    72  	var statusCode int
    73  	var goResult = parl.NewGoResult()
    74  
    75  	// HandleFunc() Listen() SendErr() Shutdown() WaitForUp()
    76  	var pnetHttp *Http = NewHttp(nearSocket, network)
    77  	handler = newShandler()
    78  	pnetHttp.HandleFunc(URIPattern, handler.Handle)
    79  	defer pnetHttp.Shutdown()
    80  
    81  	t.Log("invoking Listen")
    82  	go errChListener(pnetHttp.Listen(), goResult)
    83  
    84  	t.Log("waiting for ListenAwaitable")
    85  	<-pnetHttp.ListenAwaitable.Ch()
    86  	if !pnetHttp.Near.IsValid() {
    87  		t.Fatalf("FATAL: pnetHttp.Near invalid")
    88  	}
    89  	near = pnetHttp.Near.String()
    90  	t.Logf("Near: %s", near)
    91  
    92  	t.Log("issuing http.GET")
    93  	resp, err = http.Get(protocol + near)
    94  	if resp != nil {
    95  		statusCode = resp.StatusCode
    96  		respS = fmt.Sprintf("status code: %d", statusCode)
    97  	} else {
    98  		respS = "resp nil"
    99  	}
   100  
   101  	t.Logf("%s err: %s", respS, perrors.Short(err))
   102  	if err != nil {
   103  		t.Errorf("http.Get err %s", perrors.Short(err))
   104  	}
   105  	// status code should be 204
   106  	if resp != nil {
   107  		if resp.StatusCode != http.StatusNoContent {
   108  			t.Errorf("http.Get status code %d exp %d", resp.StatusCode, http.StatusNoContent)
   109  		}
   110  		if e := resp.Body.Close(); e != nil {
   111  			panic(e)
   112  		}
   113  	}
   114  
   115  	// handle count should be 1
   116  	if c := int(handler.Rqs.Load()); c != 1 {
   117  		t.Errorf("bad handle count: %d exp 1", c)
   118  	}
   119  
   120  	t.Logf("Shutting down server")
   121  	pnetHttp.Shutdown()
   122  
   123  	// wait for error reader to exit
   124  	goResult.ReceiveError(nil)
   125  
   126  	if !pnetHttp.ErrCh.IsClosed() {
   127  		t.Error("ErrCh not closed")
   128  	}
   129  	if !pnetHttp.EndListenAwaitable.IsClosed() {
   130  		t.Error("EndListenAwaitable not closed")
   131  	}
   132  }
   133  
   134  // errChListener is goroutine consuming http error channel
   135  func errChListener(errCh <-chan error, g parl.GoResult) {
   136  	var err error
   137  	defer g.SendError(&err)
   138  
   139  	for err = range errCh {
   140  		parl.Log(perrors.Long(err))
   141  		panic(err)
   142  	}
   143  }
   144  
   145  // sHandler counts incoming requests
   146  type sHandler struct{ Rqs atomic.Uint64 }
   147  
   148  // newShandler returns a request counter
   149  func newShandler() (s *sHandler) { return &sHandler{} }
   150  
   151  // Handle is the http-server handler function
   152  func (s *sHandler) Handle(w http.ResponseWriter, r *http.Request) {
   153  	s.Rqs.Add(1)
   154  	w.WriteHeader(http.StatusNoContent)
   155  }