How to get initial timestamp of a http request in golang? - http

I am writing a golang program using revel framework, in which i need to check the initial timestamp of a http request.
I know how to do it in C# :
HttpContextWrapper context = Request.Properties["MS_HttpContext"] as HttpContextWrapper;
DateTime t2 = context.Timestamp.ToUniversalTime();
Didn't get much how to do it in Go.

HttpContext class in .Net framework sets the timestamp when a request arrives at the server. You might as well store the timestamp in the first line of your request handler function.

The simplest thing to do is grab the current time within your handler.
type Handler struct {
}
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
rs := time.Now().UTC()
//TODO: Use the time.
}
If you want to measure the time taken by all middleware preceding your handler, then you can update the Go context and place your middleware at the start of your middleware chain.
Here's an example of what that middleware might look like:
package timemiddleware
import (
"context"
"net/http"
"time"
)
// New returns new middleware which tracks the time that a request started.
func New(next http.Handler) http.Handler {
return handler{
next: next,
}
}
type key int
const id = key(1)
type handler struct {
next http.Handler
}
func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), id, time.Now().UTC())
h.next.ServeHTTP(w, r.WithContext(ctx))
}
// GetTime returns time from the current request, where it has previously been added by the middleware.
func GetTime(r *http.Request) (t time.Time, ok bool) {
v := r.Context().Value(id)
t, ok = v.(time.Time)
return
}
You'd use this as per this example:
package main
import (
"fmt"
"net/http"
"time"
"github.com/xxxxx/timemiddleware"
)
func main() {
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(time.Second * 5)
w.Write([]byte("Hello"))
if t, ok := timemiddleware.GetTime(r); ok {
fmt.Println(t)
fmt.Println(time.Now().UTC())
}
})
h := timemiddleware.New(next)
fmt.Println(http.ListenAndServe("0.0.0.0:8080", h))
}

Related

Go: How to create a server which can serve urls described in config file

could anyone help me here please as I'm new to golang? I have a yaml file which looks like this:
port: 5000
handlers:
- name: test1
uri: /api/test1
response:
status: 200
body: test1
- name: test2
uri: /api/test2
response:
status: 500
body: test2
based on this file I want to create a server. Currently I'm trying to do it this way, but looks like it doesn't work as expected.
What am I doing wrong and what is the better way to achieve what I need?
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"gopkg.in/yaml.v2"
)
func main() {
config := parseYaml("conf.yaml")
configHandlers := config.Handlers
mux := http.NewServeMux()
for _, handler := range *configHandlers {
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(*handler.Response.Status)
fmt.Fprintf(w, *handler.Response.Body)
})
}
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", *config.Port), mux))
}
type YamlConfig struct {
Port *string `yaml:"port"`
Handlers *[]HandlerConfig `yaml:"handlers"`
}
type HandlerConfig struct {
Uri *string `yaml:"uri"`
Name *string `yaml:"name"`
Response *Response `yaml:"response"`
}
type Response struct {
Status *int `yaml:"status"`
Body *string `yaml:"body"`
}
func (c *YamlConfig) parseYaml(data []byte) error {
return yaml.Unmarshal(data, c)
}
func parseYaml(path string) YamlConfig {
data, err := ioutil.ReadFile(path)
if err != nil {
log.Fatal(err)
}
var config YamlConfig
if err := config.parseYaml(data); err != nil {
log.Fatal(err)
}
return config
}
Update:
If I run this server then regardless of which endpoint I hit, it will always return me 500 and test2 in body
What you're seeing is seemingly a common pitfall for people:
configHandlers := config.Handlers
mux := http.NewServeMux()
for _, handler := range *configHandlers {
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(*handler.Response.Status)
fmt.Fprintf(w, *handler.Response.Body)
})
}
The for loop, on each iteration, reassigns the handler variable. In the loop body, you create a new function and pass it to mux.HandlerFun. These function bodies kind of inherit the outer scope, and access this handler variable. The variable is reassigned outside of the functions, and thus the values each handler function has access to changes with it. What you can do to address the issue is mask the handler variable the loop uses, and create a scope that is unique to each handler. The classic way in languages like JavaScript (where this is - or used to be back when I wrote some JS - a common issue) is to wrap the code in an IIFE (Immediately Invoked Function Expression):
for _, handler := range *configHandlers {
func (handler *HandlerConfig) { // handler is now the argument passed to this function
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(*handler.Response.Status)
fmt.Fprintf(w, *handler.Response.Body)
})
}(handler) // call the function with the _current_ value of handler
}
This is a tad messy, and because golang is properly block-scoped, you can just do this:
for _, handler := range *configHandlers {
h := handler // create a variable in the inner scope
mux.HandleFunc(*handler.Uri, func(w http.ResponseWriter, r *http.Request) {
// now h will reference a copy unique to each iteration
w.WriteHeader(*h.Response.Status)
fmt.Fprintf(w, *h.Response.Body)
})
}
That ought to fix it. I've noticed some weirdness with your use of pointers in the types you've added to your question, though... Fields like Port being of type *string? Why wouldn't you just use string? No Same for the Body and Status fields in the Response type. By changing them to plain string fields you don't have to dereference them in your handler functions. It will look a lot cleaner.
A bigger worry is this field:
Handlers *[]HandlerConfig `yaml:"handlers"`
I'm not sure if you really know what the type of this field is, but it makes next to no sense. Handlers is now a pointer to a slice of HandlerConfig values. I'm assuming you wanted this field to be:
// Handlers is a slice of HandlerConfig values:
Handlers []HandlerConfig `yaml:"handlers"`
// or Handlers is a slice of pointers to HandlerConfig values
Handlers []*HandlerConfig `yaml:"handlers"`
Generally speaking, a pointer to a slice, especially in a config type is bad code.
If you define a struct that will represent the configuration in your YAML file, you can unmarshall the yaml into an instantiated struct of that type using the yaml package. From there, you can reference the fields in the struct as any other struct.
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
type YamlExample struct {
FieldOne string `yaml:"fieldOne"`
NestedField struct {
Name string `yaml:"name"`
} `yaml:"nestedField"`
}
const YamlEx string = `
fieldOne: one
nestedField:
name: nestedFieldName
`
func main() {
var yamlE YamlExample
err := yaml.Unmarshal([]byte(YamlEx), &yamlE)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", yamlE)
}
Link to example.
In your case, you'd probably want to handle the routes in a struct and then reference the fields in the struct for things like route name, how to handle the body of the request, etc. If your YAML is stored in a file, you'll have to use something like the io package to read the file into a byte array that the YAML package can parse. See here for a reference.

I am not sure how to create and pass a context

I have an HTTP server that when it recieves a request calls on an underlying gRPC server.
I have chosen to abstract away the gRPC call with an interface, to make testing of the http server easier.
The problem is that I am constantly getting the errors:
rpc error: code = Canceled desc = grpc: the client connection is closing
or
rpc error: code = Canceled desc = context canceled
And as I understand both of these are related to the context getting passed into the grpc call. And that I want the context to be alive throughout both the HTTP and gRPC calls.
type SetterGetter interface {
Getter(key string) (val string)
}
type Service struct {
sg SetterGetter
ctx context.Context
}
func (s *Service) getHandler(rw http.ResponseWriter, r *http.Request) {
key := r.URL.Query()["key"][0]
res := s.sg.Getter(key)
fmt.Fprintf(rw, "Successfully got value: %s\n", res)
}
func main() {
s := new(Service)
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
s.sg = gc.NewClientwrapper(ctx)
http.HandleFunc("/get", s.getHandler)
log.Fatal(http.ListenAndServe(port, nil))
}
And my Getter implementation looks like this:
type clientwrapper struct {
sc pb.ServicesClient
ctx context.Context
}
func NewClientwrapper(ctx context.Context) *clientwrapper {
cw := new(clientwrapper)
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
err = fmt.Errorf("Error could not dial address: %v", err)
}
defer conn.Close()
cw.ctx = ctx
cw.sc = pb.NewServicesClient(conn)
return cw
}
func (cw *clientwrapper) Getter(key string) (val string) {
// Make the GRPC request
res, err := cw.sc.Get(cw.ctx, &pb.GetRequest{Key: key})
if err != nil {
return ""
}
getVal := res.GetValue()
return getVal
}
So here I am creating a context in my http servers main menu, and passing it onwards. I do it like this because it worked if I removed my interface and put everything in the main file.
I have also tried to create the context both in the http handler and passing it to the Getter and I have also tried creating it in the Getter itself.
I think the correct approach is to create the context in the http request using the context that gets created by the request and then passing it to the grpc Getter. Like such:
func (s *Service) getHandler(rw http.ResponseWriter, r *http.Request) {
// Create it like such
ctx, cancel := context.WithTimeout(r.Context(), 100*time.Second)
key := r.URL.Query()["key"][0]
// And pass it onwards (of course we need to change function signature for this to work)
res := s.sg.Getter(ctx, key)
fmt.Fprintf(rw, "Successfully got value: %s\n", res)
}
So how should I create my context here, to not get these errors?
If your goal is to keep a long-running task running in the background, that doesn't cancel when the request is finalized, then don't use the request's context. Use context.Background() instead.
For example:
func (s *Service) getHandler(rw http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
// ...

Cannot access go server from commandline / adding web server logic to existing code

In my first iteration, I got the following to compile and work:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func routineHandle (query string, ch chan <- string) {
ch <- query
wg.Wait()
}
func ping () {
ch := make(chan string)
wg.Add(1)
go routineHandle("testquery",ch)
wg.Done()
msg := <-ch
fmt.Println("Channel Message",msg)
}
func main () {
ping()
}
This successfully uses 1 channel to perform the goroutine routineHandle
Now, I want to add web server capabilities which perform the following:
Listens on a port and accepts/returns requests
Hooks into the routineHandle so we can utilize that goroutine as an Api Server Method
My code is on a linux box without a gui so I don't know how to test the web server capabilities.
My code looks like as follows:
package main
import (
"fmt"
"sync"
"net/http"
)
var wg sync.WaitGroup
func routineHandle (query string, ch chan <- string) {
ch <- query
wg.Wait()
}
func ping (w http.ResponseWriter, r *http.Request) {
ch := make(chan string)
wg.Add(1)
go routineHandle("testquery",ch)
wg.Done()
msg := <-ch
//fmt.Println("Channel Message",msg)
w.Write([]byte msg)
}
func main() {
http.HandleFunc("/",ping)
http.ListenAndServe(":1234",nil)
}
You'll notice a few additions with my second piece of code:
I added the net/http package
I added the http listener to the main method
I added response writer and request parameters to the ping function
I changed from fmt.Println() to c.Write
The end goal would be for typing in a query, and then using that query in the routineHandle goroutine
Like I Said Though, I don't know how to test this final implementation on an ubuntu box without a gui
One last thing to note. If you notice any issues PLEASE let me know. I wonder if running a goroutine inside a http server would cause an issue
The code in the question uses the wait group incorrectly (wait and done should be swapped, the group should not be shared globally) and redundantly with the channel. Delete the use of the wait group to fix the code.
package main
import (
"net/http"
)
func routineHandle(query string, ch chan<- string) {
ch <- query
}
func ping(w http.ResponseWriter, r *http.Request) {
ch := make(chan string)
go routineHandle("testquery", ch)
msg := <-ch
w.Write([]byte(msg))
}
func main() {
http.HandleFunc("/", ping)
http.ListenAndServe(":1234", nil)
}

How to test http request handlers

I have a set of requests handlers like the one below:
func GetProductsHandler(w http.ResponseWriter, req *http.Request) {
defer req.Body.Close()
products := db.GetProducts()
// ...
// return products as JSON array
}
How do I test them the right way? Should I send mock ResponseWriter and Request objects to the function and see the results?
Are there tools to mock request and response objects in Go to simplify the process without having to start server before testing?
Go provides a mock writer for use in testing handlers. The standard library documentation provides an example:
package main
import (
"fmt"
"net/http"
"net/http/httptest"
)
func main() {
handler := func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "something failed", http.StatusInternalServerError)
}
req := httptest.NewRequest("GET", "http://example.com/foo", nil)
w := httptest.NewRecorder()
handler(w, req)
fmt.Printf("%d - %s", w.Code, w.Body.String())
}
I think having a global dependency (db) throws a wrench into clean unit testing. Using go your test could reassign a value, masking, the global value of db.
Another strategy (my preferred) is to package your handler in a struct, which has a db attribute..
type Handlers struct {
db DB_INTERFACE
}
func (hs *Handlers) GetProductsHandler(w http.ResponseWriter, req *http.Request) {...}
This way your test can instantiate a Handlers with a stub db object which will allow you to create IO free unit tests.

Passing in parameters to a http.HandlerFunc

I'm using Go's built in http server and pat to respond to some URLs:
mux.Get("/products", http.HandlerFunc(index))
func index(w http.ResponseWriter, r *http.Request) {
// Do something.
}
I need to pass in an extra parameter to this handler function - an interface.
func (api Api) Attach(resourceManager interface{}, route string) {
// Apply typical REST actions to Mux.
// ie: Product - to /products
mux.Get(route, http.HandlerFunc(index(resourceManager)))
// ie: Product ID: 1 - to /products/1
mux.Get(route+"/:id", http.HandlerFunc(show(resourceManager)))
}
func index(w http.ResponseWriter, r *http.Request, resourceManager interface{}) {
managerType := string(reflect.TypeOf(resourceManager).String())
w.Write([]byte(fmt.Sprintf("%v", managerType)))
}
func show(w http.ResponseWriter, r *http.Request, resourceManager interface{}) {
managerType := string(reflect.TypeOf(resourceManager).String())
w.Write([]byte(fmt.Sprintf("%v", managerType)))
}
How can I send in an extra paramter to the handler function?
You should be able to do what you wish by using closures.
Change func index() to the following (untested):
func index(resourceManager interface{}) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
managerType := string(reflect.TypeOf(resourceManager).String())
w.Write([]byte(fmt.Sprintf("%v", managerType)))
}
}
And then do the same to func show()
Another option is to use types implementing http.Handler directly rather than only using functions. For example:
type IndexHandler struct {
resourceManager interface{}
}
func (ih IndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
managerType := string(reflect.TypeOf(ih.resourceManager).String())
w.Write([]byte(fmt.Sprintf("%v", managerType)))
}
...
mux.Get(route, IndexHandler{resourceManager})
This kind of pattern can be useful if you want to refactor your ServeHTTP handler method into multiple methods.

Resources