GRPC头测试记录
阅读原文时间:2023年07月10日阅读:1

GRPC头记录

http://nodejs.cn/api/http2/note_on_authority_and_host.html

https://cloud.tencent.com/developer/section/1189948

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/User-Agent

:authority

个人理解:可视为host,表示服务的IP+Port

HTTP/2 要求请求具有 :authority 伪标头或 host 标头。 当直接构建 HTTP/2 请求时首选 :authority,从 HTTP/1 转换时首选 host(例如在代理中)。

如果 :authority 不存在,则兼容性 API 将回退到 host。 有关详细信息,请参阅 request.authority。 但是,如果不使用兼容性 API(或直接使用 req.headers),则需要自己实现任何回退行为。


":authority" 伪头字段包含目标 URI 的部分权限([RFC3986],第 3.2 节)。权限不得包含 "http" 或 "https" scheme URI 的已弃用 "userinfo" 子组件。为了确保可以准确地再现 HTTP/1.1请求行,当从具有源或星号形式的请求目标的 HTTP/1.1 请求进行转换时,必须省略该伪头字段(参见[RFC7230],第 5.3 节)。直接生成 HTTP/2 请求的客户端应该使用 ":authority" 伪头字段而不是 Host 头字段。将 HTTP/2 请求转换为 HTTP/1.1 的网络中间件必须通过复制 ":authority" 伪头字段的值来创建 Host 头字段(如果请求中不存在的话)。

user-agent

User-Agent 首部包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。

  • 192.168.10.173 PC1-IP
  • 192.168.10.96 PC2-IP
  • 192.168.10.152 kong网关-IP
  • c++ 服务在173运行
  • golang服务在96运行
  • 标签为服务,子标签为客户端

c++直接访问

Client metadata:
Header key: user-agent, value: grpc-c++/1.46.0-dev grpc-c/23.0.0 (linux; chttp2)

golang访问

Client metadata:
Header key: user-agent, value: grpc-go/1.46.2

kong网关-c++

Client metadata:
Header key: user-agent, value: grpc-c++/1.46.0-dev grpc-c/23.0.0 (linux; chttp2)
Header key: x-forwarded-for, value: 192.168.10.173
Header key: x-forwarded-host, value: 192.168.10.152
Header key: x-forwarded-path, value: /hello.HelloService/SayHello
Header key: x-forwarded-port, value: 8508
Header key: x-forwarded-proto, value: http
Header key: x-real-ip, value: 192.168.10.173

kong网关-golang

Client metadata:
Header key: user-agent, value: grpc-go/1.46.2
Header key: x-forwarded-for, value: 192.168.10.96
Header key: x-forwarded-host, value: 192.168.10.152
Header key: x-forwarded-path, value: /hello.HelloService/SayHello
Header key: x-forwarded-port, value: 8508
Header key: x-forwarded-proto, value: http
Header key: x-real-ip, value: 192.168.10.96

kong网关-grpc_web

Client metadata:
Header key: accept, value: application/grpc-web-text
Header key: accept-encoding, value: gzip, deflate
Header key: accept-language, value: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Header key: content-length, value: 9
Header key: origin, value: http://192.168.10.173:8080
Header key: referer, value: http://192.168.10.173:8080/
Header key: user-agent, value: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53
Header key: x-forwarded-for, value: 192.168.10.96
Header key: x-forwarded-host, value: 192.168.10.152
Header key: x-forwarded-path, value: /hello.HelloService/SayHello
Header key: x-forwarded-port, value: 8509
Header key: x-forwarded-prefix, value: /hello
Header key: x-forwarded-proto, value: http
Header key: x-grpc-web, value: 1
Header key: x-real-ip, value: 192.168.10.96
Header key: x-user-agent, value: grpc-web-javascript/0.1

c++ 直接访问

Client metadata:
:authority  ::  [192.168.10.96:8503]
content-type  ::  [application/grpc]
grpc-accept-encoding  ::  [identity, deflate, gzip]
user-agent  ::  [grpc-c++/1.46.0-dev grpc-c/23.0.0 (linux; chttp2)]

golang 访问

Client metadata:
:authority  ::  [192.168.10.96:8503]
content-type  ::  [application/grpc]
user-agent  ::  [grpc-go/1.46.2]

kong网关- golang

Client metadata:
x-forwarded-path  ::  [/hello.HelloService/SayHello]
x-real-ip  ::  [192.168.10.96]
x-forwarded-for  ::  [192.168.10.96]
x-forwarded-port  ::  [8508]
x-forwarded-host  ::  [192.168.10.152]
content-type  ::  [application/grpc]
user-agent  ::  [grpc-go/1.46.2]
:authority  ::  [192.168.10.96:8503]
x-forwarded-proto  ::  [http]

kong网关- c++

Client metadata:
x-forwarded-for  ::  [192.168.10.173]
content-type  ::  [application/grpc]
grpc-accept-encoding  ::  [identity, deflate, gzip]
x-forwarded-port  ::  [8508]
x-forwarded-host  ::  [192.168.10.152]
x-real-ip  ::  [192.168.10.173]
x-forwarded-path  ::  [/hello.HelloService/SayHello]
user-agent  ::  [grpc-c++/1.46.0-dev grpc-c/23.0.0 (linux; chttp2)]
:authority  ::  [192.168.10.96:8503]
x-forwarded-proto  ::  [http]

kong网关- grpc_web

Client metadata:
referer  ::  [http://192.168.10.173:8080/]
x-forwarded-for  ::  [192.168.10.96]
x-forwarded-path  ::  [/hello.HelloService/SayHello]
accept-language  ::  [zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6]
content-length  ::  [9]
x-forwarded-port  ::  [8509]
accept  ::  [application/grpc-web-text]
x-grpc-web  ::  [1]
x-real-ip  ::  [192.168.10.96]
x-user-agent  ::  [grpc-web-javascript/0.1]
accept-encoding  ::  [gzip, deflate]
x-forwarded-prefix  ::  [/hello]
user-agent  ::  [Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53]
content-type  ::  [application/grpc]
origin  ::  [http://192.168.10.173:8080]
x-forwarded-host  ::  [192.168.10.152]
:authority  ::  [192.168.10.96:8503]
x-forwarded-proto  ::  [http]

获取客户端IP

C++ 服务端

  • C++直接访问

  • Go直接访问

  • C++访问kong

  • Go访问kong

  • Grpc-WEB访问网关

    C++暂时未实现如何从context获取客户端IP

    服务端如果有需要,建议和客户端约定一个Key作为header

Go 服务端

  • C++直接访问
  • Go直接访问
  • C++访问kong
  • Go访问kong
  • Grpc-WEB访问网关

虽然Go在直接访问时,无法从header中读取到客户端IP相关,但通过google.golang.org/grpc/peer包获取到客户端IP

p, _ := peer.FromContext(ctx)
fmt.Println(p.Addr.String(),"  ",p.Addr.Network())

Addr()为IP,NetWork为传输方式(tcp/udp/..)

Go metadata

    ## clinet code
    ctx := context.Background()
    md := metadata.New(map[string]string{"demo": "go client"})
    newCtx2 := metadata.NewOutgoingContext(ctx, md)

 - go service:
    Client metadata:
    user-agent  ::  [grpc-go/1.46.2]
    demo  ::  [go client]
    :authority  ::  [192.168.10.96:8503]
    content-type  ::  [application/grpc]

 - c++ service:
    Client metadata:
    Header key: go, value: programming
    Header key: tour, value: book
    Header key: user-agent, value: grpc-go/1.46.2

C++ metadata

    ## clinet code
    grpc::ClientContext context;
    context.AddMetadata("demo", "c++ client");

  - go:
    Client metadata:
    :authority  ::  [192.168.10.96:8503]
    content-type  ::  [application/grpc]
    grpc-accept-encoding  ::  [identity, deflate, gzip]
    user-agent  ::  [grpc-c++/1.46.0-dev grpc-c/23.0.0 (linux; chttp2)]
    demo  ::  [c++ client]

  - c++:
    Client metadata:
    Header key: demo, value: c++ client
    Header key: user-agent, value: grpc-c++/1.46.0-dev grpc-c/23.0.0 (linux; chttp2)

proto

syntax = "proto2";

package hello;
option go_package = "./hello";
service HelloService {
    rpc SayHello(HelloRequest) returns (HelloResponse);
    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
    rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
}

message HelloRequest {
    optional string greeting = 1;
}

message HelloResponse {
    required string reply = 1;
}

Go

/*
 GRPC 服务端
 */
package main

import (
    "context"
    pb "demo_hello/proto/hello"
    "fmt"
    "google.golang.org/grpc"
    "google.golang.org/grpc/metadata"
    "log"
    "net"
)

type Server struct {
    pb.UnimplementedHelloServiceServer
}

func (S *Server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
    res := "this is 96"
    fmt.Println("Client metadata:")
    if headers, ok := metadata.FromIncomingContext(ctx); ok {
        for head, val := range headers {
            fmt.Println(head, " :: ", val)
        }
    }
    return &pb.HelloResponse{Reply: &res}, nil
}
func main() {
    lis, err := net.Listen("tcp", "0.0.0.0:8503")
    if err != nil {
        log.Fatalf("failed listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterHelloServiceServer(s, &Server{})
    log.Printf("Server listen:%v", lis.Addr())
    if err := s.Serve(lis); err != nil {
        log.Fatalf("Failed Server: %v", err)
    }
}



/*
    GRPC 客户端
 */
package main

import (
    "context"
    "flag"
    "google.golang.org/grpc/metadata"
    "log"
    "time"

    pb "demo_hello/proto/hello"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

const (
    defaultName = "world"
)

var (
    addr = flag.String("addr", "192.168.10.96:8503", "the address to connect to")
)

func main() {
    flag.Parse()
    // Set up a connection to the server.
    conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewHelloServiceClient(conn)

    // Contact the server and print out its response.
    ctx := context.Background()
    md := metadata.New(map[string]string{"demo": "go client"})

    newCtx2 := metadata.NewOutgoingContext(ctx, md)
    ctx, cancel := context.WithTimeout(newCtx2, time.Second)
    defer cancel()
    name := "ss"
    r, err := c.SayHello(ctx, &pb.HelloRequest{Greeting: &name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.GetReply())
}

C++

/*
    GRPC server
*/
#include <iostream>
#include <memory>
#include <string>

#include <grpcpp/grpcpp.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "hello_world/hello_world.grpc.pb.h"
#endif

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using hello::HelloRequest;
using hello::HelloResponse;
using hello::HelloService;
using Service = hello::HelloService::Service;
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Service {
    Status SayHello(ServerContext *context, const HelloRequest *request, hello::HelloResponse *reply) override {
        std::string prefix("Hello ");

        // Get the client's initial metadata
        std::cout << "Client metadata: " << std::endl;
        const std::multimap<grpc::string_ref, grpc::string_ref> metadata = context->client_metadata();
        for (auto iter = metadata.begin(); iter != metadata.end(); ++iter) {
            std::cout << "Header key: " << iter->first << ", value: ";
            // Check for binary value
            size_t isbin = iter->first.find("-bin");
            if ((isbin != std::string::npos) && (isbin + 4 == iter->first.size())) {
                std::cout << std::hex;
                for (auto c : iter->second) {
                    std::cout << static_cast<unsigned int>(c);
                }
                std::cout << std::dec;
            } else {
                std::cout << iter->second;
            }
            std::cout << std::endl;
        }

        reply->set_reply("173");
        return Status::OK;
    }
};

void RunServer() {
    std::string server_address("0.0.0.0:8503");
    GreeterServiceImpl service;

    ServerBuilder builder;
    // Listen on the given address without any authentication mechanism.
    builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
    // Register "service" as the instance through which we'll communicate with
    // clients. In this case it corresponds to an *synchronous* service.
    builder.RegisterService(&service);
    // Finally assemble the server.
    std::unique_ptr<Server> server(builder.BuildAndStart());
    std::cout << "Server listening on " << server_address << std::endl;

    // Wait for the server to shutdown. Note that some other thread must be
    // responsible for shutting down the server for this call to ever return.
    server->Wait();
}

int main(int argc, char **argv) {
    RunServer();

    return 0;
}


/*
    GRPC client
*/
//
// Created by mimo on 2022/5/24.
//
#include "iostream"

#include "memory"

#include "grpcpp/grpcpp.h"
#include "hello_world/hello_world.grpc.pb.h"

using namespace std;
using namespace hello;
class HelloClient {
public:
    explicit HelloClient(const std::shared_ptr<grpc::Channel> &channel)
        : stub_(hello::HelloService::NewStub(channel)) {}

    void SayHello() {
        grpc::ClientContext context;
        context.AddMetadata("demo", "c++ client");
        HelloRequest request;
        HelloResponse response;

        request.set_greeting("dd");

        auto res = stub_->SayHello(&context, request, &response);
        if (!res.ok()) {
            std::cout << "Failed IsConnected alarm server" << std::endl;
            return;
        }
        std::cout << response.reply();
    }

private:
    std::unique_ptr<hello::HelloService::Stub> stub_;
};

int main(){
    HelloClient client(grpc::CreateChannel("192.168.10.96:8503", grpc::InsecureChannelCredentials()));
    client.SayHello();
    return 0;
}