惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
腾讯CDC
博客园 - Franky
S
SegmentFault 最新的问题
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks
Y
Y Combinator Blog
Engineering at Meta
Engineering at Meta
Microsoft Security Blog
Microsoft Security Blog
MongoDB | Blog
MongoDB | Blog
I
InfoQ
T
Tailwind CSS Blog
Hugging Face - Blog
Hugging Face - Blog
B
Blog RSS Feed
博客园 - 叶小钗
博客园_首页
有赞技术团队
有赞技术团队
雷峰网
雷峰网
量子位
小众软件
小众软件
月光博客
月光博客
U
Unit 42
D
DataBreaches.Net

HTTP

用上了 Let’s Encrypt 的 IP 证书,太香了 safari 无法打开网站,但 chrome 可以 大家一般如何保证服务器 set-cookie 按序保存的 明御 WEB 应用防火墙 如何找回密码? 直连模式下 https 网站打不开 关于在其他国家访问中国国内数据出现严重丢包问题 求教同样的 url 参数头部 cookies, scrapy 请求返回验证码, requests 直接请求就正常,代码如下 什么工具适合方便的发送原始 HTTP 包? 冷知识:.ml 域名可以注册了 TLS 回顾 想监测某航空余票 http 代理,如果不支持 connect 方法,有没有办法访问 https 的网站? 关于 tomcat 配置国密 https 后出现的问题求助 一个 udp 的疑问 tradingview 上面策略的买入 卖出信号怎样发送给服务器,现在 webhook URL 现在收费 有什么办法可以解决的 选择重传协议疑问 有个用于测试 HTTPS 链接各阶段耗时的命令行工具想不起来叫啥了 求各位高手推荐一个 postman 的 mac 下的替代。。 有现成的 Http 请求批量发送和管理工具吗? 报文主体和实体主体的差异 请教一下各位, http2 的头部有什么简单的方法去解码吗? 后 911S5 时代 各路 ip 代理的对比与选择 (仅供参考) HTTP 协议 HEAD 请求,会有查询字符串吗? 一个 http 请求由多个 tcp 数据包组成,一个 tcp 连接可以发送多个 http 请求,是这样吗? httpCanary 的上传服务器插件,这个应该如何配置? 请教各位技术大大一个 websocket 消息丢失的问题 某些网站在特定的提交中需要手工选择 chrome 的证书,这是 https 的双向认证吗 HTTP/2 长连接的存续期多久? REST 祖师爷 Roy Fielding 说: RPC 就不适合 RESTful 风格。只有超文档适合用 REST 2022 年 TLS 1.3 HTTP/2 下到底该不该开启 Gzip 压缩?
关于 http 处理 grpc-gateway stream 流式响应时的问题
HUNYXV · 2021-05-11 · via HTTP

http 请求 stream 响应时,response body 打印出来是正确的结果:

		{"result":{"code":1,"msg":"1111"}}
        {"result":{"code":2,"msg":"2222"}}
        {"result":{"code":3,"msg":"3333"}}
        {"result":{"code":4,"msg":"4444"}}
        {"result":{"code":5,"msg":"5555"}}
        {"result":{"code":6,"msg":"6666"}}

但是使用runtime.JSONPb.Decode 时,也会得到五个结果,但每个 decode 出来是个 nil... :

=== RUN   TestHttpRespStream
    service_test.go:147: resp: <nil>
    service_test.go:147: resp: <nil>
    service_test.go:147: resp: <nil>
    service_test.go:147: resp: <nil>
    service_test.go:147: resp: <nil>
    service_test.go:147: resp: <nil>
    service_test.go:149: EOF
--- PASS: TestHttpRespStream (0.62s)

这是 proto 文件:

// ./pb/test.proto

syntax = "proto3";

package pb;

option go_package = "/pb;pb";

import "google/api/annotations.proto";

message Req {
    int32 id = 1;
    string name = 2;
}

message Resp {
    int32 code = 1;
    string msg = 2;
}

service TestService {
    rpc QueryStreamResp(Req) returns (stream Resp){
        option (google.api.http) = {
            post: "/query-stream-resp"
            body: "*"
        };
    };
    rpc QueryStreamReq(stream Req) returns (Resp){
        option (google.api.http) = {
            post: "/query-stream-req"
            body: "*"
        };
    };
    
    rpc Query(stream Req) returns (stream Resp){
        option (google.api.http) = {
            post: "/query"
            body: "*"
        };
    };
}

grpc 服务端:

func (ts *TestService) QueryStreamResp(req *pb.Req, stream pb.TestService_QueryStreamRespServer) error {
	log.Printf("QueryStreamResp|start...|req: %+v\n", req)
	result := []*pb.Resp{
		{Code: 1, Msg: "1111"},
		{Code: 2, Msg: "2222"},
		{Code: 3, Msg: "3333"},
		{Code: 4, Msg: "4444"},
		{Code: 5, Msg: "5555"},
		{Code: 6, Msg: "6666"},
	}

	// header := make(metadata.MD)
	// header.Append("content-type", "application/json")
	// stream.SendHeader(header)
	for i := range result {
		log.Printf("resp: %+v", result[i])
		
		if err := stream.Send(result[i]); err != nil {
			log.Fatal(err)
		}
		time.Sleep(100 * time.Millisecond)
	}
	log.Println("QueryStreamResp|stop...")

	return nil
}

单元测试:

func TestHttpRespStream(t *testing.T) {
	url := "http://127.0.0.1:8080/query-stream-resp"
	reqData := &pb.Req{Id: 1, Name: "111"}

	var buffer bytes.Buffer
	encoder := (&runtime.JSONPb{}).NewEncoder(&buffer)

	if err := encoder.Encode(reqData); err != nil {
		t.Fatal(err)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, &buffer)
	if err != nil {
		t.Fatal(err)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		t.Fatal(err)
	}
	defer resp.Body.Close()
	// body, err := ioutil.ReadAll(resp.Body)
	// if err != nil {
	// 	t.Fatal(err)
	// }
	// t.Logf("body: %s", string(body))
	jsonb := new(runtime.JSONPb)
	dencoder := jsonb.NewDecoder(resp.Body)

	for {
		var result *pb.Resp
		err := dencoder.Decode(result)
		if err == nil {
			t.Logf("resp: %+v", result)
		} else {
			t.Logf("%+v", err)
			break
		}
	}
}