Skip to content

Commit

Permalink
[SERVER] updates server example
Browse files Browse the repository at this point in the history
  • Loading branch information
PatAkil committed Nov 3, 2024
1 parent 102f4e6 commit d58e394
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 78 deletions.
125 changes: 74 additions & 51 deletions examples/httpserver/server.go
Original file line number Diff line number Diff line change
@@ -1,75 +1,98 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"github.com/google/uuid"
"io"
"log"
"net/http"
"net/url"
)

type Response struct {
Origin string `json:"origin"`
Method string `json:"method"`
Url string `json:"url"`
Args map[string][]string `json:"args"`
Headers map[string][]string `json:"headers"`
Body string `json:"body"`
type Server struct {
router *http.ServeMux
}

type echoHandler struct {
debug bool
}

func (eh *echoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
u, _ := url.Parse(r.RequestURI)
params, _ := url.ParseQuery(u.RawQuery)
var body string
if r.Body != nil {
asBytes, _ := ioutil.ReadAll(r.Body)
if asBytes != nil {
body = string(asBytes)
}
}
resp := Response{
Origin: r.RemoteAddr,
Method: r.Method,
Url: u.Path,
Args: params,
Headers: r.Header,
Body: body,
func main() {
s := &Server{
router: http.NewServeMux(),
}

if eh.debug {
log.Printf("Got %s on %s from %s:%s", r.Method, r.RequestURI, r.RemoteAddr, string(body))
}
s.router.HandleFunc("POST /api/patient", s.createPatientHandler())

addr := "localhost:8080"

fmt.Printf("Start listening at %s", addr)
err := http.ListenAndServe(addr, s.router)

// Encode json
jsonResp, err := json.Marshal(resp)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
log.Fatal("could not start server")
}
}

// write headers
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
type CreatePatientRequest struct {
Patient
}

// write formatted json body
var formattedResp bytes.Buffer
json.Indent(&formattedResp, jsonResp, "", "\t")
w.Write(formattedResp.Bytes())
type CreatePatientResponse struct {
UID string `json:"uid,omitempty"`
Patient
}

type Patient struct {
FullName string `json:"fullName,omitempty"`
AddressLine string `json:"addressLine,omitempty"`
Allergies []string `json:"allergies,omitempty"`
}

func main() {
mux := http.NewServeMux()
func (s *Server) createPatientHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)

if err != nil {
http.Error(w, "Unable to read request body", http.StatusBadRequest)
return
}
defer func(body io.ReadCloser) {
err := body.Close()
if err != nil {
// log error
}
}(r.Body)

cpr := CreatePatientRequest{}
err = json.Unmarshal(body, &cpr)
if err != nil {
http.Error(w, "Unable to read request body", http.StatusBadRequest)
return
}

patient := cpr.Patient
patientID, err := s.CreatePatient(patient)

resp := CreatePatientResponse{
UID: patientID,
Patient: patient,
}

b, err := json.Marshal(resp)

_, err = w.Write(b)

if err != nil {
http.Error(w, fmt.Sprintf("could not write response: %s", err), http.StatusInternalServerError)
}
}

}

h := &echoHandler{debug: true}
mux.Handle("/", h)
func (s *Server) CreatePatient(p Patient) (string, error) {
uid := uuid.New().String()
fmt.Printf("storing patient %+v, with uid %s", p, uid)

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.

fmt.Printf("Start listening at http://localhost:3000/\n")
http.ListenAndServe(":3000", mux)
return uid, nil
}
56 changes: 32 additions & 24 deletions examples/httpserver/server_test.go
Original file line number Diff line number Diff line change
@@ -1,42 +1,50 @@
package main

import (
"bytes"
"context"
"encoding/json"
"github.com/stretchr/testify/assert"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
)

func TestServer(t *testing.T) {
// mock response
recorder := httptest.NewRecorder()
ctx := context.Background()

s := Server{router: http.NewServeMux()}

cpr := CreatePatientRequest{Patient{
FullName: "Patrick",
AddressLine: "a",
Allergies: []string{"cilantro"},
}}

b, err := json.Marshal(cpr)

// create a http request that triggers your server
req, _ := http.NewRequest("GET", "", nil)
req.RemoteAddr = "1.2.3.4"
req.RequestURI = "/doit?arg1=1&arg2=two"
req.Header.Set("Accept", "application/json")
assert.NoError(t, err)

req, err := http.NewRequestWithContext(ctx, "POST", "/api/patient", bytes.NewBuffer(b))
assert.NoError(t, err)

// call subject of test
eh := echoHandler{true}
eh.ServeHTTP(recorder, req)
w := httptest.NewRecorder()

// verify response
assert.Equal(t, http.StatusOK, recorder.Code)
s.createPatientHandler().ServeHTTP(w, req)

// decode json
dec := json.NewDecoder(recorder.Body)
var resp Response
err := dec.Decode(&resp)
assert.Equal(t, http.StatusOK, w.Code)

// json body
resp := CreatePatientResponse{}
body, err := io.ReadAll(w.Body)
assert.NoError(t, err)
assert.Equal(t, "1.2.3.4", resp.Origin)
assert.Equal(t, "/doit", resp.Url)
assert.Equal(t, "1", resp.Args["arg1"][0])
assert.Equal(t, "two", resp.Args["arg2"][0])
assert.Equal(t, "application/json", resp.Headers["Accept"][0])

err = json.Unmarshal(body, &resp)
assert.NoError(t, err)

assert.Equal(t, "Patrick", resp.FullName)
assert.Equal(t, "a", resp.AddressLine)
assert.Len(t, resp.Allergies, 1)
assert.Equal(t, "cilantro", resp.Allergies[0])

}
7 changes: 4 additions & 3 deletions go-training.slide
Original file line number Diff line number Diff line change
Expand Up @@ -2062,13 +2062,14 @@ Example with "Alice"

* Exercise: http server

- Create a HTTP server that supports POST on /api/patient to create a patient
- Test it with curl or your previously created HTTP-client
- Create an HTTP server that supports POST on /api/patient to create a patient
- Simulate storing the patient object and return the patient including an ID
- Test it with curl or a created HTTP-client

# Example curl request to submit a new patient
curl -vvv \
-X POST \
--data '{"uid":"1", "fullName":"Marc","addressLine":"a","allergies":["peanut"]}' \
--data '{"fullName":"Marc","addressLine":"a","allergies":["peanut"]}' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
http://localhost:8080/api/patient
Expand Down

0 comments on commit d58e394

Please sign in to comment.