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

Nicer handling for gRPC status codes. #6

Open
lucasdicioccio opened this issue Nov 3, 2019 · 3 comments · May be fixed by #48
Open

Nicer handling for gRPC status codes. #6

lucasdicioccio opened this issue Nov 3, 2019 · 3 comments · May be fixed by #48

Comments

@lucasdicioccio
Copy link
Member

This is the new home for discussions around lucasdicioccio/http2-client-grpc#16 .

Definitely related to #5 .

@akshaymankar
Copy link

Hello, I was trying to use mu-haskell for grpc calls and get some errors out of it. But on any grpc errors, I just get "not enough bytes". My guess is that it is coming from here, but I am not sure:

-- presence of a message indicate an error
-- TODO: double check this is true in general
case lookup grpcMessageH hdrs2 of
Nothing -> fromDecoder $ pushEndOfInput
$ flip pushChunk dat
$ decodeOutput rpc decompress

Here is some code which reproduces the error:
cabal.project:
source-repository-package
  type: git
  location: https://github.com/higherkindness/mu-haskell
  tag: 4cd6560d16bddee9ef79cbfce4a28b3b8922d29f
  subdir: core/schema

source-repository-package
  type: git
  location: https://github.com/higherkindness/mu-haskell
  tag: 4cd6560d16bddee9ef79cbfce4a28b3b8922d29f
  subdir: adapter/protobuf

source-repository-package
  type: git
  location: https://github.com/haskell-grpc-native/http2-grpc-haskell.git
  tag: fcc9e6f76df0ee37313b10697a3868552555cedd
  subdir: http2-client-grpc

source-repository-package
  type: git
  location: https://github.com/akshaymankar/tracing.git
  tag: ef4d6337540603482afa89aa4f067574e089bf46

packages: ./
Main.hs:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}

module Main where

import Control.Concurrent
import Control.Concurrent.Async
import Control.Monad (forever)
import Control.Monad.Except
import Data.Text (Text)
import GHC.TypeLits
import Mu.Adapter.ProtoBuf
import Mu.GRpc.Client.Optics
import Mu.GRpc.Client.TyApps
import Mu.GRpc.Server
import Mu.Rpc
import Mu.Schema
import Mu.Server

main :: IO ()
main =
  concurrently_ serve (forever $ query >> threadDelay 1000000)

type MySchema = '[ 'DRecord "Foo" '[ 'FieldDef "foo" ('TPrimitive Text)]]

type MyService =
  'Package
    ('Just "pkg")
    '[ 'Service
         "MyService"
         '[ 'Method
              "identityFoo"
              '[ 'ArgSingle ('Nothing :: Maybe Symbol) ('SchemaRef MySchema "Foo")]
              ('RetSingle ('SchemaRef MySchema "Foo"))
          ]
     ]

type instance
  AnnotatedSchema ProtoBufAnnotation MySchema =
    '[ 'AnnField "Foo" "foo" ('ProtoBufId 1 '[])]

type Foo = Term MySchema (MySchema :/: "Foo")

identityFoo :: MonadServer m => Foo -> m Foo
identityFoo _ = throwError $ ServerError NotFound "nothing to see here"

-- identityFoo = pure

service :: MonadServer m => SingleServerT i MyService m _
service = singleService (method @"identityFoo" identityFoo)

serve :: IO ()
serve = runGRpcApp msgProtoBuf 8080 service

mkClient :: IO GrpcClient
mkClient = assertRight =<< setupGrpcClient' (grpcClientConfigSimple "127.0.0.1" 8080 False)

assertRight :: Show a => Either a b -> IO b
assertRight = either (error . show) pure

query :: IO ()
query = do
  c <- mkClient
  r :: GRpcReply Foo <- gRpcCall @'MsgProtoBuf @MyService @"MyService" @"identityFoo" c bar
  case r of
    GRpcErrorString str -> putStrLn $ "GRpcError: " <> str
    GRpcOk x -> putStrLn $ "Ok: " <> show x
    _ -> putStrLn "Unexpected!"

bar :: Foo
bar = record1 "bar"

@akshaymankar
Copy link

I looked more deeply into this and found that this happens because this part of code doesn't look at :status or grpc-status in the headers before trying to decode the body. In my case the server responds with a 404, this could also be one of 5xx responses.

This raises a design of question about the RawReply type. As of now it is defined as:

-- | A reply.
--
-- This reply object contains a lot of information because a single gRPC call
-- returns a lot of data. A future version of the library will have a proper
-- data structure with properly named-fields on the reply object.
--
-- For now, remember:
-- - 1st item: initial HTTP2 response
-- - 2nd item: second (trailers) HTTP2 response
-- - 3rd item: proper gRPC answer
type RawReply a = Either ErrorCode (CIHeaderList, Maybe CIHeaderList, Either String a)

The ErrorCode is the HTTP2 error code which doesn't seem enough to express gRPC specific of errors. Would you be open to introducing another type for gRPC Errors and replace this ErrorCode with this new type?

@akshaymankar
Copy link

Actually changing ErrorCode is not necessary, changing the third item in the tuple from Either String a to something like Either GRPCError a would also solve the problem.

@akshaymankar akshaymankar linked a pull request Mar 12, 2021 that will close this issue
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

Successfully merging a pull request may close this issue.

2 participants