Skip to content

Issue connecting with Google gRPC APIs #100

@Hirevo

Description

@Hirevo

Bug Report

Version

tonic = { version = "0.1.0-alpha.4", features = ["rustls", "prost"] }
tonic-build = { version = "0.1.0-alpha.4" }

Platform

OS: macOS Catalina (10.15)
rustc version: 1.39.0-nightly (2019-09-10)

# output from: `uname -a`
Darwin MBP 19.0.0 Darwin Kernel Version 19.0.0: Wed Sep 25 20:18:50 PDT 2019; root:xnu-6153.11.26~2/RELEASE_X86_64 x86_64

Description

Hello,

I was trying to write an abstraction library for using Google Cloud Platform APIs.
More specifically, I was writing the one for the Pub/Sub service of GCP.

So, I cloned the protobuf definitions of their services (https://github.com/googleapis/googleapis), and attempted to call one of their endpoints.

Here is the build.rs file I used:

fn main() {
    tonic_build::configure()
        .build_client(true)
        .build_server(false)
        .format(true)
        .out_dir("src/api")
        .compile(&["protos/google/pubsub/v1/pubsub.proto"], &["protos"])
        .unwrap();
    println!("cargo:rerun-if-changed=protos/google/pubsub/v1/pubsub.proto");
}

protos/ is the directory containing the clone of Google's protobufs.

Then, I attempt to connect and make a call to the service, like this:

use http::HeaderValue;
use tonic::transport::{Certificate, Channel, ClientTlsConfig};
use tonic::Request;

mod api {
    // Generated protobuf bindings
    include!("api/google.pubsub.v1.rs");
}

const ENDPOINT: &str = "https://pubsub.googleapis.com";
const SCOPES: [&str; 2] = [
    "https://www.googleapis.com/auth/cloud-platform",
    "https://www.googleapis.com/auth/pubsub",
];

#[tokio::main]
async fn main() {
    let certs = tokio::fs::read("roots.pem").await.unwrap();

    let mut tls_config = ClientTlsConfig::with_rustls();
    tls_config.ca_certificate(Certificate::from_pem(certs.as_slice()));
    tls_config.domain_name("pubsub.googleapis.com");

    let channel = Channel::from_static(ENDPOINT)
        .intercept_headers(|headers| {
            let token = "some-valid-google-oauth-token";
            let value = format!("Bearer {0}", token);
            let value = HeaderValue::from_str(value.as_str()).unwrap();
            headers.insert("authorization", value);
        })
        .tls_config(&tls_config)
        .channel();

    let mut service = api::client::PublisherClient::new(channel);

    let response = service.list_topics(Request::new(api::ListTopicsRequest {
        project: format!("projects/{0}", "some-gcp-project-name"),
        page_size: 10,
        page_token: String::default(),
    }));

    dbg!(response.await);
}

The roots.pem file I use here is the sample PEM file from Google Trust Services (https://pki.goog/roots.pem).

The dbg!(response.await) statement always yields:

[src/sample.rs:42] response.await = Err(
    Status {
        code: Internal,
        message: "Unexpected compression flag: 60",
    },
)

This happens regardless of the validity of the OAuth token.

To see if the connection flow was valid, I reproduced the experiment using Golang's google.golang.org/grpc and github.com/golang/protobuf packages:

package main

import (
	"context"
	"fmt"
	"os"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/metadata"

	pubsub "google.golang.org/genproto/googleapis/pubsub/v1" // generated protobuf bindings
)

func clientInterceptor(
	ctx context.Context,
	method string,
	req interface{},
	reply interface{},
	cc *grpc.ClientConn,
	invoker grpc.UnaryInvoker,
	opts ...grpc.CallOption,
) error {
	md := metadata.New(map[string]string{
		"authorization": "Bearer some-valid-google-oauth-token",
	})
	ctx = metadata.NewOutgoingContext(ctx, md)

	return invoker(ctx, method, req, reply, cc, opts...)
}

func main() {
	creds, err := credentials.NewClientTLSFromFile("roots.pem", "pubsub.googleapis.com")
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
	}

	conn, err := grpc.Dial("pubsub.googleapis.com:443", grpc.WithTransportCredentials(creds), grpc.WithUnaryInterceptor(clientInterceptor))
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
	}
	defer conn.Close()

	pub := pubsub.NewPublisherClient(conn)

	resp, err := pub.ListTopics(context.Background(), &pubsub.ListTopicsRequest{Project: "projects/some-gcp-project-name", PageSize: 10})
	if err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
	}
	fmt.Printf("topics: %#v\n", resp)
}

This Go code, provided with valid credentials, works as expected whereas the Rust code, with the same credentials, encounters the invalid compression flag error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-tonicC-bugCategory: Something isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions