Umang Mundhra
Umang Mundhra

Reputation: 21

gRPC server side template giving 'unexpected EOF'

I am trying to eliminate the need of proto files for running the gRPC/rpc services by directly parsing the interface that user will provide. This interface will contain the list of methods that will be exposed over the RPC connection.

For example:

package main

import "gofr.dev/pkg/gofr"

// ExampleService Define the service interface with GoFr context
type ExampleService interface {
GetExampleData(ctx \*gofr.Context, request ExampleRequest) (ExampleResponse, error)
}

// ExampleRequest Define the request struct
type ExampleRequest struct {
ID   int    `json:"id"`
Name string `json:"name"`
}

// ExampleResponse Define the response struct
type ExampleResponse struct {
Status  string `json:"status"`
Message string `json:"message"`
}

Now the plan is to parse this interface to extract out the necessary fields, methods and structs. I have made a parser that extracts out the information from interface and then populate it in this struct:

type ParsedInterface struct {
    InterfaceName string
    Methods       []ParsedMethod
}

type ParsedMethod struct {
    Name        string
    InputParams []ParsedParam
    ReturnTypes []ParsedType
}

type ParsedParam struct {
    Name string
    Type string
}

type ParsedType struct {
    Name   string
    Fields []ParsedField
}

type ParsedField struct {
    Name string
    Type string
}

This is working correctly till here, but for my third step :

i.e. to generate the go code for the gRPC client and server side i am using this generator.go

package main

import (
    "bytes"
    "go/format"
    "text/template"
)

// GenerateCode generates the client and server code
func GenerateCode(parsedInterface *ParsedInterface) (string, string, error) {
    clientTemplate := `
package main

import (
    "context"
    "fmt"
    "google.golang.org/grpc"
)

type {{.InterfaceName}}Client struct {
    cc *grpc.ClientConn
}

func New{{.InterfaceName}}Client(cc *grpc.ClientConn) *{{.InterfaceName}}Client {
    return &{{.InterfaceName}}Client{cc: cc}
}

{{range .Methods}}
func (c *{{$.InterfaceName}}Client) {{.Name}}(ctx context.Context, req interface{}) (interface{}, error) {
    out := new(interface{})
    err := c.cc.Invoke(ctx, "/{{$.InterfaceName}}/{{.Name}}", req, out)
    if err != nil {
        return nil, err
    }
    return out, nil
}
{{end}}
`

    serverTemplate := `
package main

import (
    "context"
    "fmt"
    "reflect"
    "google.golang.org/grpc"
)

type {{.InterfaceName}}Server struct {
    impl interface{}
}

func New{{.InterfaceName}}Server(impl interface{}) *{{.InterfaceName}}Server {
    return &{{.InterfaceName}}Server{impl: impl}
}

{{range .Methods}}
func (s *{{$.InterfaceName}}Server) {{.Name}}(ctx context.Context, req struct {
    {{range .InputParams}}{{.Name}} {{.Type}}
    {{end}}
}) ({{range .ReturnTypes}}{{.Name}} {{.Type}}{{end}}, error) {
    method := reflect.ValueOf(s.impl).MethodByName("{{.Name}}")
    if !method.IsValid() {
        return {{range .ReturnTypes}}{{.Name}}, fmt.Errorf("method {{.Name}} not found")
    }
    in := []reflect.Value{reflect.ValueOf(ctx)}
    {{range .InputParams}}in = append(in, reflect.ValueOf(req.{{.Name}})){{end}}
    out := method.Call(in)
    if len(out) != {{len .ReturnTypes}}+1 {
        return {{range .ReturnTypes}}{{.Name}}, fmt.Errorf("unexpected number of return values")
    }
    if err, ok := out[{{len .ReturnTypes}}].Interface().(error); ok && err != nil {
        return {{range .ReturnTypes}}{{.Name}}, err
    }
    resp := struct {
        {{range .ReturnTypes}}
            {{.Name}} {{.Type}}
        {{end}}
    }{}
    {{range $i, $type := .ReturnTypes}}
        {{if $type.Fields}}
            resp.{{.Name}} = {{.Type}}{
                {{range $field := $type.Fields}}
                    {{$field.Name}}: out[{{$i}}].Elem().FieldByName("{{$field.Name}}").Interface().({{$field.Type}}),
                {{end}}
            }
        {{else}}
            resp.{{.Name}} = out[{{$i}}].Interface().({{$type.Type}})
        {{end}}
    {{end}}
    return resp, nil
}
{{end}}

func (s *{{.InterfaceName}}Server) Register(grpcServer *grpc.Server) {
    {{.InterfaceName}}_RegisterService(grpcServer, s.impl)
}
`

    clientTmpl, err := template.New("client").Parse(clientTemplate)
    if err != nil {
        return "", "", err
    }

    serverTmpl, err := template.New("server").Parse(serverTemplate)
    if err != nil {
        return , "", err
    }

    var clientBuffer, serverBuffer bytes.Buffer

    err = clientTmpl.Execute(&clientBuffer, parsedInterface)
    if err != nil {
        return "", "", err
    }

    err = serverTmpl.Execute(&serverBuffer, parsedInterface)
    if err != nil {
        return "", "", err
    }

    clientCode, err := format.Source(clientBuffer.Bytes())
    if err != nil {
        return "", "", err
    }

    serverCode, err := format.Source(serverBuffer.Bytes())
    if err != nil {
        return "", "", err
    }

    return string(clientCode), string(serverCode), nil
}

For the client side i am able to generate the code successfully, but the server-side template is giving me this error:

template: server:60: unexpected EOF

I want to generate both the client and server-side code using these templates but i doubt maybe there is some error inside them.

So for example, the client-side code generated by this method is:

package main

import (
        "context"
        "fmt"
        "google.golang.org/grpc"
)

type ExampleServiceClient struct {
        cc *grpc.ClientConn
}

func NewExampleServiceClient(cc *grpc.ClientConn) *ExampleServiceClient {
        return &ExampleServiceClient{cc: cc}
}

func (c *ExampleServiceClient) GetExampleData(ctx context.Context, req interface{}) (interface{}, error) {
        out := new(interface{})
        err := c.cc.Invoke(ctx, "/ExampleService/GetExampleData", req, out)
        if err != nil {
                return nil, err
        }
        return out, nil
}

But for server side code getting error:

template: server:60: unexpected EOF

Upvotes: 1

Views: 196

Answers (0)

Related Questions