golang servehttp calls itself? - go

Really cannot understand ServeHTTP. I get that it's interface for the Handler and any object that implments ServeHTTP can behave as Handler. My question is the source code
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
handler.ServeHTTP(rw, req)
}
This line handler.ServeHTTP seems to be calling itself again??
handler is basically Server.Handler so it's calling itself all over again? What is the purpose of this method here? Is this just prototype? Can someone explain when you don't implment your own serveHTTP.. how does this function work?

serverHandler is an adapter whose purpose is to set things up such that DefaultServeMux is used when a nil handler is passed to ServeHTTP. The net/http package is full of implementations of the http.Handler interface, because it's very useful and composable.
It's created like this:
serverHandler{c.server}.ServeHTTP(w, w.req)
And c.server (the connection's server) is what sh.srv is accessing. So it's not calling itself. Note that the Server type is:
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
// Addr optionally specifies the TCP address for the server to listen on,
// in the form "host:port". If empty, ":http" (port 80) is used.
// The service names are defined in RFC 6335 and assigned by IANA.
// See net.Dial for details of the address format.
Addr string
Handler Handler // handler to invoke, http.DefaultServeMux if nil
{...} other fields
}
The Handler field can be legitimately nil, and it's the goal of serverHandler to handle this scenario.
I think you'll be interested in reading this post which explains the flow of events in the net/http package in detail.

Related

gorilla websocket - chain of closeHandler

In gorilla websocket, websocket.Conn struct has a method SetCloseHandler(), which set close handler of the connection.
If the passed handler is nil, it uses a default handler.
I wan't to keep the default handler, but do something else before or after the default handler.
Aka. a handler chain, e.g. some method like:
prependCloseHandler(h)
which add a handler at beginning of handler chain.
appendCloseHandler(h)
which add a handler at end of handler chain.
Then each handler in the chain will be executed in order.
Is there anyway to do that, without coping the default handler as part of my new handler?
Thanks.
The package does not provide a direct mechanism for prepending or appending a handler for a close message. Use this function as a starter for your function:
closeHandler := conn.CloseHandler()
conn.SetCloseHandler(func(code int, text string) error {
// Add your code here ...
err := closeHandler(code, text)
// ... or here.
return err
})
Note that the close handler is called when a close message is received from the peer, not when the connection is closed. Most applications should be good with the default handler.

Accessing underlying connection in GRPC server with unix socket

Wondering if there is a way to access the underlying net.Conn to retrieve user credentials using SO_PEERCRED and verify a request before it is handled by the server.
From https://blog.jbowen.dev/2019/09/using-so_peercred-in-go/, the net.UnixConn is needed to return the unix.Ucred used for verification. So if there is some way for the server request handler to get at the net.Conn, this should be easy
I looked at a UnaryServerInterceptor, but nothing provided in UnaryServerInterceptor seems to contain the net.Conn
func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
log.Printf("Intercepted: %+v %+v", info.Server, req) // anything here?
return handler(ctx, req)
}
The interface method TransportCredentials.ServerHandshake is the seam that you need. Your implementation can read from the input net.Conn and return the credential as an AuthInfo. Then in your handler code, you can get the credential out from the context via peer.FromContext. Alternatively, if you prefer to have authentication occur before the handler code is reached, you can do that directly in the TransportCredentials.ServerHandshake or via an interceptor.
See also: https://groups.google.com/g/grpc-io/c/FeQV7NXpeqA

Does WithContext method need to panic if context is nil?

I want to write a WithContext method for a struct and am taking inspiration from net/http's Request.WithContext.
My question is: why does Request.WithContext panic if the context is nil:
func (r *Request) WithContext(ctx context.Context) *Request {
if ctx == nil {
panic("nil context")
}
...
}
And should mine as well?
For more context on why I want to create a WithContext method: I am implementing an interface that does not provide a context parameter in its signature but believe the implementation requires it.
More specifically, I am writing a Redis backend for gorilla/session using the official Redis client for Go, where the Get and Set methods take context.Context.
The idea is that my redis store will be shallow copied with the new context object, when needed, and then used:
type redisStore struct {
codecs []securecookie.Codec
backend Backend // custom interface for Redis client
options *sessions.Options
ctx context.Context
}
func (s *redisStore) WithContext(ctx context.Context) *redisStore {
if ctx == nil {
panic("nil context")
}
s2 := new(redisStore)
*s2 = *s
s2.ctx = ctx
return s2
}
// Backend
type Backend interface {
Set(context.Context, string, interface{}) error
Get(context.Context, string) (string, error)
Del(context.Context, string) error
}
The purpose of panicking is to "fail fast" and reject a nil context without changing the function signature.
If the function does not panic then it must return error in order to reject a bad input:
func (r *Request) WithContext(ctx context.Context) (*Request, error) {
if ctx == nil {
return nil, errors.New("nil ctx")
}
...
}
And then who calls this function must handle the error to avoid using an invalid request:
request, err = request.WithContext(nil)
if err != nil {
}
By handling the error you are introducing a control flow branch, and you lose method chaining. You also cannot immediately use WithContext return value into a function parameter:
// cannot do, because WithContext returns an error too
data, err := fetchDataWithContext(request.WithContext(ctx), otherParam)
Also it would create an error instance that will be eventually garbage collected. This all is cumbersome, poor usability and unnecessary alloc simply for saying "don't give me a nil context".
About creating a redis store with a context, the context documentation is clear:
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
The important detail is request-scoped. So setting a context in the redis client itself is contrary to this recommendation. You should pass context values at each get/set call.
The context of an HTTP request is canceled if the client closes the connection. When the context is canceled, all its child contexts are also canceled, so a nil context would panic then. Because of this, you cannot pass a nil context to WithContext.
Whether or not your redis store should panic depends on how you are going to use that context. It is usually not a good idea to include a context in a struct. One acceptable way of doing that is if the struct itself is a context. Contexts should be created for each call, should live for the duration of that call, and then thrown away.

How to safely add values to grpc ServerStream in interceptor

I have a logging interceptor for my grpc server and want to add a value to the metadata (I want to track the request throughout its lifetime):
func (m *middleware) loggingInterceptor(srv interface{},
ss grpc.ServerStream,
info *grpc.StreamServerInfo,
handler grpc.StreamHandler)
md, ok := metadata.FromIncomingContext(ss.Context())
if !ok {
return errors.New("could not get metadata from incoming stream context")
}
// add the transaction id to the metadata so that business logic can track it
md.Append("trans-id", "some-transaction-id")
// call the handler func
return handler(srv, ss)
}
but the docs for FromIncomingContext state that:
// FromIncomingContext returns the incoming metadata in ctx if it exists. The
// returned MD should not be modified. Writing to it may cause races.
// Modification should be made to copies of the returned MD.
Ok, so I look at the copy function and copy the metadata:
mdCopy := md.Copy()
mdCopy.Append("trans-id", "some-transaction-id")
and think "how do I attach this metadata back to the ServerStream context?", and I check if there's some ss.SetContext(newCtx), but I don't see anything of the sort. Am I thinking about this from the wrong perspective, or am I missing something else?
You would need to use NewIncomingContext to create a copy of the current context in the stream.
Then you would have to create a wrappedStream type which overrides Context method in ServerStream to return the modified context. You would need to pass this wrappedStream to the handler that you received in your interceptor.
You can see an example of this here (it overrides other methods here, but the idea is the same):
https://github.com/grpc/grpc-go/blob/master/examples/features/interceptor/server/main.go#L106-L124
Hope this helps.
Easwar is right.
You can either create your own ServerStream implementation, override Context() method with your own context or there's struct inside grpc package which is WrappedServerStream (github.com/grpc-ecosystem/go-grpc-middleware) which you can pass context and original server stream object and use it inside handler.
Example:
// This methods get the current context, and creates a new one
newContext, err := interceptor.authorize(ss.Context(), info.FullMethod)
if err != nil {
log.Printf("authorization failed: %v", err)
return err
}
err = handler(srv, &grpc_middleware.WrappedServerStream{
ServerStream: ss,
WrappedContext: newContext,
})

Overriding ResponseWriter interface to catch HTTP errors

I'm coding a web application in Go, and while various mux libraries provide a way to set a custom 404 error handler, there's nothing for other 4xx and 5xx error codes.
One suggestion is to override the WriteHeader method in the ResponseWriter interface and check the status code, but I'm confused as to how this would actually be written (the overriding of ResponseWriter methods, before outputting). One possible example can be found from the negroni package.
Would this be the correct way to serve a custom template for 4xx and 5xx errors? Could anyone provide an example of how this could be implemented?
Update
Big thanks to David and elithrar for their responses and code. The Interceptor struct that David coded can be used in a wrapper for the server mux, as elithrar shows in the comments. For those looking for further explanation as to why and how this works, this section from astaxie's book gives some very good info on the workings of the net/http package, along with viewing the server.go source code from the net/http package.
The mux libraries only have a means of setting the not found handler as a means of giving you a way to intercept requests where the mux can't resolve the URL to it's known mappings.
For example, you do:
mux.Handle("/foo",fooFunc)
mux.Handle("/bar",barFunc)
But the client accesses /baz to which the mux has no mapping.
They do not actually intercept a 404 going to the client, they just invoke the not found handler when it runs in to this problem.
Also, if your /foo handler sends a 404 response, the not found does not get invoked.
If you want custom pages for various return responses from your mapped URLs, simply make the various handlers write the correct response.
If you don't control that logic (ie: the framwork is writing something and has no means to override), then you may want to intercept all requests and override the http.ResposeWriter with response code detection logic.
Here's an example interceptor that basically does what you want. On Play
package main
import (
"fmt"
"log"
)
import "net/http"
type Interceptor struct {
origWriter http.ResponseWriter
overridden bool
}
func (i *Interceptor) WriteHeader(rc int) {
switch rc {
case 500:
http.Error(i.origWriter, "Custom 500 message / content", 500)
case 404:
http.Error(i.origWriter, "Custom 404 message", 404)
case 403:
i.origWriter.WriteHeader(403)
fmt.Fprintln(i.origWriter, "Custom 403 message")
default:
i.origWriter.WriteHeader(rc)
return
}
// if the default case didn't execute (and return) we must have overridden the output
i.overridden = true
log.Println(i.overridden)
}
func (i *Interceptor) Write(b []byte) (int, error) {
if !i.overridden {
return i.origWriter.Write(b)
}
// Return nothing if we've overriden the response.
return 0, nil
}
func (i *Interceptor) Header() http.Header {
return i.origWriter.Header()
}

Resources