GRPC: Client ID or connection information? - grpc

Is there a way to get the connection information on RPC calls from server side? Or maybe something like unique client ID?

There is no connecton information which may help distinguish clients. One reason of this is proxies: different clients can have same IP and port (as I understand)
One possible solution is handshake protocol in app level. You can add rpc method "Connect" and send clientId as response from server. Afterthat you can attach custom headers (metadata) to your rpc calls.
Client side java code:
String clientId = getIdfromServer();
Metadata.Key<String> CLIENT_ID = Metadata.Key.of("client_id", ASCII_STRING_MARSHALLER);
Metadata fixedHeaders = new Metadata();
fixedHeaders.put(CLIENT_ID, clientId);
blockingStub = MetadataUtils.attachHeaders(blockingStub, fixedHeaders);
This C++ server side code shows how to handle such header on server:
::grpc::Status YourRPC(::grpc::ServerContext* context, const Your* request, YourResponse* response)
{
const auto clientMetadata = context->client_metadata();
auto it = clientMetadata.find("client_id");
auto clientId = std::string(it->second.begin(), it->second.end());
}
I noticed that metadata key is case insensitive. Grpc converts keys to lowercase.

gRPC now provide peer information (https://github.com/grpc/grpc-go/issues/334)
import (
"google.golang.org/grpc/peer"
)
func (s *server) Hello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
//p includes connection information
p, ok := peer.FromContext(ctx)
....
....
}

Yes , we can get the request information , connection information and etc.
There are generally two types of information we can get from the client request on grpc server side.
Method Information : We know that rpc call is simple method call . To get the method name (ie : which method will be invoked in grpc server when client will request?). below code will work.
import (
"google.golang.org/grpc"
)
func getMethodInfo(ctx context.Context) {
methodName := grpc.Method(ctx)
fmt.Println(methodName)
}
//outputex: /Abc
2.Peer Information:
p, ok := peer.FromContext(ctx)
Hope this will work.

For gRPC streaming
We can get the connection information below through google.golang.org/grpc/peer
// protobuf
service Server {
rpc Stream (stream GrpcStreamRequest) returns (stream GrpcStreamResponse) {}
}
func (ss *StreamServer) Stream(svr pb.Server_StreamServer) error {
for {
req, err := svr.Recv()
if err != nil {
fmt.Printf("Stream recv err %+v", err)
return err
}
p, ok := peer.FromContext(svr.Context())
if ok {
fmt.Printf("Client info %+v", p)
}
}

Related

check whether http connection is hijacked in go server

I am writing an HTTP server in Go, which uses the following pattern to handle API output:
func handler(w http.ResponsWriter, r *http.Request) {
defer reply(w, r, L)() //L is a Logger
//do things...
}
func reply(w http.ResponseWriter, r *http.Request, log Logger) func() {
cid := []byte{0, 0, 0, 0}
if log != nil {
rand.Read(cid)
log.Debug("[%x] %s %s", cid, r.Method, r.URL.String())
}
entry := time.Now()
return func() {
if log != nil {
defer log.Debug("[%x] elapsed %d millis", cid, time.Since(entry).Milliseconds())
}
_, err := w.Write(nil)
if err == http.ErrHijacked {
return //API is a WEBSOCKET entry point, do nothing
}
//handle common output logic for normal HTTP APIs...
}
}
The reason I do this, is that I found this comment in the standard library:
// ErrHijacked is returned by ResponseWriter.Write calls when
// the underlying connection has been hijacked using the
// Hijacker interface. A zero-byte write on a hijacked
// connection will return ErrHijacked without any other side
// effects.
ErrHijacked = errors.New("http: connection has been hijacked")
However following the Write() method, I got this comment:
// Write writes the data to the connection as part of an HTTP reply.
//
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// ...
Write([]byte) (int, error)
My questions are:
Is it OK to use my code to safely detect if a HTTP connection is hijacked? I only want to check the connection is hijacked or not, but do NOT want it to add headers for me!
Since the ResponseWriter is an interface, I cannot click through the source code to find out how the standard library implements that method. In general, how can I drill down to the standard library (or any open source code) to find out the implementation of an interface?
Thanks to Cerise, I found the source code of the standard response.Writer:
func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {
if w.conn.hijacked() {
if lenData > 0 {
caller := relevantCaller()
w.conn.server.logf("http: response.Write on hijacked connection from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)
}
return 0, ErrHijacked
}
... ....
So, as said in the document, there is NO side effect.

grpc-gateway: how to set allowed content-type per request

I'm using grpc-gateway inside same go app, to proxy convert HTTP to GRPC. As far as I see by default grpc-gateway sets application application/json format for all rpcs, including streaming.
So, my task is:
Incoming HTTP requests MUST always be Content-type: application/json, otherwise request should be rejected and 406 sent according to RFC.
Incoming HTTP request MAY have Accept: application/x-ndjson set for unary RPCs and Accept: applcation/x-ndjson header set for server streams. If conditions don't met 406, should be returned.
Outgoing HTTP request MUST set Content-type: applicaiton/json, for simple unary RPCs, and Content-type: application/x-ndjson for server streams.
So, grpc-gateway proposes only to set custom marshaller, for application/x-ndjson, which would do actually the same as default one, so with just overwritten ContentType method. This approach not allowing me to set marshaler per method call, and doesn't allow me to reject unsupported content type per request.
How can I achieve this still using grpc-gateway? Or I should consider then implementing http grpc conversion manually?
I suggest you not use the grpc-gateway or any other tool to convert gRPC to HTTP RPC. You are adding unnecessary complexity to your application.
If you have a gRPC service but for whatever reason, your client can not call the gRPC and you need to offer your service through a plain HTTP option... Is that your case?
If that is your case the correct way is to offer an HTTP RPC service and from it, you call your gRPC service.
HTTP RPC is way simpler than REST and you don't need any tool for that.
I have this exact case implemented in GOlang here
// Creates a new book.
func (h BookStoreService) CreateBook(w http.ResponseWriter, r *http.Request) {
request := &bookv1.CreateBookRequest{}
proxy := httputil.GetProxy(w, r, request)
proxy.SetServiceRequest(func(request proto.Message) (proto.Message, error) {
return h.client.CreateBook(r.Context(), request.(*bookv1.CreateBookRequest))
})
proxy.Call()
}
Proxy struct
func GetProxy(w http.ResponseWriter, r *http.Request, request proto.Message) *ServiceProxy {
proxy := &ServiceProxy{}
proxy.SetResponseWriter(w)
proxy.SetSourceRequest(r)
proxy.SetDestRequest(request)
return proxy
}
type ServiceProxy struct {
err error
serviceRequest func(request proto.Message) (proto.Message, error)
writer http.ResponseWriter
destRequest proto.Message
sourceRequest *http.Request
}
func (b *ServiceProxy) SetDestRequest(request proto.Message) {
b.destRequest = request
}
func (b *ServiceProxy) SetSourceRequest(request *http.Request) {
b.sourceRequest = request
}
func (b *ServiceProxy) SetServiceRequest(svcRequest func(request proto.Message) (proto.Message, error)) *ServiceProxy {
b.serviceRequest = svcRequest
return b
}
func (b *ServiceProxy) Call() {
b.writer.Header().Set("Content-Type", "application/json; charset=utf-8")
err := unmarshal(b.writer, b.sourceRequest, b.destRequest)
if err != nil {
return
}
resp, err := b.serviceRequest(b.destRequest)
if err != nil {
handleErrorResp(b.writer, err)
return
}
b.writer.WriteHeader(http.StatusOK)
json.NewEncoder(b.writer).Encode(resp)
}
func (b *ServiceProxy) SetResponseWriter(w http.ResponseWriter) {
b.writer = w
}

How to share data between 2 different API calls?

I am trying to create a framework in which I would receive requests over REST API and would wait for another service (which works over gRPC) to poll and execute the request. This is needed cause the "other" service is very deeply embedded into the network and I can't directly call it. At the same time, I would like to buffer the output of the other service back to the request origin.
Any ideas how I can share this data between 2 different asynchronous API requests? Using the file system is a way... but I was thinking can I do it better via channels or something...?
Kind of pseudo code below:
func RestHandler(payload string) (string, error){
respChan := make(chan string)
workId := placeWorkInQueue(payload)
// Start polling in the background
go pollForResult(respChan, workId)
// wait for result in the channel
var result string
select {
case result = <-respChan:
// implement your timeout logic as a another case: here
}
return result, nil
}
// This is poller for just the workId given to it.
func pollForResult(respChan chan string, workId string) {
// Do the polling for workId result
/// Write response to respChan when available
// You may have to implement a timeout to give up polling.
}
func placeWorkInQueue(s string) string {
// Place the job in queue and return a unique workId
return "unique-id"
}
Use Redis queues in both directions. The API endpoint writes request and unique id to queue, registers Go channel with unique id as key with central reader in process, and waits on Go channel.
Queue reader gets responses with id from Redis queue and sends response to appropriate Go channel.
// Response represents the response type from the
// remote service.
type Response struct {
ID string
// ... more fields as needed
}
// Request represents request to remote serivce.
type Request struct {
ID string
// ... more fields as needed
}
// enqueueRequest writes request to Redis queue.
// Implementation TBD by application.
func enqueueRequest(r *Request) error {
return nil
}
// dequeueResponse reads a response from a Redis queue.
// Implementation TBD by application.
func dequeueResponse() (*Response, error) {
return nil, nil
}
Use sync.Map to register waiting Go channels from API request handlers. The key is the unique id for the request and the value is a chan *Response.
var responseWaiters sync.Map
Run queuePump in a single goroutine to dequeue results from Redis queue and send to appropriate channel. Start the gorountine before serving HTTP requests.
func queuePump() {
for {
response, err := dequeueResponse()
if err != nil {
// handle error
}
v, ok := responseWaiters.Load(response.ID)
if ok {
c := v.(chan *Response)
c <- response
// Remove cahnel from map to ensure that pump never sends
// twice to same channel. The pump will black forever if
// this happens.
responseWaiters.Delete(response.ID)
}
}
}
The API endpoint allocates a unique id for request, registers a channel with the queue pump, enqueues the request and waits for the response.
func apiEndpoint(w http.ResponseWriter, r *http.Request) {
id := generateUniqueID()
c := make(chan *Response, 1) // capacity 1 ensures that queue pump will not block
responseWaiters.Store(id, c)
defer responseWaiters.Delete(id)
req := &Request{
ID: id,
// fill in other fields as needed
}
if err := enqueueRequest(req); err != nil {
// handle error
}
select {
case resp := <-c:
// process response
fmt.Println(resp)
case <-time.After(10 * time.Second):
// handle timeout error
}
}

How to get Server's own address in a server using net/http?

I would like to write a HTTP server using the Go's net/http package, with the reaction that would depend on server side IP address of the HTTP connection.
In other words, what I am looking for is equivalent of CGI's "SERVER_ADDR" variable.
The closest field in http.Request is "Host" - but since it will only be equal to an address if the request uses a literal address, I can not use it (the server might be used by name).
Looking at the sources at https://golang.org/src/net/http/server.go, seems the only way to get to the server's address is to Hijack() the connection within a handler and implement subsequent HTTP parsing for the subsequent requests on the same connection, but it looks very inelegant to say the least...
It seems like the ideal solution would be to have the http/request and http/server in the golang standard library modified as follows:
diff -u go-stock-library/request.go ./request.go
--- go-stock-library/request.go 2016-04-13 17:31:48.000000000 +0200
+++ ./request.go 2016-04-13 17:32:40.000000000 +0200
## -227,6 +227,15 ##
// This field is ignored by the HTTP client.
RemoteAddr string
+ // LocalAddr allows HTTP servers and other software to record
+ // the network address that the request was sent to, usually for
+ // logging. This field is not filled in by ReadRequest and
+ // has no defined format. The HTTP server in this package
+ // sets LocalAddr to an "IP:port" address before invoking a
+ // handler.
+ // This field is ignored by the HTTP client.
+ LocalAddr string
+
// RequestURI is the unmodified Request-URI of the
// Request-Line (RFC 2616, Section 5.1) as sent by the client
// to a server. Usually the URL field should be used instead.
diff -u go-stock-library/server.go ./server.go
--- go-stock-library/server.go 2016-04-13 17:29:19.000000000 +0200
+++ ./server.go 2016-04-13 17:31:38.000000000 +0200
## -161,6 +161,13 ##
// This is the value of a Handler's (*Request).RemoteAddr.
remoteAddr string
+ // serverAddr is rwc.LocalAddr().String(). It is not populated synchronously
+ // inside the Listener's Accept goroutine, as some implementations block.
+ // It is populated immediately inside the (*conn).serve goroutine.
+ // This is the value of a Handler's (*Request).LocalAddr.
+ localAddr string
+
+
// tlsState is the TLS connection state when using TLS.
// nil means not TLS.
tlsState *tls.ConnectionState
## -736,6 +743,7 ##
delete(req.Header, "Host")
req.RemoteAddr = c.remoteAddr
+ req.LocalAddr = c.localAddr
req.TLS = c.tlsState
if body, ok := req.Body.(*body); ok {
body.doEarlyClose = true
## -1382,6 +1390,7 ##
// Serve a new connection.
func (c *conn) serve() {
c.remoteAddr = c.rwc.RemoteAddr().String()
+ c.localAddr = c.rwc.LocalAddr().String()
defer func() {
if err := recover(); err != nil {
const size = 64 << 10
and then use the new LocalAddr in the code in a nice and clean way.
Is there a cleaner way to do this?
I personally wouldn't modify anything in the standard library for something I could get other ways. Is there some advantage to parsing it out of every connection?
There is probably a simpler way, but I have the following.
func getMyInterfaceAddr() (net.IP, error) {
ifaces, err := net.Interfaces()
if err != nil {
return nil, err
}
addresses := []net.IP{}
for _, iface := range ifaces {
if iface.Flags&net.FlagUp == 0 {
continue // interface down
}
if iface.Flags&net.FlagLoopback != 0 {
continue // loopback interface
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
if ip == nil || ip.IsLoopback() {
continue
}
ip = ip.To4()
if ip == nil {
continue // not an ipv4 address
}
addresses = append(addresses, ip)
}
}
if len(addresses) == 0 {
return nil, fmt.Errorf("no address Found, net.InterfaceAddrs: %v", addresses)
}
//only need first
return addresses[0], nil
}

TCP connection pool

i'm new to socket and trying to create a connection pooling over tcp socket. my implementation send 32bit length then binary message for each call. But i'm having problem with sometimes the reader receiving previous response from server (could happened when client close and re-establish socket on send error). how do i flush socket (remaining bytes from previous call) before a new request. any suggestion?
Edit: i learned that tcp always stream 0s, what if i send byte(1) before message so i can have a flush function to check if socket not empty before a new call.
Your post actually asks several questions:
How to manage a connection pool?
How to handle communication over the sockets?
These are really two different things. A connection pool is just a way to manage a set of connections. A simple way to implement this is with a class such as:
package netpool
import (
"net"
)
const MaxConnections = 3
type Error string
func (e Error) Error() string {
return string(e)
}
var ErrMaxConn = Error("Maximum connections reached")
type Netpool struct {
name string
conns int
free []net.Conn
}
func NewNetpool(name string) *Netpool {
return &Netpool{
name: name,
}
}
func (n *Netpool) Open() (conn net.Conn, err error) {
if n.conns >= MaxConnections && len(n.free) == 0 {
return nil, ErrMaxConn
}
if len(n.free) > 0 {
// return the first free connection in the pool
conn = n.free[0]
n.free = n.free[1:]
} else {
addr, err := net.ResolveTCPAddr("tcp", n.name)
if err != nil {
return nil, err
}
conn, err = net.DialTCP("tcp", nil, addr)
if err != nil {
return nil, err
}
n.conns += 1
}
return conn, err
}
func (n *Netpool) Close(conn net.Conn) error {
n.free = append(n.free, conn)
return nil
}
I have created a stand-alone class here. It would typically be implemented as part of a higher-level class such as MyHTTPHost, or MyDatabase.
In this simple implementation, connections that are returned via netpool.Open() are not tracked. It's possible to leak connections by calling Open(), then closing the connections outside of netpool.Close(). It's possible to track them if you want to hold an active and inactive pool, for example, which would solve this problem.
A couple of other things you might want to add to a pooling implementation:
Threading protection (using sync.Mutex, for example)
Closing of connections in the freepool after some length of inactivity
Error checking to be sure that closed connections are still valid
Once you have a connection, you can call Read and Write on it normally. To flush all oustanding data on the socket, you can simply use the ioutil.ReadAll() helper function. By default, this will block indefinitely if there is no data available. To avoid that, add a read timeout using:
conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
_, err = ioutil.ReadAll(conn)
neterr, ok := err.(net.Error)
if ok && neterr.Timeout() {
err = nil // timeout isn't an error in this case
}
if err != nil {
// handle the error case.
}
This will read all the data from the given connection if any is pending, or will return after 500ms with an I/O Timeout error if no data was pending.
The type assertion is required because ioutil.ReadAll() returns an Error interface, rather than a net.Error interface, and we need the latter to be able to easily find out if the call returned due to a timeout.

Resources