syntax = "proto3";

package bilibili.broadcast.v1;

option java_multiple_files = true;

import "bilibili/rpc/status.proto";
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";

// broadcast操作，对应每个target_path
service Broadcast {
  // 用户鉴权
  rpc Auth(AuthReq) returns (AuthResp);
  // 心跳保活：成功心跳为4分45秒，重试心跳为30s，三次收不到进行重连（不超过5分45）
  rpc Heartbeat(HeartbeatReq) returns (HeartbeatResp);
  // 订阅target_path
  rpc Subscribe(TargetPath) returns (google.protobuf.Empty);
  // 取消订阅target_path
  rpc Unsubscribe(TargetPath) returns (google.protobuf.Empty);
  // 消息回执
  rpc MessageAck(MessageAckReq) returns (google.protobuf.Empty);
}

// broadcast连接隧道
service BroadcastTunnel {
  // 创建双向stream连接隧道
  rpc CreateTunnel(stream BroadcastFrame) returns (stream BroadcastFrame);
}

//
enum Action {
  UNKNOWN = 0; //
  UPDATE = 1;  //
  DELETE = 2;  //
}

// 鉴权请求，通过authorization验证绑定用户mid
message AuthReq {
  // 冷启动id，算法uuid，重新起启会变
  string guid = 1;
  // 连接id，算法uuid，重连会变
  string conn_id = 2;
  // 最后收到的消息id，用于过虑重连后获取未读的消息
  int64 last_msg_id = 3;
}

// 鉴权返回
message AuthResp {

}

// target_path:
//   "/" Service-Name "/" {method name} 参考 gRPC Request Path
message BroadcastFrame {
  // 请求消息信息
  FrameOption options = 1;
  // 业务target_path
  string target_path = 2;
  // 业务pb内容
  google.protobuf.Any body = 3;
}

// message_id: 
//   client: 本次连接唯一的消息id，可用于回执
//   server: 唯一消息id，可用于上报或者回执
// sequence:
//   client: 客户端应该每次请求时frame seq++，会返回对应的对称req/resp
//   server: 服务端下行消息，只会返回默认值：0
message FrameOption {
  // 消息id
  int64 message_id = 1;
  // frame序号
  int64 sequence = 2;
  // 是否进行消息回执(发出MessageAckReq)
  // downstream 上只有服务端设置为true，客户端响应
  // upstream   上只有客户端设置为true，服务端响应
  // 响应帧禁止设置is_ack，协议上禁止循环
  // 通常只有业务帧才可能设置is_ack, 因为协议栈(例如心跳、鉴权)另有响应约定
  bool is_ack = 3;
  // 业务状态码
  bilibili.rpc.Status status = 4;
  // 业务ack来源, 仅downstream时候由服务端填写.
  string ack_origin = 5;
  //
  int64 timestamp = 6;
}

// 心跳请求
message HeartbeatReq{

}

// 心跳返回
message HeartbeatResp{

}

// 消息回执
message MessageAckReq {
  // 消息id
  int64 ack_id = 1;
  // ack来源，由业务指定用于埋点跟踪
  string ack_origin = 2;
  // 消息对应的target_path，方便业务区分和监控统计
  string target_path = 3;
}

// target_path
message TargetPath {
  // 需要订阅的target_paths
  repeated string target_paths = 1;
}
