How to get gRPC to properly serialize response - grpc

getting a strange error when trying simple gRPC implementation I.e. following the standard python example. Server seems to run OK, but get the error when I ping it with a client
grpc:
package pas;
// The PAS service definition
service PAS {
// analyze single file
rpc getPhotonRecords (PhotonRecordsRequest) returns (PhotonRecordsReply) {}
}
message PhotonRecordsRequest {
string fileName = 1;
}
message PhotonRecordsReply {
repeated uint32 PhotonRecords = 1;
}
client:
with grpc.insecure_channel("localhost:50051") as channel:
stub = pas_pb2_grpc.PASStub(channel)
msg = pas_pb2.PhotonRecordsRequest(fileName='testingFilename.flb')
response = stub.getPhotonRecords(msg)
server:
class PAS_GRPC(pas_pb2_grpc.PASServicer):
def getPhotonRecords(self, request: pas_pb2.PhotonRecordsRequest, context):
# check for required fields and error if not there or valid
# update any optional fields that the request has specified
PhotonRecordsReply = pas_pb2.PhotonRecordsReply()
PhotonRecordsReply.PhotonRecords.extend([1, 3, 7])
return pas_pb2.PhotonRecordsReply
client error:
<_InactiveRpcError of RPC that terminated with:
status = StatusCode.INTERNAL
details = "Failed to serialize response!"
server error:
TypeError: IsInitialized() missing 1 required positional argument: 'self'

Your server method getPhotonRecords returns the type:
return pas_pb2.PhotonRecordsReply
But it should return the variable you created:
return PhotonRecordsReply
You may want to use snake_case for variables to help differentiate from CamelCase class names, i.e.:
photon_records_reply = pas_pb2.PhotonRecordsReply()
...

Related

In gRPC, can the receiver of a unidirectional stream to cancel the stream and send an error to the sender?

In a gRPC unidirectional client-to-server stream, it is possible for the server to cancel the stream and return an error message to the client?
I've tried setting a trailer and returning a status message with .SendAndClose(), but neither are readable from the client. At the client, .Send returns an EOF error as expected, but .CloseAndRecv() does not return the status message sent by the server and .Trailer() returns an empty map.
// protobuf:
service Foo {
rpc Eat (stream Food) returns (Status) {}
}
// Server:
var Retval pb.Status
Retval.Status = "something went wrong"
emap := make(map[string]string)
emap["error"] = "something went wrong"
MD := metadata.New(emap)
Stream.SetTrailer(MD)
Stream.SendAndClose(&Retval)
// Client:
err = Stream.Send(Stuff) // returns EOF
if err != nil {
Status, err := o.Stream.CloseAndRecv() //returns nil, EOF
MD := o.Stream.Trailer() // returns an empty map.
}
Is there a way to do this without a bidirectional stream or a separate RPC endpoint for the client to request status messages from the server?
First, you don't need to define the Status object in your proto file. You can for example return a Well Known Type called Empty.
To do this you can do:
import "google/protobuf/empty.proto"
service Foo {
rpc Eat (stream Food) returns (google.protobuf.Empty);
}
This is just a recommendation because you don't need to provide a Status object. gRPC already has one defined that you can use in your go code.
Then on the server side you don't need to call SendAndClose when you want to return an error because your Eat function will have the following function declaration:
func (*Server) Eat(stream pb.Foo_EatServer) error
You can use something like the following code to send a status specifying that you had an error.
return nil, status.Errorf(
codes.Internal, // check suitable code
"A description for the error",
)
and before returning you can set a Trailer that will trigger error on the client side.
On the client side, you'll need to do something like:
trailer := stream.Trailer()
v, exist := trailer["error"]
if exist { // there is an error
fmt.Println("Error: ", v)
}
let me know if you need more help on that.

lua-resty-openidc: Entry thread aborted: runtime error: attempt to call field 'start' (a nil value)

Environment
lua-resty-openidc : v1.7.5
OpenID Connect provider: Keycloak
This is what I'm doing
For the info, I'm using it on our ingress-nginx-controller, with the entire content copied from /usr/local/openresty/lualib/resty to /etc/nginx/lua/. When the UI endpoint (i.e. https://ingress.myproject.local/myui) is called, it supposed to redirect the the connection to Keycloak. I have a client called myui under the master realm in Keycloak.
This my current code
location ~* "^/myui(/|$)(.*)" {
.....
.....
access_by_lua_block {
local opts = {
redirect_uri = "/redirect_uri",
accept_none_alg = true,
discovery = "http://keycloak.myproject.local:8080/auth/realms/master/.well-known/openid-configuration",
client_id = "myui",
client_secret = "ABCDEFgHIJKLMnOPQRSTuVWXYZ",
redirect_uri_scheme = "https",
logout_path = "/logout",
redirect_after_logout_uri = "http://keycloak.myproject.local:8080/auth/realms/master/protocol/openid-connect/logout?redirect_uri=https://ingress.myproject.local/myui/",
redirect_after_logout_with_id_token_hint = false,
session_contents = {id_token=true}
}
-- call introspect for OAuth 2.0 Bearer Access Token validation
local res, err = require("resty.openidc").authenticate(opts)
if err then
ngx.status = 403
ngx.say(err)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
}
expires 0;
add_header Cache-Control private;
}
}
This is what I get
Upon running, I get 500 Internal Server Error on the browser, with the error msg:
[error] 549#549: *123249 lua entry thread aborted: run time error: /etc/nginx/lua/resty/openidc.lua:1459: attempt to call field 'start' (a nil value)
stack traceback:
coroutin 0:
/etc/nginx/lua/resty/openidc.lua: in function 'authenticate'
access_by_lua(nginx.conf:1075): 16: in main chunk, client xx.xx.xx.xx , server: ingress.myproject.local, request: "GET /myui HTTP/2.0", host: "ingress.myproject.local"
I don't see anything significant in the log related to that. Any idea why I'm getting this or what am I doing wrong?
-S
The documentation for https://github.com/zmartzone/lua-resty-openidc says:
You will need
to install two extra pure-Lua dependencies that implement session management and HTTP client functions:
lua-resty-http
lua-resty-session
It would appear that you didn't install lua-resty-session. The error you're getting is because r_session is nil, which was defined as local r_session = require("resty.session").

grpc request without any request param

I have a grpc server with request of type:
service AbcService {
rpc GetInfo(GetInfoRequest) returns (GetInfoResponse) {}
}
message GetInfoRequest {}
message GetInfoResponse {
string hostname = 3;
}
And this is my client:
channel = grpc.insecure_channel('test.url:1025')
client = svc_pb2_grpc.AbcServiceStub(channel)
# get response
resp = client.GetInfo
I am facing issues with the client, since I am not able to get any response from it. grpcurl works simply fine using:
grpcurl -plaintext test.url:1025 AbcService/GetInfo
Is resp = client.GetInfo the right way in client to invoke this call (which is not expecting any request parameter)?
The "stub" encapsulates your server with a class, where the different API calls (requests/responses) are method calls.
So first of all:
resp = client.GetInfo()
However, GetInfo expects an GetInfoRequest, so you need:
resp = client.GetInfo(GetInfoRequest())

http4s Received premature EOF

I want to implement an http4s server that receives the content from another service, processes it and return the response.
The original service uses redirects so I added the Follow redirect middleware. I also added the Logger middleware to check the logs produced.
The skeleton of the service is:
implicit val clientResource = BlazeClientBuilder[F](global).resource
val wikidataEntityUrl = "http://www.wikidata.org/entity/Q"
def routes(implicit timer: Timer[F]): HttpRoutes[F] = HttpRoutes.of[F] {
case GET -> Root / "e" / entity => {
val uri = uri"http://www.wikidata.org/entity/" / ("Q" + entity)
val req: Request[F] = Request(uri = uri)
clientResource.use { c => {
val req: Request[F] = Request(Method.GET, uri)
def cb(resp: Response[F]): F[Response[F]] = Ok(resp.bodyAsText)
val redirectClient = Logger(true,true,_ => false)(FollowRedirect[F](10, _ => true)(c))
redirectClient.fetch[Response[F]](req)(cb)
}}}}
When I try to access the service with curl as:
curl -v http://localhost:8080/e/33
The response contains the first part of the original content and finnishes with:
transfer closed with outstanding read data remaining
* Closing connection 0
Looking at the logs, they content the following line:
ERROR o.h.s.blaze.Http1ServerStage$$anon$1 - Error writing body
org.http4s.InvalidBodyException: Received premature EOF.
which suggests that there was an error receiving a premature EOF.
I found a possible answer in this issue: but the answers suggest to use deprecated methods like tohttpService.
I think I would need to rewrite the code using a streams, but I am not sure what's the more idiomatic way to do it. Some suggestions?
I received some help in the http4s gitter channel to use the toHttpApp method instead of the fetch method.
I was also suggested also to pass the client as a parameter.
The resulting code is:
case GET -> Root / "s" / entity => {
val uri = uri"http://www.wikidata.org/entity/" / ("Q" + entity)
val req: Request[F] = Request(Method.GET, uri)
val redirectClient = Logger(true,true,_ => false)(FollowRedirect[F](10, _ => true)(client))
redirectClient.toHttpApp.run(req)
}
and now it works as expected.
The toHttpApp method is intended for use in proxy servers.

Angular 2 http service. Get detailed error information

Executing Angular2 http call to the offline server doesn't provide much info in it's "error response" object I'm getting in the Observable's .catch(error) operator or subscription error delegate (they are both share the same info actually). But as you can see on the screen shot of the console there's actual error was displayed by zone.js somehow.
So, how can I get this specific error info (net::ERR_CONNECTION_REFUSED)?
Thanks.
Whenever server do not respond, response.status will be always equal to 0 (zero)
{
type: 3, //ResponseType.Error
status: 0, // problem connecting endpoint
}
Also note, when you are performing CORS request, but origin (url in browser) is not authorized (not in allowed list of host names configured in remote endpoint) the response would be similar to above, with exception to type attribute which will be equal to 4 = ResponseType.Opaque...
This means, connection was made, but for instance, OPTIONS request returned with headers which do not contain origin or HTTPS request was performed from HTTP origin.
You can handle the error messages so they are easier to read. This can definitely be expanded on too:
public Get() {
return this.http.get(this.URL).map(this.extractData)
.catch(this.handleError);
}
public extractData(res: Response) {
let body = res.json();
return body || {};
}
public handleError(error: any) {
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg);
return Observable.throw(errMsg);
}
Check out this part of the docs on error handling.
Without digging in the code, my expectation is that if the server is unreachable, then no response can be returned from the server. Therefore the Response object remains its initialized state.

Resources