Skip to content

Instantly share code, notes, and snippets.

@Sequoia
Created February 23, 2019 17:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sequoia/ee57bf7a2cbd435642ed03c65ec1aefb to your computer and use it in GitHub Desktop.
Save Sequoia/ee57bf7a2cbd435642ed03c65ec1aefb to your computer and use it in GitHub Desktop.
My solution to the Lynda.com exercise

Write an HTTP server that functions as a key/value database. Users will make a POST request to /db to create or assign a value. User will make a GET request /db/ to get the value. Both request and response will be in JSON format. Use a map from string to the empty interface to hold values. And since we can't access the Go data structure from two different Go routines, limit the access to the map with a sync.Mutex.

// Key Value Server
package main
import (
"fmt"
"log"
"net/http"
"encoding/json"
"sync"
"strings"
)
type KVSet struct{
Key string `json:"key"`
Value interface{} `json:"value"`
}
type KVGet struct{
Key string `json:"key"`
}
type KVResponse = struct{
Value interface{} `json:"value"`
Error string `json:"error"`
}
// Database
var db = make(map[string]interface{})
var dbMux = &sync.Mutex{}
// POST handler
func setHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
// parse request
reqPair := &KVSet{}
if err := json.NewDecoder(r.Body).Decode(reqPair); err != nil{
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
dbMux.Lock()
// set/upate value
db[reqPair.Key] = reqPair.Value
dbMux.Unlock()
w.WriteHeader(http.StatusOK)
return
}
// GET handler
func getHandler(w http.ResponseWriter, r *http.Request) {
resp := &KVResponse{}
out := json.NewEncoder(w)
key := strings.Trim(r.URL.Path,"/")
// get value
dbMux.Lock()
v, ok := db[key]
dbMux.Unlock()
// if !ok, set response header 404
if !ok{
resp.Error = fmt.Sprintf("key %q not found", key)
w.WriteHeader(http.StatusNotFound)
if err := out.Encode(resp); err != nil{
log.Printf("Can't encode %v - %s", resp, err)
}
return
}
// marshal response
resp.Value = v
// respond with { error : "", status : "" }
if err := out.Encode(resp); err != nil {
// Can't return error to client here
log.Printf("can't encode %v - %s", resp, err)
}
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
getHandler(w, r)
case http.MethodPost:
setHandler(w, r)
default:
http.Error(
w,
fmt.Sprintf("method %s not supported", r.Method),
http.StatusBadRequest,
)
}
}
func main() {
http.HandleFunc("/", rootHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment