Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

marshaler runtime.Marshaler does not handle io.EOF when decoding #195

Closed
gyuho opened this issue Jul 18, 2016 · 7 comments
Closed

marshaler runtime.Marshaler does not handle io.EOF when decoding #195

gyuho opened this issue Jul 18, 2016 · 7 comments

Comments

@gyuho
Copy link

gyuho commented Jul 18, 2016

Hi,

after resolving golang/protobuf/jsonpb compatibility issue, we found another issue with json decoder

Details can be found at etcd-io/etcd#5969 (comment).

To summarize

dec.Decode(&protoReq) returns io.EOF in this code (full generated code can be found https://github.com/coreos/etcd/blob/master/etcdserver/etcdserverpb/rpc.pb.gw.go#L92-L93):

func request_Watch_Watch_0(ctx context.Context, marshaler runtime.Marshaler, client WatchClient, req *http.Request, pathParams map[string]string) (Watch_WatchClient, runtime.ServerMetadata, error) {
    var metadata runtime.ServerMetadata
    stream, err := client.Watch(ctx)
    if err != nil {
        grpclog.Printf("Failed to start streaming: %v", err)
        return nil, metadata, err
    }
    dec := marshaler.NewDecoder(req.Body)
    handleSend := func() error {
        var protoReq WatchRequest
        err = dec.Decode(&protoReq)
        if err != nil {
            grpclog.Printf("Failed to decode request: %v", err)
            return err
        }
        if err = stream.Send(&protoReq); err != nil {
            grpclog.Printf("Failed to send request: %v", err)
            return err
        }
        return nil
    }

But if we manually make it handle io.EOF as below:

        dec := marshaler.NewDecoder(req.Body)
        handleSend := func() error {
                var protoReq WatchRequest
                err = dec.Decode(&protoReq)
-               if err != nil {
+               if err != nil && err != io.EOF {

where req.Body is {"create_request": {"key": "Zm9v"}}, everything works as expected.

Is there any way to make this decoder handle io.EOF?

Thanks.

@yugui
Copy link
Member

yugui commented Jul 23, 2016

Fixed in #191, but we'll discuss how we should handle zero-length stream.

@yugui yugui closed this as completed Jul 23, 2016
@gyuho
Copy link
Author

gyuho commented Jul 23, 2016

Thanks. Now our gateway server doesn't complain but now seems like it returns before interacting with gateway request?

And it works if it were written as below:

        dec := marshaler.NewDecoder(req.Body)
        handleSend := func() error {
                var protoReq WatchRequest
                err = dec.Decode(&protoReq)
-               if err != nil {
+               if err != nil && err != io.EOF {
                        grpclog.Printf("Failed to decode request: %v", err)
                        return err
                }
                if err = stream.Send(&protoReq); err != nil {
                        grpclog.Printf("Failed to send request: %v", err)
                        return err
                }

or

                var protoReq WatchRequest
                err = dec.Decode(&protoReq)
+               if err == io.EOF {
+                       return nil
+               }
                if err != nil {
                        grpclog.Printf("Failed to decode request: %v", err)
                        return err
                }
                if err = stream.Send(&protoReq); err != nil {
                        grpclog.Printf("Failed to send request: %v", err)
                        return err
                }

instead of

                var protoReq WatchRequest
                err = dec.Decode(&protoReq)
+               if err == io.EOF {
+                       return err
+               }
                if err != nil {
                        grpclog.Printf("Failed to decode request: %v", err)
                        return err
                }
                if err = stream.Send(&protoReq); err != nil {
                        grpclog.Printf("Failed to send request: %v", err)
                        return err
                }

Would it be something to be handled in zero-length stream that you mentioned?

@yugui
Copy link
Member

yugui commented Jul 24, 2016

@gyuho Right. The second suggestion is what I suggested in #191.
I'll take a look at this issue by myself on Monday.

@gyuho
Copy link
Author

gyuho commented Jul 25, 2016

@yugui Another question is that why we were sending zero-length input in the first place?

        "github.com/golang/protobuf/proto"
@@ -86,7 +89,9 @@ func request_Watch_Watch_0(ctx context.Context, marshaler runtime.Marshaler, cli
                grpclog.Printf("Failed to start streaming: %v", err)
                return nil, metadata, err
        }
-       dec := marshaler.NewDecoder(req.Body)
+       bts, err := ioutil.ReadAll(req.Body)
+       fmt.Println(string(bts), err)
+       dec := marshaler.NewDecoder(bytes.NewReader(bts))

The print statement returns {"create_request": {"key": "Zm9v"}} <nil>, so the request body was not empty, but dec.Decode(&protoReq) returns io.EOF, which doesn't make sense. Is this expected? Or some kind of grpc-specific behavior?

Thanks!

@yugui
Copy link
Member

yugui commented Jul 26, 2016

why we were sending zero-length input in the first place

I don't think we are sending zero-length input. #200 just fixes an edge-case bug which can happen when the stream is empty.
Also the very first invocation of handleSend is different from the later ones just because let the caller know errors earlier.

The print statement returns {"create_request": {"key": "Zm9v"}} , so the request body was not empty, but dec.Decode(&protoReq) returns io.EOF, which doesn't make sense. Is this expected? Or some kind of grpc-specific behavior?

I can't reproduce. Could you give me a small reproducible case?

package main

import (
    "fmt"
    "strings"

    "github.com/grpc-ecosystem/grpc-gateway/runtime"
)

func main() {
    var m runtime.JSONPb
    dec := m.NewDecoder(strings.NewReader(`{"create_request": {"key": "Zm9v"}}`))

    msg := make(map[string]interface{})
    if err := dec.Decode(&msg); err != nil {
        fmt.Println(msg, err)
        return
    }
    fmt.Println(msg)
}

@gyuho
Copy link
Author

gyuho commented Jul 27, 2016

Sorry for late response. Yeah I am still seeing the same behavior. I will try to have reproducible code for this issue.

Thanks.

@gyuho
Copy link
Author

gyuho commented Jul 27, 2016

@yugui Closing this, because as you said, message itself does not send io.EOF. The problem was that we were using cURL, sending EOF when it closes the request. Thanks for help!

@gyuho gyuho closed this as completed Jul 27, 2016
@tamalsaha tamalsaha mentioned this issue Mar 30, 2017
1 task
adasari pushed a commit to adasari/grpc-gateway that referenced this issue Apr 9, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants