Customizing an existing handler in Go's http library - http

With the following being defined as is noted in the http library:
func Handle(pattern string, handler Handler)
type Handler interface { ServeHTTP(*Conn, *Request) }
How can I improve upon an existing handler (say, websocket.Draft75Handler for instance) by giving it an additional argument (and tell it what to do with the argument)?
I'm trying to create a handler that contains within it one end of a channel. It will use that channel to talk to some other part of the program. How can I get that channel into the the handler function?
Apologies if this is a silly question. I'm new at go and decided to learn by reading the tutorial and then jumping right into code. Thanks for any help!

If the type is a function, like websocket.Draft75Handler, you could wrap it in a closure:
func MyHandler(arg interface{}) websocket.Draft75Handler {
return func(c *http.ConnConn) {
// handle request
}
}
func main() {
http.Handle("/echo", MyHandler("argument"))
err := http.ListenAndServe(":12345", nil)
if err != nil {
panic("ListenAndServe: " + err.String())
}
}

Related

How to set an interface for a custom HTTP client?

I am having difficulty writing tests for this 3rd party library I am importing. I think this is because I want my CustomClient struct to have a client interface instead of the *banker.Client. This is making testing very difficult because it's hard to mock a *banker.Client. Any ideas how I can correctly turn this into an interface? So I can easily write mock tests against it and set up a fake client?
type CustomClient struct {
client *banker.Client //I want to change this to an interface
name string
address string
}
func (c *CustomClient) SetHttpClient(httpClient *banker.Client) { //I want to accept an interface so I can easily mock this.
c.client = httpClient
}
The problem is that banker.Client is a third party client I am importing with many structs and other fields inside of it. It looks like this:
type Client struct {
*restclient.Client
Monitor *Monitors
Pricing *Pricing
Verifications *Verifications
}
The end result is that my code looks like this:
func (c *CustomClient) RequestMoney() {
_, err := v.client.Verifications.GetMoney("fakeIDhere")
}
Given methods over fields on the struct, it sure wouldn't be a simple solution. However, we can try to minimize the lengthy test cases on the current package.
Add another layer (package) between your working package and banker. Simplifying the code in example to explain.
Let's say your banker package has the following code:
type Client struct {
Verification *Verification
}
type Verification struct{}
func (v Verification) GetMoney(s string) (int, error) {
...
}
Create another package that imports the banker and has interface defined, say bankops package:
type Bank struct {
BankClient *banker.Client
}
type Manager interface {
GetMoney(s string) (int, error)
}
func (b *Bank) GetMoney(s string) (int, error) {
return b.BankClient.Verification.GetMoney(s)
}
Note: The actual issue (test without interface) is still here in bankops package, but this is easier to test as we are only forwarding the result. Serves the purpose of unit tests.
Finally, in the current package (for me, it is main package), we can
type CustomClient struct {
client bankops.Manager
}
func (c *CustomClient) RequestMoney() {
_, err := c.client.GetMoney("fakeIDhere")
...
}
func main() {
client := &CustomClient{
client: &bankops.Bank{
BankClient: &banker.Client{
Verification: &banker.Verification{},
},
},
}
client.RequestMoney()
}
For working example, check in Playground.
You may add the setters or builders pattern as you were doing in your original code snippet to make the fields (like BankerClient) unexported.
I think it is impossible to make it into interface directly
because we should use the member variables of the Client.
How about making its member into interface?
For example,
for _, test := []struct{}{
testVerification VerificationInterface
}{{
testVerification: v.Client.Verifications
},{
testVerification: VerficationMock
}}{
// test code here
}

How to copy Context object without deriving

I want to make a copy of a context object - a request context to be exact, and make use of it later on in a separate go routine.
Problem is if I derive the request context using context.WithCancel(reqCtx) once the HTTP handler for this request is finished, not only will the original request context be cancelled, but also the copy of the request context will also be canceled.
I'd like to be able to copy the original request context and not have it canceled by the original context after the HTTP handler has finished executing.
Here's how to make a context that uses values from some other context, but not cancelation:
type valueOnlyContext struct{ context.Context }
func (valueOnlyContext) Deadline() (deadline time.Time, ok bool) { return }
func (valueOnlyContext) Done() <-chan struct{} { return nil }
func (valueOnlyContext) Err() error { return nil }
Use it like this:
ctx := valueOnlyContext{reqCtx}
Using the values without cancelation is probably outside the design intent of the context package. If the designers of the package thought this is a good thing, I would have expected them to bundle up the above in a context package function.

How can you bind a POST body to map?

I've been using Gin's ShouldBind() method to bind form data to a struct:
type UpdateUserInfoContext struct {
Country string `json:"country"`
EmailAddr string `json:"emailAddr"`
LoginID string `json:"loginID"`
UserName string `json:"username"`
}
func (h *handler) updateUserInfo(ctx *gin.Context) {
var json UpdateUserInfoContext
if err := ctx.ShouldBind(&json); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
h.service.UpdateUserPassword(json)
ctx.JSON(http.StatusOK, "success")
}
But now I need to build a large, dynamic UPDATE SQL based on what is and isn't present in the body of a POST request. Since ShouldBind() binds to a struct I can't iterate over the values in the body without using reflection. I figured an easier way would be to see if there's a method to bind the requests to a map instead of a struct. There is the context method PostFormMap(key string), however as far as I can tell from the example given here (https://github.com/gin-gonic/gin#another-example-query--post-form), this method requires the values correspond to to the argument key in the request body. Does anyone have any experience doing this? Thank you!
package main
import (
"fmt"
"encoding/json"
)
func main() {
strbody:=[]byte("{\"mic\":\"check\"}")
mapbody:=make(map[string]string)
json.Unmarshal(strbody,&mapbody)
fmt.Println(fmt.Sprint("Is this thing on? ", mapbody["mic"]))
}
//returns Is this thing on? check
https://play.golang.org/p/ydLuLsY8qla

go-swagger WriteResponse

I am using go-swagger but I have an case where I want to write a string to a response. I need to call the "WriteResponse" function
WriteResponse(rw http.ResponseWriter, producer runtime.Producer)
The issue that I am having is that I don't know how to convert a string to a http.ResponseWriter and create a runtime.Producer.
If it helps here is a snippit of my code...
//convert the database response to a string array of valid JSON
stringArray, conversionError := plan.ConvertPlanArrayToString(response)
if conversionError != nil {
return swaggerPlan.NewPlanGetTemplatePlanInternalServerError()
}
//Need to convert string value
stringValue := stringArray[0]
return swaggerPlan.NewPlanGetTemplatePlanOK().WriteResponse(NOT SURE HOW TO CREATE http.ResponseWriter, runtime.Producer)
Thank you very much
As mentioned by Flimzy, you'll have to write your string to the http.ResponseWriter, which is probably what your WriteResponse function does anyway.
The code you provided should somewhere be called from within a handler function:
func (...) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
// your code should be here and you can pass the http.ResponseWriter
}
Thank you both very much, you were very helpful. It worked exactly like you said but in go-swagger it is slightly different.
My handler was updated to return a custom "ResponderFunc" and then I could write my output directly to it. Worked great, here is my code...
//Handle a 200 by sending the list of plans back as json
return middleware.ResponderFunc(func(rw http.ResponseWriter, pr runtime.Producer) {
rw.WriteHeader(200)
rw.Write(byteArrayPlan)
})

How to serve shared struct with Go http package?

I have a struct with many fields (some of them are pointers to other structs as well) which are being continuosly updated in a separate goroutine. The same struct is accessed from go's http template when a page is served.
Code example:
type SharedStruct struct {
Description string
Counter int
Status_ *Status
LastChecked time.Time
//other fields
}
var shared = &SharedStruct{}
go func() {
//..updates fields every 5 minutes
}()
go-http handler:
func someHandler(w http.ResponseWriter, r *http.Request) {
t.ExecuteTemplate(w, "page.html", shared)
}
and page.html template:
...
Status: {{.Status_.StatusCode}}
Counter: {{.Counter}}
Last checked: {{.LastChecked.Format "2006-02-01 15:04:05"}}
So far everything works as expected, but I'm aware that bad things can happen without any synchronization. What is the preferred way to handle this properly?
The preferred way is the same as in any other cases.
Either use a mutex when the shared struct is read / updated:
var shared = &SharedStruct{}
var mux = &sync.RWMutex{}
func someHandler(w http.ResponseWriter, r *http.Request) {
mux.RLock()
defer mux.RUnlock()
t.ExecuteTemplate(w, "page.html", shared)
}
// Code that modifies shared:
mux.Lock()
shared.Counter++
mux.Unlock()
Or if the template execution takes long time, it may be protitable to make a copy of the shared struct and pass the copy when executing the template, so that during template execution access to shared is not blocked. Note that while making the copy you still have to use a mutex. Also if not only the pointers but the pointed values may change, you also have to make a copy of those:
func someHandler(w http.ResponseWriter, r *http.Request) {
mux.RLock()
shared2 := &SharedStruct{}
*shared2 = *shared
shared2.Status_ = new(Status)
*shared2.Status_ = *shared.Status_
mux.RUnlock()
t.ExecuteTemplate(w, "page.html", shared2)
}
If the template only uses a small subset of the shared fields, it is enough to make only a copy of those of course.

Resources