// +build linux // +build 386 amd64 /* * * Copyright 2018 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ // SocketOptions is only supported on linux system. The functions defined in // this file are to parse the socket option field and the test is specifically // to verify the behavior of socket option parsing. package service import ( "context" "reflect" "strconv" "testing" "github.com/golang/protobuf/ptypes" durpb "github.com/golang/protobuf/ptypes/duration" "golang.org/x/sys/unix" channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" "google.golang.org/grpc/internal/channelz" ) func init() { // Assign protoToSocketOption to protoToSocketOpt in order to enable socket option // data conversion from proto message to channelz defined struct. protoToSocketOpt = protoToSocketOption } func convertToDuration(d *durpb.Duration) (sec int64, usec int64) { if d != nil { if dur, err := ptypes.Duration(d); err == nil { sec = int64(int64(dur) / 1e9) usec = (int64(dur) - sec*1e9) / 1e3 } } return } func protoToLinger(protoLinger *channelzpb.SocketOptionLinger) *unix.Linger { linger := &unix.Linger{} if protoLinger.GetActive() { linger.Onoff = 1 } lv, _ := convertToDuration(protoLinger.GetDuration()) linger.Linger = int32(lv) return linger } func protoToSocketOption(skopts []*channelzpb.SocketOption) *channelz.SocketOptionData { skdata := &channelz.SocketOptionData{} for _, opt := range skopts { switch opt.GetName() { case "SO_LINGER": protoLinger := &channelzpb.SocketOptionLinger{} err := ptypes.UnmarshalAny(opt.GetAdditional(), protoLinger) if err == nil { skdata.Linger = protoToLinger(protoLinger) } case "SO_RCVTIMEO": protoTimeout := &channelzpb.SocketOptionTimeout{} err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout) if err == nil { skdata.RecvTimeout = protoToTime(protoTimeout) } case "SO_SNDTIMEO": protoTimeout := &channelzpb.SocketOptionTimeout{} err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout) if err == nil { skdata.SendTimeout = protoToTime(protoTimeout) } case "TCP_INFO": tcpi := &channelzpb.SocketOptionTcpInfo{} err := ptypes.UnmarshalAny(opt.GetAdditional(), tcpi) if err == nil { skdata.TCPInfo = &unix.TCPInfo{ State: uint8(tcpi.TcpiState), Ca_state: uint8(tcpi.TcpiCaState), Retransmits: uint8(tcpi.TcpiRetransmits), Probes: uint8(tcpi.TcpiProbes), Backoff: uint8(tcpi.TcpiBackoff), Options: uint8(tcpi.TcpiOptions), Rto: tcpi.TcpiRto, Ato: tcpi.TcpiAto, Snd_mss: tcpi.TcpiSndMss, Rcv_mss: tcpi.TcpiRcvMss, Unacked: tcpi.TcpiUnacked, Sacked: tcpi.TcpiSacked, Lost: tcpi.TcpiLost, Retrans: tcpi.TcpiRetrans, Fackets: tcpi.TcpiFackets, Last_data_sent: tcpi.TcpiLastDataSent, Last_ack_sent: tcpi.TcpiLastAckSent, Last_data_recv: tcpi.TcpiLastDataRecv, Last_ack_recv: tcpi.TcpiLastAckRecv, Pmtu: tcpi.TcpiPmtu, Rcv_ssthresh: tcpi.TcpiRcvSsthresh, Rtt: tcpi.TcpiRtt, Rttvar: tcpi.TcpiRttvar, Snd_ssthresh: tcpi.TcpiSndSsthresh, Snd_cwnd: tcpi.TcpiSndCwnd, Advmss: tcpi.TcpiAdvmss, Reordering: tcpi.TcpiReordering} } } } return skdata } func (s) TestGetSocketOptions(t *testing.T) { czCleanup := channelz.NewChannelzStorage() defer cleanupWrapper(czCleanup, t) ss := []*dummySocket{ { socketOptions: &channelz.SocketOptionData{ Linger: &unix.Linger{Onoff: 1, Linger: 2}, RecvTimeout: &unix.Timeval{Sec: 10, Usec: 1}, SendTimeout: &unix.Timeval{}, TCPInfo: &unix.TCPInfo{State: 1}, }, }, } svr := newCZServer() ids := make([]int64, len(ss)) svrID := channelz.RegisterServer(&dummyServer{}, "") defer channelz.RemoveEntry(svrID) for i, s := range ss { ids[i] = channelz.RegisterNormalSocket(s, svrID, strconv.Itoa(i)) defer channelz.RemoveEntry(ids[i]) } ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) defer cancel() for i, s := range ss { resp, _ := svr.GetSocket(ctx, &channelzpb.GetSocketRequest{SocketId: ids[i]}) metrics := resp.GetSocket() if !reflect.DeepEqual(metrics.GetRef(), &channelzpb.SocketRef{SocketId: ids[i], Name: strconv.Itoa(i)}) || !reflect.DeepEqual(socketProtoToStruct(metrics), s) { t.Fatalf("resp.GetSocket() want: metrics.GetRef() = %#v and %#v, got: metrics.GetRef() = %#v and %#v", &channelzpb.SocketRef{SocketId: ids[i], Name: strconv.Itoa(i)}, s, metrics.GetRef(), socketProtoToStruct(metrics)) } } }