@@ -2,14 +2,20 @@ package main
22
33import (
44 "bytes"
5+ "compress/gzip"
56 "encoding/json"
7+ "fmt"
8+ "io"
9+ "log"
610 "net/http"
11+ "strconv"
12+ "time"
13+
14+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
715
816 "github.com/gorilla/mux"
917)
1018
11- var limit string = "15"
12-
1319type responseCapture struct {
1420 http.ResponseWriter
1521 StatusCode int
@@ -22,8 +28,8 @@ func (r *responseCapture) WriteHeader(code int) {
2228}
2329
2430func (r * responseCapture ) Write (b []byte ) (int , error ) {
25- r .Body .Write (b )
26- return r .ResponseWriter .Write (b )
31+ return r .Body .Write (b )
32+ // return r.ResponseWriter.Write(b)
2733}
2834
2935// CreateResponseCapture initializes responseCapture with a http.ResponseWriter and empty bytes.Buffer for the body.
@@ -35,73 +41,123 @@ func CreateResponseCapture(w http.ResponseWriter) *responseCapture {
3541 }
3642}
3743
38- type ResourceMetadata struct {
39- ResourceVersion string `json:"resourceVersion"`
40- ContinueToken string `json:"continue"`
44+ type Item struct {
45+ Metadata metav1.ObjectMeta `json:"metadata"`
4146}
47+ type ResourceResponse struct {
48+ Kind string `json:"kind"`
49+ Verison string `json:"apiVersion"`
50+ Metadata metav1.ListMeta `json:"metadata"`
51+ Items []Item `json:"items"`
52+ }
53+
54+ // handleGzip function compress response if the response header have Content-Encoding as gzip.
55+ func handleGzip (rcw * responseCapture ) ([]byte , error ) {
56+ bodyBytes := rcw .Body .Bytes ()
57+ if rcw .Header ().Get ("Content-Encoding" ) == "gzip" {
58+ reader , err := gzip .NewReader (bytes .NewReader (bodyBytes ))
59+ if err != nil {
60+ return nil , err
61+ }
62+ defer reader .Close ()
63+ bodyBytes , err = io .ReadAll (reader )
64+ if err != nil {
65+ return nil , err
66+ }
67+ }
4268
43- type Metadata struct {
44- Name string `json:"name"`
45- Namespace string `json:"namespace"`
46- Uuid string `json:"uuid"`
69+ return bodyBytes , nil
4770}
4871
49- type Item struct {
50- Metadata Metadata `json:"metadata"`
72+ // returnResponseToClient helps to return the response to the client.
73+ func returnResponseToClient (rcw * responseCapture , v any , w http.ResponseWriter ) error {
74+ var err error
75+ if rcw .Header ().Get ("Content-Encoding" ) == "gzip" {
76+ w .Header ().Set ("Content-Encoding" , "gzip" )
77+ gz := gzip .NewWriter (w )
78+ defer gz .Close ()
79+ err = json .NewEncoder (gz ).Encode (v )
80+ } else {
81+ err = json .NewEncoder (w ).Encode (v )
82+ }
83+
84+ return err
5185}
52- type ResourceResponse struct {
53- Metadata ResourceMetadata `json:"metadata"`
54- Items []Item `json:"items"`
86+
87+ // sliceTheResponse helps to slice the full list to identify the startIndex and endIndex that will be
88+ // equal to the full page, which is going to be sliced from the full list.
89+ func sliceTheResponse (page int , pageSize int , sizeOfResponse int ) (int , int ) {
90+ start := (page - 1 ) * pageSize
91+ if start >= sizeOfResponse {
92+ return sizeOfResponse , sizeOfResponse // slice empty if page out of range
93+ }
94+ end := start + pageSize
95+ if end > sizeOfResponse {
96+ end = sizeOfResponse
97+ }
98+ return start , end
5599}
56100
57- var paginationMap = make (map [int ]* ResourceResponse )
101+ // This helps to unmarshal the response that is coming from K8s server, so we can
102+ // further slice the full list of response into the pages.
103+ func UnmarshalCacheData (bodyBytes []byte ) (ResourceResponse , error ) {
104+ var resourceResponseFullList ResourceResponse
105+
106+ err := json .Unmarshal (bodyBytes , & resourceResponseFullList )
107+ if err != nil {
108+ return ResourceResponse {}, err
109+ }
58110
111+ return resourceResponseFullList , nil
112+ }
113+
114+ var limit = 15
115+
116+ // handlePagination is the middleware which will help to paginate the response coming from
117+ // the K8's server.
59118func handlePagination (c * HeadlampConfig ) mux.MiddlewareFunc {
60119 return func (h http.Handler ) http.Handler {
61120 return http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
121+ start := time .Now ()
62122
63123 rcw := CreateResponseCapture (w )
64124
65125 q := r .URL .Query ()
66- q . Set ( "limit" , limit )
67- r . URL . RawQuery = q .Encode ( )
126+
127+ pageNo : = q .Get ( "pageNo" )
68128
69129 h .ServeHTTP (rcw , r )
70130
71- var currentPageResponse ResourceResponse
72- json .Unmarshal (rcw .Body .Bytes (), & currentPageResponse )
131+ bodyBytes , err := handleGzip (rcw )
132+ if err != nil {
133+ log .Fatalf ("error while converting from gzip: %v " , err )
134+ }
73135
74- paginationMap [0 ] = & currentPageResponse
136+ responseFullList , err := UnmarshalCacheData (bodyBytes )
137+ if err != nil {
138+ log .Fatalf ("error while unmarshalling bodybytes: %v" , responseFullList )
139+ }
75140
76- // Loop to pre-fetch pages 1 through 4
77- currentContinueToken := currentPageResponse .Metadata .ContinueToken
78- for i := 1 ; i <= 4 ; i ++ {
79- // Stop if there is no next page
80- if currentContinueToken == "" {
81- break
141+ if pageNo == "" {
142+ if err := returnResponseToClient (rcw , responseFullList , w ); err != nil {
143+ log .Fatalf ("error while returning response to client: %v" , err )
82144 }
145+ return
146+ }
83147
84- // Create a new request for the next page
85- query := r .URL .Query ()
86- query .Set ("continue" , currentContinueToken )
87- r .URL .RawQuery = query .Encode ()
88-
89- // Clear the response capture buffer before the next request
90- rcw .Body .Reset ()
91-
92- // Serve the next request
93- h .ServeHTTP (rcw , r )
148+ page , _ := strconv .Atoi (pageNo )
94149
95- // Unmarshal the new response
96- var nextResponse ResourceResponse
97- json .Unmarshal (rcw .Body .Bytes (), & nextResponse )
150+ startPage , endPage := sliceTheResponse (page , limit , len (responseFullList .Items ))
98151
99- // Store the new response in the map
100- paginationMap [i ] = & nextResponse
152+ returnedData := responseFullList .Items [startPage :endPage ]
101153
102- // Update the continue token for the next loop iteration
103- currentContinueToken = nextResponse . Metadata . ContinueToken
154+ if err := returnResponseToClient ( rcw , returnedData , w ); err != nil {
155+ log . Fatalf ( "error while returning response to client: %v" , err )
104156 }
157+
158+ // To check the response time, this help understand how much time is it taking to send
159+ // the desired page that the client wants to access in the frontend.
160+ fmt .Println ("time: " , time .Since (start ))
105161 })
106162 }
107163}
0 commit comments