Ai
1 Star 0 Fork 0

0x43/dubbo-go

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
header.go 7.64 KB
一键复制 编辑 原始数据 按行查看 历史
0x43 提交于 2024-12-28 00:42 +08:00 . first commit
// Copyright 2021-2023 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package triple_protocol
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"strings"
)
// EncodeBinaryHeader base64-encodes the data. It always emits unpadded values.
//
// In the Triple, gRPC, and gRPC-Web protocols, binary headers must have keys
// ending in "-Bin".
func EncodeBinaryHeader(data []byte) string {
// gRPC specification says that implementations should emit unpadded values.
return base64.RawStdEncoding.EncodeToString(data)
}
// DecodeBinaryHeader base64-decodes the data. It can decode padded or unpadded
// values. Following usual HTTP semantics, multiple base64-encoded values may
// be joined with a comma. When receiving such comma-separated values, split
// them with [strings.Split] before calling DecodeBinaryHeader.
//
// Binary headers sent using the Triple, gRPC, and gRPC-Web protocols have
// keys ending in "-Bin".
func DecodeBinaryHeader(data string) ([]byte, error) {
if len(data)%4 != 0 {
// Data definitely isn't padded.
return base64.RawStdEncoding.DecodeString(data)
}
// Either the data was padded, or padding wasn't necessary. In both cases,
// the padding-aware decoder works.
return base64.StdEncoding.DecodeString(data)
}
func mergeHeaders(into, from http.Header) {
for k, vals := range from {
into[k] = append(into[k], vals...)
}
}
// getCanonicalHeader is a shortcut for Header.Get() which
// bypasses the CanonicalMIMEHeaderKey operation when we
// know the key is already in canonical form.
func getHeaderCanonical(h http.Header, key string) string {
if h == nil {
return ""
}
v := h[key]
if len(v) == 0 {
return ""
}
return v[0]
}
// setHeaderCanonical is a shortcut for Header.Set() which
// bypasses the CanonicalMIMEHeaderKey operation when we
// know the key is already in canonical form.
func setHeaderCanonical(h http.Header, key, value string) {
h[key] = []string{value}
}
// delHeaderCanonical is a shortcut for Header.Del() which
// bypasses the CanonicalMIMEHeaderKey operation when we
// know the key is already in canonical form.
func delHeaderCanonical(h http.Header, key string) {
delete(h, key)
}
// addHeaderCanonical is a shortcut for Header.Add() which
// bypasses the CanonicalMIMEHeaderKey operation when we
// know the key is already in canonical form.
func addHeaderCanonical(h http.Header, key, value string) {
h[key] = append(h[key], value)
}
type extraDataKey struct{}
const headerIncomingKey string = "headerIncomingKey"
const headerOutgoingKey string = "headerOutgoingKey"
type handlerOutgoingKey struct{}
func newIncomingContext(ctx context.Context, data http.Header) context.Context {
var header = http.Header{}
extraData, ok := ctx.Value(extraDataKey{}).(map[string]http.Header)
if !ok {
extraData = map[string]http.Header{}
}
if data != nil {
for key, vals := range data {
header[strings.ToLower(key)] = vals
}
}
extraData[headerIncomingKey] = header
return context.WithValue(ctx, extraDataKey{}, extraData)
}
// NewOutgoingContext sets headers entirely. If there are existing headers, they would be replaced.
// It is used for passing headers to server-side.
// It is like grpc.NewOutgoingContext.
// Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#sending-metadata.
func NewOutgoingContext(ctx context.Context, data http.Header) context.Context {
var header = http.Header{}
if data != nil {
for key, vals := range data {
header[strings.ToLower(key)] = vals
}
}
extraData, ok := ctx.Value(extraDataKey{}).(map[string]http.Header)
if !ok {
extraData = map[string]http.Header{}
}
extraData[headerOutgoingKey] = header
return context.WithValue(ctx, extraDataKey{}, extraData)
}
// AppendToOutgoingContext merges kv pairs from user and existing headers.
// It is used for passing headers to server-side.
// It is like grpc.AppendToOutgoingContext.
// Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#sending-metadata.
func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
if len(kv)%2 == 1 {
panic(fmt.Sprintf("AppendToOutgoingContext got an odd number of input pairs for header: %d", len(kv)))
}
extraData, ok := ctx.Value(extraDataKey{}).(map[string]http.Header)
if !ok {
extraData = map[string]http.Header{}
ctx = context.WithValue(ctx, extraDataKey{}, extraData)
}
header, ok := extraData[headerOutgoingKey]
if !ok {
header = make(http.Header)
extraData[headerOutgoingKey] = header
}
for i := 0; i < len(kv); i += 2 {
// todo(DMwangnima): think about lowering
header.Add(strings.ToLower(kv[i]), kv[i+1])
}
return ctx
}
func ExtractFromOutgoingContext(ctx context.Context) http.Header {
extraData, ok := ctx.Value(extraDataKey{}).(map[string]http.Header)
if !ok {
return nil
}
if outGoingDataHeader, ok := extraData[headerOutgoingKey]; !ok {
return nil
} else {
return outGoingDataHeader
}
}
// FromIncomingContext retrieves headers passed by client-side. It is like grpc.FromIncomingContext.
// it must call after append/setOutgoingContext to return current value
// Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#receiving-metadata-1.
func FromIncomingContext(ctx context.Context) (http.Header, bool) {
data, ok := ctx.Value(extraDataKey{}).(map[string]http.Header)
if !ok {
return nil, false
} else if incomingDataHeader, ok := data[headerIncomingKey]; !ok {
return nil, false
} else {
return incomingDataHeader, true
}
}
// SetHeader is used for setting response header in server-side. It is like grpc.SendHeader(ctx, header) but
// not send header.
// Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#unary-call-2.
func SetHeader(ctx context.Context, header http.Header) error {
conn, ok := ctx.Value(handlerOutgoingKey{}).(StreamingHandlerConn)
if !ok {
// todo(DMwangnima): return standard error
return fmt.Errorf("triple: failed to fetch the connection from the context %v", ctx)
}
mergeHeaders(conn.ResponseHeader(), header)
return nil
}
// SetTrailer is used for setting response trailers in server-side. It is like grpc.SetTrailer(ctx, header).
// Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#unary-call-2.
func SetTrailer(ctx context.Context, trailer http.Header) error {
conn, ok := ctx.Value(handlerOutgoingKey{}).(StreamingHandlerConn)
if !ok {
// todo(DMwangnima): return standard error
return fmt.Errorf("triple: failed to fetch the connection from the context %v", ctx)
}
mergeHeaders(conn.ResponseTrailer(), trailer)
return nil
}
// SendHeader is used for setting response headers in server-side and send them directly. It is like grpc.SendHeader(ctx, header).
// Please refer to https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md#unary-call-2.
func SendHeader(ctx context.Context, header http.Header) error {
conn, ok := ctx.Value(handlerOutgoingKey{}).(StreamingHandlerConn)
if !ok {
// todo(DMwangnima): return standard error
return fmt.Errorf("triple: failed to fetch the connection from the context %v", ctx)
}
mergeHeaders(conn.RequestHeader(), header)
return conn.Send(nil)
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/git4chen/dubbo-go.git
git@gitee.com:git4chen/dubbo-go.git
git4chen
dubbo-go
dubbo-go
v1.0.0

搜索帮助