@@ -12,16 +12,17 @@ import (
1212 "time"
1313
1414 ds "github.com/ipfs/go-datastore"
15+ "github.com/ipfs/go-datastore/query"
1516)
1617
17- var acceptedResp = []byte ("Transaction accepted" )
18-
18+ // HTTPServer wraps a KVExecutor and provides an HTTP interface for it
1919type HTTPServer struct {
2020 executor * KVExecutor
2121 server * http.Server
2222 injectedTxs atomic.Uint64
2323}
2424
25+ // NewHTTPServer creates a new HTTP server for the KVExecutor
2526func NewHTTPServer (executor * KVExecutor , listenAddr string ) * HTTPServer {
2627 hs := & HTTPServer {
2728 executor : executor ,
@@ -38,13 +39,14 @@ func NewHTTPServer(executor *KVExecutor, listenAddr string) *HTTPServer {
3839 Addr : listenAddr ,
3940 Handler : mux ,
4041 ReadHeaderTimeout : 10 * time .Second ,
41- MaxHeaderBytes : 4096 ,
4242 }
4343
4444 return hs
4545}
4646
47+ // Start begins listening for HTTP requests
4748func (hs * HTTPServer ) Start (ctx context.Context ) error {
49+ // Start the server in a goroutine
4850 errCh := make (chan error , 1 )
4951 go func () {
5052 fmt .Printf ("KV Executor HTTP server starting on %s\n " , hs .server .Addr )
@@ -53,8 +55,10 @@ func (hs *HTTPServer) Start(ctx context.Context) error {
5355 }
5456 }()
5557
58+ // Monitor for context cancellation
5659 go func () {
5760 <- ctx .Done ()
61+ // Create a timeout context for shutdown
5862 shutdownCtx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
5963 defer cancel ()
6064
@@ -64,30 +68,42 @@ func (hs *HTTPServer) Start(ctx context.Context) error {
6468 }
6569 }()
6670
71+ // Check if the server started successfully
6772 select {
6873 case err := <- errCh :
6974 return err
70- case <- time .After (100 * time .Millisecond ):
75+ case <- time .After (100 * time .Millisecond ): // Give it a moment to start
76+ // Server started successfully
7177 return nil
7278 }
7379}
7480
81+ // Stop shuts down the HTTP server
7582func (hs * HTTPServer ) Stop () error {
7683 return hs .server .Close ()
7784}
7885
86+ // handleTx handles transaction submissions
87+ // POST /tx with raw binary data or text in request body
88+ // It is recommended to use transactions in the format "key=value" to be consistent
89+ // with the KVExecutor implementation that parses transactions in this format.
90+ // Example: "mykey=myvalue"
7991func (hs * HTTPServer ) handleTx (w http.ResponseWriter , r * http.Request ) {
8092 if r .Method != http .MethodPost {
8193 http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
8294 return
8395 }
8496
85- body , err := io .ReadAll (io .LimitReader (r .Body , 4096 ))
86- r .Body .Close ()
97+ body , err := io .ReadAll (r .Body )
8798 if err != nil {
8899 http .Error (w , "Failed to read request body" , http .StatusBadRequest )
89100 return
90101 }
102+ defer func () {
103+ if err := r .Body .Close (); err != nil {
104+ fmt .Printf ("Error closing request body: %v\n " , err )
105+ }
106+ }()
91107
92108 if len (body ) == 0 {
93109 http .Error (w , "Empty transaction" , http .StatusBadRequest )
@@ -97,7 +113,10 @@ func (hs *HTTPServer) handleTx(w http.ResponseWriter, r *http.Request) {
97113 hs .executor .InjectTx (body )
98114 hs .injectedTxs .Add (1 )
99115 w .WriteHeader (http .StatusAccepted )
100- w .Write (acceptedResp )
116+ _ , err = w .Write ([]byte ("Transaction accepted" ))
117+ if err != nil {
118+ fmt .Printf ("Error writing response: %v\n " , err )
119+ }
101120}
102121
103122func (hs * HTTPServer ) handleTxBatch (w http.ResponseWriter , r * http.Request ) {
@@ -160,39 +179,77 @@ func (r *bytesReaderImpl) Read(p []byte) (int, error) {
160179 return n , nil
161180}
162181
182+ // handleKV handles direct key-value operations (GET/POST) against the database
183+ // GET /kv?key=somekey - retrieve a value
184+ // POST /kv with JSON {"key": "somekey", "value": "somevalue"} - set a value
163185func (hs * HTTPServer ) handleKV (w http.ResponseWriter , r * http.Request ) {
164- if r .Method != http .MethodGet {
165- http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
166- return
167- }
186+ switch r .Method {
187+ case http .MethodGet :
188+ key := r .URL .Query ().Get ("key" )
189+ if key == "" {
190+ http .Error (w , "Missing key parameter" , http .StatusBadRequest )
191+ return
192+ }
168193
169- key := r .URL .Query ().Get ("key" )
170- if key == "" {
171- http .Error (w , "Missing key parameter" , http .StatusBadRequest )
172- return
173- }
194+ // Use r.Context() when calling the executor method
195+ value , exists := hs .executor .GetStoreValue (r .Context (), key )
196+ if ! exists {
197+ // GetStoreValue now returns false on error too, check logs for details
198+ // Check if the key truly doesn't exist vs a DB error occurred.
199+ // For simplicity here, we treat both as Not Found for the client.
200+ // A more robust implementation might check the error type.
201+ _ , err := hs .executor .db .Get (r .Context (), ds .NewKey (key ))
202+ if errors .Is (err , ds .ErrNotFound ) {
203+ http .Error (w , "Key not found" , http .StatusNotFound )
204+ } else {
205+ // Some other DB error occurred
206+ http .Error (w , "Failed to retrieve key" , http .StatusInternalServerError )
207+ fmt .Printf ("Error retrieving key '%s' from DB: %v\n " , key , err )
208+ }
209+ return
210+ }
174211
175- value , exists := hs .executor .GetStoreValue (r .Context (), key )
176- if ! exists {
177- if _ , err := hs .executor .db .Get (r .Context (), ds .NewKey (key )); errors .Is (err , ds .ErrNotFound ) {
178- http .Error (w , "Key not found" , http .StatusNotFound )
179- } else {
180- http .Error (w , "Failed to retrieve key" , http .StatusInternalServerError )
181- fmt .Printf ("Error retrieving key '%s' from DB: %v\n " , key , err )
212+ _ , err := w .Write ([]byte (value ))
213+ if err != nil {
214+ fmt .Printf ("Error writing response: %v\n " , err )
182215 }
183- return
184- }
185216
186- w .Write ([]byte (value ))
217+ default :
218+ http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
219+ }
187220}
188221
222+ // handleStore returns all non-reserved key-value pairs in the store by querying the database
223+ // GET /store
189224func (hs * HTTPServer ) handleStore (w http.ResponseWriter , r * http.Request ) {
190225 if r .Method != http .MethodGet {
191226 http .Error (w , "Method not allowed" , http .StatusMethodNotAllowed )
192227 return
193228 }
194229
195- store := hs .executor .GetAllEntries ()
230+ store := make (map [string ]string )
231+ q := query.Query {} // Query all entries
232+ results , err := hs .executor .db .Query (r .Context (), q )
233+ if err != nil {
234+ http .Error (w , "Failed to query store" , http .StatusInternalServerError )
235+ fmt .Printf ("Error querying datastore: %v\n " , err )
236+ return
237+ }
238+ defer results .Close ()
239+
240+ for result := range results .Next () {
241+ if result .Error != nil {
242+ http .Error (w , "Failed during store iteration" , http .StatusInternalServerError )
243+ fmt .Printf ("Error iterating datastore results: %v\n " , result .Error )
244+ return
245+ }
246+ // Exclude reserved genesis keys from the output
247+ dsKey := ds .NewKey (result .Key )
248+ if dsKey .Equal (genesisInitializedKey ) || dsKey .Equal (genesisStateRootKey ) {
249+ continue
250+ }
251+ store [result .Key ] = string (result .Value )
252+ }
196253
197254 w .Header ().Set ("Content-Type" , "application/json" )
198255 if err := json .NewEncoder (w ).Encode (store ); err != nil {
@@ -218,5 +275,7 @@ func (hs *HTTPServer) handleStats(w http.ResponseWriter, r *http.Request) {
218275 }
219276
220277 w .Header ().Set ("Content-Type" , "application/json" )
221- json .NewEncoder (w ).Encode (stats )
278+ if err := json .NewEncoder (w ).Encode (stats ); err != nil {
279+ fmt .Printf ("Error encoding stats response: %v\n " , err )
280+ }
222281}
0 commit comments