this is my actual code :
package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
const BaseUrl = "http://127.0.01:5000"
const ListeningPort = "80"
func main() {
// intercept call
http.HandleFunc("/test", Test)
// all other traffic pass on
http.HandleFunc("/", ProxyFunc)
http.ListenAndServe(":"+ListeningPort, nil)
}
func ProxyFunc(w http.ResponseWriter, r *http.Request) {
u, err := url.Parse(BaseUrl)
if err != nil {
w.Write([]byte(err.Error()))
return
}
proxy := httputil.NewSingleHostReverseProxy(u)
proxy.ServeHTTP(w, r)
}
func Test(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("TEST"))
}
first to accept client connexion, i want to check if browser have enabled javascript, how i can do this in my actual code ?
i want check with this method :
https://pastebin.com/ZASFQumf
It is not possible to do that in Golang since it is a server side language. I don't even think it is possible with JavaScript.
It is not something you can add/set/get from the headers.
What you are trying to do is check browser specific flags.
You might be able to find third party libraries used to manage Chrome flags or Firefox flags etc. That is your best option.
Related
I'm studying Golang and have question on context.
I want to send request from server1 to server2 with context and want to read context value set by server1.
But it seems context of sercer2 doesn't carry server1's context value.
When I send request to server1 by curl http://localhost:8080
server1's console.
Send request to http://localhost:8082
server2's console.
request coming in
<nil>
How can I retrieve context value set by server1?
And if it is possible, I also want to know whether it is a correct way of exchanging value's like authentication between servers.
Middleware pattern is more desirable?
Thank you.
Codes
package main
import (
"context"
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.HandleFunc("/", hello)
fmt.Println("Starting listening on port 8080...")
http.ListenAndServe(":8080", r)
}
// Context's key.
type Sample string
var sampleKey Sample = "sample"
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("Send request to http://localhost:8082")
ctx := context.WithValue(context.Background(), sampleKey, "1234")
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://localhost:8082", nil)
if err != nil {
fmt.Println("Error while sending request: ", err)
}
// Send request.
var c http.Client
c.Do(req)
}
package main
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.HandleFunc("/", receive)
fmt.Println("Start listening on port 8082...")
http.ListenAndServe(":8082", r)
}
type Sample string
var sampleKey Sample
func receive(w http.ResponseWriter, r *http.Request) {
fmt.Println("request coming in")
fmt.Println(r.Context().Value(sampleKey))
}
The context is supposed to be used in a function call stack to share information and cancellation point between these functions. There is no automatic mechanism to serialize the value bound in the context over http. If you want to send data over http between these two servers, you mostly have three solution, you can:
encode that as an url parameter in the path somewhere
GET /hello/1234
send that in the http body, for example in JSON
POST /hello
{
"sample": 1234
}
as url key/value encoded as url parameters
/hello?sample=1234
I've looked into various different tools that can be used for mock testing in golang, but I'm trying to accomplish this task using httptest. In particular, I have a function as such:
type contact struct {
username string
number int
}
func getResponse(c contact) string {
url := fmt.Sprintf("https://mywebsite/%s", c.username)
req, err := http.NewRequest(http.MethodGet, url, nil)
// error checking
resp, err := http.DefaultClient.Do(req)
// error checking
return response
}
A lot of the documentation I've read seems to require creating a client interface or a custom transport. Is there no way to mock a response in a test file without changing this main code at all? I want to keep my client, response, and all the related details within the getResponse function. I could have the wrong idea, but I'm trying to find a way to intercept the http.DefaultClient.Do(req) call and return a custom response, is that possible?
https://pkg.go.dev/net/http/httptest#example-Server is a good example for your use case with a small refactoring of your code.
You just have to change the getResponse() by getResponse(url string) to be able to give the server mock url.
I've read seems to require creating a client interface
without changing this main code at all
Keeping your code clean is a good practice and you'll finally get used to it, a testable code is cleaner and a cleaner code is more testable, so don't worry to change your code (using interfaces) so it can accept mock objects.
Your code in its simplest form can be like this:
package main
import (
"fmt"
"net/http"
)
type contact struct {
username string
number int
}
type Client interface {
Do(req *http.Request) (*http.Response, error)
}
func main() {
getResponse(http.DefaultClient, contact{})
}
func getResponse(client Client, c contact) string {
url := fmt.Sprintf("https://mywebsite/%s", c.username)
req, _ := http.NewRequest(http.MethodGet, url, nil)
// error checking
resp, _ := http.DefaultClient.Do(req)
// error checking and response processing
return response
}
And your test can be like this:
package main
import (
"net/http"
"testing"
)
type mockClient struct {
}
// Do function will cause mockClient to implement the Client interface
func (tc mockClient) Do(req *http.Request) (*http.Response, error) {
return &http.Response{}, nil
}
func TestGetResponse(t *testing.T) {
client := new(mockClient)
getResponse(client, contact{})
}
But if you prefer to use httptest:
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
)
type contact struct {
username string
number int
}
func main() {
fmt.Println(getResponse(contact{}))
}
func getResponse(c contact) string {
// Make a test server
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "your response")
}))
defer ts.Close()
// You should still set your base url
base_url := ts.URL
url := fmt.Sprintf("%s/%s", base_url, c.username)
req, _ := http.NewRequest(http.MethodGet, url, nil)
// Use ts.Client() instead of http.DefaultClient in your tests.
resp, _ := ts.Client().Do(req)
// Processing the response
response, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return string(response)
}
I'm having some issues with implementing a slight MVC design with gorilla/mux.
The layout of the modules is as follows:
main.go
-- controllers
---- base.controller.go
---- example.controller.go
-- models
---- base.model.go
---- example.controller.go
All the files in controllers is in the controllers package, same with models and then the main.go is the main package.
Currently I'm just trying to get the Base Controller to be able to be shared with the main package which is working, although it's throwing some errors when trying to implement routes. The build is not throwing any errors, but the routes are not available. If I implement the Walk function in the Gorilla/Mux documentation to print out all the registered routes for the mux.Router then it gives me this error:
&{%!!(MISSING)s(*mux.Router=&{ [0xc4200901b0] map[] true
false false false}) %!!(MISSING)s(http.HandlerFunc=0xc8df0)
[%!!(MISSING)s(*mux.routeRegexp=&{/ false false true false
0xc420095360 / [] []})] %!!(MISSING)s(*mux.routeRegexpGroup=&{
0xc420016240 []}) %!!(MISSING)s(bool=true) %!!(MISSING)s(bool=false)
%!!(MISSING)s(bool=false) %!!(MISSING)s(bool=false)
%!!(MISSING)s(mux.BuildVarsFunc=)}
The reasoning for the global var V1Router *mux.Router is firstly to access it in the main package and also to create subrouters in the other controllers.
I am fairly new to Go, but I'm trying my best to learn the best practices! Any help would be greatly appreciated!
Example code below:
base.controllers.go
package controllers
import (
"fmt"
"bytes"
"net/http"
"github.com/gorilla/mux"
)
var V1Router *mux.Router
func init () {
V1Router = mux.NewRouter()
V1Router.StrictSlash(true)
V1Router.HandleFunc("/", BaseHandler)
}
// Base route to access the API Documentation.
func BaseHandler (w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, Gophers!")
}
main.go
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
In response to the comments, I tried this solution with the same result:
package main
import (
"net/http"
"log"
"github.com/projectrepo/project/models"
"github.com/projectrepo/project/controllers"
"github.com/gorilla/mux"
)
func main () {
r := mux.NewRouter()
r.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Gorilla mux.Router is supposed to be used to create mapping between a set of predefined rules (e.g. host, path, protocol, scheme, etc...) and it's handler (http.Handler or http.HandlerFunc). Gorilla mux can be used to replace standard server mux. If you combine gorilla/mux with built in http server mux as your original question, i.e.
func main () {
http.Handle("/v1", controllers.V1Router)
if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal("Serving error.")
}
}
what actually happen when a client access /v1 is controllers.V1Router will be called with request path /v1 passed to V1Router1. In the controllers.V1Router, you defined that / will be handled by BaseHandler. However, since incoming request path is /v1, it won't match to your routing table. If you want to define sub routing, you can do as follows (this is what I mean in first comment):
func main () {
r := mux.NewRouter()
v1 := r.PathPrefix("/v1").Subrouter()
controllers.RegisterHandlers(v1)
if err := http.ListenAndServe(":8000", r); err != nil {
log.Fatal("Serving error.")
}
}
Then in the controllers (base.controllers.go) define
//Register handlers and it's sub router
func RegisterHandlers(r *mux.Router) {
//base handler, i.e. /v1
r.StrictSlash(true)
r.HandleFunc("/", BaseHandler)
//example sub-router, i.e. /v1/example
ex := r.PathPrefix("/example").Subrouter()
ex.HandleFunc("/", ExampleHandler)
//other handlers...
}
I'm trying to get my hands dirty while playing with some Gorilla/Mux and Go-Redis but I'm facing a little implementation problem here.
Essentially I have a project structured like the following:
Where redismanager.go handles the initialization of a Redis Client:
package redismanager
import (
"fmt"
"github.com/go-redis/redis"
)
func InitRedisClient() redis.Client {
client := redis.NewClient(&redis.Options{
Addr : "localhost:6379",
Password: "",
DB : 0, //default
})
pong, err := client.Ping().Result()
if( err != nil ){
fmt.Println("Cannot Initialize Redis Client ", err)
}
fmt.Println("Redis Client Successfully Initialized . . .", pong)
return *client
}
Where main.go calls redismanager.InitRedisClient and initializes mux.Handlers:
package main
import (
"github.com/gorilla/mux"
"github.com/go-redis/redis"
"net/http"
"fmt"
"log"
"encoding/json"
"io/ioutil"
"../redismanager"
"../api"
)
type RedisInstance struct {
RInstance *redis.Client
}
func main() {
//Initialize Redis Client
client := redismanager.InitRedisClient()
//Get current redis instance to get passed to different Gorilla-Mux Handlers
redisHandler := &RedisInstance{RInstance:&client}
//Initialize Router Handlers
r := mux.NewRouter()
r.HandleFunc("/todo", redisHandler.AddTodoHandler).
Methods("POST")
fmt.Println("Listening on port :8000 . . .")
// Bind to a port and pass our router in
log.Fatal(http.ListenAndServe(":8000", r))
}
Now, I can easily define and let work properly AddTodoHandler in the same file like:
func (c *RedisInstance) AddTodoHandler(w http.ResponseWriter, r *http.Request) {
. . . doSomething
}
But, to make things a bit more modular, I'm trying to move all of these RouteHandlers inside their respective files in api package. In order to make that, I need to pass a reference to redisHandler but I'm having some difficulties when trying to make that with an Handler inside api package.
For instance, If in the main I add:
r.HandleFunc("/todo/{id}", api.GetTodoHandler(&client)).
Methods("GET")
with gettodo.go
package api
import (
"net/http"
"github.com/gorilla/mux"
"fmt"
"encoding/json"
"github.com/go-redis/redis"
)
func GetTodoHandler(c *RedisInstance) func (w http.ResponseWriter, r *http.Request) {
func (w http.ResponseWriter, r *http.Request) {
. . . doSomething
}
}
It works nicely.
I'm still pretty new to Go and haven't found any cleaner solution to that even after several researches and reads.
Is my approach correct or are there any better ones?
Write a function that converts a function with the Redis instance argument to an HTTP handler:
func redisHandler(c *RedisInstance,
f func(c *RedisInstance, w http.ResponseWriter, r *http.Request)) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { f(c, w, r) })
}
Write your API handlers like this:
func AddTodoHandler(c *RedisInstance, w http.ResponseWriter, r *http.Request) {
...
}
Add to the mux like this:
r.Handler("/todo", redisHandler(client, api.AddTodoHandler)).Methods("POST")
where client is the Redis instance.
I would recommend using an App struct which initializes DB and Routes. And all Redis methods will be called inside.
e.g. type App struct{Routes *mux.Router, DB *DB_TYPE}
And which will have App.initializeRoutes method.
type App struct {
Router *mux.Router
DB *redis.NewClient
}
func (a *App) Run(addr string) {
log.Fatal(http.ListenAndServe(":8000", a.Router))
}
func (a *App) Initialize(addr, password string, db int) error {
// Connect postgres
db, err := redis.NewClient(&redis.Options{
Addr: addr,
Password: password,
DB: db,
})
if err != nil {
return err
}
// Ping to connection
err = db.Ping()
if err != nil {
return err
}
// Set db in Model
a.DB = db
a.Router = mux.NewRouter()
a.initializeRoutes()
return nil
}
func (a *App) initializeRoutes() {
a.Router.HandleFunc("/todo", a.AddTodoHandler).Methods("POST")
a.Router.HandleFunc("/todo/{id}", a.GetTodoHandler).Methods("GET")
}
// AddTodoHandler has access to DB, in your case Redis
// you can replace the steps for Redis.
func (a *App) AddTodoHandler() {
//has access to DB
a.DB
}
Hope you get the point, you can even extract out the Model work into a separate Struct and then pass it inside func's
r.HandleFunc("/todo/{id}", redisHandler.api.GetTodoHandler).Methods("GET")
Your redisHandler, as defined in main, has no api field, so this naturally doesn't compile.
If you re-defined your RedisInstance type in the api package, and you defined the handler methods on that type in the method-specific files, then you can initialize your redisHandler using that api.RedisInstance type and you can delete the main.RedisInstance type definition:
package main
import (
"github.com/gorilla/mux"
"github.com/go-redis/redis"
"net/http"
"fmt"
"log"
"encoding/json"
"io/ioutil"
"../redismanager"
"../api"
)
func main() {
//Initialize Redis Client
client := redismanager.InitRedisClient()
//Get current redis instance to get passed to different Gorilla-Mux Handlers
redisHandler := &api.RedisInstance{RInstance:&client}
//Initialize Router Handlers
r := mux.NewRouter()
r.HandleFunc("/todo", redisHandler.AddTodoHandler).Methods("POST")
r.HandleFunc("/todo/{id}", redisHandler.GetTodoHandler).Methods("GET")
fmt.Println("Listening on port :8000 . . .")
// Bind to a port and pass our router in
log.Fatal(http.ListenAndServe(":8000", r))
}
I am using Go and the Gorilla web toolkit's mux and handler packages to build a complex application, part of which requires a http server. Gorilla's mux and handler packages work wonderfully and I am able to successfully get the http server up and running and it has been quite simple to log requests.
However, I am unable to determine how I may log responses. Ideally, I would like a mechanism, similar to Gorilla's LoggingHandler, that "wraps" the logging mechanism easily.
Is there a Go package that does easily wraps / logs responses? Is there a way to use Go or Gorilla's capabilities in this fashion that I have not considered?
Thanks for the great suggestions. I tried a few of the suggestions and landed on a rather simple solution that uses a minimalist wrapper. Here is the solution that worked for me (feel free to offer comments, or better yet, other solutions):
import (
"fmt"
"log"
"net/http"
"net/http/httptest"
"net/http/httputil"
"github.com/gorilla/mux"
)
:
func logHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Println(fmt.Sprintf("%q", x))
rec := httptest.NewRecorder()
fn(rec, r)
log.Println(fmt.Sprintf("%q", rec.Body))
}
}
func MessageHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "A message was received")
}
And the following code will use the aforementioned handler:
:
router := mux.NewRouter()
router.HandleFunc("/", logHandler(MessageHandler))
:
Output from the above code will be something along the lines of:
:
2016/07/20 14:44:29 "GET ... HTTP/1.1\r\nHost: localhost:8088\r\nAccept: */*\r\nUser-Agent: curl/7.43.0\r\n\r\n"
2016/07/20 14:44:29 ...[response body]
:
The accepted answer by Eric Broda won't help much if you want to actually send your response to the client. I've made a modification to that code that will actually work:
func logHandler(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
x, err := httputil.DumpRequest(r, true)
if err != nil {
http.Error(w, fmt.Sprint(err), http.StatusInternalServerError)
return
}
log.Println(fmt.Sprintf("%q", x))
rec := httptest.NewRecorder()
fn(rec, r)
log.Println(fmt.Sprintf("%q", rec.Body))
// this copies the recorded response to the response writer
for k, v := range rec.HeaderMap {
w.Header()[k] = v
}
w.WriteHeader(rec.Code)
rec.Body.WriteTo(w)
}
}
edit sorry, I didn't notice your mention of gorilla-mux, I have only tried this with gin, but if it uses middlewares this should still work.
the trick is, c.Next() in a middleware blocks until all subsequent middlewares return. Here's a logrus solution. Put this as your first middleware:
func Logrus(logger *logrus.Logger) gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now().UTC()
path := c.Request.URL.Path
c.Next()
end := time.Now().UTC()
latency := end.Sub(start)
logger.WithFields(logrus.Fields{
"status": c.Writer.Status(),
"method": c.Request.Method,
"path": path,
"ip": c.ClientIP(),
"duration": latency,
"user_agent": c.Request.UserAgent(),
}).Info()
}
}
GinEngine.Use(Logrus(logrus.StandardLogger()))