tracing: add the jaeger exporter
once this env variable is set, then the docs in https://docs.docker.com/build/building/opentelemetry/ will work fixes https://github.com/docker/buildx/issues/1847 Signed-off-by: Nick Santos <nick.santos@docker.com>pull/1848/head
parent
63fc01e08a
commit
4b1530e1ea
@ -0,0 +1,59 @@
|
|||||||
|
package jaeger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/moby/buildkit/util/tracing/detect"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
detect.Register("jaeger", jaegerExporter, 11)
|
||||||
|
}
|
||||||
|
|
||||||
|
func jaegerExporter() (sdktrace.SpanExporter, error) {
|
||||||
|
set := os.Getenv("OTEL_TRACES_EXPORTER") == "jaeger" || os.Getenv("JAEGER_TRACE") != "" || os.Getenv("OTEL_EXPORTER_JAEGER_AGENT_HOST") != "" || os.Getenv("OTEL_EXPORTER_JAEGER_ENDPOINT") != ""
|
||||||
|
if !set {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint := envOr("OTEL_EXPORTER_JAEGER_ENDPOINT", "http://localhost:14250")
|
||||||
|
host := envOr("OTEL_EXPORTER_JAEGER_HOST", "localhost")
|
||||||
|
port := envOr("OTEL_EXPORTER_JAEGER_PORT", "6831")
|
||||||
|
var isEndpoint bool
|
||||||
|
|
||||||
|
// JAEGER_TRACE is not env defined by opentelemetry spec but buildkit backward compatibility
|
||||||
|
if v := os.Getenv("JAEGER_TRACE"); v != "" {
|
||||||
|
if strings.HasPrefix(v, "http://") || strings.HasPrefix(v, "https://") {
|
||||||
|
isEndpoint = true
|
||||||
|
endpoint = v
|
||||||
|
} else {
|
||||||
|
h, p, err := net.SplitHostPort(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
host = h
|
||||||
|
port = p
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isEndpoint = os.Getenv("OTEL_EXPORTER_JAEGER_ENDPOINT") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
epo := jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(endpoint))
|
||||||
|
|
||||||
|
if !isEndpoint {
|
||||||
|
epo = jaeger.WithAgentEndpoint(jaeger.WithAgentHost(host), jaeger.WithAgentPort(port))
|
||||||
|
}
|
||||||
|
|
||||||
|
return jaeger.New(epo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func envOr(key, defaultValue string) string {
|
||||||
|
if v, ok := os.LookupEnv(key); ok && v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
@ -0,0 +1,50 @@
|
|||||||
|
# OpenTelemetry-Go Jaeger Exporter
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger)
|
||||||
|
|
||||||
|
[OpenTelemetry span exporter for Jaeger](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md) implementation.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u go.opentelemetry.io/otel/exporters/jaeger
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
See [../../example/jaeger](../../example/jaeger).
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The exporter can be used to send spans to:
|
||||||
|
|
||||||
|
- Jaeger agent using `jaeger.thrift` over compact thrift protocol via
|
||||||
|
[`WithAgentEndpoint`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithAgentEndpoint) option.
|
||||||
|
- Jaeger collector using `jaeger.thrift` over HTTP via
|
||||||
|
[`WithCollectorEndpoint`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithCollectorEndpoint) option.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
|
||||||
|
The following environment variables can be used
|
||||||
|
(instead of options objects) to override the default configuration.
|
||||||
|
|
||||||
|
| Environment variable | Option | Default value |
|
||||||
|
| --------------------------------- | --------------------------------------------------------------------------------------------- | ----------------------------------- |
|
||||||
|
| `OTEL_EXPORTER_JAEGER_AGENT_HOST` | [`WithAgentHost`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithAgentHost) | `localhost` |
|
||||||
|
| `OTEL_EXPORTER_JAEGER_AGENT_PORT` | [`WithAgentPort`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithAgentPort) | `6831` |
|
||||||
|
| `OTEL_EXPORTER_JAEGER_ENDPOINT` | [`WithEndpoint`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithEndpoint) | `http://localhost:14268/api/traces` |
|
||||||
|
| `OTEL_EXPORTER_JAEGER_USER` | [`WithUsername`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithUsername) | |
|
||||||
|
| `OTEL_EXPORTER_JAEGER_PASSWORD` | [`WithPassword`](https://pkg.go.dev/go.opentelemetry.io/otel/exporters/jaeger#WithPassword) | |
|
||||||
|
|
||||||
|
Configuration using options have precedence over the environment variables.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
This exporter uses a vendored copy of the Apache Thrift library (v0.14.1) at a custom import path.
|
||||||
|
When re-generating Thrift code in the future, please adapt import paths as necessary.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [Jaeger](https://www.jaegertracing.io/)
|
||||||
|
- [OpenTelemetry to Jaeger Transformation](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk_exporters/jaeger.md)
|
||||||
|
- [OpenTelemetry Environment Variable Specification](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/sdk-environment-variables.md)
|
@ -0,0 +1,213 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
|
||||||
|
genAgent "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent"
|
||||||
|
gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// udpPacketMaxLength is the max size of UDP packet we want to send, synced with jaeger-agent.
|
||||||
|
udpPacketMaxLength = 65000
|
||||||
|
// emitBatchOverhead is the additional overhead bytes used for enveloping the datagram,
|
||||||
|
// synced with jaeger-agent https://github.com/jaegertracing/jaeger-client-go/blob/master/transport_udp.go#L37
|
||||||
|
emitBatchOverhead = 70
|
||||||
|
)
|
||||||
|
|
||||||
|
// agentClientUDP is a UDP client to Jaeger agent that implements gen.Agent interface.
|
||||||
|
type agentClientUDP struct {
|
||||||
|
genAgent.Agent
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
connUDP udpConn
|
||||||
|
client *genAgent.AgentClient
|
||||||
|
maxPacketSize int // max size of datagram in bytes
|
||||||
|
thriftBuffer *thrift.TMemoryBuffer // buffer used to calculate byte size of a span
|
||||||
|
thriftProtocol thrift.TProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
type udpConn interface {
|
||||||
|
Write([]byte) (int, error)
|
||||||
|
SetWriteBuffer(int) error
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type agentClientUDPParams struct {
|
||||||
|
Host string
|
||||||
|
Port string
|
||||||
|
MaxPacketSize int
|
||||||
|
Logger logr.Logger
|
||||||
|
AttemptReconnecting bool
|
||||||
|
AttemptReconnectInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAgentClientUDP creates a client that sends spans to Jaeger Agent over UDP.
|
||||||
|
func newAgentClientUDP(params agentClientUDPParams) (*agentClientUDP, error) {
|
||||||
|
hostPort := net.JoinHostPort(params.Host, params.Port)
|
||||||
|
// validate hostport
|
||||||
|
if _, _, err := net.SplitHostPort(hostPort); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.MaxPacketSize <= 0 || params.MaxPacketSize > udpPacketMaxLength {
|
||||||
|
params.MaxPacketSize = udpPacketMaxLength
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.AttemptReconnecting && params.AttemptReconnectInterval <= 0 {
|
||||||
|
params.AttemptReconnectInterval = time.Second * 30
|
||||||
|
}
|
||||||
|
|
||||||
|
thriftBuffer := thrift.NewTMemoryBufferLen(params.MaxPacketSize)
|
||||||
|
protocolFactory := thrift.NewTCompactProtocolFactoryConf(&thrift.TConfiguration{})
|
||||||
|
thriftProtocol := protocolFactory.GetProtocol(thriftBuffer)
|
||||||
|
client := genAgent.NewAgentClientFactory(thriftBuffer, protocolFactory)
|
||||||
|
|
||||||
|
var connUDP udpConn
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if params.AttemptReconnecting {
|
||||||
|
// host is hostname, setup resolver loop in case host record changes during operation
|
||||||
|
connUDP, err = newReconnectingUDPConn(hostPort, params.MaxPacketSize, params.AttemptReconnectInterval, net.ResolveUDPAddr, net.DialUDP, params.Logger)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
destAddr, err := net.ResolveUDPAddr("udp", hostPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
connUDP, err = net.DialUDP(destAddr.Network(), nil, destAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := connUDP.SetWriteBuffer(params.MaxPacketSize); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &agentClientUDP{
|
||||||
|
connUDP: connUDP,
|
||||||
|
client: client,
|
||||||
|
maxPacketSize: params.MaxPacketSize,
|
||||||
|
thriftBuffer: thriftBuffer,
|
||||||
|
thriftProtocol: thriftProtocol,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EmitBatch buffers batch to fit into UDP packets and sends the data to the agent.
|
||||||
|
func (a *agentClientUDP) EmitBatch(ctx context.Context, batch *gen.Batch) error {
|
||||||
|
var errs []error
|
||||||
|
processSize, err := a.calcSizeOfSerializedThrift(ctx, batch.Process)
|
||||||
|
if err != nil {
|
||||||
|
// drop the batch if serialization of process fails.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
maxPacketSize := a.maxPacketSize
|
||||||
|
if maxPacketSize > udpPacketMaxLength-emitBatchOverhead {
|
||||||
|
maxPacketSize = udpPacketMaxLength - emitBatchOverhead
|
||||||
|
}
|
||||||
|
totalSize := processSize
|
||||||
|
var spans []*gen.Span
|
||||||
|
for _, span := range batch.Spans {
|
||||||
|
spanSize, err := a.calcSizeOfSerializedThrift(ctx, span)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, fmt.Errorf("thrift serialization failed: %v", span))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if spanSize+processSize >= maxPacketSize {
|
||||||
|
// drop the span that exceeds the limit.
|
||||||
|
errs = append(errs, fmt.Errorf("span too large to send: %v", span))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if totalSize+spanSize >= maxPacketSize {
|
||||||
|
if err := a.flush(ctx, &gen.Batch{
|
||||||
|
Process: batch.Process,
|
||||||
|
Spans: spans,
|
||||||
|
}); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
spans = spans[:0]
|
||||||
|
totalSize = processSize
|
||||||
|
}
|
||||||
|
totalSize += spanSize
|
||||||
|
spans = append(spans, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(spans) > 0 {
|
||||||
|
if err := a.flush(ctx, &gen.Batch{
|
||||||
|
Process: batch.Process,
|
||||||
|
Spans: spans,
|
||||||
|
}); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) == 1 {
|
||||||
|
return errs[0]
|
||||||
|
} else if len(errs) > 1 {
|
||||||
|
joined := a.makeJoinedErrorString(errs)
|
||||||
|
return fmt.Errorf("multiple errors during transform: %s", joined)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeJoinedErrorString join all the errors to one error message.
|
||||||
|
func (a *agentClientUDP) makeJoinedErrorString(errs []error) string {
|
||||||
|
var errMsgs []string
|
||||||
|
for _, err := range errs {
|
||||||
|
errMsgs = append(errMsgs, err.Error())
|
||||||
|
}
|
||||||
|
return strings.Join(errMsgs, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush will send the batch of spans to the agent.
|
||||||
|
func (a *agentClientUDP) flush(ctx context.Context, batch *gen.Batch) error {
|
||||||
|
a.thriftBuffer.Reset()
|
||||||
|
if err := a.client.EmitBatch(ctx, batch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if a.thriftBuffer.Len() > a.maxPacketSize {
|
||||||
|
return fmt.Errorf("data does not fit within one UDP packet; size %d, max %d, spans %d",
|
||||||
|
a.thriftBuffer.Len(), a.maxPacketSize, len(batch.Spans))
|
||||||
|
}
|
||||||
|
_, err := a.connUDP.Write(a.thriftBuffer.Bytes())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// calcSizeOfSerializedThrift calculate the serialized thrift packet size.
|
||||||
|
func (a *agentClientUDP) calcSizeOfSerializedThrift(ctx context.Context, thriftStruct thrift.TStruct) (int, error) {
|
||||||
|
a.thriftBuffer.Reset()
|
||||||
|
err := thriftStruct.Write(ctx, a.thriftProtocol)
|
||||||
|
return a.thriftBuffer.Len(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements Close() of io.Closer and closes the underlying UDP connection.
|
||||||
|
func (a *agentClientUDP) Close() error {
|
||||||
|
return a.connUDP.Close()
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// 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 jaeger contains an OpenTelemetry tracing exporter for Jaeger.
|
||||||
|
package jaeger // import "go.opentelemetry.io/otel/exporters/jaeger"
|
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Environment variable names.
|
||||||
|
const (
|
||||||
|
// Hostname for the Jaeger agent, part of address where exporter sends spans
|
||||||
|
// i.e. "localhost".
|
||||||
|
envAgentHost = "OTEL_EXPORTER_JAEGER_AGENT_HOST"
|
||||||
|
// Port for the Jaeger agent, part of address where exporter sends spans
|
||||||
|
// i.e. 6831.
|
||||||
|
envAgentPort = "OTEL_EXPORTER_JAEGER_AGENT_PORT"
|
||||||
|
// The HTTP endpoint for sending spans directly to a collector,
|
||||||
|
// i.e. http://jaeger-collector:14268/api/traces.
|
||||||
|
envEndpoint = "OTEL_EXPORTER_JAEGER_ENDPOINT"
|
||||||
|
// Username to send as part of "Basic" authentication to the collector endpoint.
|
||||||
|
envUser = "OTEL_EXPORTER_JAEGER_USER"
|
||||||
|
// Password to send as part of "Basic" authentication to the collector endpoint.
|
||||||
|
envPassword = "OTEL_EXPORTER_JAEGER_PASSWORD"
|
||||||
|
)
|
||||||
|
|
||||||
|
// envOr returns an env variable's value if it is exists or the default if not.
|
||||||
|
func envOr(key, defaultValue string) string {
|
||||||
|
if v, ok := os.LookupEnv(key); ok && v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return defaultValue
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
var GoUnusedProtection__ int;
|
||||||
|
|
27
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent/agent-consts.go
generated
vendored
27
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent/agent-consts.go
generated
vendored
@ -0,0 +1,27 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift"
|
||||||
|
)
|
||||||
|
|
||||||
|
// (needed to ensure safety because of naive import list construction.)
|
||||||
|
var _ = thrift.ZERO
|
||||||
|
var _ = fmt.Printf
|
||||||
|
var _ = context.Background
|
||||||
|
var _ = time.Now
|
||||||
|
var _ = bytes.Equal
|
||||||
|
|
||||||
|
var _ = jaeger.GoUnusedProtection__
|
||||||
|
var _ = zipkincore.GoUnusedProtection__
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
}
|
412
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent/agent.go
generated
vendored
412
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/agent/agent.go
generated
vendored
@ -0,0 +1,412 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift"
|
||||||
|
)
|
||||||
|
|
||||||
|
// (needed to ensure safety because of naive import list construction.)
|
||||||
|
var _ = thrift.ZERO
|
||||||
|
var _ = fmt.Printf
|
||||||
|
var _ = context.Background
|
||||||
|
var _ = time.Now
|
||||||
|
var _ = bytes.Equal
|
||||||
|
|
||||||
|
var _ = jaeger.GoUnusedProtection__
|
||||||
|
var _ = zipkincore.GoUnusedProtection__
|
||||||
|
|
||||||
|
type Agent interface {
|
||||||
|
// Parameters:
|
||||||
|
// - Spans
|
||||||
|
EmitZipkinBatch(ctx context.Context, spans []*zipkincore.Span) (_err error)
|
||||||
|
// Parameters:
|
||||||
|
// - Batch
|
||||||
|
EmitBatch(ctx context.Context, batch *jaeger.Batch) (_err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentClient struct {
|
||||||
|
c thrift.TClient
|
||||||
|
meta thrift.ResponseMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient {
|
||||||
|
return &AgentClient{
|
||||||
|
c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient {
|
||||||
|
return &AgentClient{
|
||||||
|
c: thrift.NewTStandardClient(iprot, oprot),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentClient(c thrift.TClient) *AgentClient {
|
||||||
|
return &AgentClient{
|
||||||
|
c: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentClient) Client_() thrift.TClient {
|
||||||
|
return p.c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentClient) LastResponseMeta_() thrift.ResponseMeta {
|
||||||
|
return p.meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentClient) SetLastResponseMeta_(meta thrift.ResponseMeta) {
|
||||||
|
p.meta = meta
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters:
|
||||||
|
// - Spans
|
||||||
|
func (p *AgentClient) EmitZipkinBatch(ctx context.Context, spans []*zipkincore.Span) (_err error) {
|
||||||
|
var _args0 AgentEmitZipkinBatchArgs
|
||||||
|
_args0.Spans = spans
|
||||||
|
p.SetLastResponseMeta_(thrift.ResponseMeta{})
|
||||||
|
if _, err := p.Client_().Call(ctx, "emitZipkinBatch", &_args0, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parameters:
|
||||||
|
// - Batch
|
||||||
|
func (p *AgentClient) EmitBatch(ctx context.Context, batch *jaeger.Batch) (_err error) {
|
||||||
|
var _args1 AgentEmitBatchArgs
|
||||||
|
_args1.Batch = batch
|
||||||
|
p.SetLastResponseMeta_(thrift.ResponseMeta{})
|
||||||
|
if _, err := p.Client_().Call(ctx, "emitBatch", &_args1, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AgentProcessor struct {
|
||||||
|
processorMap map[string]thrift.TProcessorFunction
|
||||||
|
handler Agent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) {
|
||||||
|
p.processorMap[key] = processor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) {
|
||||||
|
processor, ok = p.processorMap[key]
|
||||||
|
return processor, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction {
|
||||||
|
return p.processorMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentProcessor(handler Agent) *AgentProcessor {
|
||||||
|
|
||||||
|
self2 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)}
|
||||||
|
self2.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler: handler}
|
||||||
|
self2.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler}
|
||||||
|
return self2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
|
||||||
|
name, _, seqId, err2 := iprot.ReadMessageBegin(ctx)
|
||||||
|
if err2 != nil {
|
||||||
|
return false, thrift.WrapTException(err2)
|
||||||
|
}
|
||||||
|
if processor, ok := p.GetProcessorFunction(name); ok {
|
||||||
|
return processor.Process(ctx, seqId, iprot, oprot)
|
||||||
|
}
|
||||||
|
iprot.Skip(ctx, thrift.STRUCT)
|
||||||
|
iprot.ReadMessageEnd(ctx)
|
||||||
|
x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name)
|
||||||
|
oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId)
|
||||||
|
x3.Write(ctx, oprot)
|
||||||
|
oprot.WriteMessageEnd(ctx)
|
||||||
|
oprot.Flush(ctx)
|
||||||
|
return false, x3
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type agentProcessorEmitZipkinBatch struct {
|
||||||
|
handler Agent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *agentProcessorEmitZipkinBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
|
||||||
|
args := AgentEmitZipkinBatchArgs{}
|
||||||
|
var err2 error
|
||||||
|
if err2 = args.Read(ctx, iprot); err2 != nil {
|
||||||
|
iprot.ReadMessageEnd(ctx)
|
||||||
|
return false, thrift.WrapTException(err2)
|
||||||
|
}
|
||||||
|
iprot.ReadMessageEnd(ctx)
|
||||||
|
|
||||||
|
tickerCancel := func() {}
|
||||||
|
_ = tickerCancel
|
||||||
|
|
||||||
|
if err2 = p.handler.EmitZipkinBatch(ctx, args.Spans); err2 != nil {
|
||||||
|
tickerCancel()
|
||||||
|
return true, thrift.WrapTException(err2)
|
||||||
|
}
|
||||||
|
tickerCancel()
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type agentProcessorEmitBatch struct {
|
||||||
|
handler Agent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *agentProcessorEmitBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) {
|
||||||
|
args := AgentEmitBatchArgs{}
|
||||||
|
var err2 error
|
||||||
|
if err2 = args.Read(ctx, iprot); err2 != nil {
|
||||||
|
iprot.ReadMessageEnd(ctx)
|
||||||
|
return false, thrift.WrapTException(err2)
|
||||||
|
}
|
||||||
|
iprot.ReadMessageEnd(ctx)
|
||||||
|
|
||||||
|
tickerCancel := func() {}
|
||||||
|
_ = tickerCancel
|
||||||
|
|
||||||
|
if err2 = p.handler.EmitBatch(ctx, args.Batch); err2 != nil {
|
||||||
|
tickerCancel()
|
||||||
|
return true, thrift.WrapTException(err2)
|
||||||
|
}
|
||||||
|
tickerCancel()
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HELPER FUNCTIONS AND STRUCTURES
|
||||||
|
|
||||||
|
// Attributes:
|
||||||
|
// - Spans
|
||||||
|
type AgentEmitZipkinBatchArgs struct {
|
||||||
|
Spans []*zipkincore.Span `thrift:"spans,1" db:"spans" json:"spans"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentEmitZipkinBatchArgs() *AgentEmitZipkinBatchArgs {
|
||||||
|
return &AgentEmitZipkinBatchArgs{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitZipkinBatchArgs) GetSpans() []*zipkincore.Span {
|
||||||
|
return p.Spans
|
||||||
|
}
|
||||||
|
func (p *AgentEmitZipkinBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error {
|
||||||
|
if _, err := iprot.ReadStructBegin(ctx); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
|
||||||
|
}
|
||||||
|
if fieldTypeId == thrift.STOP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch fieldId {
|
||||||
|
case 1:
|
||||||
|
if fieldTypeId == thrift.LIST {
|
||||||
|
if err := p.ReadField1(ctx, iprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := iprot.Skip(ctx, fieldTypeId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if err := iprot.Skip(ctx, fieldTypeId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := iprot.ReadFieldEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := iprot.ReadStructEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitZipkinBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
|
||||||
|
_, size, err := iprot.ReadListBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return thrift.PrependError("error reading list begin: ", err)
|
||||||
|
}
|
||||||
|
tSlice := make([]*zipkincore.Span, 0, size)
|
||||||
|
p.Spans = tSlice
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
_elem4 := &zipkincore.Span{}
|
||||||
|
if err := _elem4.Read(ctx, iprot); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err)
|
||||||
|
}
|
||||||
|
p.Spans = append(p.Spans, _elem4)
|
||||||
|
}
|
||||||
|
if err := iprot.ReadListEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError("error reading list end: ", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitZipkinBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error {
|
||||||
|
if err := oprot.WriteStructBegin(ctx, "emitZipkinBatch_args"); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
|
||||||
|
}
|
||||||
|
if p != nil {
|
||||||
|
if err := p.writeField1(ctx, oprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := oprot.WriteFieldStop(ctx); err != nil {
|
||||||
|
return thrift.PrependError("write field stop error: ", err)
|
||||||
|
}
|
||||||
|
if err := oprot.WriteStructEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError("write struct stop error: ", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitZipkinBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
|
||||||
|
if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 1); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err)
|
||||||
|
}
|
||||||
|
if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil {
|
||||||
|
return thrift.PrependError("error writing list begin: ", err)
|
||||||
|
}
|
||||||
|
for _, v := range p.Spans {
|
||||||
|
if err := v.Write(ctx, oprot); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := oprot.WriteListEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError("error writing list end: ", err)
|
||||||
|
}
|
||||||
|
if err := oprot.WriteFieldEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitZipkinBatchArgs) String() string {
|
||||||
|
if p == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributes:
|
||||||
|
// - Batch
|
||||||
|
type AgentEmitBatchArgs struct {
|
||||||
|
Batch *jaeger.Batch `thrift:"batch,1" db:"batch" json:"batch"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAgentEmitBatchArgs() *AgentEmitBatchArgs {
|
||||||
|
return &AgentEmitBatchArgs{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var AgentEmitBatchArgs_Batch_DEFAULT *jaeger.Batch
|
||||||
|
|
||||||
|
func (p *AgentEmitBatchArgs) GetBatch() *jaeger.Batch {
|
||||||
|
if !p.IsSetBatch() {
|
||||||
|
return AgentEmitBatchArgs_Batch_DEFAULT
|
||||||
|
}
|
||||||
|
return p.Batch
|
||||||
|
}
|
||||||
|
func (p *AgentEmitBatchArgs) IsSetBatch() bool {
|
||||||
|
return p.Batch != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error {
|
||||||
|
if _, err := iprot.ReadStructBegin(ctx); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err)
|
||||||
|
}
|
||||||
|
if fieldTypeId == thrift.STOP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch fieldId {
|
||||||
|
case 1:
|
||||||
|
if fieldTypeId == thrift.STRUCT {
|
||||||
|
if err := p.ReadField1(ctx, iprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := iprot.Skip(ctx, fieldTypeId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if err := iprot.Skip(ctx, fieldTypeId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := iprot.ReadFieldEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := iprot.ReadStructEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error {
|
||||||
|
p.Batch = &jaeger.Batch{}
|
||||||
|
if err := p.Batch.Read(ctx, iprot); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error {
|
||||||
|
if err := oprot.WriteStructBegin(ctx, "emitBatch_args"); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err)
|
||||||
|
}
|
||||||
|
if p != nil {
|
||||||
|
if err := p.writeField1(ctx, oprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := oprot.WriteFieldStop(ctx); err != nil {
|
||||||
|
return thrift.PrependError("write field stop error: ", err)
|
||||||
|
}
|
||||||
|
if err := oprot.WriteStructEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError("write struct stop error: ", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) {
|
||||||
|
if err := oprot.WriteFieldBegin(ctx, "batch", thrift.STRUCT, 1); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err)
|
||||||
|
}
|
||||||
|
if err := p.Batch.Write(ctx, oprot); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err)
|
||||||
|
}
|
||||||
|
if err := oprot.WriteFieldEnd(ctx); err != nil {
|
||||||
|
return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *AgentEmitBatchArgs) String() string {
|
||||||
|
if p == nil {
|
||||||
|
return "<nil>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p)
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package jaeger
|
||||||
|
|
||||||
|
var GoUnusedProtection__ int;
|
||||||
|
|
22
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger/jaeger-consts.go
generated
vendored
22
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger/jaeger-consts.go
generated
vendored
@ -0,0 +1,22 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package jaeger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift"
|
||||||
|
)
|
||||||
|
|
||||||
|
// (needed to ensure safety because of naive import list construction.)
|
||||||
|
var _ = thrift.ZERO
|
||||||
|
var _ = fmt.Printf
|
||||||
|
var _ = context.Background
|
||||||
|
var _ = time.Now
|
||||||
|
var _ = bytes.Equal
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
}
|
3022
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger/jaeger.go
generated
vendored
3022
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger/jaeger.go
generated
vendored
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package zipkincore
|
||||||
|
|
||||||
|
var GoUnusedProtection__ int;
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT.
|
||||||
|
|
||||||
|
package zipkincore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift"
|
||||||
|
)
|
||||||
|
|
||||||
|
// (needed to ensure safety because of naive import list construction.)
|
||||||
|
var _ = thrift.ZERO
|
||||||
|
var _ = fmt.Printf
|
||||||
|
var _ = context.Background
|
||||||
|
var _ = time.Now
|
||||||
|
var _ = bytes.Equal
|
||||||
|
|
||||||
|
const CLIENT_SEND = "cs"
|
||||||
|
const CLIENT_RECV = "cr"
|
||||||
|
const SERVER_SEND = "ss"
|
||||||
|
const SERVER_RECV = "sr"
|
||||||
|
const MESSAGE_SEND = "ms"
|
||||||
|
const MESSAGE_RECV = "mr"
|
||||||
|
const WIRE_SEND = "ws"
|
||||||
|
const WIRE_RECV = "wr"
|
||||||
|
const CLIENT_SEND_FRAGMENT = "csf"
|
||||||
|
const CLIENT_RECV_FRAGMENT = "crf"
|
||||||
|
const SERVER_SEND_FRAGMENT = "ssf"
|
||||||
|
const SERVER_RECV_FRAGMENT = "srf"
|
||||||
|
const LOCAL_COMPONENT = "lc"
|
||||||
|
const CLIENT_ADDR = "ca"
|
||||||
|
const SERVER_ADDR = "sa"
|
||||||
|
const MESSAGE_ADDR = "ma"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
}
|
2067
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore/zipkincore.go
generated
vendored
2067
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/zipkincore/zipkincore.go
generated
vendored
File diff suppressed because it is too large
Load Diff
306
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/LICENSE
generated
vendored
306
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/LICENSE
generated
vendored
@ -0,0 +1,306 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
SOFTWARE DISTRIBUTED WITH THRIFT:
|
||||||
|
|
||||||
|
The Apache Thrift software includes a number of subcomponents with
|
||||||
|
separate copyright notices and license terms. Your use of the source
|
||||||
|
code for the these subcomponents is subject to the terms and
|
||||||
|
conditions of the following licenses.
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
Portions of the following files are licensed under the MIT License:
|
||||||
|
|
||||||
|
lib/erl/src/Makefile.am
|
||||||
|
|
||||||
|
Please see doc/otp-base-license.txt for the full terms of this license.
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
For the aclocal/ax_boost_base.m4 and contrib/fb303/aclocal/ax_boost_base.m4 components:
|
||||||
|
|
||||||
|
# Copyright (c) 2007 Thomas Porschberg <thomas@randspringer.de>
|
||||||
|
#
|
||||||
|
# Copying and distribution of this file, with or without
|
||||||
|
# modification, are permitted in any medium without royalty provided
|
||||||
|
# the copyright notice and this notice are preserved.
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
For the lib/nodejs/lib/thrift/json_parse.js:
|
||||||
|
|
||||||
|
/*
|
||||||
|
json_parse.js
|
||||||
|
2015-05-02
|
||||||
|
Public Domain.
|
||||||
|
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||||||
|
|
||||||
|
*/
|
||||||
|
(By Douglas Crockford <douglas@crockford.com>)
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
For lib/cpp/src/thrift/windows/SocketPair.cpp
|
||||||
|
|
||||||
|
/* socketpair.c
|
||||||
|
* Copyright 2007 by Nathan C. Myers <ncm@cantrip.org>; some rights reserved.
|
||||||
|
* This code is Free Software. It may be copied freely, in original or
|
||||||
|
* modified form, subject only to the restrictions that (1) the author is
|
||||||
|
* relieved from all responsibilities for any use for any purpose, and (2)
|
||||||
|
* this copyright notice must be retained, unchanged, in its entirety. If
|
||||||
|
* for any reason the author might be held responsible for any consequences
|
||||||
|
* of copying or use, license is withheld.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
For lib/py/compat/win32/stdint.h
|
||||||
|
|
||||||
|
// ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||||
|
// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||||
|
//
|
||||||
|
// Copyright (c) 2006-2008 Alexander Chemeris
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// 1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
// this list of conditions and the following disclaimer.
|
||||||
|
//
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// 3. The name of the author may be used to endorse or promote products
|
||||||
|
// derived from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||||
|
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||||
|
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||||
|
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||||
|
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||||
|
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
//
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
--------------------------------------------------
|
||||||
|
Codegen template in t_html_generator.h
|
||||||
|
|
||||||
|
* Bootstrap v2.0.3
|
||||||
|
*
|
||||||
|
* Copyright 2012 Twitter, Inc
|
||||||
|
* Licensed under the Apache License v2.0
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||||
|
|
||||||
|
---------------------------------------------------
|
||||||
|
For t_cl_generator.cc
|
||||||
|
|
||||||
|
* Copyright (c) 2008- Patrick Collison <patrick@collison.ie>
|
||||||
|
* Copyright (c) 2006- Facebook
|
||||||
|
|
||||||
|
---------------------------------------------------
|
5
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/NOTICE
generated
vendored
5
vendor/go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/NOTICE
generated
vendored
@ -0,0 +1,5 @@
|
|||||||
|
Apache Thrift
|
||||||
|
Copyright (C) 2006 - 2019, The Apache Software Foundation
|
||||||
|
|
||||||
|
This product includes software developed at
|
||||||
|
The Apache Software Foundation (http://www.apache.org/).
|
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UNKNOWN_APPLICATION_EXCEPTION = 0
|
||||||
|
UNKNOWN_METHOD = 1
|
||||||
|
INVALID_MESSAGE_TYPE_EXCEPTION = 2
|
||||||
|
WRONG_METHOD_NAME = 3
|
||||||
|
BAD_SEQUENCE_ID = 4
|
||||||
|
MISSING_RESULT = 5
|
||||||
|
INTERNAL_ERROR = 6
|
||||||
|
PROTOCOL_ERROR = 7
|
||||||
|
INVALID_TRANSFORM = 8
|
||||||
|
INVALID_PROTOCOL = 9
|
||||||
|
UNSUPPORTED_CLIENT_TYPE = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultApplicationExceptionMessage = map[int32]string{
|
||||||
|
UNKNOWN_APPLICATION_EXCEPTION: "unknown application exception",
|
||||||
|
UNKNOWN_METHOD: "unknown method",
|
||||||
|
INVALID_MESSAGE_TYPE_EXCEPTION: "invalid message type",
|
||||||
|
WRONG_METHOD_NAME: "wrong method name",
|
||||||
|
BAD_SEQUENCE_ID: "bad sequence ID",
|
||||||
|
MISSING_RESULT: "missing result",
|
||||||
|
INTERNAL_ERROR: "unknown internal error",
|
||||||
|
PROTOCOL_ERROR: "unknown protocol error",
|
||||||
|
INVALID_TRANSFORM: "Invalid transform",
|
||||||
|
INVALID_PROTOCOL: "Invalid protocol",
|
||||||
|
UNSUPPORTED_CLIENT_TYPE: "Unsupported client type",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application level Thrift exception
|
||||||
|
type TApplicationException interface {
|
||||||
|
TException
|
||||||
|
TypeId() int32
|
||||||
|
Read(ctx context.Context, iprot TProtocol) error
|
||||||
|
Write(ctx context.Context, oprot TProtocol) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type tApplicationException struct {
|
||||||
|
message string
|
||||||
|
type_ int32
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TApplicationException = (*tApplicationException)(nil)
|
||||||
|
|
||||||
|
func (tApplicationException) TExceptionType() TExceptionType {
|
||||||
|
return TExceptionTypeApplication
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e tApplicationException) Error() string {
|
||||||
|
if e.message != "" {
|
||||||
|
return e.message
|
||||||
|
}
|
||||||
|
return defaultApplicationExceptionMessage[e.type_]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTApplicationException(type_ int32, message string) TApplicationException {
|
||||||
|
return &tApplicationException{message, type_}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tApplicationException) TypeId() int32 {
|
||||||
|
return p.type_
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tApplicationException) Read(ctx context.Context, iprot TProtocol) error {
|
||||||
|
// TODO: this should really be generated by the compiler
|
||||||
|
_, err := iprot.ReadStructBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
message := ""
|
||||||
|
type_ := int32(UNKNOWN_APPLICATION_EXCEPTION)
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, ttype, id, err := iprot.ReadFieldBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if ttype == STOP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch id {
|
||||||
|
case 1:
|
||||||
|
if ttype == STRING {
|
||||||
|
if message, err = iprot.ReadString(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if ttype == I32 {
|
||||||
|
if type_, err = iprot.ReadI32(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = iprot.ReadFieldEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := iprot.ReadStructEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.message = message
|
||||||
|
p.type_ = type_
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tApplicationException) Write(ctx context.Context, oprot TProtocol) (err error) {
|
||||||
|
err = oprot.WriteStructBegin(ctx, "TApplicationException")
|
||||||
|
if len(p.Error()) > 0 {
|
||||||
|
err = oprot.WriteFieldBegin(ctx, "message", STRING, 1)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = oprot.WriteString(ctx, p.Error())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = oprot.WriteFieldEnd(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = oprot.WriteFieldBegin(ctx, "type", I32, 2)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = oprot.WriteI32(ctx, p.type_)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = oprot.WriteFieldEnd(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = oprot.WriteFieldStop(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = oprot.WriteStructEnd(ctx)
|
||||||
|
return
|
||||||
|
}
|
@ -0,0 +1,555 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TBinaryProtocol struct {
|
||||||
|
trans TRichTransport
|
||||||
|
origTransport TTransport
|
||||||
|
cfg *TConfiguration
|
||||||
|
buffer [64]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type TBinaryProtocolFactory struct {
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTBinaryProtocolConf instead.
|
||||||
|
func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol {
|
||||||
|
return NewTBinaryProtocolConf(t, &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTBinaryProtocolConf instead.
|
||||||
|
func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol {
|
||||||
|
return NewTBinaryProtocolConf(t, &TConfiguration{
|
||||||
|
TBinaryStrictRead: &strictRead,
|
||||||
|
TBinaryStrictWrite: &strictWrite,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTBinaryProtocolConf(t TTransport, conf *TConfiguration) *TBinaryProtocol {
|
||||||
|
PropagateTConfiguration(t, conf)
|
||||||
|
p := &TBinaryProtocol{
|
||||||
|
origTransport: t,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
if et, ok := t.(TRichTransport); ok {
|
||||||
|
p.trans = et
|
||||||
|
} else {
|
||||||
|
p.trans = NewTRichTransport(t)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTBinaryProtocolFactoryConf instead.
|
||||||
|
func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory {
|
||||||
|
return NewTBinaryProtocolFactoryConf(&TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTBinaryProtocolFactoryConf instead.
|
||||||
|
func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory {
|
||||||
|
return NewTBinaryProtocolFactoryConf(&TConfiguration{
|
||||||
|
TBinaryStrictRead: &strictRead,
|
||||||
|
TBinaryStrictWrite: &strictWrite,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTBinaryProtocolFactoryConf(conf *TConfiguration) *TBinaryProtocolFactory {
|
||||||
|
return &TBinaryProtocolFactory{
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol {
|
||||||
|
return NewTBinaryProtocolConf(t, p.cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocolFactory) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
p.cfg = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writing Methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error {
|
||||||
|
if p.cfg.GetTBinaryStrictWrite() {
|
||||||
|
version := uint32(VERSION_1) | uint32(typeId)
|
||||||
|
e := p.WriteI32(ctx, int32(version))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteString(ctx, name)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteI32(ctx, seqId)
|
||||||
|
return e
|
||||||
|
} else {
|
||||||
|
e := p.WriteString(ctx, name)
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteByte(ctx, int8(typeId))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteI32(ctx, seqId)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteMessageEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteStructBegin(ctx context.Context, name string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteStructEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error {
|
||||||
|
e := p.WriteByte(ctx, int8(typeId))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteI16(ctx, id)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteFieldEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteFieldStop(ctx context.Context) error {
|
||||||
|
e := p.WriteByte(ctx, STOP)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
|
||||||
|
e := p.WriteByte(ctx, int8(keyType))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteByte(ctx, int8(valueType))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteI32(ctx, int32(size))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteMapEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
e := p.WriteByte(ctx, int8(elemType))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteI32(ctx, int32(size))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteListEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
e := p.WriteByte(ctx, int8(elemType))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
e = p.WriteI32(ctx, int32(size))
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteSetEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteBool(ctx context.Context, value bool) error {
|
||||||
|
if value {
|
||||||
|
return p.WriteByte(ctx, 1)
|
||||||
|
}
|
||||||
|
return p.WriteByte(ctx, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteByte(ctx context.Context, value int8) error {
|
||||||
|
e := p.trans.WriteByte(byte(value))
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteI16(ctx context.Context, value int16) error {
|
||||||
|
v := p.buffer[0:2]
|
||||||
|
binary.BigEndian.PutUint16(v, uint16(value))
|
||||||
|
_, e := p.trans.Write(v)
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteI32(ctx context.Context, value int32) error {
|
||||||
|
v := p.buffer[0:4]
|
||||||
|
binary.BigEndian.PutUint32(v, uint32(value))
|
||||||
|
_, e := p.trans.Write(v)
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteI64(ctx context.Context, value int64) error {
|
||||||
|
v := p.buffer[0:8]
|
||||||
|
binary.BigEndian.PutUint64(v, uint64(value))
|
||||||
|
_, err := p.trans.Write(v)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteDouble(ctx context.Context, value float64) error {
|
||||||
|
return p.WriteI64(ctx, int64(math.Float64bits(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteString(ctx context.Context, value string) error {
|
||||||
|
e := p.WriteI32(ctx, int32(len(value)))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
_, err := p.trans.WriteString(value)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) WriteBinary(ctx context.Context, value []byte) error {
|
||||||
|
e := p.WriteI32(ctx, int32(len(value)))
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
_, err := p.trans.Write(value)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reading methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) {
|
||||||
|
size, e := p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return "", typeId, 0, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
if size < 0 {
|
||||||
|
typeId = TMessageType(size & 0x0ff)
|
||||||
|
version := int64(int64(size) & VERSION_MASK)
|
||||||
|
if version != VERSION_1 {
|
||||||
|
return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin"))
|
||||||
|
}
|
||||||
|
name, e = p.ReadString(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return name, typeId, seqId, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
seqId, e = p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return name, typeId, seqId, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
return name, typeId, seqId, nil
|
||||||
|
}
|
||||||
|
if p.cfg.GetTBinaryStrictRead() {
|
||||||
|
return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin"))
|
||||||
|
}
|
||||||
|
name, e2 := p.readStringBody(size)
|
||||||
|
if e2 != nil {
|
||||||
|
return name, typeId, seqId, e2
|
||||||
|
}
|
||||||
|
b, e3 := p.ReadByte(ctx)
|
||||||
|
if e3 != nil {
|
||||||
|
return name, typeId, seqId, e3
|
||||||
|
}
|
||||||
|
typeId = TMessageType(b)
|
||||||
|
seqId, e4 := p.ReadI32(ctx)
|
||||||
|
if e4 != nil {
|
||||||
|
return name, typeId, seqId, e4
|
||||||
|
}
|
||||||
|
return name, typeId, seqId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadMessageEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadStructEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, seqId int16, err error) {
|
||||||
|
t, err := p.ReadByte(ctx)
|
||||||
|
typeId = TType(t)
|
||||||
|
if err != nil {
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
if t != STOP {
|
||||||
|
seqId, err = p.ReadI16(ctx)
|
||||||
|
}
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadFieldEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length"))
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadMapBegin(ctx context.Context) (kType, vType TType, size int, err error) {
|
||||||
|
k, e := p.ReadByte(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
kType = TType(k)
|
||||||
|
v, e := p.ReadByte(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vType = TType(v)
|
||||||
|
size32, e := p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size32 < 0 {
|
||||||
|
err = invalidDataLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = int(size32)
|
||||||
|
return kType, vType, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadMapEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
b, e := p.ReadByte(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elemType = TType(b)
|
||||||
|
size32, e := p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size32 < 0 {
|
||||||
|
err = invalidDataLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = int(size32)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadListEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
b, e := p.ReadByte(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elemType = TType(b)
|
||||||
|
size32, e := p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size32 < 0 {
|
||||||
|
err = invalidDataLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = int(size32)
|
||||||
|
return elemType, size, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadSetEnd(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadBool(ctx context.Context) (bool, error) {
|
||||||
|
b, e := p.ReadByte(ctx)
|
||||||
|
v := true
|
||||||
|
if b != 1 {
|
||||||
|
v = false
|
||||||
|
}
|
||||||
|
return v, e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadByte(ctx context.Context) (int8, error) {
|
||||||
|
v, err := p.trans.ReadByte()
|
||||||
|
return int8(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadI16(ctx context.Context) (value int16, err error) {
|
||||||
|
buf := p.buffer[0:2]
|
||||||
|
err = p.readAll(ctx, buf)
|
||||||
|
value = int16(binary.BigEndian.Uint16(buf))
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadI32(ctx context.Context) (value int32, err error) {
|
||||||
|
buf := p.buffer[0:4]
|
||||||
|
err = p.readAll(ctx, buf)
|
||||||
|
value = int32(binary.BigEndian.Uint32(buf))
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadI64(ctx context.Context) (value int64, err error) {
|
||||||
|
buf := p.buffer[0:8]
|
||||||
|
err = p.readAll(ctx, buf)
|
||||||
|
value = int64(binary.BigEndian.Uint64(buf))
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadDouble(ctx context.Context) (value float64, err error) {
|
||||||
|
buf := p.buffer[0:8]
|
||||||
|
err = p.readAll(ctx, buf)
|
||||||
|
value = math.Float64frombits(binary.BigEndian.Uint64(buf))
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadString(ctx context.Context) (value string, err error) {
|
||||||
|
size, e := p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return "", e
|
||||||
|
}
|
||||||
|
err = checkSizeForProtocol(size, p.cfg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size < 0 {
|
||||||
|
err = invalidDataLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if size < int32(len(p.buffer)) {
|
||||||
|
// Avoid allocation on small reads
|
||||||
|
buf := p.buffer[:size]
|
||||||
|
read, e := io.ReadFull(p.trans, buf)
|
||||||
|
return string(buf[:read]), NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.readStringBody(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) ReadBinary(ctx context.Context) ([]byte, error) {
|
||||||
|
size, e := p.ReadI32(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return nil, e
|
||||||
|
}
|
||||||
|
if err := checkSizeForProtocol(size, p.cfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := safeReadBytes(size, p.trans)
|
||||||
|
return buf, NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) Flush(ctx context.Context) (err error) {
|
||||||
|
return NewTProtocolException(p.trans.Flush(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) Skip(ctx context.Context, fieldType TType) (err error) {
|
||||||
|
return SkipDefaultDepth(ctx, p, fieldType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) Transport() TTransport {
|
||||||
|
return p.origTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) readAll(ctx context.Context, buf []byte) (err error) {
|
||||||
|
var read int
|
||||||
|
_, deadlineSet := ctx.Deadline()
|
||||||
|
for {
|
||||||
|
read, err = io.ReadFull(p.trans, buf)
|
||||||
|
if deadlineSet && read == 0 && isTimeoutError(err) && ctx.Err() == nil {
|
||||||
|
// This is I/O timeout without anything read,
|
||||||
|
// and we still have time left, keep retrying.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// For anything else, don't retry
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) {
|
||||||
|
buf, err := safeReadBytes(size, p.trans)
|
||||||
|
return string(buf), NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBinaryProtocol) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.trans, conf)
|
||||||
|
PropagateTConfiguration(p.origTransport, conf)
|
||||||
|
p.cfg = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ TConfigurationSetter = (*TBinaryProtocolFactory)(nil)
|
||||||
|
_ TConfigurationSetter = (*TBinaryProtocol)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// This function is shared between TBinaryProtocol and TCompactProtocol.
|
||||||
|
//
|
||||||
|
// It tries to read size bytes from trans, in a way that prevents large
|
||||||
|
// allocations when size is insanely large (mostly caused by malformed message).
|
||||||
|
func safeReadBytes(size int32, trans io.Reader) ([]byte, error) {
|
||||||
|
if size < 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err := io.CopyN(buf, trans, int64(size))
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TBufferedTransportFactory struct {
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
type TBufferedTransport struct {
|
||||||
|
bufio.ReadWriter
|
||||||
|
tp TTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
return NewTBufferedTransport(trans, p.size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTBufferedTransportFactory(bufferSize int) *TBufferedTransportFactory {
|
||||||
|
return &TBufferedTransportFactory{size: bufferSize}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTBufferedTransport(trans TTransport, bufferSize int) *TBufferedTransport {
|
||||||
|
return &TBufferedTransport{
|
||||||
|
ReadWriter: bufio.ReadWriter{
|
||||||
|
Reader: bufio.NewReaderSize(trans, bufferSize),
|
||||||
|
Writer: bufio.NewWriterSize(trans, bufferSize),
|
||||||
|
},
|
||||||
|
tp: trans,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) IsOpen() bool {
|
||||||
|
return p.tp.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) Open() (err error) {
|
||||||
|
return p.tp.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) Close() (err error) {
|
||||||
|
return p.tp.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) Read(b []byte) (int, error) {
|
||||||
|
n, err := p.ReadWriter.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
p.ReadWriter.Reader.Reset(p.tp)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) Write(b []byte) (int, error) {
|
||||||
|
n, err := p.ReadWriter.Write(b)
|
||||||
|
if err != nil {
|
||||||
|
p.ReadWriter.Writer.Reset(p.tp)
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) Flush(ctx context.Context) error {
|
||||||
|
if err := p.ReadWriter.Flush(); err != nil {
|
||||||
|
p.ReadWriter.Writer.Reset(p.tp)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.tp.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TBufferedTransport) RemainingBytes() (num_bytes uint64) {
|
||||||
|
return p.tp.RemainingBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter for propagation.
|
||||||
|
func (p *TBufferedTransport) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.tp, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*TBufferedTransport)(nil)
|
@ -0,0 +1,109 @@
|
|||||||
|
package thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResponseMeta represents the metadata attached to the response.
|
||||||
|
type ResponseMeta struct {
|
||||||
|
// The headers in the response, if any.
|
||||||
|
// If the underlying transport/protocol is not THeader, this will always be nil.
|
||||||
|
Headers THeaderMap
|
||||||
|
}
|
||||||
|
|
||||||
|
type TClient interface {
|
||||||
|
Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TStandardClient struct {
|
||||||
|
seqId int32
|
||||||
|
iprot, oprot TProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// TStandardClient implements TClient, and uses the standard message format for Thrift.
|
||||||
|
// It is not safe for concurrent use.
|
||||||
|
func NewTStandardClient(inputProtocol, outputProtocol TProtocol) *TStandardClient {
|
||||||
|
return &TStandardClient{
|
||||||
|
iprot: inputProtocol,
|
||||||
|
oprot: outputProtocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TStandardClient) Send(ctx context.Context, oprot TProtocol, seqId int32, method string, args TStruct) error {
|
||||||
|
// Set headers from context object on THeaderProtocol
|
||||||
|
if headerProt, ok := oprot.(*THeaderProtocol); ok {
|
||||||
|
headerProt.ClearWriteHeaders()
|
||||||
|
for _, key := range GetWriteHeaderList(ctx) {
|
||||||
|
if value, ok := GetHeader(ctx, key); ok {
|
||||||
|
headerProt.SetWriteHeader(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := oprot.WriteMessageBegin(ctx, method, CALL, seqId); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := args.Write(ctx, oprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := oprot.WriteMessageEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return oprot.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TStandardClient) Recv(ctx context.Context, iprot TProtocol, seqId int32, method string, result TStruct) error {
|
||||||
|
rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if method != rMethod {
|
||||||
|
return NewTApplicationException(WRONG_METHOD_NAME, fmt.Sprintf("%s: wrong method name", method))
|
||||||
|
} else if seqId != rSeqId {
|
||||||
|
return NewTApplicationException(BAD_SEQUENCE_ID, fmt.Sprintf("%s: out of order sequence response", method))
|
||||||
|
} else if rTypeId == EXCEPTION {
|
||||||
|
var exception tApplicationException
|
||||||
|
if err := exception.Read(ctx, iprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := iprot.ReadMessageEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &exception
|
||||||
|
} else if rTypeId != REPLY {
|
||||||
|
return NewTApplicationException(INVALID_MESSAGE_TYPE_EXCEPTION, fmt.Sprintf("%s: invalid message type", method))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := result.Read(ctx, iprot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return iprot.ReadMessageEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) {
|
||||||
|
p.seqId++
|
||||||
|
seqId := p.seqId
|
||||||
|
|
||||||
|
if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil {
|
||||||
|
return ResponseMeta{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// method is oneway
|
||||||
|
if result == nil {
|
||||||
|
return ResponseMeta{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := p.Recv(ctx, p.iprot, seqId, method, result)
|
||||||
|
var headers THeaderMap
|
||||||
|
if hp, ok := p.iprot.(*THeaderProtocol); ok {
|
||||||
|
headers = hp.transport.readHeaders
|
||||||
|
}
|
||||||
|
return ResponseMeta{
|
||||||
|
Headers: headers,
|
||||||
|
}, err
|
||||||
|
}
|
@ -0,0 +1,865 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
COMPACT_PROTOCOL_ID = 0x082
|
||||||
|
COMPACT_VERSION = 1
|
||||||
|
COMPACT_VERSION_MASK = 0x1f
|
||||||
|
COMPACT_TYPE_MASK = 0x0E0
|
||||||
|
COMPACT_TYPE_BITS = 0x07
|
||||||
|
COMPACT_TYPE_SHIFT_AMOUNT = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
type tCompactType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
COMPACT_BOOLEAN_TRUE = 0x01
|
||||||
|
COMPACT_BOOLEAN_FALSE = 0x02
|
||||||
|
COMPACT_BYTE = 0x03
|
||||||
|
COMPACT_I16 = 0x04
|
||||||
|
COMPACT_I32 = 0x05
|
||||||
|
COMPACT_I64 = 0x06
|
||||||
|
COMPACT_DOUBLE = 0x07
|
||||||
|
COMPACT_BINARY = 0x08
|
||||||
|
COMPACT_LIST = 0x09
|
||||||
|
COMPACT_SET = 0x0A
|
||||||
|
COMPACT_MAP = 0x0B
|
||||||
|
COMPACT_STRUCT = 0x0C
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ttypeToCompactType map[TType]tCompactType
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ttypeToCompactType = map[TType]tCompactType{
|
||||||
|
STOP: STOP,
|
||||||
|
BOOL: COMPACT_BOOLEAN_TRUE,
|
||||||
|
BYTE: COMPACT_BYTE,
|
||||||
|
I16: COMPACT_I16,
|
||||||
|
I32: COMPACT_I32,
|
||||||
|
I64: COMPACT_I64,
|
||||||
|
DOUBLE: COMPACT_DOUBLE,
|
||||||
|
STRING: COMPACT_BINARY,
|
||||||
|
LIST: COMPACT_LIST,
|
||||||
|
SET: COMPACT_SET,
|
||||||
|
MAP: COMPACT_MAP,
|
||||||
|
STRUCT: COMPACT_STRUCT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TCompactProtocolFactory struct {
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTCompactProtocolFactoryConf instead.
|
||||||
|
func NewTCompactProtocolFactory() *TCompactProtocolFactory {
|
||||||
|
return NewTCompactProtocolFactoryConf(&TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory {
|
||||||
|
return &TCompactProtocolFactory{
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol {
|
||||||
|
return NewTCompactProtocolConf(trans, p.cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
p.cfg = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
type TCompactProtocol struct {
|
||||||
|
trans TRichTransport
|
||||||
|
origTransport TTransport
|
||||||
|
|
||||||
|
cfg *TConfiguration
|
||||||
|
|
||||||
|
// Used to keep track of the last field for the current and previous structs,
|
||||||
|
// so we can do the delta stuff.
|
||||||
|
lastField []int
|
||||||
|
lastFieldId int
|
||||||
|
|
||||||
|
// If we encounter a boolean field begin, save the TField here so it can
|
||||||
|
// have the value incorporated.
|
||||||
|
booleanFieldName string
|
||||||
|
booleanFieldId int16
|
||||||
|
booleanFieldPending bool
|
||||||
|
|
||||||
|
// If we read a field header, and it's a boolean field, save the boolean
|
||||||
|
// value here so that readBool can use it.
|
||||||
|
boolValue bool
|
||||||
|
boolValueIsNotNull bool
|
||||||
|
buffer [64]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTCompactProtocolConf instead.
|
||||||
|
func NewTCompactProtocol(trans TTransport) *TCompactProtocol {
|
||||||
|
return NewTCompactProtocolConf(trans, &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol {
|
||||||
|
PropagateTConfiguration(trans, conf)
|
||||||
|
p := &TCompactProtocol{
|
||||||
|
origTransport: trans,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
if et, ok := trans.(TRichTransport); ok {
|
||||||
|
p.trans = et
|
||||||
|
} else {
|
||||||
|
p.trans = NewTRichTransport(trans)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Public Writing methods.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Write a message header to the wire. Compact Protocol messages contain the
|
||||||
|
// protocol version so we can migrate forwards in the future if need be.
|
||||||
|
func (p *TCompactProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error {
|
||||||
|
err := p.writeByteDirect(COMPACT_PROTOCOL_ID)
|
||||||
|
if err != nil {
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
err = p.writeByteDirect((COMPACT_VERSION & COMPACT_VERSION_MASK) | ((byte(typeId) << COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_MASK))
|
||||||
|
if err != nil {
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
_, err = p.writeVarint32(seqid)
|
||||||
|
if err != nil {
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
e := p.WriteString(ctx, name)
|
||||||
|
return e
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteMessageEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Write a struct begin. This doesn't actually put anything on the wire. We
|
||||||
|
// use it as an opportunity to put special placeholder markers on the field
|
||||||
|
// stack so we can get the field id deltas correct.
|
||||||
|
func (p *TCompactProtocol) WriteStructBegin(ctx context.Context, name string) error {
|
||||||
|
p.lastField = append(p.lastField, p.lastFieldId)
|
||||||
|
p.lastFieldId = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a struct end. This doesn't actually put anything on the wire. We use
|
||||||
|
// this as an opportunity to pop the last field from the current struct off
|
||||||
|
// of the field stack.
|
||||||
|
func (p *TCompactProtocol) WriteStructEnd(ctx context.Context) error {
|
||||||
|
if len(p.lastField) <= 0 {
|
||||||
|
return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("WriteStructEnd called without matching WriteStructBegin call before"))
|
||||||
|
}
|
||||||
|
p.lastFieldId = p.lastField[len(p.lastField)-1]
|
||||||
|
p.lastField = p.lastField[:len(p.lastField)-1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error {
|
||||||
|
if typeId == BOOL {
|
||||||
|
// we want to possibly include the value, so we'll wait.
|
||||||
|
p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := p.writeFieldBeginInternal(ctx, name, typeId, id, 0xFF)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The workhorse of writeFieldBegin. It has the option of doing a
|
||||||
|
// 'type override' of the type header. This is used specifically in the
|
||||||
|
// boolean field case.
|
||||||
|
func (p *TCompactProtocol) writeFieldBeginInternal(ctx context.Context, name string, typeId TType, id int16, typeOverride byte) (int, error) {
|
||||||
|
// short lastField = lastField_.pop();
|
||||||
|
|
||||||
|
// if there's a type override, use that.
|
||||||
|
var typeToWrite byte
|
||||||
|
if typeOverride == 0xFF {
|
||||||
|
typeToWrite = byte(p.getCompactType(typeId))
|
||||||
|
} else {
|
||||||
|
typeToWrite = typeOverride
|
||||||
|
}
|
||||||
|
// check if we can use delta encoding for the field id
|
||||||
|
fieldId := int(id)
|
||||||
|
written := 0
|
||||||
|
if fieldId > p.lastFieldId && fieldId-p.lastFieldId <= 15 {
|
||||||
|
// write them together
|
||||||
|
err := p.writeByteDirect(byte((fieldId-p.lastFieldId)<<4) | typeToWrite)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// write them separate
|
||||||
|
err := p.writeByteDirect(typeToWrite)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
err = p.WriteI16(ctx, id)
|
||||||
|
written = 1 + 2
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.lastFieldId = fieldId
|
||||||
|
return written, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteFieldEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteFieldStop(ctx context.Context) error {
|
||||||
|
err := p.writeByteDirect(STOP)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
|
||||||
|
if size == 0 {
|
||||||
|
err := p.writeByteDirect(0)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
_, err := p.writeVarint32(int32(size))
|
||||||
|
if err != nil {
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
err = p.writeByteDirect(byte(p.getCompactType(keyType))<<4 | byte(p.getCompactType(valueType)))
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteMapEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Write a list header.
|
||||||
|
func (p *TCompactProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
_, err := p.writeCollectionBegin(elemType, size)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteListEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Write a set header.
|
||||||
|
func (p *TCompactProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
_, err := p.writeCollectionBegin(elemType, size)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteSetEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) WriteBool(ctx context.Context, value bool) error {
|
||||||
|
v := byte(COMPACT_BOOLEAN_FALSE)
|
||||||
|
if value {
|
||||||
|
v = byte(COMPACT_BOOLEAN_TRUE)
|
||||||
|
}
|
||||||
|
if p.booleanFieldPending {
|
||||||
|
// we haven't written the field header yet
|
||||||
|
_, err := p.writeFieldBeginInternal(ctx, p.booleanFieldName, BOOL, p.booleanFieldId, v)
|
||||||
|
p.booleanFieldPending = false
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
// we're not part of a field, so just write the value.
|
||||||
|
err := p.writeByteDirect(v)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a byte. Nothing to see here!
|
||||||
|
func (p *TCompactProtocol) WriteByte(ctx context.Context, value int8) error {
|
||||||
|
err := p.writeByteDirect(byte(value))
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an I16 as a zigzag varint.
|
||||||
|
func (p *TCompactProtocol) WriteI16(ctx context.Context, value int16) error {
|
||||||
|
_, err := p.writeVarint32(p.int32ToZigzag(int32(value)))
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an i32 as a zigzag varint.
|
||||||
|
func (p *TCompactProtocol) WriteI32(ctx context.Context, value int32) error {
|
||||||
|
_, err := p.writeVarint32(p.int32ToZigzag(value))
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an i64 as a zigzag varint.
|
||||||
|
func (p *TCompactProtocol) WriteI64(ctx context.Context, value int64) error {
|
||||||
|
_, err := p.writeVarint64(p.int64ToZigzag(value))
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a double to the wire as 8 bytes.
|
||||||
|
func (p *TCompactProtocol) WriteDouble(ctx context.Context, value float64) error {
|
||||||
|
buf := p.buffer[0:8]
|
||||||
|
binary.LittleEndian.PutUint64(buf, math.Float64bits(value))
|
||||||
|
_, err := p.trans.Write(buf)
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a string to the wire with a varint size preceding.
|
||||||
|
func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error {
|
||||||
|
_, e := p.writeVarint32(int32(len(value)))
|
||||||
|
if e != nil {
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
if len(value) > 0 {
|
||||||
|
}
|
||||||
|
_, e = p.trans.WriteString(value)
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a byte array, using a varint for the size.
|
||||||
|
func (p *TCompactProtocol) WriteBinary(ctx context.Context, bin []byte) error {
|
||||||
|
_, e := p.writeVarint32(int32(len(bin)))
|
||||||
|
if e != nil {
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
if len(bin) > 0 {
|
||||||
|
_, e = p.trans.Write(bin)
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Reading methods.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Read a message header.
|
||||||
|
func (p *TCompactProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) {
|
||||||
|
var protocolId byte
|
||||||
|
|
||||||
|
_, deadlineSet := ctx.Deadline()
|
||||||
|
for {
|
||||||
|
protocolId, err = p.readByteDirect()
|
||||||
|
if deadlineSet && isTimeoutError(err) && ctx.Err() == nil {
|
||||||
|
// keep retrying I/O timeout errors since we still have
|
||||||
|
// time left
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// For anything else, don't retry
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if protocolId != COMPACT_PROTOCOL_ID {
|
||||||
|
e := fmt.Errorf("Expected protocol id %02x but got %02x", COMPACT_PROTOCOL_ID, protocolId)
|
||||||
|
return "", typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionAndType, err := p.readByteDirect()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
version := versionAndType & COMPACT_VERSION_MASK
|
||||||
|
typeId = TMessageType((versionAndType >> COMPACT_TYPE_SHIFT_AMOUNT) & COMPACT_TYPE_BITS)
|
||||||
|
if version != COMPACT_VERSION {
|
||||||
|
e := fmt.Errorf("Expected version %02x but got %02x", COMPACT_VERSION, version)
|
||||||
|
err = NewTProtocolExceptionWithType(BAD_VERSION, e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
seqId, e := p.readVarint32()
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name, err = p.ReadString(ctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) ReadMessageEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Read a struct begin. There's nothing on the wire for this, but it is our
|
||||||
|
// opportunity to push a new struct begin marker onto the field stack.
|
||||||
|
func (p *TCompactProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
|
||||||
|
p.lastField = append(p.lastField, p.lastFieldId)
|
||||||
|
p.lastFieldId = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Doesn't actually consume any wire data, just removes the last field for
|
||||||
|
// this struct from the field stack.
|
||||||
|
func (p *TCompactProtocol) ReadStructEnd(ctx context.Context) error {
|
||||||
|
// consume the last field we read off the wire.
|
||||||
|
if len(p.lastField) <= 0 {
|
||||||
|
return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("ReadStructEnd called without matching ReadStructBegin call before"))
|
||||||
|
}
|
||||||
|
p.lastFieldId = p.lastField[len(p.lastField)-1]
|
||||||
|
p.lastField = p.lastField[:len(p.lastField)-1]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a field header off the wire.
|
||||||
|
func (p *TCompactProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) {
|
||||||
|
t, err := p.readByteDirect()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's a stop, then we can return immediately, as the struct is over.
|
||||||
|
if (t & 0x0f) == STOP {
|
||||||
|
return "", STOP, 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mask off the 4 MSB of the type header. it could contain a field id delta.
|
||||||
|
modifier := int16((t & 0xf0) >> 4)
|
||||||
|
if modifier == 0 {
|
||||||
|
// not a delta. look ahead for the zigzag varint field id.
|
||||||
|
id, err = p.ReadI16(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// has a delta. add the delta to the last read field id.
|
||||||
|
id = int16(p.lastFieldId) + modifier
|
||||||
|
}
|
||||||
|
typeId, e := p.getTType(tCompactType(t & 0x0f))
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this happens to be a boolean field, the value is encoded in the type
|
||||||
|
if p.isBoolType(t) {
|
||||||
|
// save the boolean value in a special instance variable.
|
||||||
|
p.boolValue = (byte(t)&0x0f == COMPACT_BOOLEAN_TRUE)
|
||||||
|
p.boolValueIsNotNull = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// push the new field onto the field stack so we can keep the deltas going.
|
||||||
|
p.lastFieldId = int(id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) ReadFieldEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Read a map header off the wire. If the size is zero, skip reading the key
|
||||||
|
// and value type. This means that 0-length maps will yield TMaps without the
|
||||||
|
// "correct" types.
|
||||||
|
func (p *TCompactProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) {
|
||||||
|
size32, e := p.readVarint32()
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size32 < 0 {
|
||||||
|
err = invalidDataLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = int(size32)
|
||||||
|
|
||||||
|
keyAndValueType := byte(STOP)
|
||||||
|
if size != 0 {
|
||||||
|
keyAndValueType, err = p.readByteDirect()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4))
|
||||||
|
valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) ReadMapEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Read a list header off the wire. If the list size is 0-14, the size will
|
||||||
|
// be packed into the element type header. If it's a longer list, the 4 MSB
|
||||||
|
// of the element type header will be 0xF, and a varint will follow with the
|
||||||
|
// true size.
|
||||||
|
func (p *TCompactProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
size_and_type, err := p.readByteDirect()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = int((size_and_type >> 4) & 0x0f)
|
||||||
|
if size == 15 {
|
||||||
|
size2, e := p.readVarint32()
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if size2 < 0 {
|
||||||
|
err = invalidDataLength
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = int(size2)
|
||||||
|
}
|
||||||
|
elemType, e := p.getTType(tCompactType(size_and_type))
|
||||||
|
if e != nil {
|
||||||
|
err = NewTProtocolException(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) ReadListEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Read a set header off the wire. If the set size is 0-14, the size will
|
||||||
|
// be packed into the element type header. If it's a longer set, the 4 MSB
|
||||||
|
// of the element type header will be 0xF, and a varint will follow with the
|
||||||
|
// true size.
|
||||||
|
func (p *TCompactProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
return p.ReadListBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) ReadSetEnd(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
// Read a boolean off the wire. If this is a boolean field, the value should
|
||||||
|
// already have been read during readFieldBegin, so we'll just consume the
|
||||||
|
// pre-stored value. Otherwise, read a byte.
|
||||||
|
func (p *TCompactProtocol) ReadBool(ctx context.Context) (value bool, err error) {
|
||||||
|
if p.boolValueIsNotNull {
|
||||||
|
p.boolValueIsNotNull = false
|
||||||
|
return p.boolValue, nil
|
||||||
|
}
|
||||||
|
v, err := p.readByteDirect()
|
||||||
|
return v == COMPACT_BOOLEAN_TRUE, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a single byte off the wire. Nothing interesting here.
|
||||||
|
func (p *TCompactProtocol) ReadByte(ctx context.Context) (int8, error) {
|
||||||
|
v, err := p.readByteDirect()
|
||||||
|
if err != nil {
|
||||||
|
return 0, NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
return int8(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read an i16 from the wire as a zigzag varint.
|
||||||
|
func (p *TCompactProtocol) ReadI16(ctx context.Context) (value int16, err error) {
|
||||||
|
v, err := p.ReadI32(ctx)
|
||||||
|
return int16(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read an i32 from the wire as a zigzag varint.
|
||||||
|
func (p *TCompactProtocol) ReadI32(ctx context.Context) (value int32, err error) {
|
||||||
|
v, e := p.readVarint32()
|
||||||
|
if e != nil {
|
||||||
|
return 0, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
value = p.zigzagToInt32(v)
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read an i64 from the wire as a zigzag varint.
|
||||||
|
func (p *TCompactProtocol) ReadI64(ctx context.Context) (value int64, err error) {
|
||||||
|
v, e := p.readVarint64()
|
||||||
|
if e != nil {
|
||||||
|
return 0, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
value = p.zigzagToInt64(v)
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No magic here - just read a double off the wire.
|
||||||
|
func (p *TCompactProtocol) ReadDouble(ctx context.Context) (value float64, err error) {
|
||||||
|
longBits := p.buffer[0:8]
|
||||||
|
_, e := io.ReadFull(p.trans, longBits)
|
||||||
|
if e != nil {
|
||||||
|
return 0.0, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
return math.Float64frombits(p.bytesToUint64(longBits)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a []byte (via readBinary), and then UTF-8 decodes it.
|
||||||
|
func (p *TCompactProtocol) ReadString(ctx context.Context) (value string, err error) {
|
||||||
|
length, e := p.readVarint32()
|
||||||
|
if e != nil {
|
||||||
|
return "", NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
err = checkSizeForProtocol(length, p.cfg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if length == 0 {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
if length < int32(len(p.buffer)) {
|
||||||
|
// Avoid allocation on small reads
|
||||||
|
buf := p.buffer[:length]
|
||||||
|
read, e := io.ReadFull(p.trans, buf)
|
||||||
|
return string(buf[:read]), NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, e := safeReadBytes(length, p.trans)
|
||||||
|
return string(buf), NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a []byte from the wire.
|
||||||
|
func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
|
||||||
|
length, e := p.readVarint32()
|
||||||
|
if e != nil {
|
||||||
|
return nil, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
err = checkSizeForProtocol(length, p.cfg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if length == 0 {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, e := safeReadBytes(length, p.trans)
|
||||||
|
return buf, NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) Flush(ctx context.Context) (err error) {
|
||||||
|
return NewTProtocolException(p.trans.Flush(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) Skip(ctx context.Context, fieldType TType) (err error) {
|
||||||
|
return SkipDefaultDepth(ctx, p, fieldType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) Transport() TTransport {
|
||||||
|
return p.origTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Internal writing methods
|
||||||
|
//
|
||||||
|
|
||||||
|
// Abstract method for writing the start of lists and sets. List and sets on
|
||||||
|
// the wire differ only by the type indicator.
|
||||||
|
func (p *TCompactProtocol) writeCollectionBegin(elemType TType, size int) (int, error) {
|
||||||
|
if size <= 14 {
|
||||||
|
return 1, p.writeByteDirect(byte(int32(size<<4) | int32(p.getCompactType(elemType))))
|
||||||
|
}
|
||||||
|
err := p.writeByteDirect(0xf0 | byte(p.getCompactType(elemType)))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
m, err := p.writeVarint32(int32(size))
|
||||||
|
return 1 + m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an i32 as a varint. Results in 1-5 bytes on the wire.
|
||||||
|
// TODO(pomack): make a permanent buffer like writeVarint64?
|
||||||
|
func (p *TCompactProtocol) writeVarint32(n int32) (int, error) {
|
||||||
|
i32buf := p.buffer[0:5]
|
||||||
|
idx := 0
|
||||||
|
for {
|
||||||
|
if (n & ^0x7F) == 0 {
|
||||||
|
i32buf[idx] = byte(n)
|
||||||
|
idx++
|
||||||
|
// p.writeByteDirect(byte(n));
|
||||||
|
break
|
||||||
|
// return;
|
||||||
|
} else {
|
||||||
|
i32buf[idx] = byte((n & 0x7F) | 0x80)
|
||||||
|
idx++
|
||||||
|
// p.writeByteDirect(byte(((n & 0x7F) | 0x80)));
|
||||||
|
u := uint32(n)
|
||||||
|
n = int32(u >> 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.trans.Write(i32buf[0:idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an i64 as a varint. Results in 1-10 bytes on the wire.
|
||||||
|
func (p *TCompactProtocol) writeVarint64(n int64) (int, error) {
|
||||||
|
varint64out := p.buffer[0:10]
|
||||||
|
idx := 0
|
||||||
|
for {
|
||||||
|
if (n & ^0x7F) == 0 {
|
||||||
|
varint64out[idx] = byte(n)
|
||||||
|
idx++
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
varint64out[idx] = byte((n & 0x7F) | 0x80)
|
||||||
|
idx++
|
||||||
|
u := uint64(n)
|
||||||
|
n = int64(u >> 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.trans.Write(varint64out[0:idx])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert l into a zigzag long. This allows negative numbers to be
|
||||||
|
// represented compactly as a varint.
|
||||||
|
func (p *TCompactProtocol) int64ToZigzag(l int64) int64 {
|
||||||
|
return (l << 1) ^ (l >> 63)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert l into a zigzag long. This allows negative numbers to be
|
||||||
|
// represented compactly as a varint.
|
||||||
|
func (p *TCompactProtocol) int32ToZigzag(n int32) int32 {
|
||||||
|
return (n << 1) ^ (n >> 31)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) fixedUint64ToBytes(n uint64, buf []byte) {
|
||||||
|
binary.LittleEndian.PutUint64(buf, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) fixedInt64ToBytes(n int64, buf []byte) {
|
||||||
|
binary.LittleEndian.PutUint64(buf, uint64(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes a byte without any possibility of all that field header nonsense.
|
||||||
|
// Used internally by other writing methods that know they need to write a byte.
|
||||||
|
func (p *TCompactProtocol) writeByteDirect(b byte) error {
|
||||||
|
return p.trans.WriteByte(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes a byte without any possibility of all that field header nonsense.
|
||||||
|
func (p *TCompactProtocol) writeIntAsByteDirect(n int) (int, error) {
|
||||||
|
return 1, p.writeByteDirect(byte(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Internal reading methods
|
||||||
|
//
|
||||||
|
|
||||||
|
// Read an i32 from the wire as a varint. The MSB of each byte is set
|
||||||
|
// if there is another byte to follow. This can read up to 5 bytes.
|
||||||
|
func (p *TCompactProtocol) readVarint32() (int32, error) {
|
||||||
|
// if the wire contains the right stuff, this will just truncate the i64 we
|
||||||
|
// read and get us the right sign.
|
||||||
|
v, err := p.readVarint64()
|
||||||
|
return int32(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read an i64 from the wire as a proper varint. The MSB of each byte is set
|
||||||
|
// if there is another byte to follow. This can read up to 10 bytes.
|
||||||
|
func (p *TCompactProtocol) readVarint64() (int64, error) {
|
||||||
|
shift := uint(0)
|
||||||
|
result := int64(0)
|
||||||
|
for {
|
||||||
|
b, err := p.readByteDirect()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
result |= int64(b&0x7f) << shift
|
||||||
|
if (b & 0x80) != 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
shift += 7
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a byte, unlike ReadByte that reads Thrift-byte that is i8.
|
||||||
|
func (p *TCompactProtocol) readByteDirect() (byte, error) {
|
||||||
|
return p.trans.ReadByte()
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// encoding helpers
|
||||||
|
//
|
||||||
|
|
||||||
|
// Convert from zigzag int to int.
|
||||||
|
func (p *TCompactProtocol) zigzagToInt32(n int32) int32 {
|
||||||
|
u := uint32(n)
|
||||||
|
return int32(u>>1) ^ -(n & 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from zigzag long to long.
|
||||||
|
func (p *TCompactProtocol) zigzagToInt64(n int64) int64 {
|
||||||
|
u := uint64(n)
|
||||||
|
return int64(u>>1) ^ -(n & 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that it's important that the mask bytes are long literals,
|
||||||
|
// otherwise they'll default to ints, and when you shift an int left 56 bits,
|
||||||
|
// you just get a messed up int.
|
||||||
|
func (p *TCompactProtocol) bytesToInt64(b []byte) int64 {
|
||||||
|
return int64(binary.LittleEndian.Uint64(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that it's important that the mask bytes are long literals,
|
||||||
|
// otherwise they'll default to ints, and when you shift an int left 56 bits,
|
||||||
|
// you just get a messed up int.
|
||||||
|
func (p *TCompactProtocol) bytesToUint64(b []byte) uint64 {
|
||||||
|
return binary.LittleEndian.Uint64(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// type testing and converting
|
||||||
|
//
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) isBoolType(b byte) bool {
|
||||||
|
return (b&0x0f) == COMPACT_BOOLEAN_TRUE || (b&0x0f) == COMPACT_BOOLEAN_FALSE
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a tCompactType constant, convert it to its corresponding
|
||||||
|
// TType value.
|
||||||
|
func (p *TCompactProtocol) getTType(t tCompactType) (TType, error) {
|
||||||
|
switch byte(t) & 0x0f {
|
||||||
|
case STOP:
|
||||||
|
return STOP, nil
|
||||||
|
case COMPACT_BOOLEAN_FALSE, COMPACT_BOOLEAN_TRUE:
|
||||||
|
return BOOL, nil
|
||||||
|
case COMPACT_BYTE:
|
||||||
|
return BYTE, nil
|
||||||
|
case COMPACT_I16:
|
||||||
|
return I16, nil
|
||||||
|
case COMPACT_I32:
|
||||||
|
return I32, nil
|
||||||
|
case COMPACT_I64:
|
||||||
|
return I64, nil
|
||||||
|
case COMPACT_DOUBLE:
|
||||||
|
return DOUBLE, nil
|
||||||
|
case COMPACT_BINARY:
|
||||||
|
return STRING, nil
|
||||||
|
case COMPACT_LIST:
|
||||||
|
return LIST, nil
|
||||||
|
case COMPACT_SET:
|
||||||
|
return SET, nil
|
||||||
|
case COMPACT_MAP:
|
||||||
|
return MAP, nil
|
||||||
|
case COMPACT_STRUCT:
|
||||||
|
return STRUCT, nil
|
||||||
|
}
|
||||||
|
return STOP, NewTProtocolException(fmt.Errorf("don't know what type: %v", t&0x0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a TType value, find the appropriate TCompactProtocol.Types constant.
|
||||||
|
func (p *TCompactProtocol) getCompactType(t TType) tCompactType {
|
||||||
|
return ttypeToCompactType[t]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TCompactProtocol) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.trans, conf)
|
||||||
|
PropagateTConfiguration(p.origTransport, conf)
|
||||||
|
p.cfg = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ TConfigurationSetter = (*TCompactProtocolFactory)(nil)
|
||||||
|
_ TConfigurationSetter = (*TCompactProtocol)(nil)
|
||||||
|
)
|
@ -0,0 +1,378 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default TConfiguration values.
|
||||||
|
const (
|
||||||
|
DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024
|
||||||
|
DEFAULT_MAX_FRAME_SIZE = 16384000
|
||||||
|
|
||||||
|
DEFAULT_TBINARY_STRICT_READ = false
|
||||||
|
DEFAULT_TBINARY_STRICT_WRITE = true
|
||||||
|
|
||||||
|
DEFAULT_CONNECT_TIMEOUT = 0
|
||||||
|
DEFAULT_SOCKET_TIMEOUT = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// TConfiguration defines some configurations shared between TTransport,
|
||||||
|
// TProtocol, TTransportFactory, TProtocolFactory, and other implementations.
|
||||||
|
//
|
||||||
|
// When constructing TConfiguration, you only need to specify the non-default
|
||||||
|
// fields. All zero values have sane default values.
|
||||||
|
//
|
||||||
|
// Not all configurations defined are applicable to all implementations.
|
||||||
|
// Implementations are free to ignore the configurations not applicable to them.
|
||||||
|
//
|
||||||
|
// All functions attached to this type are nil-safe.
|
||||||
|
//
|
||||||
|
// See [1] for spec.
|
||||||
|
//
|
||||||
|
// NOTE: When using TConfiguration, fill in all the configurations you want to
|
||||||
|
// set across the stack, not only the ones you want to set in the immediate
|
||||||
|
// TTransport/TProtocol.
|
||||||
|
//
|
||||||
|
// For example, say you want to migrate this old code into using TConfiguration:
|
||||||
|
//
|
||||||
|
// sccket := thrift.NewTSocketTimeout("host:port", time.Second)
|
||||||
|
// transFactory := thrift.NewTFramedTransportFactoryMaxLength(
|
||||||
|
// thrift.NewTTransportFactory(),
|
||||||
|
// 1024 * 1024 * 256,
|
||||||
|
// )
|
||||||
|
// protoFactory := thrift.NewTBinaryProtocolFactory(true, true)
|
||||||
|
//
|
||||||
|
// This is the wrong way to do it because in the end the TConfiguration used by
|
||||||
|
// socket and transFactory will be overwritten by the one used by protoFactory
|
||||||
|
// because of TConfiguration propagation:
|
||||||
|
//
|
||||||
|
// // bad example, DO NOT USE
|
||||||
|
// sccket := thrift.NewTSocketConf("host:port", &thrift.TConfiguration{
|
||||||
|
// ConnectTimeout: time.Second,
|
||||||
|
// SocketTimeout: time.Second,
|
||||||
|
// })
|
||||||
|
// transFactory := thrift.NewTFramedTransportFactoryConf(
|
||||||
|
// thrift.NewTTransportFactory(),
|
||||||
|
// &thrift.TConfiguration{
|
||||||
|
// MaxFrameSize: 1024 * 1024 * 256,
|
||||||
|
// },
|
||||||
|
// )
|
||||||
|
// protoFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{
|
||||||
|
// TBinaryStrictRead: thrift.BoolPtr(true),
|
||||||
|
// TBinaryStrictWrite: thrift.BoolPtr(true),
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// This is the correct way to do it:
|
||||||
|
//
|
||||||
|
// conf := &thrift.TConfiguration{
|
||||||
|
// ConnectTimeout: time.Second,
|
||||||
|
// SocketTimeout: time.Second,
|
||||||
|
//
|
||||||
|
// MaxFrameSize: 1024 * 1024 * 256,
|
||||||
|
//
|
||||||
|
// TBinaryStrictRead: thrift.BoolPtr(true),
|
||||||
|
// TBinaryStrictWrite: thrift.BoolPtr(true),
|
||||||
|
// }
|
||||||
|
// sccket := thrift.NewTSocketConf("host:port", conf)
|
||||||
|
// transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf)
|
||||||
|
// protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf)
|
||||||
|
//
|
||||||
|
// [1]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-tconfiguration.md
|
||||||
|
type TConfiguration struct {
|
||||||
|
// If <= 0, DEFAULT_MAX_MESSAGE_SIZE will be used instead.
|
||||||
|
MaxMessageSize int32
|
||||||
|
|
||||||
|
// If <= 0, DEFAULT_MAX_FRAME_SIZE will be used instead.
|
||||||
|
//
|
||||||
|
// Also if MaxMessageSize < MaxFrameSize,
|
||||||
|
// MaxMessageSize will be used instead.
|
||||||
|
MaxFrameSize int32
|
||||||
|
|
||||||
|
// Connect and socket timeouts to be used by TSocket and TSSLSocket.
|
||||||
|
//
|
||||||
|
// 0 means no timeout.
|
||||||
|
//
|
||||||
|
// If <0, DEFAULT_CONNECT_TIMEOUT and DEFAULT_SOCKET_TIMEOUT will be
|
||||||
|
// used.
|
||||||
|
ConnectTimeout time.Duration
|
||||||
|
SocketTimeout time.Duration
|
||||||
|
|
||||||
|
// TLS config to be used by TSSLSocket.
|
||||||
|
TLSConfig *tls.Config
|
||||||
|
|
||||||
|
// Strict read/write configurations for TBinaryProtocol.
|
||||||
|
//
|
||||||
|
// BoolPtr helper function is available to use literal values.
|
||||||
|
TBinaryStrictRead *bool
|
||||||
|
TBinaryStrictWrite *bool
|
||||||
|
|
||||||
|
// The wrapped protocol id to be used in THeader transport/protocol.
|
||||||
|
//
|
||||||
|
// THeaderProtocolIDPtr and THeaderProtocolIDPtrMust helper functions
|
||||||
|
// are provided to help filling this value.
|
||||||
|
THeaderProtocolID *THeaderProtocolID
|
||||||
|
|
||||||
|
// Used internally by deprecated constructors, to avoid overriding
|
||||||
|
// underlying TTransport/TProtocol's cfg by accidental propagations.
|
||||||
|
//
|
||||||
|
// For external users this is always false.
|
||||||
|
noPropagation bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxMessageSize returns the max message size an implementation should
|
||||||
|
// follow.
|
||||||
|
//
|
||||||
|
// It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil.
|
||||||
|
func (tc *TConfiguration) GetMaxMessageSize() int32 {
|
||||||
|
if tc == nil || tc.MaxMessageSize <= 0 {
|
||||||
|
return DEFAULT_MAX_MESSAGE_SIZE
|
||||||
|
}
|
||||||
|
return tc.MaxMessageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMaxFrameSize returns the max frame size an implementation should follow.
|
||||||
|
//
|
||||||
|
// It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil.
|
||||||
|
//
|
||||||
|
// If the configured max message size is smaller than the configured max frame
|
||||||
|
// size, the smaller one will be returned instead.
|
||||||
|
func (tc *TConfiguration) GetMaxFrameSize() int32 {
|
||||||
|
if tc == nil {
|
||||||
|
return DEFAULT_MAX_FRAME_SIZE
|
||||||
|
}
|
||||||
|
maxFrameSize := tc.MaxFrameSize
|
||||||
|
if maxFrameSize <= 0 {
|
||||||
|
maxFrameSize = DEFAULT_MAX_FRAME_SIZE
|
||||||
|
}
|
||||||
|
if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize {
|
||||||
|
return maxMessageSize
|
||||||
|
}
|
||||||
|
return maxFrameSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConnectTimeout returns the connect timeout should be used by TSocket and
|
||||||
|
// TSSLSocket.
|
||||||
|
//
|
||||||
|
// It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead.
|
||||||
|
func (tc *TConfiguration) GetConnectTimeout() time.Duration {
|
||||||
|
if tc == nil || tc.ConnectTimeout < 0 {
|
||||||
|
return DEFAULT_CONNECT_TIMEOUT
|
||||||
|
}
|
||||||
|
return tc.ConnectTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSocketTimeout returns the socket timeout should be used by TSocket and
|
||||||
|
// TSSLSocket.
|
||||||
|
//
|
||||||
|
// It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead.
|
||||||
|
func (tc *TConfiguration) GetSocketTimeout() time.Duration {
|
||||||
|
if tc == nil || tc.SocketTimeout < 0 {
|
||||||
|
return DEFAULT_SOCKET_TIMEOUT
|
||||||
|
}
|
||||||
|
return tc.SocketTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTLSConfig returns the tls config should be used by TSSLSocket.
|
||||||
|
//
|
||||||
|
// It's nil-safe. If tc is nil, nil will be returned instead.
|
||||||
|
func (tc *TConfiguration) GetTLSConfig() *tls.Config {
|
||||||
|
if tc == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tc.TLSConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol
|
||||||
|
// should follow.
|
||||||
|
//
|
||||||
|
// It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or
|
||||||
|
// tc.TBinaryStrictRead is nil.
|
||||||
|
func (tc *TConfiguration) GetTBinaryStrictRead() bool {
|
||||||
|
if tc == nil || tc.TBinaryStrictRead == nil {
|
||||||
|
return DEFAULT_TBINARY_STRICT_READ
|
||||||
|
}
|
||||||
|
return *tc.TBinaryStrictRead
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol
|
||||||
|
// should follow.
|
||||||
|
//
|
||||||
|
// It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or
|
||||||
|
// tc.TBinaryStrictWrite is nil.
|
||||||
|
func (tc *TConfiguration) GetTBinaryStrictWrite() bool {
|
||||||
|
if tc == nil || tc.TBinaryStrictWrite == nil {
|
||||||
|
return DEFAULT_TBINARY_STRICT_WRITE
|
||||||
|
}
|
||||||
|
return *tc.TBinaryStrictWrite
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTHeaderProtocolID returns the THeaderProtocolID should be used by
|
||||||
|
// THeaderProtocol clients (for servers, they always use the same one as the
|
||||||
|
// client instead).
|
||||||
|
//
|
||||||
|
// It's nil-safe. If either tc or tc.THeaderProtocolID is nil,
|
||||||
|
// THeaderProtocolDefault will be returned instead.
|
||||||
|
// THeaderProtocolDefault will also be returned if configured value is invalid.
|
||||||
|
func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID {
|
||||||
|
if tc == nil || tc.THeaderProtocolID == nil {
|
||||||
|
return THeaderProtocolDefault
|
||||||
|
}
|
||||||
|
protoID := *tc.THeaderProtocolID
|
||||||
|
if err := protoID.Validate(); err != nil {
|
||||||
|
return THeaderProtocolDefault
|
||||||
|
}
|
||||||
|
return protoID
|
||||||
|
}
|
||||||
|
|
||||||
|
// THeaderProtocolIDPtr validates and returns the pointer to id.
|
||||||
|
//
|
||||||
|
// If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault
|
||||||
|
// and the validation error will be returned.
|
||||||
|
func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) {
|
||||||
|
err := id.Validate()
|
||||||
|
if err != nil {
|
||||||
|
id = THeaderProtocolDefault
|
||||||
|
}
|
||||||
|
return &id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// THeaderProtocolIDPtrMust validates and returns the pointer to id.
|
||||||
|
//
|
||||||
|
// It's similar to THeaderProtocolIDPtr, but it panics on validation errors
|
||||||
|
// instead of returning them.
|
||||||
|
func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID {
|
||||||
|
ptr, err := THeaderProtocolIDPtr(id)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
// TConfigurationSetter is an optional interface TProtocol, TTransport,
|
||||||
|
// TProtocolFactory, TTransportFactory, and other implementations can implement.
|
||||||
|
//
|
||||||
|
// It's intended to be called during intializations.
|
||||||
|
// The behavior of calling SetTConfiguration on a TTransport/TProtocol in the
|
||||||
|
// middle of a message is undefined:
|
||||||
|
// It may or may not change the behavior of the current processing message,
|
||||||
|
// and it may even cause the current message to fail.
|
||||||
|
//
|
||||||
|
// Note for implementations: SetTConfiguration might be called multiple times
|
||||||
|
// with the same value in quick successions due to the implementation of the
|
||||||
|
// propagation. Implementations should make SetTConfiguration as simple as
|
||||||
|
// possible (usually just overwrite the stored configuration and propagate it to
|
||||||
|
// the wrapped TTransports/TProtocols).
|
||||||
|
type TConfigurationSetter interface {
|
||||||
|
SetTConfiguration(*TConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropagateTConfiguration propagates cfg to impl if impl implements
|
||||||
|
// TConfigurationSetter and cfg is non-nil, otherwise it does nothing.
|
||||||
|
//
|
||||||
|
// NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration
|
||||||
|
// with everything being default value, use &TConfiguration{} explicitly instead.
|
||||||
|
func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) {
|
||||||
|
if cfg == nil || cfg.noPropagation {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if setter, ok := impl.(TConfigurationSetter); ok {
|
||||||
|
setter.SetTConfiguration(cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSizeForProtocol(size int32, cfg *TConfiguration) error {
|
||||||
|
if size < 0 {
|
||||||
|
return NewTProtocolExceptionWithType(
|
||||||
|
NEGATIVE_SIZE,
|
||||||
|
fmt.Errorf("negative size: %d", size),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if size > cfg.GetMaxMessageSize() {
|
||||||
|
return NewTProtocolExceptionWithType(
|
||||||
|
SIZE_LIMIT,
|
||||||
|
fmt.Errorf("size exceeded max allowed: %d", size),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tTransportFactoryConf struct {
|
||||||
|
delegate TTransportFactory
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) {
|
||||||
|
trans, err := f.delegate.GetTransport(orig)
|
||||||
|
if err == nil {
|
||||||
|
PropagateTConfiguration(orig, f.cfg)
|
||||||
|
PropagateTConfiguration(trans, f.cfg)
|
||||||
|
}
|
||||||
|
return trans, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(f.delegate, f.cfg)
|
||||||
|
f.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTransportFactoryConf wraps a TTransportFactory to propagate
|
||||||
|
// TConfiguration on the factory's GetTransport calls.
|
||||||
|
func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory {
|
||||||
|
return &tTransportFactoryConf{
|
||||||
|
delegate: delegate,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tProtocolFactoryConf struct {
|
||||||
|
delegate TProtocolFactory
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol {
|
||||||
|
proto := f.delegate.GetProtocol(trans)
|
||||||
|
PropagateTConfiguration(trans, f.cfg)
|
||||||
|
PropagateTConfiguration(proto, f.cfg)
|
||||||
|
return proto
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(f.delegate, f.cfg)
|
||||||
|
f.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// TProtocolFactoryConf wraps a TProtocolFactory to propagate
|
||||||
|
// TConfiguration on the factory's GetProtocol calls.
|
||||||
|
func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory {
|
||||||
|
return &tProtocolFactoryConf{
|
||||||
|
delegate: delegate,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ TConfigurationSetter = (*tTransportFactoryConf)(nil)
|
||||||
|
_ TConfigurationSetter = (*tProtocolFactoryConf)(nil)
|
||||||
|
)
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
var defaultCtx = context.Background()
|
@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TDebugProtocol struct {
|
||||||
|
// Required. The actual TProtocol to do the read/write.
|
||||||
|
Delegate TProtocol
|
||||||
|
|
||||||
|
// Optional. The logger and prefix to log all the args/return values
|
||||||
|
// from Delegate TProtocol calls.
|
||||||
|
//
|
||||||
|
// If Logger is nil, StdLogger using stdlib log package with os.Stderr
|
||||||
|
// will be used. If disable logging is desired, set Logger to NopLogger
|
||||||
|
// explicitly instead of leaving it as nil/unset.
|
||||||
|
Logger Logger
|
||||||
|
LogPrefix string
|
||||||
|
|
||||||
|
// Optional. An TProtocol to duplicate everything read/written from Delegate.
|
||||||
|
//
|
||||||
|
// A typical use case of this is to use TSimpleJSONProtocol wrapping
|
||||||
|
// TMemoryBuffer in a middleware to json logging requests/responses.
|
||||||
|
//
|
||||||
|
// This feature is not available from TDebugProtocolFactory. In order to
|
||||||
|
// use it you have to construct TDebugProtocol directly, or set DuplicateTo
|
||||||
|
// field after getting a TDebugProtocol from the factory.
|
||||||
|
DuplicateTo TProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
type TDebugProtocolFactory struct {
|
||||||
|
Underlying TProtocolFactory
|
||||||
|
LogPrefix string
|
||||||
|
Logger Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTDebugProtocolFactory creates a TDebugProtocolFactory.
|
||||||
|
//
|
||||||
|
// Deprecated: Please use NewTDebugProtocolFactoryWithLogger or the struct
|
||||||
|
// itself instead. This version will use the default logger from standard
|
||||||
|
// library.
|
||||||
|
func NewTDebugProtocolFactory(underlying TProtocolFactory, logPrefix string) *TDebugProtocolFactory {
|
||||||
|
return &TDebugProtocolFactory{
|
||||||
|
Underlying: underlying,
|
||||||
|
LogPrefix: logPrefix,
|
||||||
|
Logger: StdLogger(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTDebugProtocolFactoryWithLogger creates a TDebugProtocolFactory.
|
||||||
|
func NewTDebugProtocolFactoryWithLogger(underlying TProtocolFactory, logPrefix string, logger Logger) *TDebugProtocolFactory {
|
||||||
|
return &TDebugProtocolFactory{
|
||||||
|
Underlying: underlying,
|
||||||
|
LogPrefix: logPrefix,
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TDebugProtocolFactory) GetProtocol(trans TTransport) TProtocol {
|
||||||
|
return &TDebugProtocol{
|
||||||
|
Delegate: t.Underlying.GetProtocol(trans),
|
||||||
|
LogPrefix: t.LogPrefix,
|
||||||
|
Logger: fallbackLogger(t.Logger),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tdp *TDebugProtocol) logf(format string, v ...interface{}) {
|
||||||
|
fallbackLogger(tdp.Logger)(fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tdp *TDebugProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error {
|
||||||
|
err := tdp.Delegate.WriteMessageBegin(ctx, name, typeId, seqid)
|
||||||
|
tdp.logf("%sWriteMessageBegin(name=%#v, typeId=%#v, seqid=%#v) => %#v", tdp.LogPrefix, name, typeId, seqid, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMessageBegin(ctx, name, typeId, seqid)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteMessageEnd(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteMessageEnd(ctx)
|
||||||
|
tdp.logf("%sWriteMessageEnd() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMessageEnd(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteStructBegin(ctx context.Context, name string) error {
|
||||||
|
err := tdp.Delegate.WriteStructBegin(ctx, name)
|
||||||
|
tdp.logf("%sWriteStructBegin(name=%#v) => %#v", tdp.LogPrefix, name, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteStructBegin(ctx, name)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteStructEnd(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteStructEnd(ctx)
|
||||||
|
tdp.logf("%sWriteStructEnd() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteStructEnd(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error {
|
||||||
|
err := tdp.Delegate.WriteFieldBegin(ctx, name, typeId, id)
|
||||||
|
tdp.logf("%sWriteFieldBegin(name=%#v, typeId=%#v, id%#v) => %#v", tdp.LogPrefix, name, typeId, id, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteFieldBegin(ctx, name, typeId, id)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteFieldEnd(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteFieldEnd(ctx)
|
||||||
|
tdp.logf("%sWriteFieldEnd() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteFieldEnd(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteFieldStop(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteFieldStop(ctx)
|
||||||
|
tdp.logf("%sWriteFieldStop() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteFieldStop(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
|
||||||
|
err := tdp.Delegate.WriteMapBegin(ctx, keyType, valueType, size)
|
||||||
|
tdp.logf("%sWriteMapBegin(keyType=%#v, valueType=%#v, size=%#v) => %#v", tdp.LogPrefix, keyType, valueType, size, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMapBegin(ctx, keyType, valueType, size)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteMapEnd(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteMapEnd(ctx)
|
||||||
|
tdp.logf("%sWriteMapEnd() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMapEnd(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
err := tdp.Delegate.WriteListBegin(ctx, elemType, size)
|
||||||
|
tdp.logf("%sWriteListBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteListBegin(ctx, elemType, size)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteListEnd(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteListEnd(ctx)
|
||||||
|
tdp.logf("%sWriteListEnd() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteListEnd(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
err := tdp.Delegate.WriteSetBegin(ctx, elemType, size)
|
||||||
|
tdp.logf("%sWriteSetBegin(elemType=%#v, size=%#v) => %#v", tdp.LogPrefix, elemType, size, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteSetBegin(ctx, elemType, size)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteSetEnd(ctx context.Context) error {
|
||||||
|
err := tdp.Delegate.WriteSetEnd(ctx)
|
||||||
|
tdp.logf("%sWriteSetEnd() => %#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteSetEnd(ctx)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteBool(ctx context.Context, value bool) error {
|
||||||
|
err := tdp.Delegate.WriteBool(ctx, value)
|
||||||
|
tdp.logf("%sWriteBool(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteBool(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteByte(ctx context.Context, value int8) error {
|
||||||
|
err := tdp.Delegate.WriteByte(ctx, value)
|
||||||
|
tdp.logf("%sWriteByte(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteByte(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteI16(ctx context.Context, value int16) error {
|
||||||
|
err := tdp.Delegate.WriteI16(ctx, value)
|
||||||
|
tdp.logf("%sWriteI16(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteI16(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteI32(ctx context.Context, value int32) error {
|
||||||
|
err := tdp.Delegate.WriteI32(ctx, value)
|
||||||
|
tdp.logf("%sWriteI32(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteI32(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteI64(ctx context.Context, value int64) error {
|
||||||
|
err := tdp.Delegate.WriteI64(ctx, value)
|
||||||
|
tdp.logf("%sWriteI64(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteI64(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteDouble(ctx context.Context, value float64) error {
|
||||||
|
err := tdp.Delegate.WriteDouble(ctx, value)
|
||||||
|
tdp.logf("%sWriteDouble(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteDouble(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteString(ctx context.Context, value string) error {
|
||||||
|
err := tdp.Delegate.WriteString(ctx, value)
|
||||||
|
tdp.logf("%sWriteString(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteString(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) WriteBinary(ctx context.Context, value []byte) error {
|
||||||
|
err := tdp.Delegate.WriteBinary(ctx, value)
|
||||||
|
tdp.logf("%sWriteBinary(value=%#v) => %#v", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteBinary(ctx, value)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tdp *TDebugProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) {
|
||||||
|
name, typeId, seqid, err = tdp.Delegate.ReadMessageBegin(ctx)
|
||||||
|
tdp.logf("%sReadMessageBegin() (name=%#v, typeId=%#v, seqid=%#v, err=%#v)", tdp.LogPrefix, name, typeId, seqid, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMessageBegin(ctx, name, typeId, seqid)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadMessageEnd(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.ReadMessageEnd(ctx)
|
||||||
|
tdp.logf("%sReadMessageEnd() err=%#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMessageEnd(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
|
||||||
|
name, err = tdp.Delegate.ReadStructBegin(ctx)
|
||||||
|
tdp.logf("%sReadStructBegin() (name%#v, err=%#v)", tdp.LogPrefix, name, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteStructBegin(ctx, name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadStructEnd(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.ReadStructEnd(ctx)
|
||||||
|
tdp.logf("%sReadStructEnd() err=%#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteStructEnd(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) {
|
||||||
|
name, typeId, id, err = tdp.Delegate.ReadFieldBegin(ctx)
|
||||||
|
tdp.logf("%sReadFieldBegin() (name=%#v, typeId=%#v, id=%#v, err=%#v)", tdp.LogPrefix, name, typeId, id, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteFieldBegin(ctx, name, typeId, id)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadFieldEnd(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.ReadFieldEnd(ctx)
|
||||||
|
tdp.logf("%sReadFieldEnd() err=%#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteFieldEnd(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) {
|
||||||
|
keyType, valueType, size, err = tdp.Delegate.ReadMapBegin(ctx)
|
||||||
|
tdp.logf("%sReadMapBegin() (keyType=%#v, valueType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, keyType, valueType, size, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMapBegin(ctx, keyType, valueType, size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadMapEnd(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.ReadMapEnd(ctx)
|
||||||
|
tdp.logf("%sReadMapEnd() err=%#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteMapEnd(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
elemType, size, err = tdp.Delegate.ReadListBegin(ctx)
|
||||||
|
tdp.logf("%sReadListBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteListBegin(ctx, elemType, size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadListEnd(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.ReadListEnd(ctx)
|
||||||
|
tdp.logf("%sReadListEnd() err=%#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteListEnd(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
elemType, size, err = tdp.Delegate.ReadSetBegin(ctx)
|
||||||
|
tdp.logf("%sReadSetBegin() (elemType=%#v, size=%#v, err=%#v)", tdp.LogPrefix, elemType, size, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteSetBegin(ctx, elemType, size)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadSetEnd(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.ReadSetEnd(ctx)
|
||||||
|
tdp.logf("%sReadSetEnd() err=%#v", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteSetEnd(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadBool(ctx context.Context) (value bool, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadBool(ctx)
|
||||||
|
tdp.logf("%sReadBool() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteBool(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadByte(ctx context.Context) (value int8, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadByte(ctx)
|
||||||
|
tdp.logf("%sReadByte() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteByte(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadI16(ctx context.Context) (value int16, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadI16(ctx)
|
||||||
|
tdp.logf("%sReadI16() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteI16(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadI32(ctx context.Context) (value int32, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadI32(ctx)
|
||||||
|
tdp.logf("%sReadI32() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteI32(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadI64(ctx context.Context) (value int64, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadI64(ctx)
|
||||||
|
tdp.logf("%sReadI64() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteI64(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadDouble(ctx context.Context) (value float64, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadDouble(ctx)
|
||||||
|
tdp.logf("%sReadDouble() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteDouble(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadString(ctx context.Context) (value string, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadString(ctx)
|
||||||
|
tdp.logf("%sReadString() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteString(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
|
||||||
|
value, err = tdp.Delegate.ReadBinary(ctx)
|
||||||
|
tdp.logf("%sReadBinary() (value=%#v, err=%#v)", tdp.LogPrefix, value, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.WriteBinary(ctx, value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) Skip(ctx context.Context, fieldType TType) (err error) {
|
||||||
|
err = tdp.Delegate.Skip(ctx, fieldType)
|
||||||
|
tdp.logf("%sSkip(fieldType=%#v) (err=%#v)", tdp.LogPrefix, fieldType, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.Skip(ctx, fieldType)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (tdp *TDebugProtocol) Flush(ctx context.Context) (err error) {
|
||||||
|
err = tdp.Delegate.Flush(ctx)
|
||||||
|
tdp.logf("%sFlush() (err=%#v)", tdp.LogPrefix, err)
|
||||||
|
if tdp.DuplicateTo != nil {
|
||||||
|
tdp.DuplicateTo.Flush(ctx)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tdp *TDebugProtocol) Transport() TTransport {
|
||||||
|
return tdp.Delegate.Transport()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter for propagation.
|
||||||
|
func (tdp *TDebugProtocol) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
PropagateTConfiguration(tdp.Delegate, conf)
|
||||||
|
PropagateTConfiguration(tdp.DuplicateTo, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*TDebugProtocol)(nil)
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TDeserializer struct {
|
||||||
|
Transport *TMemoryBuffer
|
||||||
|
Protocol TProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTDeserializer() *TDeserializer {
|
||||||
|
transport := NewTMemoryBufferLen(1024)
|
||||||
|
protocol := NewTBinaryProtocolTransport(transport)
|
||||||
|
|
||||||
|
return &TDeserializer{
|
||||||
|
Transport: transport,
|
||||||
|
Protocol: protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TDeserializer) ReadString(ctx context.Context, msg TStruct, s string) (err error) {
|
||||||
|
t.Transport.Reset()
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
if _, err = t.Transport.Write([]byte(s)); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = msg.Read(ctx, t.Protocol); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TDeserializer) Read(ctx context.Context, msg TStruct, b []byte) (err error) {
|
||||||
|
t.Transport.Reset()
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
if _, err = t.Transport.Write(b); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = msg.Read(ctx, t.Protocol); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TDeserializerPool is the thread-safe version of TDeserializer,
|
||||||
|
// it uses resource pool of TDeserializer under the hood.
|
||||||
|
//
|
||||||
|
// It must be initialized with either NewTDeserializerPool or
|
||||||
|
// NewTDeserializerPoolSizeFactory.
|
||||||
|
type TDeserializerPool struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTDeserializerPool creates a new TDeserializerPool.
|
||||||
|
//
|
||||||
|
// NewTDeserializer can be used as the arg here.
|
||||||
|
func NewTDeserializerPool(f func() *TDeserializer) *TDeserializerPool {
|
||||||
|
return &TDeserializerPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return f()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTDeserializerPoolSizeFactory creates a new TDeserializerPool with
|
||||||
|
// the given size and protocol factory.
|
||||||
|
//
|
||||||
|
// Note that the size is not the limit. The TMemoryBuffer underneath can grow
|
||||||
|
// larger than that. It just dictates the initial size.
|
||||||
|
func NewTDeserializerPoolSizeFactory(size int, factory TProtocolFactory) *TDeserializerPool {
|
||||||
|
return &TDeserializerPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
transport := NewTMemoryBufferLen(size)
|
||||||
|
protocol := factory.GetProtocol(transport)
|
||||||
|
|
||||||
|
return &TDeserializer{
|
||||||
|
Transport: transport,
|
||||||
|
Protocol: protocol,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TDeserializerPool) ReadString(ctx context.Context, msg TStruct, s string) error {
|
||||||
|
d := t.pool.Get().(*TDeserializer)
|
||||||
|
defer t.pool.Put(d)
|
||||||
|
return d.ReadString(ctx, msg, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TDeserializerPool) Read(ctx context.Context, msg TStruct, b []byte) error {
|
||||||
|
d := t.pool.Get().(*TDeserializer)
|
||||||
|
defer t.pool.Put(d)
|
||||||
|
return d.Read(ctx, msg, b)
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generic Thrift exception
|
||||||
|
type TException interface {
|
||||||
|
error
|
||||||
|
|
||||||
|
TExceptionType() TExceptionType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepends additional information to an error without losing the Thrift exception interface
|
||||||
|
func PrependError(prepend string, err error) error {
|
||||||
|
msg := prepend + err.Error()
|
||||||
|
|
||||||
|
var te TException
|
||||||
|
if errors.As(err, &te) {
|
||||||
|
switch te.TExceptionType() {
|
||||||
|
case TExceptionTypeTransport:
|
||||||
|
if t, ok := err.(TTransportException); ok {
|
||||||
|
return prependTTransportException(prepend, t)
|
||||||
|
}
|
||||||
|
case TExceptionTypeProtocol:
|
||||||
|
if t, ok := err.(TProtocolException); ok {
|
||||||
|
return prependTProtocolException(prepend, t)
|
||||||
|
}
|
||||||
|
case TExceptionTypeApplication:
|
||||||
|
var t TApplicationException
|
||||||
|
if errors.As(err, &t) {
|
||||||
|
return NewTApplicationException(t.TypeId(), msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrappedTException{
|
||||||
|
err: err,
|
||||||
|
msg: msg,
|
||||||
|
tExceptionType: te.TExceptionType(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TExceptionType is an enum type to categorize different "subclasses" of TExceptions.
|
||||||
|
type TExceptionType byte
|
||||||
|
|
||||||
|
// TExceptionType values
|
||||||
|
const (
|
||||||
|
TExceptionTypeUnknown TExceptionType = iota
|
||||||
|
TExceptionTypeCompiled // TExceptions defined in thrift files and generated by thrift compiler
|
||||||
|
TExceptionTypeApplication // TApplicationExceptions
|
||||||
|
TExceptionTypeProtocol // TProtocolExceptions
|
||||||
|
TExceptionTypeTransport // TTransportExceptions
|
||||||
|
)
|
||||||
|
|
||||||
|
// WrapTException wraps an error into TException.
|
||||||
|
//
|
||||||
|
// If err is nil or already TException, it's returned as-is.
|
||||||
|
// Otherwise it will be wraped into TException with TExceptionType() returning
|
||||||
|
// TExceptionTypeUnknown, and Unwrap() returning the original error.
|
||||||
|
func WrapTException(err error) TException {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if te, ok := err.(TException); ok {
|
||||||
|
return te
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrappedTException{
|
||||||
|
err: err,
|
||||||
|
msg: err.Error(),
|
||||||
|
tExceptionType: TExceptionTypeUnknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrappedTException struct {
|
||||||
|
err error
|
||||||
|
msg string
|
||||||
|
tExceptionType TExceptionType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w wrappedTException) Error() string {
|
||||||
|
return w.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w wrappedTException) TExceptionType() TExceptionType {
|
||||||
|
return w.tExceptionType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w wrappedTException) Unwrap() error {
|
||||||
|
return w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TException = wrappedTException{}
|
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Deprecated: Use DEFAULT_MAX_FRAME_SIZE instead.
|
||||||
|
const DEFAULT_MAX_LENGTH = 16384000
|
||||||
|
|
||||||
|
type TFramedTransport struct {
|
||||||
|
transport TTransport
|
||||||
|
|
||||||
|
cfg *TConfiguration
|
||||||
|
|
||||||
|
writeBuf bytes.Buffer
|
||||||
|
|
||||||
|
reader *bufio.Reader
|
||||||
|
readBuf bytes.Buffer
|
||||||
|
|
||||||
|
buffer [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type tFramedTransportFactory struct {
|
||||||
|
factory TTransportFactory
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTFramedTransportFactoryConf instead.
|
||||||
|
func NewTFramedTransportFactory(factory TTransportFactory) TTransportFactory {
|
||||||
|
return NewTFramedTransportFactoryConf(factory, &TConfiguration{
|
||||||
|
MaxFrameSize: DEFAULT_MAX_LENGTH,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTFramedTransportFactoryConf instead.
|
||||||
|
func NewTFramedTransportFactoryMaxLength(factory TTransportFactory, maxLength uint32) TTransportFactory {
|
||||||
|
return NewTFramedTransportFactoryConf(factory, &TConfiguration{
|
||||||
|
MaxFrameSize: int32(maxLength),
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTFramedTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory {
|
||||||
|
PropagateTConfiguration(factory, conf)
|
||||||
|
return &tFramedTransportFactory{
|
||||||
|
factory: factory,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tFramedTransportFactory) GetTransport(base TTransport) (TTransport, error) {
|
||||||
|
PropagateTConfiguration(base, p.cfg)
|
||||||
|
tt, err := p.factory.GetTransport(base)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewTFramedTransportConf(tt, p.cfg), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tFramedTransportFactory) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.factory, cfg)
|
||||||
|
p.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTFramedTransportConf instead.
|
||||||
|
func NewTFramedTransport(transport TTransport) *TFramedTransport {
|
||||||
|
return NewTFramedTransportConf(transport, &TConfiguration{
|
||||||
|
MaxFrameSize: DEFAULT_MAX_LENGTH,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTFramedTransportConf instead.
|
||||||
|
func NewTFramedTransportMaxLength(transport TTransport, maxLength uint32) *TFramedTransport {
|
||||||
|
return NewTFramedTransportConf(transport, &TConfiguration{
|
||||||
|
MaxFrameSize: int32(maxLength),
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTFramedTransportConf(transport TTransport, conf *TConfiguration) *TFramedTransport {
|
||||||
|
PropagateTConfiguration(transport, conf)
|
||||||
|
return &TFramedTransport{
|
||||||
|
transport: transport,
|
||||||
|
reader: bufio.NewReader(transport),
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) Open() error {
|
||||||
|
return p.transport.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) IsOpen() bool {
|
||||||
|
return p.transport.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) Close() error {
|
||||||
|
return p.transport.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) Read(buf []byte) (read int, err error) {
|
||||||
|
read, err = p.readBuf.Read(buf)
|
||||||
|
if err != io.EOF {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// For bytes.Buffer.Read, EOF would only happen when read is zero,
|
||||||
|
// but still, do a sanity check,
|
||||||
|
// in case that behavior is changed in a future version of go stdlib.
|
||||||
|
// When that happens, just return nil error,
|
||||||
|
// and let the caller call Read again to read the next frame.
|
||||||
|
if read > 0 {
|
||||||
|
return read, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reaching here means that the last Read finished the last frame,
|
||||||
|
// so we need to read the next frame into readBuf now.
|
||||||
|
if err = p.readFrame(); err != nil {
|
||||||
|
return read, err
|
||||||
|
}
|
||||||
|
newRead, err := p.Read(buf[read:])
|
||||||
|
return read + newRead, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) ReadByte() (c byte, err error) {
|
||||||
|
buf := p.buffer[:1]
|
||||||
|
_, err = p.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c = buf[0]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) Write(buf []byte) (int, error) {
|
||||||
|
n, err := p.writeBuf.Write(buf)
|
||||||
|
return n, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) WriteByte(c byte) error {
|
||||||
|
return p.writeBuf.WriteByte(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) WriteString(s string) (n int, err error) {
|
||||||
|
return p.writeBuf.WriteString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) Flush(ctx context.Context) error {
|
||||||
|
size := p.writeBuf.Len()
|
||||||
|
buf := p.buffer[:4]
|
||||||
|
binary.BigEndian.PutUint32(buf, uint32(size))
|
||||||
|
_, err := p.transport.Write(buf)
|
||||||
|
if err != nil {
|
||||||
|
p.writeBuf.Reset()
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if size > 0 {
|
||||||
|
if _, err := io.Copy(p.transport, &p.writeBuf); err != nil {
|
||||||
|
p.writeBuf.Reset()
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = p.transport.Flush(ctx)
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) readFrame() error {
|
||||||
|
buf := p.buffer[:4]
|
||||||
|
if _, err := io.ReadFull(p.reader, buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
size := binary.BigEndian.Uint32(buf)
|
||||||
|
if size < 0 || size > uint32(p.cfg.GetMaxFrameSize()) {
|
||||||
|
return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, fmt.Sprintf("Incorrect frame size (%d)", size))
|
||||||
|
}
|
||||||
|
_, err := io.CopyN(&p.readBuf, p.reader, int64(size))
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TFramedTransport) RemainingBytes() (num_bytes uint64) {
|
||||||
|
return uint64(p.readBuf.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter.
|
||||||
|
func (p *TFramedTransport) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.transport, cfg)
|
||||||
|
p.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ TConfigurationSetter = (*tFramedTransportFactory)(nil)
|
||||||
|
_ TConfigurationSetter = (*TFramedTransport)(nil)
|
||||||
|
)
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://godoc.org/context#WithValue on why do we need the unexported typedefs.
|
||||||
|
type (
|
||||||
|
headerKey string
|
||||||
|
headerKeyList int
|
||||||
|
)
|
||||||
|
|
||||||
|
// Values for headerKeyList.
|
||||||
|
const (
|
||||||
|
headerKeyListRead headerKeyList = iota
|
||||||
|
headerKeyListWrite
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetHeader sets a header in the context.
|
||||||
|
func SetHeader(ctx context.Context, key, value string) context.Context {
|
||||||
|
return context.WithValue(
|
||||||
|
ctx,
|
||||||
|
headerKey(key),
|
||||||
|
value,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsetHeader unsets a previously set header in the context.
|
||||||
|
func UnsetHeader(ctx context.Context, key string) context.Context {
|
||||||
|
return context.WithValue(
|
||||||
|
ctx,
|
||||||
|
headerKey(key),
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeader returns a value of the given header from the context.
|
||||||
|
func GetHeader(ctx context.Context, key string) (value string, ok bool) {
|
||||||
|
if v := ctx.Value(headerKey(key)); v != nil {
|
||||||
|
value, ok = v.(string)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReadHeaderList sets the key list of read THeaders in the context.
|
||||||
|
func SetReadHeaderList(ctx context.Context, keys []string) context.Context {
|
||||||
|
return context.WithValue(
|
||||||
|
ctx,
|
||||||
|
headerKeyListRead,
|
||||||
|
keys,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReadHeaderList returns the key list of read THeaders from the context.
|
||||||
|
func GetReadHeaderList(ctx context.Context) []string {
|
||||||
|
if v := ctx.Value(headerKeyListRead); v != nil {
|
||||||
|
if value, ok := v.([]string); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteHeaderList sets the key list of THeaders to write in the context.
|
||||||
|
func SetWriteHeaderList(ctx context.Context, keys []string) context.Context {
|
||||||
|
return context.WithValue(
|
||||||
|
ctx,
|
||||||
|
headerKeyListWrite,
|
||||||
|
keys,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWriteHeaderList returns the key list of THeaders to write from the context.
|
||||||
|
func GetWriteHeaderList(ctx context.Context) []string {
|
||||||
|
if v := ctx.Value(headerKeyListWrite); v != nil {
|
||||||
|
if value, ok := v.([]string); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReadTHeaderToContext adds the whole THeader headers into context.
|
||||||
|
func AddReadTHeaderToContext(ctx context.Context, headers THeaderMap) context.Context {
|
||||||
|
keys := make([]string, 0, len(headers))
|
||||||
|
for key, value := range headers {
|
||||||
|
ctx = SetHeader(ctx, key, value)
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
return SetReadHeaderList(ctx, keys)
|
||||||
|
}
|
@ -0,0 +1,351 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THeaderProtocol is a thrift protocol that implements THeader:
|
||||||
|
// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md
|
||||||
|
//
|
||||||
|
// It supports either binary or compact protocol as the wrapped protocol.
|
||||||
|
//
|
||||||
|
// Most of the THeader handlings are happening inside THeaderTransport.
|
||||||
|
type THeaderProtocol struct {
|
||||||
|
transport *THeaderTransport
|
||||||
|
|
||||||
|
// Will be initialized on first read/write.
|
||||||
|
protocol TProtocol
|
||||||
|
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHeaderProtocolConf instead.
|
||||||
|
func NewTHeaderProtocol(trans TTransport) *THeaderProtocol {
|
||||||
|
return newTHeaderProtocolConf(trans, &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTHeaderProtocolConf creates a new THeaderProtocol from the underlying
|
||||||
|
// transport with given TConfiguration.
|
||||||
|
//
|
||||||
|
// The passed in transport will be wrapped with THeaderTransport.
|
||||||
|
//
|
||||||
|
// Note that THeaderTransport handles frame and zlib by itself,
|
||||||
|
// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket),
|
||||||
|
// instead of rich transports like TZlibTransport or TFramedTransport.
|
||||||
|
func NewTHeaderProtocolConf(trans TTransport, conf *TConfiguration) *THeaderProtocol {
|
||||||
|
return newTHeaderProtocolConf(trans, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTHeaderProtocolConf(trans TTransport, cfg *TConfiguration) *THeaderProtocol {
|
||||||
|
t := NewTHeaderTransportConf(trans, cfg)
|
||||||
|
p, _ := t.cfg.GetTHeaderProtocolID().GetProtocol(t)
|
||||||
|
PropagateTConfiguration(p, cfg)
|
||||||
|
return &THeaderProtocol{
|
||||||
|
transport: t,
|
||||||
|
protocol: p,
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tHeaderProtocolFactory struct {
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f tHeaderProtocolFactory) GetProtocol(trans TTransport) TProtocol {
|
||||||
|
return newTHeaderProtocolConf(trans, f.cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *tHeaderProtocolFactory) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
f.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHeaderProtocolFactoryConf instead.
|
||||||
|
func NewTHeaderProtocolFactory() TProtocolFactory {
|
||||||
|
return NewTHeaderProtocolFactoryConf(&TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTHeaderProtocolFactoryConf creates a factory for THeader with given
|
||||||
|
// TConfiguration.
|
||||||
|
func NewTHeaderProtocolFactoryConf(conf *TConfiguration) TProtocolFactory {
|
||||||
|
return tHeaderProtocolFactory{
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transport returns the underlying transport.
|
||||||
|
//
|
||||||
|
// It's guaranteed to be of type *THeaderTransport.
|
||||||
|
func (p *THeaderProtocol) Transport() TTransport {
|
||||||
|
return p.transport
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReadHeaders returns the THeaderMap read from transport.
|
||||||
|
func (p *THeaderProtocol) GetReadHeaders() THeaderMap {
|
||||||
|
return p.transport.GetReadHeaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteHeader sets a header for write.
|
||||||
|
func (p *THeaderProtocol) SetWriteHeader(key, value string) {
|
||||||
|
p.transport.SetWriteHeader(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearWriteHeaders clears all write headers previously set.
|
||||||
|
func (p *THeaderProtocol) ClearWriteHeaders() {
|
||||||
|
p.transport.ClearWriteHeaders()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransform add a transform for writing.
|
||||||
|
func (p *THeaderProtocol) AddTransform(transform THeaderTransformID) error {
|
||||||
|
return p.transport.AddTransform(transform)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) Flush(ctx context.Context) error {
|
||||||
|
return p.transport.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteMessageBegin(ctx context.Context, name string, typeID TMessageType, seqID int32) error {
|
||||||
|
newProto, err := p.transport.Protocol().GetProtocol(p.transport)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
PropagateTConfiguration(newProto, p.cfg)
|
||||||
|
p.protocol = newProto
|
||||||
|
p.transport.SequenceID = seqID
|
||||||
|
return p.protocol.WriteMessageBegin(ctx, name, typeID, seqID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteMessageEnd(ctx context.Context) error {
|
||||||
|
if err := p.protocol.WriteMessageEnd(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return p.transport.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteStructBegin(ctx context.Context, name string) error {
|
||||||
|
return p.protocol.WriteStructBegin(ctx, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteStructEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.WriteStructEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteFieldBegin(ctx context.Context, name string, typeID TType, id int16) error {
|
||||||
|
return p.protocol.WriteFieldBegin(ctx, name, typeID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteFieldEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.WriteFieldEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteFieldStop(ctx context.Context) error {
|
||||||
|
return p.protocol.WriteFieldStop(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
|
||||||
|
return p.protocol.WriteMapBegin(ctx, keyType, valueType, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteMapEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.WriteMapEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
return p.protocol.WriteListBegin(ctx, elemType, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteListEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.WriteListEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
return p.protocol.WriteSetBegin(ctx, elemType, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteSetEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.WriteSetEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteBool(ctx context.Context, value bool) error {
|
||||||
|
return p.protocol.WriteBool(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteByte(ctx context.Context, value int8) error {
|
||||||
|
return p.protocol.WriteByte(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteI16(ctx context.Context, value int16) error {
|
||||||
|
return p.protocol.WriteI16(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteI32(ctx context.Context, value int32) error {
|
||||||
|
return p.protocol.WriteI32(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteI64(ctx context.Context, value int64) error {
|
||||||
|
return p.protocol.WriteI64(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteDouble(ctx context.Context, value float64) error {
|
||||||
|
return p.protocol.WriteDouble(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteString(ctx context.Context, value string) error {
|
||||||
|
return p.protocol.WriteString(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) WriteBinary(ctx context.Context, value []byte) error {
|
||||||
|
return p.protocol.WriteBinary(ctx, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrame calls underlying THeaderTransport's ReadFrame function.
|
||||||
|
func (p *THeaderProtocol) ReadFrame(ctx context.Context) error {
|
||||||
|
return p.transport.ReadFrame(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadMessageBegin(ctx context.Context) (name string, typeID TMessageType, seqID int32, err error) {
|
||||||
|
if err = p.transport.ReadFrame(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var newProto TProtocol
|
||||||
|
newProto, err = p.transport.Protocol().GetProtocol(p.transport)
|
||||||
|
if err != nil {
|
||||||
|
var tAppExc TApplicationException
|
||||||
|
if !errors.As(err, &tAppExc) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := p.protocol.WriteMessageBegin(ctx, "", EXCEPTION, seqID); e != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := tAppExc.Write(ctx, p.protocol); e != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := p.protocol.WriteMessageEnd(ctx); e != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e := p.transport.Flush(ctx); e != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
PropagateTConfiguration(newProto, p.cfg)
|
||||||
|
p.protocol = newProto
|
||||||
|
|
||||||
|
return p.protocol.ReadMessageBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadMessageEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.ReadMessageEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
|
||||||
|
return p.protocol.ReadStructBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadStructEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.ReadStructEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadFieldBegin(ctx context.Context) (name string, typeID TType, id int16, err error) {
|
||||||
|
return p.protocol.ReadFieldBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadFieldEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.ReadFieldEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) {
|
||||||
|
return p.protocol.ReadMapBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadMapEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.ReadMapEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
return p.protocol.ReadListBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadListEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.ReadListEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) {
|
||||||
|
return p.protocol.ReadSetBegin(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadSetEnd(ctx context.Context) error {
|
||||||
|
return p.protocol.ReadSetEnd(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadBool(ctx context.Context) (value bool, err error) {
|
||||||
|
return p.protocol.ReadBool(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadByte(ctx context.Context) (value int8, err error) {
|
||||||
|
return p.protocol.ReadByte(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadI16(ctx context.Context) (value int16, err error) {
|
||||||
|
return p.protocol.ReadI16(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadI32(ctx context.Context) (value int32, err error) {
|
||||||
|
return p.protocol.ReadI32(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadI64(ctx context.Context) (value int64, err error) {
|
||||||
|
return p.protocol.ReadI64(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadDouble(ctx context.Context) (value float64, err error) {
|
||||||
|
return p.protocol.ReadDouble(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadString(ctx context.Context) (value string, err error) {
|
||||||
|
return p.protocol.ReadString(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) ReadBinary(ctx context.Context) (value []byte, err error) {
|
||||||
|
return p.protocol.ReadBinary(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THeaderProtocol) Skip(ctx context.Context, fieldType TType) error {
|
||||||
|
return p.protocol.Skip(ctx, fieldType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter.
|
||||||
|
func (p *THeaderProtocol) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.transport, cfg)
|
||||||
|
PropagateTConfiguration(p.protocol, cfg)
|
||||||
|
p.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ TConfigurationSetter = (*tHeaderProtocolFactory)(nil)
|
||||||
|
_ TConfigurationSetter = (*THeaderProtocol)(nil)
|
||||||
|
)
|
@ -0,0 +1,809 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"compress/zlib"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Size in bytes for 32-bit ints.
|
||||||
|
const size32 = 4
|
||||||
|
|
||||||
|
type headerMeta struct {
|
||||||
|
MagicFlags uint32
|
||||||
|
SequenceID int32
|
||||||
|
HeaderLength uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerMetaSize = 10
|
||||||
|
|
||||||
|
type clientType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
clientUnknown clientType = iota
|
||||||
|
clientHeaders
|
||||||
|
clientFramedBinary
|
||||||
|
clientUnframedBinary
|
||||||
|
clientFramedCompact
|
||||||
|
clientUnframedCompact
|
||||||
|
)
|
||||||
|
|
||||||
|
// Constants defined in THeader format:
|
||||||
|
// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md
|
||||||
|
const (
|
||||||
|
THeaderHeaderMagic uint32 = 0x0fff0000
|
||||||
|
THeaderHeaderMask uint32 = 0xffff0000
|
||||||
|
THeaderFlagsMask uint32 = 0x0000ffff
|
||||||
|
THeaderMaxFrameSize uint32 = 0x3fffffff
|
||||||
|
)
|
||||||
|
|
||||||
|
// THeaderMap is the type of the header map in THeader transport.
|
||||||
|
type THeaderMap map[string]string
|
||||||
|
|
||||||
|
// THeaderProtocolID is the wrapped protocol id used in THeader.
|
||||||
|
type THeaderProtocolID int32
|
||||||
|
|
||||||
|
// Supported THeaderProtocolID values.
|
||||||
|
const (
|
||||||
|
THeaderProtocolBinary THeaderProtocolID = 0x00
|
||||||
|
THeaderProtocolCompact THeaderProtocolID = 0x02
|
||||||
|
THeaderProtocolDefault = THeaderProtocolBinary
|
||||||
|
)
|
||||||
|
|
||||||
|
// Declared globally to avoid repetitive allocations, not really used.
|
||||||
|
var globalMemoryBuffer = NewTMemoryBuffer()
|
||||||
|
|
||||||
|
// Validate checks whether the THeaderProtocolID is a valid/supported one.
|
||||||
|
func (id THeaderProtocolID) Validate() error {
|
||||||
|
_, err := id.GetProtocol(globalMemoryBuffer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProtocol gets the corresponding TProtocol from the wrapped protocol id.
|
||||||
|
func (id THeaderProtocolID) GetProtocol(trans TTransport) (TProtocol, error) {
|
||||||
|
switch id {
|
||||||
|
default:
|
||||||
|
return nil, NewTApplicationException(
|
||||||
|
INVALID_PROTOCOL,
|
||||||
|
fmt.Sprintf("THeader protocol id %d not supported", id),
|
||||||
|
)
|
||||||
|
case THeaderProtocolBinary:
|
||||||
|
return NewTBinaryProtocolTransport(trans), nil
|
||||||
|
case THeaderProtocolCompact:
|
||||||
|
return NewTCompactProtocol(trans), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// THeaderTransformID defines the numeric id of the transform used.
|
||||||
|
type THeaderTransformID int32
|
||||||
|
|
||||||
|
// THeaderTransformID values.
|
||||||
|
//
|
||||||
|
// Values not defined here are not currently supported, namely HMAC and Snappy.
|
||||||
|
const (
|
||||||
|
TransformNone THeaderTransformID = iota // 0, no special handling
|
||||||
|
TransformZlib // 1, zlib
|
||||||
|
)
|
||||||
|
|
||||||
|
var supportedTransformIDs = map[THeaderTransformID]bool{
|
||||||
|
TransformNone: true,
|
||||||
|
TransformZlib: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransformReader is an io.ReadCloser that handles transforms reading.
|
||||||
|
type TransformReader struct {
|
||||||
|
io.Reader
|
||||||
|
|
||||||
|
closers []io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ io.ReadCloser = (*TransformReader)(nil)
|
||||||
|
|
||||||
|
// NewTransformReaderWithCapacity initializes a TransformReader with expected
|
||||||
|
// closers capacity.
|
||||||
|
//
|
||||||
|
// If you don't know the closers capacity beforehand, just use
|
||||||
|
//
|
||||||
|
// &TransformReader{Reader: baseReader}
|
||||||
|
//
|
||||||
|
// instead would be sufficient.
|
||||||
|
func NewTransformReaderWithCapacity(baseReader io.Reader, capacity int) *TransformReader {
|
||||||
|
return &TransformReader{
|
||||||
|
Reader: baseReader,
|
||||||
|
closers: make([]io.Closer, 0, capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close calls the underlying closers in appropriate order,
|
||||||
|
// stops at and returns the first error encountered.
|
||||||
|
func (tr *TransformReader) Close() error {
|
||||||
|
// Call closers in reversed order
|
||||||
|
for i := len(tr.closers) - 1; i >= 0; i-- {
|
||||||
|
if err := tr.closers[i].Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransform adds a transform.
|
||||||
|
func (tr *TransformReader) AddTransform(id THeaderTransformID) error {
|
||||||
|
switch id {
|
||||||
|
default:
|
||||||
|
return NewTApplicationException(
|
||||||
|
INVALID_TRANSFORM,
|
||||||
|
fmt.Sprintf("THeaderTransformID %d not supported", id),
|
||||||
|
)
|
||||||
|
case TransformNone:
|
||||||
|
// no-op
|
||||||
|
case TransformZlib:
|
||||||
|
readCloser, err := zlib.NewReader(tr.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tr.Reader = readCloser
|
||||||
|
tr.closers = append(tr.closers, readCloser)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransformWriter is an io.WriteCloser that handles transforms writing.
|
||||||
|
type TransformWriter struct {
|
||||||
|
io.Writer
|
||||||
|
|
||||||
|
closers []io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ io.WriteCloser = (*TransformWriter)(nil)
|
||||||
|
|
||||||
|
// NewTransformWriter creates a new TransformWriter with base writer and transforms.
|
||||||
|
func NewTransformWriter(baseWriter io.Writer, transforms []THeaderTransformID) (io.WriteCloser, error) {
|
||||||
|
writer := &TransformWriter{
|
||||||
|
Writer: baseWriter,
|
||||||
|
closers: make([]io.Closer, 0, len(transforms)),
|
||||||
|
}
|
||||||
|
for _, id := range transforms {
|
||||||
|
if err := writer.AddTransform(id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return writer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close calls the underlying closers in appropriate order,
|
||||||
|
// stops at and returns the first error encountered.
|
||||||
|
func (tw *TransformWriter) Close() error {
|
||||||
|
// Call closers in reversed order
|
||||||
|
for i := len(tw.closers) - 1; i >= 0; i-- {
|
||||||
|
if err := tw.closers[i].Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransform adds a transform.
|
||||||
|
func (tw *TransformWriter) AddTransform(id THeaderTransformID) error {
|
||||||
|
switch id {
|
||||||
|
default:
|
||||||
|
return NewTApplicationException(
|
||||||
|
INVALID_TRANSFORM,
|
||||||
|
fmt.Sprintf("THeaderTransformID %d not supported", id),
|
||||||
|
)
|
||||||
|
case TransformNone:
|
||||||
|
// no-op
|
||||||
|
case TransformZlib:
|
||||||
|
writeCloser := zlib.NewWriter(tw.Writer)
|
||||||
|
tw.Writer = writeCloser
|
||||||
|
tw.closers = append(tw.closers, writeCloser)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// THeaderInfoType is the type id of the info headers.
|
||||||
|
type THeaderInfoType int32
|
||||||
|
|
||||||
|
// Supported THeaderInfoType values.
|
||||||
|
const (
|
||||||
|
_ THeaderInfoType = iota // Skip 0
|
||||||
|
InfoKeyValue // 1
|
||||||
|
// Rest of the info types are not supported.
|
||||||
|
)
|
||||||
|
|
||||||
|
// THeaderTransport is a Transport mode that implements THeader.
|
||||||
|
//
|
||||||
|
// Note that THeaderTransport handles frame and zlib by itself,
|
||||||
|
// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket),
|
||||||
|
// instead of rich transports like TZlibTransport or TFramedTransport.
|
||||||
|
type THeaderTransport struct {
|
||||||
|
SequenceID int32
|
||||||
|
Flags uint32
|
||||||
|
|
||||||
|
transport TTransport
|
||||||
|
|
||||||
|
// THeaderMap for read and write
|
||||||
|
readHeaders THeaderMap
|
||||||
|
writeHeaders THeaderMap
|
||||||
|
|
||||||
|
// Reading related variables.
|
||||||
|
reader *bufio.Reader
|
||||||
|
// When frame is detected, we read the frame fully into frameBuffer.
|
||||||
|
frameBuffer bytes.Buffer
|
||||||
|
// When it's non-nil, Read should read from frameReader instead of
|
||||||
|
// reader, and EOF error indicates end of frame instead of end of all
|
||||||
|
// transport.
|
||||||
|
frameReader io.ReadCloser
|
||||||
|
|
||||||
|
// Writing related variables
|
||||||
|
writeBuffer bytes.Buffer
|
||||||
|
writeTransforms []THeaderTransformID
|
||||||
|
|
||||||
|
clientType clientType
|
||||||
|
protocolID THeaderProtocolID
|
||||||
|
cfg *TConfiguration
|
||||||
|
|
||||||
|
// buffer is used in the following scenarios to avoid repetitive
|
||||||
|
// allocations, while 4 is big enough for all those scenarios:
|
||||||
|
//
|
||||||
|
// * header padding (max size 4)
|
||||||
|
// * write the frame size (size 4)
|
||||||
|
buffer [4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TTransport = (*THeaderTransport)(nil)
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHeaderTransportConf instead.
|
||||||
|
func NewTHeaderTransport(trans TTransport) *THeaderTransport {
|
||||||
|
return NewTHeaderTransportConf(trans, &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTHeaderTransportConf creates THeaderTransport from the
|
||||||
|
// underlying transport, with given TConfiguration attached.
|
||||||
|
//
|
||||||
|
// If trans is already a *THeaderTransport, it will be returned as is,
|
||||||
|
// but with TConfiguration overridden by the value passed in.
|
||||||
|
//
|
||||||
|
// The protocol ID in TConfiguration is only useful for client transports.
|
||||||
|
// For servers,
|
||||||
|
// the protocol ID will be overridden again to the one set by the client,
|
||||||
|
// to ensure that servers always speak the same dialect as the client.
|
||||||
|
func NewTHeaderTransportConf(trans TTransport, conf *TConfiguration) *THeaderTransport {
|
||||||
|
if ht, ok := trans.(*THeaderTransport); ok {
|
||||||
|
ht.SetTConfiguration(conf)
|
||||||
|
return ht
|
||||||
|
}
|
||||||
|
PropagateTConfiguration(trans, conf)
|
||||||
|
return &THeaderTransport{
|
||||||
|
transport: trans,
|
||||||
|
reader: bufio.NewReader(trans),
|
||||||
|
writeHeaders: make(THeaderMap),
|
||||||
|
protocolID: conf.GetTHeaderProtocolID(),
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open calls the underlying transport's Open function.
|
||||||
|
func (t *THeaderTransport) Open() error {
|
||||||
|
return t.transport.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOpen calls the underlying transport's IsOpen function.
|
||||||
|
func (t *THeaderTransport) IsOpen() bool {
|
||||||
|
return t.transport.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrame tries to read the frame header, guess the client type, and handle
|
||||||
|
// unframed clients.
|
||||||
|
func (t *THeaderTransport) ReadFrame(ctx context.Context) error {
|
||||||
|
if !t.needReadFrame() {
|
||||||
|
// No need to read frame, skipping.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peek and handle the first 32 bits.
|
||||||
|
// They could either be the length field of a framed message,
|
||||||
|
// or the first bytes of an unframed message.
|
||||||
|
var buf []byte
|
||||||
|
var err error
|
||||||
|
// This is also usually the first read from a connection,
|
||||||
|
// so handle retries around socket timeouts.
|
||||||
|
_, deadlineSet := ctx.Deadline()
|
||||||
|
for {
|
||||||
|
buf, err = t.reader.Peek(size32)
|
||||||
|
if deadlineSet && isTimeoutError(err) && ctx.Err() == nil {
|
||||||
|
// This is I/O timeout and we still have time,
|
||||||
|
// continue trying
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// For anything else, do not retry
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
frameSize := binary.BigEndian.Uint32(buf)
|
||||||
|
if frameSize&VERSION_MASK == VERSION_1 {
|
||||||
|
t.clientType = clientUnframedBinary
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION {
|
||||||
|
t.clientType = clientUnframedCompact
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point it should be a framed message,
|
||||||
|
// sanity check on frameSize then discard the peeked part.
|
||||||
|
if frameSize > THeaderMaxFrameSize || frameSize > uint32(t.cfg.GetMaxFrameSize()) {
|
||||||
|
return NewTProtocolExceptionWithType(
|
||||||
|
SIZE_LIMIT,
|
||||||
|
errors.New("frame too large"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
t.reader.Discard(size32)
|
||||||
|
|
||||||
|
// Read the frame fully into frameBuffer.
|
||||||
|
_, err = io.CopyN(&t.frameBuffer, t.reader, int64(frameSize))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.frameReader = io.NopCloser(&t.frameBuffer)
|
||||||
|
|
||||||
|
// Peek and handle the next 32 bits.
|
||||||
|
buf = t.frameBuffer.Bytes()[:size32]
|
||||||
|
version := binary.BigEndian.Uint32(buf)
|
||||||
|
if version&THeaderHeaderMask == THeaderHeaderMagic {
|
||||||
|
t.clientType = clientHeaders
|
||||||
|
return t.parseHeaders(ctx, frameSize)
|
||||||
|
}
|
||||||
|
if version&VERSION_MASK == VERSION_1 {
|
||||||
|
t.clientType = clientFramedBinary
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION {
|
||||||
|
t.clientType = clientFramedCompact
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := t.endOfFrame(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return NewTProtocolExceptionWithType(
|
||||||
|
NOT_IMPLEMENTED,
|
||||||
|
errors.New("unsupported client transport type"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endOfFrame does end of frame handling.
|
||||||
|
//
|
||||||
|
// It closes frameReader, and also resets frame related states.
|
||||||
|
func (t *THeaderTransport) endOfFrame() error {
|
||||||
|
defer func() {
|
||||||
|
t.frameBuffer.Reset()
|
||||||
|
t.frameReader = nil
|
||||||
|
}()
|
||||||
|
return t.frameReader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *THeaderTransport) parseHeaders(ctx context.Context, frameSize uint32) error {
|
||||||
|
if t.clientType != clientHeaders {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var meta headerMeta
|
||||||
|
if err = binary.Read(&t.frameBuffer, binary.BigEndian, &meta); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
frameSize -= headerMetaSize
|
||||||
|
t.Flags = meta.MagicFlags & THeaderFlagsMask
|
||||||
|
t.SequenceID = meta.SequenceID
|
||||||
|
headerLength := int64(meta.HeaderLength) * 4
|
||||||
|
if int64(frameSize) < headerLength {
|
||||||
|
return NewTProtocolExceptionWithType(
|
||||||
|
SIZE_LIMIT,
|
||||||
|
errors.New("header size is larger than the whole frame"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
headerBuf := NewTMemoryBuffer()
|
||||||
|
_, err = io.CopyN(headerBuf, &t.frameBuffer, headerLength)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hp := NewTCompactProtocol(headerBuf)
|
||||||
|
hp.SetTConfiguration(t.cfg)
|
||||||
|
|
||||||
|
// At this point the header is already read into headerBuf,
|
||||||
|
// and t.frameBuffer starts from the actual payload.
|
||||||
|
protoID, err := hp.readVarint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.protocolID = THeaderProtocolID(protoID)
|
||||||
|
|
||||||
|
var transformCount int32
|
||||||
|
transformCount, err = hp.readVarint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if transformCount > 0 {
|
||||||
|
reader := NewTransformReaderWithCapacity(
|
||||||
|
&t.frameBuffer,
|
||||||
|
int(transformCount),
|
||||||
|
)
|
||||||
|
t.frameReader = reader
|
||||||
|
transformIDs := make([]THeaderTransformID, transformCount)
|
||||||
|
for i := 0; i < int(transformCount); i++ {
|
||||||
|
id, err := hp.readVarint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
transformIDs[i] = THeaderTransformID(id)
|
||||||
|
}
|
||||||
|
// The transform IDs on the wire was added based on the order of
|
||||||
|
// writing, so on the reading side we need to reverse the order.
|
||||||
|
for i := transformCount - 1; i >= 0; i-- {
|
||||||
|
id := transformIDs[i]
|
||||||
|
if err := reader.AddTransform(id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The info part does not use the transforms yet, so it's
|
||||||
|
// important to continue using headerBuf.
|
||||||
|
headers := make(THeaderMap)
|
||||||
|
for {
|
||||||
|
infoType, err := hp.readVarint32()
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if THeaderInfoType(infoType) == InfoKeyValue {
|
||||||
|
count, err := hp.readVarint32()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
key, err := hp.ReadString(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value, err := hp.ReadString(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
headers[key] = value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip reading info section on the first
|
||||||
|
// unsupported info type.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.readHeaders = headers
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *THeaderTransport) needReadFrame() bool {
|
||||||
|
if t.clientType == clientUnknown {
|
||||||
|
// This is a new connection that's never read before.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if t.isFramed() && t.frameReader == nil {
|
||||||
|
// We just finished the last frame.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *THeaderTransport) Read(p []byte) (read int, err error) {
|
||||||
|
// Here using context.Background instead of a context passed in is safe.
|
||||||
|
// First is that there's no way to pass context into this function.
|
||||||
|
// Then, 99% of the case when calling this Read frame is already read
|
||||||
|
// into frameReader. ReadFrame here is more of preventing bugs that
|
||||||
|
// didn't call ReadFrame before calling Read.
|
||||||
|
err = t.ReadFrame(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if t.frameReader != nil {
|
||||||
|
read, err = t.frameReader.Read(p)
|
||||||
|
if err == nil && t.frameBuffer.Len() <= 0 {
|
||||||
|
// the last Read finished the frame, do endOfFrame
|
||||||
|
// handling here.
|
||||||
|
err = t.endOfFrame()
|
||||||
|
} else if err == io.EOF {
|
||||||
|
err = t.endOfFrame()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if read == 0 {
|
||||||
|
// Try to read the next frame when we hit EOF
|
||||||
|
// (end of frame) immediately.
|
||||||
|
// When we got here, it means the last read
|
||||||
|
// finished the previous frame, but didn't
|
||||||
|
// do endOfFrame handling yet.
|
||||||
|
// We have to read the next frame here,
|
||||||
|
// as otherwise we would return 0 and nil,
|
||||||
|
// which is a case not handled well by most
|
||||||
|
// protocol implementations.
|
||||||
|
return t.Read(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return t.reader.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes data to the write buffer.
|
||||||
|
//
|
||||||
|
// You need to call Flush to actually write them to the transport.
|
||||||
|
func (t *THeaderTransport) Write(p []byte) (int, error) {
|
||||||
|
return t.writeBuffer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush writes the appropriate header and the write buffer to the underlying transport.
|
||||||
|
func (t *THeaderTransport) Flush(ctx context.Context) error {
|
||||||
|
if t.writeBuffer.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer t.writeBuffer.Reset()
|
||||||
|
|
||||||
|
switch t.clientType {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
case clientUnknown:
|
||||||
|
t.clientType = clientHeaders
|
||||||
|
fallthrough
|
||||||
|
case clientHeaders:
|
||||||
|
headers := NewTMemoryBuffer()
|
||||||
|
hp := NewTCompactProtocol(headers)
|
||||||
|
hp.SetTConfiguration(t.cfg)
|
||||||
|
if _, err := hp.writeVarint32(int32(t.protocolID)); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if _, err := hp.writeVarint32(int32(len(t.writeTransforms))); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
for _, transform := range t.writeTransforms {
|
||||||
|
if _, err := hp.writeVarint32(int32(transform)); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(t.writeHeaders) > 0 {
|
||||||
|
if _, err := hp.writeVarint32(int32(InfoKeyValue)); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if _, err := hp.writeVarint32(int32(len(t.writeHeaders))); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
for key, value := range t.writeHeaders {
|
||||||
|
if err := hp.WriteString(ctx, key); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if err := hp.WriteString(ctx, value); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
padding := 4 - headers.Len()%4
|
||||||
|
if padding < 4 {
|
||||||
|
buf := t.buffer[:padding]
|
||||||
|
for i := range buf {
|
||||||
|
buf[i] = 0
|
||||||
|
}
|
||||||
|
if _, err := headers.Write(buf); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload bytes.Buffer
|
||||||
|
meta := headerMeta{
|
||||||
|
MagicFlags: THeaderHeaderMagic + t.Flags&THeaderFlagsMask,
|
||||||
|
SequenceID: t.SequenceID,
|
||||||
|
HeaderLength: uint16(headers.Len() / 4),
|
||||||
|
}
|
||||||
|
if err := binary.Write(&payload, binary.BigEndian, meta); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(&payload, headers); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
writer, err := NewTransformWriter(&payload, t.writeTransforms)
|
||||||
|
if err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if _, err := io.Copy(writer, &t.writeBuffer); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if err := writer.Close(); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// First write frame length
|
||||||
|
buf := t.buffer[:size32]
|
||||||
|
binary.BigEndian.PutUint32(buf, uint32(payload.Len()))
|
||||||
|
if _, err := t.transport.Write(buf); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
// Then write the payload
|
||||||
|
if _, err := io.Copy(t.transport, &payload); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
case clientFramedBinary, clientFramedCompact:
|
||||||
|
buf := t.buffer[:size32]
|
||||||
|
binary.BigEndian.PutUint32(buf, uint32(t.writeBuffer.Len()))
|
||||||
|
if _, err := t.transport.Write(buf); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case clientUnframedBinary, clientUnframedCompact:
|
||||||
|
if _, err := io.Copy(t.transport, &t.writeBuffer); err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
default:
|
||||||
|
case <-ctx.Done():
|
||||||
|
return NewTTransportExceptionFromError(ctx.Err())
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.transport.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the transport, along with its underlying transport.
|
||||||
|
func (t *THeaderTransport) Close() error {
|
||||||
|
if err := t.Flush(context.Background()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return t.transport.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemainingBytes calls underlying transport's RemainingBytes.
|
||||||
|
//
|
||||||
|
// Even in framed cases, because of all the possible compression transforms
|
||||||
|
// involved, the remaining frame size is likely to be different from the actual
|
||||||
|
// remaining readable bytes, so we don't bother to keep tracking the remaining
|
||||||
|
// frame size by ourselves and just use the underlying transport's
|
||||||
|
// RemainingBytes directly.
|
||||||
|
func (t *THeaderTransport) RemainingBytes() uint64 {
|
||||||
|
return t.transport.RemainingBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReadHeaders returns the THeaderMap read from transport.
|
||||||
|
func (t *THeaderTransport) GetReadHeaders() THeaderMap {
|
||||||
|
return t.readHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteHeader sets a header for write.
|
||||||
|
func (t *THeaderTransport) SetWriteHeader(key, value string) {
|
||||||
|
t.writeHeaders[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearWriteHeaders clears all write headers previously set.
|
||||||
|
func (t *THeaderTransport) ClearWriteHeaders() {
|
||||||
|
t.writeHeaders = make(THeaderMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddTransform add a transform for writing.
|
||||||
|
func (t *THeaderTransport) AddTransform(transform THeaderTransformID) error {
|
||||||
|
if !supportedTransformIDs[transform] {
|
||||||
|
return NewTProtocolExceptionWithType(
|
||||||
|
NOT_IMPLEMENTED,
|
||||||
|
fmt.Errorf("THeaderTransformID %d not supported", transform),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
t.writeTransforms = append(t.writeTransforms, transform)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol returns the wrapped protocol id used in this THeaderTransport.
|
||||||
|
func (t *THeaderTransport) Protocol() THeaderProtocolID {
|
||||||
|
switch t.clientType {
|
||||||
|
default:
|
||||||
|
return t.protocolID
|
||||||
|
case clientFramedBinary, clientUnframedBinary:
|
||||||
|
return THeaderProtocolBinary
|
||||||
|
case clientFramedCompact, clientUnframedCompact:
|
||||||
|
return THeaderProtocolCompact
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *THeaderTransport) isFramed() bool {
|
||||||
|
switch t.clientType {
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
case clientHeaders, clientFramedBinary, clientFramedCompact:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter.
|
||||||
|
func (t *THeaderTransport) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(t.transport, cfg)
|
||||||
|
t.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// THeaderTransportFactory is a TTransportFactory implementation to create
|
||||||
|
// THeaderTransport.
|
||||||
|
//
|
||||||
|
// It also implements TConfigurationSetter.
|
||||||
|
type THeaderTransportFactory struct {
|
||||||
|
// The underlying factory, could be nil.
|
||||||
|
Factory TTransportFactory
|
||||||
|
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHeaderTransportFactoryConf instead.
|
||||||
|
func NewTHeaderTransportFactory(factory TTransportFactory) TTransportFactory {
|
||||||
|
return NewTHeaderTransportFactoryConf(factory, &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTHeaderTransportFactoryConf creates a new *THeaderTransportFactory with
|
||||||
|
// the given *TConfiguration.
|
||||||
|
func NewTHeaderTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory {
|
||||||
|
return &THeaderTransportFactory{
|
||||||
|
Factory: factory,
|
||||||
|
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransport implements TTransportFactory.
|
||||||
|
func (f *THeaderTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
if f.Factory != nil {
|
||||||
|
t, err := f.Factory.GetTransport(trans)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewTHeaderTransportConf(t, f.cfg), nil
|
||||||
|
}
|
||||||
|
return NewTHeaderTransportConf(trans, f.cfg), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter.
|
||||||
|
func (f *THeaderTransportFactory) SetTConfiguration(cfg *TConfiguration) {
|
||||||
|
PropagateTConfiguration(f.Factory, f.cfg)
|
||||||
|
f.cfg = cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ TConfigurationSetter = (*THeaderTransportFactory)(nil)
|
||||||
|
_ TConfigurationSetter = (*THeaderTransport)(nil)
|
||||||
|
)
|
@ -0,0 +1,257 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default to using the shared http client. Library users are
|
||||||
|
// free to change this global client or specify one through
|
||||||
|
// THttpClientOptions.
|
||||||
|
var DefaultHttpClient *http.Client = http.DefaultClient
|
||||||
|
|
||||||
|
type THttpClient struct {
|
||||||
|
client *http.Client
|
||||||
|
response *http.Response
|
||||||
|
url *url.URL
|
||||||
|
requestBuffer *bytes.Buffer
|
||||||
|
header http.Header
|
||||||
|
nsecConnectTimeout int64
|
||||||
|
nsecReadTimeout int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type THttpClientTransportFactory struct {
|
||||||
|
options THttpClientOptions
|
||||||
|
url string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClientTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
if trans != nil {
|
||||||
|
t, ok := trans.(*THttpClient)
|
||||||
|
if ok && t.url != nil {
|
||||||
|
return NewTHttpClientWithOptions(t.url.String(), p.options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewTHttpClientWithOptions(p.url, p.options)
|
||||||
|
}
|
||||||
|
|
||||||
|
type THttpClientOptions struct {
|
||||||
|
// If nil, DefaultHttpClient is used
|
||||||
|
Client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTHttpClientTransportFactory(url string) *THttpClientTransportFactory {
|
||||||
|
return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTHttpClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {
|
||||||
|
return &THttpClientTransportFactory{url: url, options: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTHttpClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {
|
||||||
|
parsedURL, err := url.Parse(urlstr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf := make([]byte, 0, 1024)
|
||||||
|
client := options.Client
|
||||||
|
if client == nil {
|
||||||
|
client = DefaultHttpClient
|
||||||
|
}
|
||||||
|
httpHeader := map[string][]string{"Content-Type": {"application/x-thrift"}}
|
||||||
|
return &THttpClient{client: client, url: parsedURL, requestBuffer: bytes.NewBuffer(buf), header: httpHeader}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTHttpClient(urlstr string) (TTransport, error) {
|
||||||
|
return NewTHttpClientWithOptions(urlstr, THttpClientOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the HTTP Header for this specific Thrift Transport
|
||||||
|
// It is important that you first assert the TTransport as a THttpClient type
|
||||||
|
// like so:
|
||||||
|
//
|
||||||
|
// httpTrans := trans.(THttpClient)
|
||||||
|
// httpTrans.SetHeader("User-Agent","Thrift Client 1.0")
|
||||||
|
func (p *THttpClient) SetHeader(key string, value string) {
|
||||||
|
p.header.Add(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the HTTP Header represented by the supplied Header Key for this specific Thrift Transport
|
||||||
|
// It is important that you first assert the TTransport as a THttpClient type
|
||||||
|
// like so:
|
||||||
|
//
|
||||||
|
// httpTrans := trans.(THttpClient)
|
||||||
|
// hdrValue := httpTrans.GetHeader("User-Agent")
|
||||||
|
func (p *THttpClient) GetHeader(key string) string {
|
||||||
|
return p.header.Get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the HTTP Header given a Header Key for this specific Thrift Transport
|
||||||
|
// It is important that you first assert the TTransport as a THttpClient type
|
||||||
|
// like so:
|
||||||
|
//
|
||||||
|
// httpTrans := trans.(THttpClient)
|
||||||
|
// httpTrans.DelHeader("User-Agent")
|
||||||
|
func (p *THttpClient) DelHeader(key string) {
|
||||||
|
p.header.Del(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) Open() error {
|
||||||
|
// do nothing
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) IsOpen() bool {
|
||||||
|
return p.response != nil || p.requestBuffer != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) closeResponse() error {
|
||||||
|
var err error
|
||||||
|
if p.response != nil && p.response.Body != nil {
|
||||||
|
// The docs specify that if keepalive is enabled and the response body is not
|
||||||
|
// read to completion the connection will never be returned to the pool and
|
||||||
|
// reused. Errors are being ignored here because if the connection is invalid
|
||||||
|
// and this fails for some reason, the Close() method will do any remaining
|
||||||
|
// cleanup.
|
||||||
|
io.Copy(io.Discard, p.response.Body)
|
||||||
|
|
||||||
|
err = p.response.Body.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
p.response = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) Close() error {
|
||||||
|
if p.requestBuffer != nil {
|
||||||
|
p.requestBuffer.Reset()
|
||||||
|
p.requestBuffer = nil
|
||||||
|
}
|
||||||
|
return p.closeResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) Read(buf []byte) (int, error) {
|
||||||
|
if p.response == nil {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.")
|
||||||
|
}
|
||||||
|
n, err := p.response.Body.Read(buf)
|
||||||
|
if n > 0 && (err == nil || errors.Is(err, io.EOF)) {
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
return n, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) ReadByte() (c byte, err error) {
|
||||||
|
if p.response == nil {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Response buffer is empty, no request.")
|
||||||
|
}
|
||||||
|
return readByte(p.response.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) Write(buf []byte) (int, error) {
|
||||||
|
if p.requestBuffer == nil {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.")
|
||||||
|
}
|
||||||
|
return p.requestBuffer.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) WriteByte(c byte) error {
|
||||||
|
if p.requestBuffer == nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.")
|
||||||
|
}
|
||||||
|
return p.requestBuffer.WriteByte(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) WriteString(s string) (n int, err error) {
|
||||||
|
if p.requestBuffer == nil {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Request buffer is nil, connection may have been closed.")
|
||||||
|
}
|
||||||
|
return p.requestBuffer.WriteString(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) Flush(ctx context.Context) error {
|
||||||
|
// Close any previous response body to avoid leaking connections.
|
||||||
|
p.closeResponse()
|
||||||
|
|
||||||
|
// Give up the ownership of the current request buffer to http request,
|
||||||
|
// and create a new buffer for the next request.
|
||||||
|
buf := p.requestBuffer
|
||||||
|
p.requestBuffer = new(bytes.Buffer)
|
||||||
|
req, err := http.NewRequest("POST", p.url.String(), buf)
|
||||||
|
if err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
req.Header = p.header
|
||||||
|
if ctx != nil {
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
}
|
||||||
|
response, err := p.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
// Close the response to avoid leaking file descriptors. closeResponse does
|
||||||
|
// more than just call Close(), so temporarily assign it and reuse the logic.
|
||||||
|
p.response = response
|
||||||
|
p.closeResponse()
|
||||||
|
|
||||||
|
// TODO(pomack) log bad response
|
||||||
|
return NewTTransportException(UNKNOWN_TRANSPORT_EXCEPTION, "HTTP Response code: "+strconv.Itoa(response.StatusCode))
|
||||||
|
}
|
||||||
|
p.response = response
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *THttpClient) RemainingBytes() (num_bytes uint64) {
|
||||||
|
len := p.response.ContentLength
|
||||||
|
if len >= 0 {
|
||||||
|
return uint64(len)
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxSize = ^uint64(0)
|
||||||
|
return maxSize // the truth is, we just don't know unless framed is used
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHttpClientTransportFactory instead.
|
||||||
|
func NewTHttpPostClientTransportFactory(url string) *THttpClientTransportFactory {
|
||||||
|
return NewTHttpClientTransportFactoryWithOptions(url, THttpClientOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHttpClientTransportFactoryWithOptions instead.
|
||||||
|
func NewTHttpPostClientTransportFactoryWithOptions(url string, options THttpClientOptions) *THttpClientTransportFactory {
|
||||||
|
return NewTHttpClientTransportFactoryWithOptions(url, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHttpClientWithOptions instead.
|
||||||
|
func NewTHttpPostClientWithOptions(urlstr string, options THttpClientOptions) (TTransport, error) {
|
||||||
|
return NewTHttpClientWithOptions(urlstr, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTHttpClient instead.
|
||||||
|
func NewTHttpPostClient(urlstr string) (TTransport, error) {
|
||||||
|
return NewTHttpClientWithOptions(urlstr, THttpClientOptions{})
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/gzip"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewThriftHandlerFunc is a function that create a ready to use Apache Thrift Handler function
|
||||||
|
func NewThriftHandlerFunc(processor TProcessor,
|
||||||
|
inPfactory, outPfactory TProtocolFactory) func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
return gz(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Content-Type", "application/x-thrift")
|
||||||
|
|
||||||
|
transport := NewStreamTransport(r.Body, w)
|
||||||
|
processor.Process(r.Context(), inPfactory.GetProtocol(transport), outPfactory.GetProtocol(transport))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// gz transparently compresses the HTTP response if the client supports it.
|
||||||
|
func gz(handler http.HandlerFunc) http.HandlerFunc {
|
||||||
|
sp := &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return gzip.NewWriter(nil)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
||||||
|
handler(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Encoding", "gzip")
|
||||||
|
gz := sp.Get().(*gzip.Writer)
|
||||||
|
gz.Reset(w)
|
||||||
|
defer func() {
|
||||||
|
_ = gz.Close()
|
||||||
|
sp.Put(gz)
|
||||||
|
}()
|
||||||
|
gzw := gzipResponseWriter{Writer: gz, ResponseWriter: w}
|
||||||
|
handler(gzw, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gzipResponseWriter struct {
|
||||||
|
io.Writer
|
||||||
|
http.ResponseWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w gzipResponseWriter) Write(b []byte) (int, error) {
|
||||||
|
return w.Writer.Write(b)
|
||||||
|
}
|
@ -0,0 +1,222 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StreamTransport is a Transport made of an io.Reader and/or an io.Writer
|
||||||
|
type StreamTransport struct {
|
||||||
|
io.Reader
|
||||||
|
io.Writer
|
||||||
|
isReadWriter bool
|
||||||
|
closed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type StreamTransportFactory struct {
|
||||||
|
Reader io.Reader
|
||||||
|
Writer io.Writer
|
||||||
|
isReadWriter bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
if trans != nil {
|
||||||
|
t, ok := trans.(*StreamTransport)
|
||||||
|
if ok {
|
||||||
|
if t.isReadWriter {
|
||||||
|
return NewStreamTransportRW(t.Reader.(io.ReadWriter)), nil
|
||||||
|
}
|
||||||
|
if t.Reader != nil && t.Writer != nil {
|
||||||
|
return NewStreamTransport(t.Reader, t.Writer), nil
|
||||||
|
}
|
||||||
|
if t.Reader != nil && t.Writer == nil {
|
||||||
|
return NewStreamTransportR(t.Reader), nil
|
||||||
|
}
|
||||||
|
if t.Reader == nil && t.Writer != nil {
|
||||||
|
return NewStreamTransportW(t.Writer), nil
|
||||||
|
}
|
||||||
|
return &StreamTransport{}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.isReadWriter {
|
||||||
|
return NewStreamTransportRW(p.Reader.(io.ReadWriter)), nil
|
||||||
|
}
|
||||||
|
if p.Reader != nil && p.Writer != nil {
|
||||||
|
return NewStreamTransport(p.Reader, p.Writer), nil
|
||||||
|
}
|
||||||
|
if p.Reader != nil && p.Writer == nil {
|
||||||
|
return NewStreamTransportR(p.Reader), nil
|
||||||
|
}
|
||||||
|
if p.Reader == nil && p.Writer != nil {
|
||||||
|
return NewStreamTransportW(p.Writer), nil
|
||||||
|
}
|
||||||
|
return &StreamTransport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStreamTransportFactory(reader io.Reader, writer io.Writer, isReadWriter bool) *StreamTransportFactory {
|
||||||
|
return &StreamTransportFactory{Reader: reader, Writer: writer, isReadWriter: isReadWriter}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStreamTransport(r io.Reader, w io.Writer) *StreamTransport {
|
||||||
|
return &StreamTransport{Reader: bufio.NewReader(r), Writer: bufio.NewWriter(w)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStreamTransportR(r io.Reader) *StreamTransport {
|
||||||
|
return &StreamTransport{Reader: bufio.NewReader(r)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStreamTransportW(w io.Writer) *StreamTransport {
|
||||||
|
return &StreamTransport{Writer: bufio.NewWriter(w)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStreamTransportRW(rw io.ReadWriter) *StreamTransport {
|
||||||
|
bufrw := bufio.NewReadWriter(bufio.NewReader(rw), bufio.NewWriter(rw))
|
||||||
|
return &StreamTransport{Reader: bufrw, Writer: bufrw, isReadWriter: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) IsOpen() bool {
|
||||||
|
return !p.closed
|
||||||
|
}
|
||||||
|
|
||||||
|
// implicitly opened on creation, can't be reopened once closed
|
||||||
|
func (p *StreamTransport) Open() error {
|
||||||
|
if !p.closed {
|
||||||
|
return NewTTransportException(ALREADY_OPEN, "StreamTransport already open.")
|
||||||
|
} else {
|
||||||
|
return NewTTransportException(NOT_OPEN, "cannot reopen StreamTransport.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes both the input and output streams.
|
||||||
|
func (p *StreamTransport) Close() error {
|
||||||
|
if p.closed {
|
||||||
|
return NewTTransportException(NOT_OPEN, "StreamTransport already closed.")
|
||||||
|
}
|
||||||
|
p.closed = true
|
||||||
|
closedReader := false
|
||||||
|
if p.Reader != nil {
|
||||||
|
c, ok := p.Reader.(io.Closer)
|
||||||
|
if ok {
|
||||||
|
e := c.Close()
|
||||||
|
closedReader = true
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Reader = nil
|
||||||
|
}
|
||||||
|
if p.Writer != nil && (!closedReader || !p.isReadWriter) {
|
||||||
|
c, ok := p.Writer.(io.Closer)
|
||||||
|
if ok {
|
||||||
|
e := c.Close()
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Writer = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flushes the underlying output stream if not null.
|
||||||
|
func (p *StreamTransport) Flush(ctx context.Context) error {
|
||||||
|
if p.Writer == nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot flush null outputStream")
|
||||||
|
}
|
||||||
|
f, ok := p.Writer.(Flusher)
|
||||||
|
if ok {
|
||||||
|
err := f.Flush()
|
||||||
|
if err != nil {
|
||||||
|
return NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) Read(c []byte) (n int, err error) {
|
||||||
|
n, err = p.Reader.Read(c)
|
||||||
|
if err != nil {
|
||||||
|
err = NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) ReadByte() (c byte, err error) {
|
||||||
|
f, ok := p.Reader.(io.ByteReader)
|
||||||
|
if ok {
|
||||||
|
c, err = f.ReadByte()
|
||||||
|
} else {
|
||||||
|
c, err = readByte(p.Reader)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) Write(c []byte) (n int, err error) {
|
||||||
|
n, err = p.Writer.Write(c)
|
||||||
|
if err != nil {
|
||||||
|
err = NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) WriteByte(c byte) (err error) {
|
||||||
|
f, ok := p.Writer.(io.ByteWriter)
|
||||||
|
if ok {
|
||||||
|
err = f.WriteByte(c)
|
||||||
|
} else {
|
||||||
|
err = writeByte(p.Writer, c)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) WriteString(s string) (n int, err error) {
|
||||||
|
f, ok := p.Writer.(stringWriter)
|
||||||
|
if ok {
|
||||||
|
n, err = f.WriteString(s)
|
||||||
|
} else {
|
||||||
|
n, err = p.Writer.Write([]byte(s))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *StreamTransport) RemainingBytes() (num_bytes uint64) {
|
||||||
|
const maxSize = ^uint64(0)
|
||||||
|
return maxSize // the truth is, we just don't know unless framed is used
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter for propagation.
|
||||||
|
func (p *StreamTransport) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
PropagateTConfiguration(p.Reader, conf)
|
||||||
|
PropagateTConfiguration(p.Writer, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*StreamTransport)(nil)
|
@ -0,0 +1,591 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
THRIFT_JSON_PROTOCOL_VERSION = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// for references to _ParseContext see tsimplejson_protocol.go
|
||||||
|
|
||||||
|
// JSON protocol implementation for thrift.
|
||||||
|
// Utilizes Simple JSON protocol
|
||||||
|
//
|
||||||
|
type TJSONProtocol struct {
|
||||||
|
*TSimpleJSONProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
func NewTJSONProtocol(t TTransport) *TJSONProtocol {
|
||||||
|
v := &TJSONProtocol{TSimpleJSONProtocol: NewTSimpleJSONProtocol(t)}
|
||||||
|
v.parseContextStack.push(_CONTEXT_IN_TOPLEVEL)
|
||||||
|
v.dumpContext.push(_CONTEXT_IN_TOPLEVEL)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factory
|
||||||
|
type TJSONProtocolFactory struct{}
|
||||||
|
|
||||||
|
func (p *TJSONProtocolFactory) GetProtocol(trans TTransport) TProtocol {
|
||||||
|
return NewTJSONProtocol(trans)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTJSONProtocolFactory() *TJSONProtocolFactory {
|
||||||
|
return &TJSONProtocolFactory{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error {
|
||||||
|
p.resetContextStack() // THRIFT-3735
|
||||||
|
if e := p.OutputListBegin(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.WriteI32(ctx, THRIFT_JSON_PROTOCOL_VERSION); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.WriteString(ctx, name); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.WriteByte(ctx, int8(typeId)); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.WriteI32(ctx, seqId); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteMessageEnd(ctx context.Context) error {
|
||||||
|
return p.OutputListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteStructBegin(ctx context.Context, name string) error {
|
||||||
|
if e := p.OutputObjectBegin(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteStructEnd(ctx context.Context) error {
|
||||||
|
return p.OutputObjectEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error {
|
||||||
|
if e := p.WriteI16(ctx, id); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.OutputObjectBegin(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
s, e1 := p.TypeIdToString(typeId)
|
||||||
|
if e1 != nil {
|
||||||
|
return e1
|
||||||
|
}
|
||||||
|
if e := p.WriteString(ctx, s); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteFieldEnd(ctx context.Context) error {
|
||||||
|
return p.OutputObjectEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteFieldStop(ctx context.Context) error { return nil }
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error {
|
||||||
|
if e := p.OutputListBegin(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
s, e1 := p.TypeIdToString(keyType)
|
||||||
|
if e1 != nil {
|
||||||
|
return e1
|
||||||
|
}
|
||||||
|
if e := p.WriteString(ctx, s); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
s, e1 = p.TypeIdToString(valueType)
|
||||||
|
if e1 != nil {
|
||||||
|
return e1
|
||||||
|
}
|
||||||
|
if e := p.WriteString(ctx, s); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.WriteI64(ctx, int64(size)); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return p.OutputObjectBegin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteMapEnd(ctx context.Context) error {
|
||||||
|
if e := p.OutputObjectEnd(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return p.OutputListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
return p.OutputElemListBegin(elemType, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteListEnd(ctx context.Context) error {
|
||||||
|
return p.OutputListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error {
|
||||||
|
return p.OutputElemListBegin(elemType, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteSetEnd(ctx context.Context) error {
|
||||||
|
return p.OutputListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteBool(ctx context.Context, b bool) error {
|
||||||
|
if b {
|
||||||
|
return p.WriteI32(ctx, 1)
|
||||||
|
}
|
||||||
|
return p.WriteI32(ctx, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteByte(ctx context.Context, b int8) error {
|
||||||
|
return p.WriteI32(ctx, int32(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteI16(ctx context.Context, v int16) error {
|
||||||
|
return p.WriteI32(ctx, int32(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteI32(ctx context.Context, v int32) error {
|
||||||
|
return p.OutputI64(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteI64(ctx context.Context, v int64) error {
|
||||||
|
return p.OutputI64(int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteDouble(ctx context.Context, v float64) error {
|
||||||
|
return p.OutputF64(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteString(ctx context.Context, v string) error {
|
||||||
|
return p.OutputString(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) WriteBinary(ctx context.Context, v []byte) error {
|
||||||
|
// JSON library only takes in a string,
|
||||||
|
// not an arbitrary byte array, to ensure bytes are transmitted
|
||||||
|
// efficiently we must convert this into a valid JSON string
|
||||||
|
// therefore we use base64 encoding to avoid excessive escaping/quoting
|
||||||
|
if e := p.OutputPreValue(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
writer := base64.NewEncoder(base64.StdEncoding, p.writer)
|
||||||
|
if _, e := writer.Write(v); e != nil {
|
||||||
|
p.writer.Reset(p.trans) // THRIFT-3735
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
if e := writer.Close(); e != nil {
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
if _, e := p.write(JSON_QUOTE_BYTES); e != nil {
|
||||||
|
return NewTProtocolException(e)
|
||||||
|
}
|
||||||
|
return p.OutputPostValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading methods.
|
||||||
|
func (p *TJSONProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) {
|
||||||
|
p.resetContextStack() // THRIFT-3735
|
||||||
|
if isNull, err := p.ParseListBegin(); isNull || err != nil {
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
version, err := p.ReadI32(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
if version != THRIFT_JSON_PROTOCOL_VERSION {
|
||||||
|
e := fmt.Errorf("Unknown Protocol version %d, expected version %d", version, THRIFT_JSON_PROTOCOL_VERSION)
|
||||||
|
return name, typeId, seqId, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
|
||||||
|
}
|
||||||
|
if name, err = p.ReadString(ctx); err != nil {
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
bTypeId, err := p.ReadByte(ctx)
|
||||||
|
typeId = TMessageType(bTypeId)
|
||||||
|
if err != nil {
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
if seqId, err = p.ReadI32(ctx); err != nil {
|
||||||
|
return name, typeId, seqId, err
|
||||||
|
}
|
||||||
|
return name, typeId, seqId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadMessageEnd(ctx context.Context) error {
|
||||||
|
err := p.ParseListEnd()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadStructBegin(ctx context.Context) (name string, err error) {
|
||||||
|
_, err = p.ParseObjectStart()
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadStructEnd(ctx context.Context) error {
|
||||||
|
return p.ParseObjectEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadFieldBegin(ctx context.Context) (string, TType, int16, error) {
|
||||||
|
b, _ := p.reader.Peek(1)
|
||||||
|
if len(b) < 1 || b[0] == JSON_RBRACE[0] || b[0] == JSON_RBRACKET[0] {
|
||||||
|
return "", STOP, -1, nil
|
||||||
|
}
|
||||||
|
fieldId, err := p.ReadI16(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", STOP, fieldId, err
|
||||||
|
}
|
||||||
|
if _, err = p.ParseObjectStart(); err != nil {
|
||||||
|
return "", STOP, fieldId, err
|
||||||
|
}
|
||||||
|
sType, err := p.ReadString(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", STOP, fieldId, err
|
||||||
|
}
|
||||||
|
fType, err := p.StringToTypeId(sType)
|
||||||
|
return "", fType, fieldId, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadFieldEnd(ctx context.Context) error {
|
||||||
|
return p.ParseObjectEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, e error) {
|
||||||
|
if isNull, e := p.ParseListBegin(); isNull || e != nil {
|
||||||
|
return VOID, VOID, 0, e
|
||||||
|
}
|
||||||
|
|
||||||
|
// read keyType
|
||||||
|
sKeyType, e := p.ReadString(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return keyType, valueType, size, e
|
||||||
|
}
|
||||||
|
keyType, e = p.StringToTypeId(sKeyType)
|
||||||
|
if e != nil {
|
||||||
|
return keyType, valueType, size, e
|
||||||
|
}
|
||||||
|
|
||||||
|
// read valueType
|
||||||
|
sValueType, e := p.ReadString(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return keyType, valueType, size, e
|
||||||
|
}
|
||||||
|
valueType, e = p.StringToTypeId(sValueType)
|
||||||
|
if e != nil {
|
||||||
|
return keyType, valueType, size, e
|
||||||
|
}
|
||||||
|
|
||||||
|
// read size
|
||||||
|
iSize, e := p.ReadI64(ctx)
|
||||||
|
if e != nil {
|
||||||
|
return keyType, valueType, size, e
|
||||||
|
}
|
||||||
|
size = int(iSize)
|
||||||
|
|
||||||
|
_, e = p.ParseObjectStart()
|
||||||
|
return keyType, valueType, size, e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadMapEnd(ctx context.Context) error {
|
||||||
|
e := p.ParseObjectEnd()
|
||||||
|
if e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return p.ParseListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, e error) {
|
||||||
|
return p.ParseElemListBegin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadListEnd(ctx context.Context) error {
|
||||||
|
return p.ParseListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, e error) {
|
||||||
|
return p.ParseElemListBegin()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadSetEnd(ctx context.Context) error {
|
||||||
|
return p.ParseListEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadBool(ctx context.Context) (bool, error) {
|
||||||
|
value, err := p.ReadI32(ctx)
|
||||||
|
return (value != 0), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadByte(ctx context.Context) (int8, error) {
|
||||||
|
v, err := p.ReadI64(ctx)
|
||||||
|
return int8(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadI16(ctx context.Context) (int16, error) {
|
||||||
|
v, err := p.ReadI64(ctx)
|
||||||
|
return int16(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadI32(ctx context.Context) (int32, error) {
|
||||||
|
v, err := p.ReadI64(ctx)
|
||||||
|
return int32(v), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadI64(ctx context.Context) (int64, error) {
|
||||||
|
v, _, err := p.ParseI64()
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadDouble(ctx context.Context) (float64, error) {
|
||||||
|
v, _, err := p.ParseF64()
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadString(ctx context.Context) (string, error) {
|
||||||
|
var v string
|
||||||
|
if err := p.ParsePreValue(); err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
f, _ := p.reader.Peek(1)
|
||||||
|
if len(f) > 0 && f[0] == JSON_QUOTE {
|
||||||
|
p.reader.ReadByte()
|
||||||
|
value, err := p.ParseStringBody()
|
||||||
|
v = value
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
} else if len(f) > 0 && f[0] == JSON_NULL[0] {
|
||||||
|
b := make([]byte, len(JSON_NULL))
|
||||||
|
_, err := p.reader.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return v, NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
if string(b) != string(JSON_NULL) {
|
||||||
|
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
|
||||||
|
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
|
||||||
|
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
}
|
||||||
|
return v, p.ParsePostValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ReadBinary(ctx context.Context) ([]byte, error) {
|
||||||
|
var v []byte
|
||||||
|
if err := p.ParsePreValue(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, _ := p.reader.Peek(1)
|
||||||
|
if len(f) > 0 && f[0] == JSON_QUOTE {
|
||||||
|
p.reader.ReadByte()
|
||||||
|
value, err := p.ParseBase64EncodedBody()
|
||||||
|
v = value
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
} else if len(f) > 0 && f[0] == JSON_NULL[0] {
|
||||||
|
b := make([]byte, len(JSON_NULL))
|
||||||
|
_, err := p.reader.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return v, NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
if string(b) != string(JSON_NULL) {
|
||||||
|
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(b))
|
||||||
|
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
e := fmt.Errorf("Expected a JSON string, found unquoted data started with %s", string(f))
|
||||||
|
return v, NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v, p.ParsePostValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) Flush(ctx context.Context) (err error) {
|
||||||
|
err = p.writer.Flush()
|
||||||
|
if err == nil {
|
||||||
|
err = p.trans.Flush(ctx)
|
||||||
|
}
|
||||||
|
return NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) Skip(ctx context.Context, fieldType TType) (err error) {
|
||||||
|
return SkipDefaultDepth(ctx, p, fieldType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) Transport() TTransport {
|
||||||
|
return p.trans
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) OutputElemListBegin(elemType TType, size int) error {
|
||||||
|
if e := p.OutputListBegin(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
s, e1 := p.TypeIdToString(elemType)
|
||||||
|
if e1 != nil {
|
||||||
|
return e1
|
||||||
|
}
|
||||||
|
if e := p.OutputString(s); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.OutputI64(int64(size)); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) ParseElemListBegin() (elemType TType, size int, e error) {
|
||||||
|
if isNull, e := p.ParseListBegin(); isNull || e != nil {
|
||||||
|
return VOID, 0, e
|
||||||
|
}
|
||||||
|
// We don't really use the ctx in ReadString implementation,
|
||||||
|
// so this is safe for now.
|
||||||
|
// We might want to add context to ParseElemListBegin if we start to use
|
||||||
|
// ctx in ReadString implementation in the future.
|
||||||
|
sElemType, err := p.ReadString(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return VOID, size, err
|
||||||
|
}
|
||||||
|
elemType, err = p.StringToTypeId(sElemType)
|
||||||
|
if err != nil {
|
||||||
|
return elemType, size, err
|
||||||
|
}
|
||||||
|
nSize, _, err2 := p.ParseI64()
|
||||||
|
size = int(nSize)
|
||||||
|
return elemType, size, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) readElemListBegin() (elemType TType, size int, e error) {
|
||||||
|
if isNull, e := p.ParseListBegin(); isNull || e != nil {
|
||||||
|
return VOID, 0, e
|
||||||
|
}
|
||||||
|
// We don't really use the ctx in ReadString implementation,
|
||||||
|
// so this is safe for now.
|
||||||
|
// We might want to add context to ParseElemListBegin if we start to use
|
||||||
|
// ctx in ReadString implementation in the future.
|
||||||
|
sElemType, err := p.ReadString(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
return VOID, size, err
|
||||||
|
}
|
||||||
|
elemType, err = p.StringToTypeId(sElemType)
|
||||||
|
if err != nil {
|
||||||
|
return elemType, size, err
|
||||||
|
}
|
||||||
|
nSize, _, err2 := p.ParseI64()
|
||||||
|
size = int(nSize)
|
||||||
|
return elemType, size, err2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) writeElemListBegin(elemType TType, size int) error {
|
||||||
|
if e := p.OutputListBegin(); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
s, e1 := p.TypeIdToString(elemType)
|
||||||
|
if e1 != nil {
|
||||||
|
return e1
|
||||||
|
}
|
||||||
|
if e := p.OutputString(s); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
if e := p.OutputI64(int64(size)); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) TypeIdToString(fieldType TType) (string, error) {
|
||||||
|
switch byte(fieldType) {
|
||||||
|
case BOOL:
|
||||||
|
return "tf", nil
|
||||||
|
case BYTE:
|
||||||
|
return "i8", nil
|
||||||
|
case I16:
|
||||||
|
return "i16", nil
|
||||||
|
case I32:
|
||||||
|
return "i32", nil
|
||||||
|
case I64:
|
||||||
|
return "i64", nil
|
||||||
|
case DOUBLE:
|
||||||
|
return "dbl", nil
|
||||||
|
case STRING:
|
||||||
|
return "str", nil
|
||||||
|
case STRUCT:
|
||||||
|
return "rec", nil
|
||||||
|
case MAP:
|
||||||
|
return "map", nil
|
||||||
|
case SET:
|
||||||
|
return "set", nil
|
||||||
|
case LIST:
|
||||||
|
return "lst", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
e := fmt.Errorf("Unknown fieldType: %d", int(fieldType))
|
||||||
|
return "", NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TJSONProtocol) StringToTypeId(fieldType string) (TType, error) {
|
||||||
|
switch fieldType {
|
||||||
|
case "tf":
|
||||||
|
return TType(BOOL), nil
|
||||||
|
case "i8":
|
||||||
|
return TType(BYTE), nil
|
||||||
|
case "i16":
|
||||||
|
return TType(I16), nil
|
||||||
|
case "i32":
|
||||||
|
return TType(I32), nil
|
||||||
|
case "i64":
|
||||||
|
return TType(I64), nil
|
||||||
|
case "dbl":
|
||||||
|
return TType(DOUBLE), nil
|
||||||
|
case "str":
|
||||||
|
return TType(STRING), nil
|
||||||
|
case "rec":
|
||||||
|
return TType(STRUCT), nil
|
||||||
|
case "map":
|
||||||
|
return TType(MAP), nil
|
||||||
|
case "set":
|
||||||
|
return TType(SET), nil
|
||||||
|
case "lst":
|
||||||
|
return TType(LIST), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
e := fmt.Errorf("Unknown type identifier: %s", fieldType)
|
||||||
|
return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*TJSONProtocol)(nil)
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger is a simple wrapper of a logging function.
|
||||||
|
//
|
||||||
|
// In reality the users might actually use different logging libraries, and they
|
||||||
|
// are not always compatible with each other.
|
||||||
|
//
|
||||||
|
// Logger is meant to be a simple common ground that it's easy to wrap whatever
|
||||||
|
// logging library they use into.
|
||||||
|
//
|
||||||
|
// See https://issues.apache.org/jira/browse/THRIFT-4985 for the design
|
||||||
|
// discussion behind it.
|
||||||
|
type Logger func(msg string)
|
||||||
|
|
||||||
|
// NopLogger is a Logger implementation that does nothing.
|
||||||
|
func NopLogger(msg string) {}
|
||||||
|
|
||||||
|
// StdLogger wraps stdlib log package into a Logger.
|
||||||
|
//
|
||||||
|
// If logger passed in is nil, it will fallback to use stderr and default flags.
|
||||||
|
func StdLogger(logger *log.Logger) Logger {
|
||||||
|
if logger == nil {
|
||||||
|
logger = log.New(os.Stderr, "", log.LstdFlags)
|
||||||
|
}
|
||||||
|
return func(msg string) {
|
||||||
|
logger.Print(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestLogger is a Logger implementation can be used in test codes.
|
||||||
|
//
|
||||||
|
// It fails the test when being called.
|
||||||
|
func TestLogger(tb testing.TB) Logger {
|
||||||
|
return func(msg string) {
|
||||||
|
tb.Errorf("logger called with msg: %q", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fallbackLogger(logger Logger) Logger {
|
||||||
|
if logger == nil {
|
||||||
|
return StdLogger(nil)
|
||||||
|
}
|
||||||
|
return logger
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Memory buffer-based implementation of the TTransport interface.
|
||||||
|
type TMemoryBuffer struct {
|
||||||
|
*bytes.Buffer
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
type TMemoryBufferTransportFactory struct {
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
if trans != nil {
|
||||||
|
t, ok := trans.(*TMemoryBuffer)
|
||||||
|
if ok && t.size > 0 {
|
||||||
|
return NewTMemoryBufferLen(t.size), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewTMemoryBufferLen(p.size), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory {
|
||||||
|
return &TMemoryBufferTransportFactory{size: size}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTMemoryBuffer() *TMemoryBuffer {
|
||||||
|
return &TMemoryBuffer{Buffer: &bytes.Buffer{}, size: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTMemoryBufferLen(size int) *TMemoryBuffer {
|
||||||
|
buf := make([]byte, 0, size)
|
||||||
|
return &TMemoryBuffer{Buffer: bytes.NewBuffer(buf), size: size}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TMemoryBuffer) IsOpen() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TMemoryBuffer) Open() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TMemoryBuffer) Close() error {
|
||||||
|
p.Buffer.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flushing a memory buffer is a no-op
|
||||||
|
func (p *TMemoryBuffer) Flush(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TMemoryBuffer) RemainingBytes() (num_bytes uint64) {
|
||||||
|
return uint64(p.Buffer.Len())
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
// Message type constants in the Thrift protocol.
|
||||||
|
type TMessageType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
INVALID_TMESSAGE_TYPE TMessageType = 0
|
||||||
|
CALL TMessageType = 1
|
||||||
|
REPLY TMessageType = 2
|
||||||
|
EXCEPTION TMessageType = 3
|
||||||
|
ONEWAY TMessageType = 4
|
||||||
|
)
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// ProcessorMiddleware is a function that can be passed to WrapProcessor to wrap the
|
||||||
|
// TProcessorFunctions for that TProcessor.
|
||||||
|
//
|
||||||
|
// Middlewares are passed in the name of the function as set in the processor
|
||||||
|
// map of the TProcessor.
|
||||||
|
type ProcessorMiddleware func(name string, next TProcessorFunction) TProcessorFunction
|
||||||
|
|
||||||
|
// WrapProcessor takes an existing TProcessor and wraps each of its inner
|
||||||
|
// TProcessorFunctions with the middlewares passed in and returns it.
|
||||||
|
//
|
||||||
|
// Middlewares will be called in the order that they are defined:
|
||||||
|
//
|
||||||
|
// 1. Middlewares[0]
|
||||||
|
// 2. Middlewares[1]
|
||||||
|
// ...
|
||||||
|
// N. Middlewares[n]
|
||||||
|
func WrapProcessor(processor TProcessor, middlewares ...ProcessorMiddleware) TProcessor {
|
||||||
|
for name, processorFunc := range processor.ProcessorMap() {
|
||||||
|
wrapped := processorFunc
|
||||||
|
// Add middlewares in reverse so the first in the list is the outermost.
|
||||||
|
for i := len(middlewares) - 1; i >= 0; i-- {
|
||||||
|
wrapped = middlewares[i](name, wrapped)
|
||||||
|
}
|
||||||
|
processor.AddToProcessorMap(name, wrapped)
|
||||||
|
}
|
||||||
|
return processor
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrappedTProcessorFunction is a convenience struct that implements the
|
||||||
|
// TProcessorFunction interface that can be used when implementing custom
|
||||||
|
// Middleware.
|
||||||
|
type WrappedTProcessorFunction struct {
|
||||||
|
// Wrapped is called by WrappedTProcessorFunction.Process and should be a
|
||||||
|
// "wrapped" call to a base TProcessorFunc.Process call.
|
||||||
|
Wrapped func(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process implements the TProcessorFunction interface using p.Wrapped.
|
||||||
|
func (p WrappedTProcessorFunction) Process(ctx context.Context, seqID int32, in, out TProtocol) (bool, TException) {
|
||||||
|
return p.Wrapped(ctx, seqID, in, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that WrappedTProcessorFunction implements TProcessorFunction
|
||||||
|
var (
|
||||||
|
_ TProcessorFunction = WrappedTProcessorFunction{}
|
||||||
|
_ TProcessorFunction = (*WrappedTProcessorFunction)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// ClientMiddleware can be passed to WrapClient in order to wrap TClient calls
|
||||||
|
// with custom middleware.
|
||||||
|
type ClientMiddleware func(TClient) TClient
|
||||||
|
|
||||||
|
// WrappedTClient is a convenience struct that implements the TClient interface
|
||||||
|
// using inner Wrapped function.
|
||||||
|
//
|
||||||
|
// This is provided to aid in developing ClientMiddleware.
|
||||||
|
type WrappedTClient struct {
|
||||||
|
Wrapped func(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call implements the TClient interface by calling and returning c.Wrapped.
|
||||||
|
func (c WrappedTClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) {
|
||||||
|
return c.Wrapped(ctx, method, args, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that WrappedTClient implements TClient
|
||||||
|
var (
|
||||||
|
_ TClient = WrappedTClient{}
|
||||||
|
_ TClient = (*WrappedTClient)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
// WrapClient wraps the given TClient in the given middlewares.
|
||||||
|
//
|
||||||
|
// Middlewares will be called in the order that they are defined:
|
||||||
|
//
|
||||||
|
// 1. Middlewares[0]
|
||||||
|
// 2. Middlewares[1]
|
||||||
|
// ...
|
||||||
|
// N. Middlewares[n]
|
||||||
|
func WrapClient(client TClient, middlewares ...ClientMiddleware) TClient {
|
||||||
|
// Add middlewares in reverse so the first in the list is the outermost.
|
||||||
|
for i := len(middlewares) - 1; i >= 0; i-- {
|
||||||
|
client = middlewares[i](client)
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
@ -0,0 +1,237 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
TMultiplexedProtocol is a protocol-independent concrete decorator
|
||||||
|
that allows a Thrift client to communicate with a multiplexing Thrift server,
|
||||||
|
by prepending the service name to the function name during function calls.
|
||||||
|
|
||||||
|
NOTE: THIS IS NOT USED BY SERVERS. On the server, use TMultiplexedProcessor to handle request
|
||||||
|
from a multiplexing client.
|
||||||
|
|
||||||
|
This example uses a single socket transport to invoke two services:
|
||||||
|
|
||||||
|
socket := thrift.NewTSocketFromAddrTimeout(addr, TIMEOUT)
|
||||||
|
transport := thrift.NewTFramedTransport(socket)
|
||||||
|
protocol := thrift.NewTBinaryProtocolTransport(transport)
|
||||||
|
|
||||||
|
mp := thrift.NewTMultiplexedProtocol(protocol, "Calculator")
|
||||||
|
service := Calculator.NewCalculatorClient(mp)
|
||||||
|
|
||||||
|
mp2 := thrift.NewTMultiplexedProtocol(protocol, "WeatherReport")
|
||||||
|
service2 := WeatherReport.NewWeatherReportClient(mp2)
|
||||||
|
|
||||||
|
err := transport.Open()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Unable to open client socket", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(service.Add(2,2))
|
||||||
|
fmt.Println(service2.GetTemperature())
|
||||||
|
*/
|
||||||
|
|
||||||
|
type TMultiplexedProtocol struct {
|
||||||
|
TProtocol
|
||||||
|
serviceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
const MULTIPLEXED_SEPARATOR = ":"
|
||||||
|
|
||||||
|
func NewTMultiplexedProtocol(protocol TProtocol, serviceName string) *TMultiplexedProtocol {
|
||||||
|
return &TMultiplexedProtocol{
|
||||||
|
TProtocol: protocol,
|
||||||
|
serviceName: serviceName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TMultiplexedProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error {
|
||||||
|
if typeId == CALL || typeId == ONEWAY {
|
||||||
|
return t.TProtocol.WriteMessageBegin(ctx, t.serviceName+MULTIPLEXED_SEPARATOR+name, typeId, seqid)
|
||||||
|
} else {
|
||||||
|
return t.TProtocol.WriteMessageBegin(ctx, name, typeId, seqid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TMultiplexedProcessor is a TProcessor allowing
|
||||||
|
a single TServer to provide multiple services.
|
||||||
|
|
||||||
|
To do so, you instantiate the processor and then register additional
|
||||||
|
processors with it, as shown in the following example:
|
||||||
|
|
||||||
|
var processor = thrift.NewTMultiplexedProcessor()
|
||||||
|
|
||||||
|
firstProcessor :=
|
||||||
|
processor.RegisterProcessor("FirstService", firstProcessor)
|
||||||
|
|
||||||
|
processor.registerProcessor(
|
||||||
|
"Calculator",
|
||||||
|
Calculator.NewCalculatorProcessor(&CalculatorHandler{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
processor.registerProcessor(
|
||||||
|
"WeatherReport",
|
||||||
|
WeatherReport.NewWeatherReportProcessor(&WeatherReportHandler{}),
|
||||||
|
)
|
||||||
|
|
||||||
|
serverTransport, err := thrift.NewTServerSocketTimeout(addr, TIMEOUT)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Unable to create server socket", err)
|
||||||
|
}
|
||||||
|
server := thrift.NewTSimpleServer2(processor, serverTransport)
|
||||||
|
server.Serve();
|
||||||
|
*/
|
||||||
|
|
||||||
|
type TMultiplexedProcessor struct {
|
||||||
|
serviceProcessorMap map[string]TProcessor
|
||||||
|
DefaultProcessor TProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTMultiplexedProcessor() *TMultiplexedProcessor {
|
||||||
|
return &TMultiplexedProcessor{
|
||||||
|
serviceProcessorMap: make(map[string]TProcessor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessorMap returns a mapping of "{ProcessorName}{MULTIPLEXED_SEPARATOR}{FunctionName}"
|
||||||
|
// to TProcessorFunction for any registered processors. If there is also a
|
||||||
|
// DefaultProcessor, the keys for the methods on that processor will simply be
|
||||||
|
// "{FunctionName}". If the TMultiplexedProcessor has both a DefaultProcessor and
|
||||||
|
// other registered processors, then the keys will be a mix of both formats.
|
||||||
|
//
|
||||||
|
// The implementation differs with other TProcessors in that the map returned is
|
||||||
|
// a new map, while most TProcessors just return their internal mapping directly.
|
||||||
|
// This means that edits to the map returned by this implementation of ProcessorMap
|
||||||
|
// will not affect the underlying mapping within the TMultiplexedProcessor.
|
||||||
|
func (t *TMultiplexedProcessor) ProcessorMap() map[string]TProcessorFunction {
|
||||||
|
processorFuncMap := make(map[string]TProcessorFunction)
|
||||||
|
for name, processor := range t.serviceProcessorMap {
|
||||||
|
for method, processorFunc := range processor.ProcessorMap() {
|
||||||
|
processorFuncName := name + MULTIPLEXED_SEPARATOR + method
|
||||||
|
processorFuncMap[processorFuncName] = processorFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.DefaultProcessor != nil {
|
||||||
|
for method, processorFunc := range t.DefaultProcessor.ProcessorMap() {
|
||||||
|
processorFuncMap[method] = processorFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return processorFuncMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToProcessorMap updates the underlying TProcessor ProccessorMaps depending on
|
||||||
|
// the format of "name".
|
||||||
|
//
|
||||||
|
// If "name" is in the format "{ProcessorName}{MULTIPLEXED_SEPARATOR}{FunctionName}",
|
||||||
|
// then it sets the given TProcessorFunction on the inner TProcessor with the
|
||||||
|
// ProcessorName component using the FunctionName component.
|
||||||
|
//
|
||||||
|
// If "name" is just in the format "{FunctionName}", that is to say there is no
|
||||||
|
// MULTIPLEXED_SEPARATOR, and the TMultiplexedProcessor has a DefaultProcessor
|
||||||
|
// configured, then it will set the given TProcessorFunction on the DefaultProcessor
|
||||||
|
// using the given name.
|
||||||
|
//
|
||||||
|
// If there is not a TProcessor available for the given name, then this function
|
||||||
|
// does nothing. This can happen when there is no TProcessor registered for
|
||||||
|
// the given ProcessorName or if all that is given is the FunctionName and there
|
||||||
|
// is no DefaultProcessor set.
|
||||||
|
func (t *TMultiplexedProcessor) AddToProcessorMap(name string, processorFunc TProcessorFunction) {
|
||||||
|
components := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
|
||||||
|
if len(components) != 2 {
|
||||||
|
if t.DefaultProcessor != nil && len(components) == 1 {
|
||||||
|
t.DefaultProcessor.AddToProcessorMap(components[0], processorFunc)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
processorName := components[0]
|
||||||
|
funcName := components[1]
|
||||||
|
if processor, ok := t.serviceProcessorMap[processorName]; ok {
|
||||||
|
processor.AddToProcessorMap(funcName, processorFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify that TMultiplexedProcessor implements TProcessor
|
||||||
|
var _ TProcessor = (*TMultiplexedProcessor)(nil)
|
||||||
|
|
||||||
|
func (t *TMultiplexedProcessor) RegisterDefault(processor TProcessor) {
|
||||||
|
t.DefaultProcessor = processor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TMultiplexedProcessor) RegisterProcessor(name string, processor TProcessor) {
|
||||||
|
if t.serviceProcessorMap == nil {
|
||||||
|
t.serviceProcessorMap = make(map[string]TProcessor)
|
||||||
|
}
|
||||||
|
t.serviceProcessorMap[name] = processor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol) (bool, TException) {
|
||||||
|
name, typeId, seqid, err := in.ReadMessageBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return false, NewTProtocolException(err)
|
||||||
|
}
|
||||||
|
if typeId != CALL && typeId != ONEWAY {
|
||||||
|
return false, NewTProtocolException(fmt.Errorf("Unexpected message type %v", typeId))
|
||||||
|
}
|
||||||
|
//extract the service name
|
||||||
|
v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
|
||||||
|
if len(v) != 2 {
|
||||||
|
if t.DefaultProcessor != nil {
|
||||||
|
smb := NewStoredMessageProtocol(in, name, typeId, seqid)
|
||||||
|
return t.DefaultProcessor.Process(ctx, smb, out)
|
||||||
|
}
|
||||||
|
return false, NewTProtocolException(fmt.Errorf(
|
||||||
|
"Service name not found in message name: %s. Did you forget to use a TMultiplexProtocol in your client?",
|
||||||
|
name,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
actualProcessor, ok := t.serviceProcessorMap[v[0]]
|
||||||
|
if !ok {
|
||||||
|
return false, NewTProtocolException(fmt.Errorf(
|
||||||
|
"Service name not found: %s. Did you forget to call registerProcessor()?",
|
||||||
|
v[0],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
smb := NewStoredMessageProtocol(in, v[1], typeId, seqid)
|
||||||
|
return actualProcessor.Process(ctx, smb, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Protocol that use stored message for ReadMessageBegin
|
||||||
|
type storedMessageProtocol struct {
|
||||||
|
TProtocol
|
||||||
|
name string
|
||||||
|
typeId TMessageType
|
||||||
|
seqid int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStoredMessageProtocol(protocol TProtocol, name string, typeId TMessageType, seqid int32) *storedMessageProtocol {
|
||||||
|
return &storedMessageProtocol{protocol, name, typeId, seqid}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *storedMessageProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) {
|
||||||
|
return s.name, s.typeId, s.seqid, nil
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Numeric interface {
|
||||||
|
Int64() int64
|
||||||
|
Int32() int32
|
||||||
|
Int16() int16
|
||||||
|
Byte() byte
|
||||||
|
Int() int
|
||||||
|
Float64() float64
|
||||||
|
Float32() float32
|
||||||
|
String() string
|
||||||
|
isNull() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type numeric struct {
|
||||||
|
iValue int64
|
||||||
|
dValue float64
|
||||||
|
sValue string
|
||||||
|
isNil bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
INFINITY Numeric
|
||||||
|
NEGATIVE_INFINITY Numeric
|
||||||
|
NAN Numeric
|
||||||
|
ZERO Numeric
|
||||||
|
NUMERIC_NULL Numeric
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewNumericFromDouble(dValue float64) Numeric {
|
||||||
|
if math.IsInf(dValue, 1) {
|
||||||
|
return INFINITY
|
||||||
|
}
|
||||||
|
if math.IsInf(dValue, -1) {
|
||||||
|
return NEGATIVE_INFINITY
|
||||||
|
}
|
||||||
|
if math.IsNaN(dValue) {
|
||||||
|
return NAN
|
||||||
|
}
|
||||||
|
iValue := int64(dValue)
|
||||||
|
sValue := strconv.FormatFloat(dValue, 'g', 10, 64)
|
||||||
|
isNil := false
|
||||||
|
return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumericFromI64(iValue int64) Numeric {
|
||||||
|
dValue := float64(iValue)
|
||||||
|
sValue := strconv.FormatInt(iValue, 10)
|
||||||
|
isNil := false
|
||||||
|
return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumericFromI32(iValue int32) Numeric {
|
||||||
|
dValue := float64(iValue)
|
||||||
|
sValue := strconv.FormatInt(int64(iValue), 10)
|
||||||
|
isNil := false
|
||||||
|
return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumericFromString(sValue string) Numeric {
|
||||||
|
if sValue == INFINITY.String() {
|
||||||
|
return INFINITY
|
||||||
|
}
|
||||||
|
if sValue == NEGATIVE_INFINITY.String() {
|
||||||
|
return NEGATIVE_INFINITY
|
||||||
|
}
|
||||||
|
if sValue == NAN.String() {
|
||||||
|
return NAN
|
||||||
|
}
|
||||||
|
iValue, _ := strconv.ParseInt(sValue, 10, 64)
|
||||||
|
dValue, _ := strconv.ParseFloat(sValue, 64)
|
||||||
|
isNil := len(sValue) == 0
|
||||||
|
return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNumericFromJSONString(sValue string, isNull bool) Numeric {
|
||||||
|
if isNull {
|
||||||
|
return NewNullNumeric()
|
||||||
|
}
|
||||||
|
if sValue == JSON_INFINITY {
|
||||||
|
return INFINITY
|
||||||
|
}
|
||||||
|
if sValue == JSON_NEGATIVE_INFINITY {
|
||||||
|
return NEGATIVE_INFINITY
|
||||||
|
}
|
||||||
|
if sValue == JSON_NAN {
|
||||||
|
return NAN
|
||||||
|
}
|
||||||
|
iValue, _ := strconv.ParseInt(sValue, 10, 64)
|
||||||
|
dValue, _ := strconv.ParseFloat(sValue, 64)
|
||||||
|
return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNull}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNullNumeric() Numeric {
|
||||||
|
return &numeric{iValue: 0, dValue: 0.0, sValue: "", isNil: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Int64() int64 {
|
||||||
|
return p.iValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Int32() int32 {
|
||||||
|
return int32(p.iValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Int16() int16 {
|
||||||
|
return int16(p.iValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Byte() byte {
|
||||||
|
return byte(p.iValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Int() int {
|
||||||
|
return int(p.iValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Float64() float64 {
|
||||||
|
return p.dValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) Float32() float32 {
|
||||||
|
return float32(p.dValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) String() string {
|
||||||
|
return p.sValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *numeric) isNull() bool {
|
||||||
|
return p.isNil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
INFINITY = &numeric{iValue: 0, dValue: math.Inf(1), sValue: "Infinity", isNil: false}
|
||||||
|
NEGATIVE_INFINITY = &numeric{iValue: 0, dValue: math.Inf(-1), sValue: "-Infinity", isNil: false}
|
||||||
|
NAN = &numeric{iValue: 0, dValue: math.NaN(), sValue: "NaN", isNil: false}
|
||||||
|
ZERO = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: false}
|
||||||
|
NUMERIC_NULL = &numeric{iValue: 0, dValue: 0, sValue: "0", isNil: true}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This file is home to helpers that convert from various base types to
|
||||||
|
// respective pointer types. This is necessary because Go does not permit
|
||||||
|
// references to constants, nor can a pointer type to base type be allocated
|
||||||
|
// and initialized in a single expression.
|
||||||
|
//
|
||||||
|
// E.g., this is not allowed:
|
||||||
|
//
|
||||||
|
// var ip *int = &5
|
||||||
|
//
|
||||||
|
// But this *is* allowed:
|
||||||
|
//
|
||||||
|
// func IntPtr(i int) *int { return &i }
|
||||||
|
// var ip *int = IntPtr(5)
|
||||||
|
//
|
||||||
|
// Since pointers to base types are commonplace as [optional] fields in
|
||||||
|
// exported thrift structs, we factor such helpers here.
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
func Float32Ptr(v float32) *float32 { return &v }
|
||||||
|
func Float64Ptr(v float64) *float64 { return &v }
|
||||||
|
func IntPtr(v int) *int { return &v }
|
||||||
|
func Int8Ptr(v int8) *int8 { return &v }
|
||||||
|
func Int16Ptr(v int16) *int16 { return &v }
|
||||||
|
func Int32Ptr(v int32) *int32 { return &v }
|
||||||
|
func Int64Ptr(v int64) *int64 { return &v }
|
||||||
|
func StringPtr(v string) *string { return &v }
|
||||||
|
func Uint32Ptr(v uint32) *uint32 { return &v }
|
||||||
|
func Uint64Ptr(v uint64) *uint64 { return &v }
|
||||||
|
func BoolPtr(v bool) *bool { return &v }
|
||||||
|
func ByteSlicePtr(v []byte) *[]byte { return &v }
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
// A processor is a generic object which operates upon an input stream and
|
||||||
|
// writes to some output stream.
|
||||||
|
type TProcessor interface {
|
||||||
|
Process(ctx context.Context, in, out TProtocol) (bool, TException)
|
||||||
|
|
||||||
|
// ProcessorMap returns a map of thrift method names to TProcessorFunctions.
|
||||||
|
ProcessorMap() map[string]TProcessorFunction
|
||||||
|
|
||||||
|
// AddToProcessorMap adds the given TProcessorFunction to the internal
|
||||||
|
// processor map at the given key.
|
||||||
|
//
|
||||||
|
// If one is already set at the given key, it will be replaced with the new
|
||||||
|
// TProcessorFunction.
|
||||||
|
AddToProcessorMap(string, TProcessorFunction)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TProcessorFunction interface {
|
||||||
|
Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default processor factory just returns a singleton
|
||||||
|
// instance.
|
||||||
|
type TProcessorFactory interface {
|
||||||
|
GetProcessor(trans TTransport) TProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
type tProcessorFactory struct {
|
||||||
|
processor TProcessor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTProcessorFactory(p TProcessor) TProcessorFactory {
|
||||||
|
return &tProcessorFactory{processor: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor {
|
||||||
|
return p.processor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default processor factory just returns a singleton
|
||||||
|
* instance.
|
||||||
|
*/
|
||||||
|
type TProcessorFunctionFactory interface {
|
||||||
|
GetProcessorFunction(trans TTransport) TProcessorFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
type tProcessorFunctionFactory struct {
|
||||||
|
processor TProcessorFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory {
|
||||||
|
return &tProcessorFunctionFactory{processor: p}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction {
|
||||||
|
return p.processor
|
||||||
|
}
|
@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
VERSION_MASK = 0xffff0000
|
||||||
|
VERSION_1 = 0x80010000
|
||||||
|
)
|
||||||
|
|
||||||
|
type TProtocol interface {
|
||||||
|
WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error
|
||||||
|
WriteMessageEnd(ctx context.Context) error
|
||||||
|
WriteStructBegin(ctx context.Context, name string) error
|
||||||
|
WriteStructEnd(ctx context.Context) error
|
||||||
|
WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error
|
||||||
|
WriteFieldEnd(ctx context.Context) error
|
||||||
|
WriteFieldStop(ctx context.Context) error
|
||||||
|
WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error
|
||||||
|
WriteMapEnd(ctx context.Context) error
|
||||||
|
WriteListBegin(ctx context.Context, elemType TType, size int) error
|
||||||
|
WriteListEnd(ctx context.Context) error
|
||||||
|
WriteSetBegin(ctx context.Context, elemType TType, size int) error
|
||||||
|
WriteSetEnd(ctx context.Context) error
|
||||||
|
WriteBool(ctx context.Context, value bool) error
|
||||||
|
WriteByte(ctx context.Context, value int8) error
|
||||||
|
WriteI16(ctx context.Context, value int16) error
|
||||||
|
WriteI32(ctx context.Context, value int32) error
|
||||||
|
WriteI64(ctx context.Context, value int64) error
|
||||||
|
WriteDouble(ctx context.Context, value float64) error
|
||||||
|
WriteString(ctx context.Context, value string) error
|
||||||
|
WriteBinary(ctx context.Context, value []byte) error
|
||||||
|
|
||||||
|
ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error)
|
||||||
|
ReadMessageEnd(ctx context.Context) error
|
||||||
|
ReadStructBegin(ctx context.Context) (name string, err error)
|
||||||
|
ReadStructEnd(ctx context.Context) error
|
||||||
|
ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error)
|
||||||
|
ReadFieldEnd(ctx context.Context) error
|
||||||
|
ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error)
|
||||||
|
ReadMapEnd(ctx context.Context) error
|
||||||
|
ReadListBegin(ctx context.Context) (elemType TType, size int, err error)
|
||||||
|
ReadListEnd(ctx context.Context) error
|
||||||
|
ReadSetBegin(ctx context.Context) (elemType TType, size int, err error)
|
||||||
|
ReadSetEnd(ctx context.Context) error
|
||||||
|
ReadBool(ctx context.Context) (value bool, err error)
|
||||||
|
ReadByte(ctx context.Context) (value int8, err error)
|
||||||
|
ReadI16(ctx context.Context) (value int16, err error)
|
||||||
|
ReadI32(ctx context.Context) (value int32, err error)
|
||||||
|
ReadI64(ctx context.Context) (value int64, err error)
|
||||||
|
ReadDouble(ctx context.Context) (value float64, err error)
|
||||||
|
ReadString(ctx context.Context) (value string, err error)
|
||||||
|
ReadBinary(ctx context.Context) (value []byte, err error)
|
||||||
|
|
||||||
|
Skip(ctx context.Context, fieldType TType) (err error)
|
||||||
|
Flush(ctx context.Context) (err error)
|
||||||
|
|
||||||
|
Transport() TTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum recursive depth the skip() function will traverse
|
||||||
|
const DEFAULT_RECURSION_DEPTH = 64
|
||||||
|
|
||||||
|
// Skips over the next data element from the provided input TProtocol object.
|
||||||
|
func SkipDefaultDepth(ctx context.Context, prot TProtocol, typeId TType) (err error) {
|
||||||
|
return Skip(ctx, prot, typeId, DEFAULT_RECURSION_DEPTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skips over the next data element from the provided input TProtocol object.
|
||||||
|
func Skip(ctx context.Context, self TProtocol, fieldType TType, maxDepth int) (err error) {
|
||||||
|
|
||||||
|
if maxDepth <= 0 {
|
||||||
|
return NewTProtocolExceptionWithType(DEPTH_LIMIT, errors.New("Depth limit exceeded"))
|
||||||
|
}
|
||||||
|
|
||||||
|
switch fieldType {
|
||||||
|
case BOOL:
|
||||||
|
_, err = self.ReadBool(ctx)
|
||||||
|
return
|
||||||
|
case BYTE:
|
||||||
|
_, err = self.ReadByte(ctx)
|
||||||
|
return
|
||||||
|
case I16:
|
||||||
|
_, err = self.ReadI16(ctx)
|
||||||
|
return
|
||||||
|
case I32:
|
||||||
|
_, err = self.ReadI32(ctx)
|
||||||
|
return
|
||||||
|
case I64:
|
||||||
|
_, err = self.ReadI64(ctx)
|
||||||
|
return
|
||||||
|
case DOUBLE:
|
||||||
|
_, err = self.ReadDouble(ctx)
|
||||||
|
return
|
||||||
|
case STRING:
|
||||||
|
_, err = self.ReadString(ctx)
|
||||||
|
return
|
||||||
|
case STRUCT:
|
||||||
|
if _, err = self.ReadStructBegin(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
_, typeId, _, _ := self.ReadFieldBegin(ctx)
|
||||||
|
if typeId == STOP {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
err := Skip(ctx, self, typeId, maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.ReadFieldEnd(ctx)
|
||||||
|
}
|
||||||
|
return self.ReadStructEnd(ctx)
|
||||||
|
case MAP:
|
||||||
|
keyType, valueType, size, err := self.ReadMapBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
err := Skip(ctx, self, keyType, maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.Skip(ctx, valueType)
|
||||||
|
}
|
||||||
|
return self.ReadMapEnd(ctx)
|
||||||
|
case SET:
|
||||||
|
elemType, size, err := self.ReadSetBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
err := Skip(ctx, self, elemType, maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self.ReadSetEnd(ctx)
|
||||||
|
case LIST:
|
||||||
|
elemType, size, err := self.ReadListBegin(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
err := Skip(ctx, self, elemType, maxDepth-1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self.ReadListEnd(ctx)
|
||||||
|
default:
|
||||||
|
return NewTProtocolExceptionWithType(INVALID_DATA, errors.New(fmt.Sprintf("Unknown data type %d", fieldType)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Thrift Protocol exception
|
||||||
|
type TProtocolException interface {
|
||||||
|
TException
|
||||||
|
TypeId() int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
UNKNOWN_PROTOCOL_EXCEPTION = 0
|
||||||
|
INVALID_DATA = 1
|
||||||
|
NEGATIVE_SIZE = 2
|
||||||
|
SIZE_LIMIT = 3
|
||||||
|
BAD_VERSION = 4
|
||||||
|
NOT_IMPLEMENTED = 5
|
||||||
|
DEPTH_LIMIT = 6
|
||||||
|
)
|
||||||
|
|
||||||
|
type tProtocolException struct {
|
||||||
|
typeId int
|
||||||
|
err error
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TProtocolException = (*tProtocolException)(nil)
|
||||||
|
|
||||||
|
func (tProtocolException) TExceptionType() TExceptionType {
|
||||||
|
return TExceptionTypeProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tProtocolException) TypeId() int {
|
||||||
|
return p.typeId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tProtocolException) String() string {
|
||||||
|
return p.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tProtocolException) Error() string {
|
||||||
|
return p.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tProtocolException) Unwrap() error {
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTProtocolException(err error) TProtocolException {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := err.(TProtocolException); ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.As(err, new(base64.CorruptInputError)) {
|
||||||
|
return NewTProtocolExceptionWithType(INVALID_DATA, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewTProtocolExceptionWithType(UNKNOWN_PROTOCOL_EXCEPTION, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTProtocolExceptionWithType(errType int, err error) TProtocolException {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &tProtocolException{
|
||||||
|
typeId: errType,
|
||||||
|
err: err,
|
||||||
|
msg: err.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prependTProtocolException(prepend string, err TProtocolException) TProtocolException {
|
||||||
|
return &tProtocolException{
|
||||||
|
typeId: err.TypeId(),
|
||||||
|
err: err,
|
||||||
|
msg: prepend + err.Error(),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
// Factory interface for constructing protocol instances.
|
||||||
|
type TProtocolFactory interface {
|
||||||
|
GetProtocol(trans TTransport) TProtocol
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// See https://godoc.org/context#WithValue on why do we need the unexported typedefs.
|
||||||
|
type responseHelperKey struct{}
|
||||||
|
|
||||||
|
// TResponseHelper defines a object with a set of helper functions that can be
|
||||||
|
// retrieved from the context object passed into server handler functions.
|
||||||
|
//
|
||||||
|
// Use GetResponseHelper to retrieve the injected TResponseHelper implementation
|
||||||
|
// from the context object.
|
||||||
|
//
|
||||||
|
// The zero value of TResponseHelper is valid with all helper functions being
|
||||||
|
// no-op.
|
||||||
|
type TResponseHelper struct {
|
||||||
|
// THeader related functions
|
||||||
|
*THeaderResponseHelper
|
||||||
|
}
|
||||||
|
|
||||||
|
// THeaderResponseHelper defines THeader related TResponseHelper functions.
|
||||||
|
//
|
||||||
|
// The zero value of *THeaderResponseHelper is valid with all helper functions
|
||||||
|
// being no-op.
|
||||||
|
type THeaderResponseHelper struct {
|
||||||
|
proto *THeaderProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTHeaderResponseHelper creates a new THeaderResponseHelper from the
|
||||||
|
// underlying TProtocol.
|
||||||
|
func NewTHeaderResponseHelper(proto TProtocol) *THeaderResponseHelper {
|
||||||
|
if hp, ok := proto.(*THeaderProtocol); ok {
|
||||||
|
return &THeaderResponseHelper{
|
||||||
|
proto: hp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetHeader sets a response header.
|
||||||
|
//
|
||||||
|
// It's no-op if the underlying protocol/transport does not support THeader.
|
||||||
|
func (h *THeaderResponseHelper) SetHeader(key, value string) {
|
||||||
|
if h != nil && h.proto != nil {
|
||||||
|
h.proto.SetWriteHeader(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearHeaders clears all the response headers previously set.
|
||||||
|
//
|
||||||
|
// It's no-op if the underlying protocol/transport does not support THeader.
|
||||||
|
func (h *THeaderResponseHelper) ClearHeaders() {
|
||||||
|
if h != nil && h.proto != nil {
|
||||||
|
h.proto.ClearWriteHeaders()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResponseHelper retrieves the TResponseHelper implementation injected into
|
||||||
|
// the context object.
|
||||||
|
//
|
||||||
|
// If no helper was found in the context object, a nop helper with ok == false
|
||||||
|
// will be returned.
|
||||||
|
func GetResponseHelper(ctx context.Context) (helper TResponseHelper, ok bool) {
|
||||||
|
if v := ctx.Value(responseHelperKey{}); v != nil {
|
||||||
|
helper, ok = v.(TResponseHelper)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetResponseHelper injects TResponseHelper into the context object.
|
||||||
|
func SetResponseHelper(ctx context.Context, helper TResponseHelper) context.Context {
|
||||||
|
return context.WithValue(ctx, responseHelperKey{}, helper)
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RichTransport struct {
|
||||||
|
TTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wraps Transport to provide TRichTransport interface
|
||||||
|
func NewTRichTransport(trans TTransport) *RichTransport {
|
||||||
|
return &RichTransport{trans}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RichTransport) ReadByte() (c byte, err error) {
|
||||||
|
return readByte(r.TTransport)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RichTransport) WriteByte(c byte) error {
|
||||||
|
return writeByte(r.TTransport, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RichTransport) WriteString(s string) (n int, err error) {
|
||||||
|
return r.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RichTransport) RemainingBytes() (num_bytes uint64) {
|
||||||
|
return r.TTransport.RemainingBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func readByte(r io.Reader) (c byte, err error) {
|
||||||
|
v := [1]byte{0}
|
||||||
|
n, err := r.Read(v[0:1])
|
||||||
|
if n > 0 && (err == nil || errors.Is(err, io.EOF)) {
|
||||||
|
return v[0], nil
|
||||||
|
}
|
||||||
|
if n > 0 && err != nil {
|
||||||
|
return v[0], err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return v[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeByte(w io.Writer, c byte) error {
|
||||||
|
v := [1]byte{c}
|
||||||
|
_, err := w.Write(v[0:1])
|
||||||
|
return err
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TSerializer struct {
|
||||||
|
Transport *TMemoryBuffer
|
||||||
|
Protocol TProtocol
|
||||||
|
}
|
||||||
|
|
||||||
|
type TStruct interface {
|
||||||
|
Write(ctx context.Context, p TProtocol) error
|
||||||
|
Read(ctx context.Context, p TProtocol) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSerializer() *TSerializer {
|
||||||
|
transport := NewTMemoryBufferLen(1024)
|
||||||
|
protocol := NewTBinaryProtocolTransport(transport)
|
||||||
|
|
||||||
|
return &TSerializer{
|
||||||
|
Transport: transport,
|
||||||
|
Protocol: protocol,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, err error) {
|
||||||
|
t.Transport.Reset()
|
||||||
|
|
||||||
|
if err = msg.Write(ctx, t.Protocol); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = t.Protocol.Flush(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = t.Transport.Flush(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.Transport.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err error) {
|
||||||
|
t.Transport.Reset()
|
||||||
|
|
||||||
|
if err = msg.Write(ctx, t.Protocol); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = t.Protocol.Flush(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = t.Transport.Flush(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b = append(b, t.Transport.Bytes()...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TSerializerPool is the thread-safe version of TSerializer, it uses resource
|
||||||
|
// pool of TSerializer under the hood.
|
||||||
|
//
|
||||||
|
// It must be initialized with either NewTSerializerPool or
|
||||||
|
// NewTSerializerPoolSizeFactory.
|
||||||
|
type TSerializerPool struct {
|
||||||
|
pool sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSerializerPool creates a new TSerializerPool.
|
||||||
|
//
|
||||||
|
// NewTSerializer can be used as the arg here.
|
||||||
|
func NewTSerializerPool(f func() *TSerializer) *TSerializerPool {
|
||||||
|
return &TSerializerPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return f()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSerializerPoolSizeFactory creates a new TSerializerPool with the given
|
||||||
|
// size and protocol factory.
|
||||||
|
//
|
||||||
|
// Note that the size is not the limit. The TMemoryBuffer underneath can grow
|
||||||
|
// larger than that. It just dictates the initial size.
|
||||||
|
func NewTSerializerPoolSizeFactory(size int, factory TProtocolFactory) *TSerializerPool {
|
||||||
|
return &TSerializerPool{
|
||||||
|
pool: sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
transport := NewTMemoryBufferLen(size)
|
||||||
|
protocol := factory.GetProtocol(transport)
|
||||||
|
|
||||||
|
return &TSerializer{
|
||||||
|
Transport: transport,
|
||||||
|
Protocol: protocol,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TSerializerPool) WriteString(ctx context.Context, msg TStruct) (string, error) {
|
||||||
|
s := t.pool.Get().(*TSerializer)
|
||||||
|
defer t.pool.Put(s)
|
||||||
|
return s.WriteString(ctx, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TSerializerPool) Write(ctx context.Context, msg TStruct) ([]byte, error) {
|
||||||
|
s := t.pool.Get().(*TSerializer)
|
||||||
|
defer t.pool.Put(s)
|
||||||
|
return s.Write(ctx, msg)
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
type TServer interface {
|
||||||
|
ProcessorFactory() TProcessorFactory
|
||||||
|
ServerTransport() TServerTransport
|
||||||
|
InputTransportFactory() TTransportFactory
|
||||||
|
OutputTransportFactory() TTransportFactory
|
||||||
|
InputProtocolFactory() TProtocolFactory
|
||||||
|
OutputProtocolFactory() TProtocolFactory
|
||||||
|
|
||||||
|
// Starts the server
|
||||||
|
Serve() error
|
||||||
|
// Stops the server. This is optional on a per-implementation basis. Not
|
||||||
|
// all servers are required to be cleanly stoppable.
|
||||||
|
Stop() error
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TServerSocket struct {
|
||||||
|
listener net.Listener
|
||||||
|
addr net.Addr
|
||||||
|
clientTimeout time.Duration
|
||||||
|
|
||||||
|
// Protects the interrupted value to make it thread safe.
|
||||||
|
mu sync.RWMutex
|
||||||
|
interrupted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTServerSocket(listenAddr string) (*TServerSocket, error) {
|
||||||
|
return NewTServerSocketTimeout(listenAddr, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTServerSocketTimeout(listenAddr string, clientTimeout time.Duration) (*TServerSocket, error) {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &TServerSocket{addr: addr, clientTimeout: clientTimeout}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a TServerSocket from a net.Addr
|
||||||
|
func NewTServerSocketFromAddrTimeout(addr net.Addr, clientTimeout time.Duration) *TServerSocket {
|
||||||
|
return &TServerSocket{addr: addr, clientTimeout: clientTimeout}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TServerSocket) Listen() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.IsListening() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
l, err := net.Listen(p.addr.Network(), p.addr.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.listener = l
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TServerSocket) Accept() (TTransport, error) {
|
||||||
|
p.mu.RLock()
|
||||||
|
interrupted := p.interrupted
|
||||||
|
p.mu.RUnlock()
|
||||||
|
|
||||||
|
if interrupted {
|
||||||
|
return nil, errTransportInterrupted
|
||||||
|
}
|
||||||
|
|
||||||
|
p.mu.Lock()
|
||||||
|
listener := p.listener
|
||||||
|
p.mu.Unlock()
|
||||||
|
if listener == nil {
|
||||||
|
return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return NewTSocketFromConnTimeout(conn, p.clientTimeout), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the socket is listening.
|
||||||
|
func (p *TServerSocket) IsListening() bool {
|
||||||
|
return p.listener != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connects the socket, creating a new socket object if necessary.
|
||||||
|
func (p *TServerSocket) Open() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if p.IsListening() {
|
||||||
|
return NewTTransportException(ALREADY_OPEN, "Server socket already open")
|
||||||
|
}
|
||||||
|
if l, err := net.Listen(p.addr.Network(), p.addr.String()); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.listener = l
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TServerSocket) Addr() net.Addr {
|
||||||
|
if p.listener != nil {
|
||||||
|
return p.listener.Addr()
|
||||||
|
}
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TServerSocket) Close() error {
|
||||||
|
var err error
|
||||||
|
p.mu.Lock()
|
||||||
|
if p.IsListening() {
|
||||||
|
err = p.listener.Close()
|
||||||
|
p.listener = nil
|
||||||
|
}
|
||||||
|
p.mu.Unlock()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TServerSocket) Interrupt() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
p.interrupted = true
|
||||||
|
p.mu.Unlock()
|
||||||
|
p.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
// Server transport. Object which provides client transports.
|
||||||
|
type TServerTransport interface {
|
||||||
|
Listen() error
|
||||||
|
Accept() (TTransport, error)
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// Optional method implementation. This signals to the server transport
|
||||||
|
// that it should break out of any accept() or listen() that it is currently
|
||||||
|
// blocked on. This method, if implemented, MUST be thread safe, as it may
|
||||||
|
// be called from a different thread context than the other TServerTransport
|
||||||
|
// methods.
|
||||||
|
Interrupt() error
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrAbandonRequest is a special error server handler implementations can
|
||||||
|
// return to indicate that the request has been abandoned.
|
||||||
|
//
|
||||||
|
// TSimpleServer will check for this error, and close the client connection
|
||||||
|
// instead of writing the response/error back to the client.
|
||||||
|
//
|
||||||
|
// It shall only be used when the server handler implementation know that the
|
||||||
|
// client already abandoned the request (by checking that the passed in context
|
||||||
|
// is already canceled, for example).
|
||||||
|
var ErrAbandonRequest = errors.New("request abandoned")
|
||||||
|
|
||||||
|
// ServerConnectivityCheckInterval defines the ticker interval used by
|
||||||
|
// connectivity check in thrift compiled TProcessorFunc implementations.
|
||||||
|
//
|
||||||
|
// It's defined as a variable instead of constant, so that thrift server
|
||||||
|
// implementations can change its value to control the behavior.
|
||||||
|
//
|
||||||
|
// If it's changed to <=0, the feature will be disabled.
|
||||||
|
var ServerConnectivityCheckInterval = time.Millisecond * 5
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is not a typical TSimpleServer as it is not blocked after accept a socket.
|
||||||
|
* It is more like a TThreadedServer that can handle different connections in different goroutines.
|
||||||
|
* This will work if golang user implements a conn-pool like thing in client side.
|
||||||
|
*/
|
||||||
|
type TSimpleServer struct {
|
||||||
|
closed int32
|
||||||
|
wg sync.WaitGroup
|
||||||
|
mu sync.Mutex
|
||||||
|
|
||||||
|
processorFactory TProcessorFactory
|
||||||
|
serverTransport TServerTransport
|
||||||
|
inputTransportFactory TTransportFactory
|
||||||
|
outputTransportFactory TTransportFactory
|
||||||
|
inputProtocolFactory TProtocolFactory
|
||||||
|
outputProtocolFactory TProtocolFactory
|
||||||
|
|
||||||
|
// Headers to auto forward in THeaderProtocol
|
||||||
|
forwardHeaders []string
|
||||||
|
|
||||||
|
logger Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer {
|
||||||
|
return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
|
||||||
|
return NewTSimpleServerFactory4(NewTProcessorFactory(processor),
|
||||||
|
serverTransport,
|
||||||
|
transportFactory,
|
||||||
|
protocolFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
|
||||||
|
return NewTSimpleServerFactory6(NewTProcessorFactory(processor),
|
||||||
|
serverTransport,
|
||||||
|
inputTransportFactory,
|
||||||
|
outputTransportFactory,
|
||||||
|
inputProtocolFactory,
|
||||||
|
outputProtocolFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer {
|
||||||
|
return NewTSimpleServerFactory6(processorFactory,
|
||||||
|
serverTransport,
|
||||||
|
NewTTransportFactory(),
|
||||||
|
NewTTransportFactory(),
|
||||||
|
NewTBinaryProtocolFactoryDefault(),
|
||||||
|
NewTBinaryProtocolFactoryDefault(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer {
|
||||||
|
return NewTSimpleServerFactory6(processorFactory,
|
||||||
|
serverTransport,
|
||||||
|
transportFactory,
|
||||||
|
transportFactory,
|
||||||
|
protocolFactory,
|
||||||
|
protocolFactory,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer {
|
||||||
|
return &TSimpleServer{
|
||||||
|
processorFactory: processorFactory,
|
||||||
|
serverTransport: serverTransport,
|
||||||
|
inputTransportFactory: inputTransportFactory,
|
||||||
|
outputTransportFactory: outputTransportFactory,
|
||||||
|
inputProtocolFactory: inputProtocolFactory,
|
||||||
|
outputProtocolFactory: outputProtocolFactory,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) ProcessorFactory() TProcessorFactory {
|
||||||
|
return p.processorFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) ServerTransport() TServerTransport {
|
||||||
|
return p.serverTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) InputTransportFactory() TTransportFactory {
|
||||||
|
return p.inputTransportFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) OutputTransportFactory() TTransportFactory {
|
||||||
|
return p.outputTransportFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory {
|
||||||
|
return p.inputProtocolFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory {
|
||||||
|
return p.outputProtocolFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) Listen() error {
|
||||||
|
return p.serverTransport.Listen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForwardHeaders sets the list of header keys that will be auto forwarded
|
||||||
|
// while using THeaderProtocol.
|
||||||
|
//
|
||||||
|
// "forward" means that when the server is also a client to other upstream
|
||||||
|
// thrift servers, the context object user gets in the processor functions will
|
||||||
|
// have both read and write headers set, with write headers being forwarded.
|
||||||
|
// Users can always override the write headers by calling SetWriteHeaderList
|
||||||
|
// before calling thrift client functions.
|
||||||
|
func (p *TSimpleServer) SetForwardHeaders(headers []string) {
|
||||||
|
size := len(headers)
|
||||||
|
if size == 0 {
|
||||||
|
p.forwardHeaders = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
keys := make([]string, size)
|
||||||
|
copy(keys, headers)
|
||||||
|
p.forwardHeaders = keys
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogger sets the logger used by this TSimpleServer.
|
||||||
|
//
|
||||||
|
// If no logger was set before Serve is called, a default logger using standard
|
||||||
|
// log library will be used.
|
||||||
|
func (p *TSimpleServer) SetLogger(logger Logger) {
|
||||||
|
p.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) innerAccept() (int32, error) {
|
||||||
|
client, err := p.serverTransport.Accept()
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
closed := atomic.LoadInt32(&p.closed)
|
||||||
|
if closed != 0 {
|
||||||
|
return closed, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if client != nil {
|
||||||
|
p.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer p.wg.Done()
|
||||||
|
if err := p.processRequests(client); err != nil {
|
||||||
|
p.logger(fmt.Sprintf("error processing request: %v", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) AcceptLoop() error {
|
||||||
|
for {
|
||||||
|
closed, err := p.innerAccept()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if closed != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) Serve() error {
|
||||||
|
p.logger = fallbackLogger(p.logger)
|
||||||
|
|
||||||
|
err := p.Listen()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.AcceptLoop()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) Stop() error {
|
||||||
|
p.mu.Lock()
|
||||||
|
defer p.mu.Unlock()
|
||||||
|
if atomic.LoadInt32(&p.closed) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
atomic.StoreInt32(&p.closed, 1)
|
||||||
|
p.serverTransport.Interrupt()
|
||||||
|
p.wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If err is actually EOF, return nil, otherwise return err as-is.
|
||||||
|
func treatEOFErrorsAsNil(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var te TTransportException
|
||||||
|
if errors.As(err, &te) && te.TypeId() == END_OF_FILE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSimpleServer) processRequests(client TTransport) (err error) {
|
||||||
|
defer func() {
|
||||||
|
err = treatEOFErrorsAsNil(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
processor := p.processorFactory.GetProcessor(client)
|
||||||
|
inputTransport, err := p.inputTransportFactory.GetTransport(client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport)
|
||||||
|
var outputTransport TTransport
|
||||||
|
var outputProtocol TProtocol
|
||||||
|
|
||||||
|
// for THeaderProtocol, we must use the same protocol instance for
|
||||||
|
// input and output so that the response is in the same dialect that
|
||||||
|
// the server detected the request was in.
|
||||||
|
headerProtocol, ok := inputProtocol.(*THeaderProtocol)
|
||||||
|
if ok {
|
||||||
|
outputProtocol = inputProtocol
|
||||||
|
} else {
|
||||||
|
oTrans, err := p.outputTransportFactory.GetTransport(client)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
outputTransport = oTrans
|
||||||
|
outputProtocol = p.outputProtocolFactory.GetProtocol(outputTransport)
|
||||||
|
}
|
||||||
|
|
||||||
|
if inputTransport != nil {
|
||||||
|
defer inputTransport.Close()
|
||||||
|
}
|
||||||
|
if outputTransport != nil {
|
||||||
|
defer outputTransport.Close()
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
if atomic.LoadInt32(&p.closed) != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := SetResponseHelper(
|
||||||
|
defaultCtx,
|
||||||
|
TResponseHelper{
|
||||||
|
THeaderResponseHelper: NewTHeaderResponseHelper(outputProtocol),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if headerProtocol != nil {
|
||||||
|
// We need to call ReadFrame here, otherwise we won't
|
||||||
|
// get any headers on the AddReadTHeaderToContext call.
|
||||||
|
//
|
||||||
|
// ReadFrame is safe to be called multiple times so it
|
||||||
|
// won't break when it's called again later when we
|
||||||
|
// actually start to read the message.
|
||||||
|
if err := headerProtocol.ReadFrame(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx = AddReadTHeaderToContext(ctx, headerProtocol.GetReadHeaders())
|
||||||
|
ctx = SetWriteHeaderList(ctx, p.forwardHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := processor.Process(ctx, inputProtocol, outputProtocol)
|
||||||
|
if errors.Is(err, ErrAbandonRequest) {
|
||||||
|
return client.Close()
|
||||||
|
}
|
||||||
|
if errors.As(err, new(TTransportException)) && err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var tae TApplicationException
|
||||||
|
if errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,238 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TSocket struct {
|
||||||
|
conn *socketConn
|
||||||
|
addr net.Addr
|
||||||
|
cfg *TConfiguration
|
||||||
|
|
||||||
|
connectTimeout time.Duration
|
||||||
|
socketTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSocketConf instead.
|
||||||
|
func NewTSocket(hostPort string) (*TSocket, error) {
|
||||||
|
return NewTSocketConf(hostPort, &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSocketConf creates a net.Conn-backed TTransport, given a host and port.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// trans, err := thrift.NewTSocketConf("localhost:9090", &TConfiguration{
|
||||||
|
// ConnectTimeout: time.Second, // Use 0 for no timeout
|
||||||
|
// SocketTimeout: time.Second, // Use 0 for no timeout
|
||||||
|
// })
|
||||||
|
func NewTSocketConf(hostPort string, conf *TConfiguration) (*TSocket, error) {
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", hostPort)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewTSocketFromAddrConf(addr, conf), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSocketConf instead.
|
||||||
|
func NewTSocketTimeout(hostPort string, connTimeout time.Duration, soTimeout time.Duration) (*TSocket, error) {
|
||||||
|
return NewTSocketConf(hostPort, &TConfiguration{
|
||||||
|
ConnectTimeout: connTimeout,
|
||||||
|
SocketTimeout: soTimeout,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSocketFromAddrConf creates a TSocket from a net.Addr
|
||||||
|
func NewTSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSocket {
|
||||||
|
return &TSocket{
|
||||||
|
addr: addr,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSocketFromAddrConf instead.
|
||||||
|
func NewTSocketFromAddrTimeout(addr net.Addr, connTimeout time.Duration, soTimeout time.Duration) *TSocket {
|
||||||
|
return NewTSocketFromAddrConf(addr, &TConfiguration{
|
||||||
|
ConnectTimeout: connTimeout,
|
||||||
|
SocketTimeout: soTimeout,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSocketFromConnConf creates a TSocket from an existing net.Conn.
|
||||||
|
func NewTSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSocket {
|
||||||
|
return &TSocket{
|
||||||
|
conn: wrapSocketConn(conn),
|
||||||
|
addr: conn.RemoteAddr(),
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSocketFromConnConf instead.
|
||||||
|
func NewTSocketFromConnTimeout(conn net.Conn, socketTimeout time.Duration) *TSocket {
|
||||||
|
return NewTSocketFromConnConf(conn, &TConfiguration{
|
||||||
|
SocketTimeout: socketTimeout,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter.
|
||||||
|
//
|
||||||
|
// It can be used to set connect and socket timeouts.
|
||||||
|
func (p *TSocket) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
p.cfg = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the connect timeout
|
||||||
|
func (p *TSocket) SetConnTimeout(timeout time.Duration) error {
|
||||||
|
if p.cfg == nil {
|
||||||
|
p.cfg = &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.cfg.ConnectTimeout = timeout
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the socket timeout
|
||||||
|
func (p *TSocket) SetSocketTimeout(timeout time.Duration) error {
|
||||||
|
if p.cfg == nil {
|
||||||
|
p.cfg = &TConfiguration{
|
||||||
|
noPropagation: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.cfg.SocketTimeout = timeout
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSocket) pushDeadline(read, write bool) {
|
||||||
|
var t time.Time
|
||||||
|
if timeout := p.cfg.GetSocketTimeout(); timeout > 0 {
|
||||||
|
t = time.Now().Add(time.Duration(timeout))
|
||||||
|
}
|
||||||
|
if read && write {
|
||||||
|
p.conn.SetDeadline(t)
|
||||||
|
} else if read {
|
||||||
|
p.conn.SetReadDeadline(t)
|
||||||
|
} else if write {
|
||||||
|
p.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connects the socket, creating a new socket object if necessary.
|
||||||
|
func (p *TSocket) Open() error {
|
||||||
|
if p.conn.isValid() {
|
||||||
|
return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
|
||||||
|
}
|
||||||
|
if p.addr == nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
|
||||||
|
}
|
||||||
|
if len(p.addr.Network()) == 0 {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
|
||||||
|
}
|
||||||
|
if len(p.addr.String()) == 0 {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
if p.conn, err = createSocketConnFromReturn(net.DialTimeout(
|
||||||
|
p.addr.Network(),
|
||||||
|
p.addr.String(),
|
||||||
|
p.cfg.GetConnectTimeout(),
|
||||||
|
)); err != nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the underlying net.Conn
|
||||||
|
func (p *TSocket) Conn() net.Conn {
|
||||||
|
return p.conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the connection is open
|
||||||
|
func (p *TSocket) IsOpen() bool {
|
||||||
|
return p.conn.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes the socket.
|
||||||
|
func (p *TSocket) Close() error {
|
||||||
|
// Close the socket
|
||||||
|
if p.conn != nil {
|
||||||
|
err := p.conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.conn = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns the remote address of the socket.
|
||||||
|
func (p *TSocket) Addr() net.Addr {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSocket) Read(buf []byte) (int, error) {
|
||||||
|
if !p.conn.isValid() {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||||
|
}
|
||||||
|
p.pushDeadline(true, false)
|
||||||
|
// NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between
|
||||||
|
// p.pushDeadline and p.conn.Read could cause the deadline set inside
|
||||||
|
// p.pushDeadline being reset, thus need to be avoided.
|
||||||
|
n, err := p.conn.Read(buf)
|
||||||
|
return n, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSocket) Write(buf []byte) (int, error) {
|
||||||
|
if !p.conn.isValid() {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||||
|
}
|
||||||
|
p.pushDeadline(false, true)
|
||||||
|
return p.conn.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSocket) Flush(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSocket) Interrupt() error {
|
||||||
|
if !p.conn.isValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSocket) RemainingBytes() (num_bytes uint64) {
|
||||||
|
const maxSize = ^uint64(0)
|
||||||
|
return maxSize // the truth is, we just don't know unless framed is used
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*TSocket)(nil)
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// socketConn is a wrapped net.Conn that tries to do connectivity check.
|
||||||
|
type socketConn struct {
|
||||||
|
net.Conn
|
||||||
|
|
||||||
|
buffer [1]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ net.Conn = (*socketConn)(nil)
|
||||||
|
|
||||||
|
// createSocketConnFromReturn is a language sugar to help create socketConn from
|
||||||
|
// return values of functions like net.Dial, tls.Dial, net.Listener.Accept, etc.
|
||||||
|
func createSocketConnFromReturn(conn net.Conn, err error) (*socketConn, error) {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &socketConn{
|
||||||
|
Conn: conn,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapSocketConn wraps an existing net.Conn into *socketConn.
|
||||||
|
func wrapSocketConn(conn net.Conn) *socketConn {
|
||||||
|
// In case conn is already wrapped,
|
||||||
|
// return it as-is and avoid double wrapping.
|
||||||
|
if sc, ok := conn.(*socketConn); ok {
|
||||||
|
return sc
|
||||||
|
}
|
||||||
|
|
||||||
|
return &socketConn{
|
||||||
|
Conn: conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValid checks whether there's a valid connection.
|
||||||
|
//
|
||||||
|
// It's nil safe, and returns false if sc itself is nil, or if the underlying
|
||||||
|
// connection is nil.
|
||||||
|
//
|
||||||
|
// It's the same as the previous implementation of TSocket.IsOpen and
|
||||||
|
// TSSLSocket.IsOpen before we added connectivity check.
|
||||||
|
func (sc *socketConn) isValid() bool {
|
||||||
|
return sc != nil && sc.Conn != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOpen checks whether the connection is open.
|
||||||
|
//
|
||||||
|
// It's nil safe, and returns false if sc itself is nil, or if the underlying
|
||||||
|
// connection is nil.
|
||||||
|
//
|
||||||
|
// Otherwise, it tries to do a connectivity check and returns the result.
|
||||||
|
//
|
||||||
|
// It also has the side effect of resetting the previously set read deadline on
|
||||||
|
// the socket. As a result, it shouldn't be called between setting read deadline
|
||||||
|
// and doing actual read.
|
||||||
|
func (sc *socketConn) IsOpen() bool {
|
||||||
|
if !sc.isValid() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return sc.checkConn() == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read implements io.Reader.
|
||||||
|
//
|
||||||
|
// On Windows, it behaves the same as the underlying net.Conn.Read.
|
||||||
|
//
|
||||||
|
// On non-Windows, it treats len(p) == 0 as a connectivity check instead of
|
||||||
|
// readability check, which means instead of blocking until there's something to
|
||||||
|
// read (readability check), or always return (0, nil) (the default behavior of
|
||||||
|
// go's stdlib implementation on non-Windows), it never blocks, and will return
|
||||||
|
// an error if the connection is lost.
|
||||||
|
func (sc *socketConn) Read(p []byte) (n int, err error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, sc.read0()
|
||||||
|
}
|
||||||
|
|
||||||
|
return sc.Conn.Read(p)
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We rely on this variable to be the zero time,
|
||||||
|
// but define it as global variable to avoid repetitive allocations.
|
||||||
|
// Please DO NOT mutate this variable in any way.
|
||||||
|
var zeroTime time.Time
|
||||||
|
|
||||||
|
func (sc *socketConn) read0() error {
|
||||||
|
return sc.checkConn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *socketConn) checkConn() error {
|
||||||
|
syscallConn, ok := sc.Conn.(syscall.Conn)
|
||||||
|
if !ok {
|
||||||
|
// No way to check, return nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The reading about to be done here is non-blocking so we don't really
|
||||||
|
// need a read deadline. We just need to clear the previously set read
|
||||||
|
// deadline, if any.
|
||||||
|
sc.Conn.SetReadDeadline(zeroTime)
|
||||||
|
|
||||||
|
rc, err := syscallConn.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
|
||||||
|
if readErr := rc.Read(func(fd uintptr) bool {
|
||||||
|
n, _, err = syscall.Recvfrom(int(fd), sc.buffer[:], syscall.MSG_PEEK|syscall.MSG_DONTWAIT)
|
||||||
|
return true
|
||||||
|
}); readErr != nil {
|
||||||
|
return readErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
// We got something, which means we are good
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(err, syscall.EAGAIN) || errors.Is(err, syscall.EWOULDBLOCK) {
|
||||||
|
// This means the connection is still open but we don't have
|
||||||
|
// anything to read right now.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, it means the other side already closed the connection.
|
||||||
|
return io.EOF
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
func (sc *socketConn) read0() error {
|
||||||
|
// On windows, we fallback to the default behavior of reading 0 bytes.
|
||||||
|
var p []byte
|
||||||
|
_, err := sc.Conn.Read(p)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *socketConn) checkConn() error {
|
||||||
|
// On windows, we always return nil for this check.
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TSSLServerSocket struct {
|
||||||
|
listener net.Listener
|
||||||
|
addr net.Addr
|
||||||
|
clientTimeout time.Duration
|
||||||
|
interrupted bool
|
||||||
|
cfg *tls.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSSLServerSocket(listenAddr string, cfg *tls.Config) (*TSSLServerSocket, error) {
|
||||||
|
return NewTSSLServerSocketTimeout(listenAddr, cfg, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTSSLServerSocketTimeout(listenAddr string, cfg *tls.Config, clientTimeout time.Duration) (*TSSLServerSocket, error) {
|
||||||
|
if cfg.MinVersion == 0 {
|
||||||
|
cfg.MinVersion = tls.VersionTLS10
|
||||||
|
}
|
||||||
|
addr, err := net.ResolveTCPAddr("tcp", listenAddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &TSSLServerSocket{addr: addr, clientTimeout: clientTimeout, cfg: cfg}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLServerSocket) Listen() error {
|
||||||
|
if p.IsListening() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.listener = l
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLServerSocket) Accept() (TTransport, error) {
|
||||||
|
if p.interrupted {
|
||||||
|
return nil, errTransportInterrupted
|
||||||
|
}
|
||||||
|
if p.listener == nil {
|
||||||
|
return nil, NewTTransportException(NOT_OPEN, "No underlying server socket")
|
||||||
|
}
|
||||||
|
conn, err := p.listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
return NewTSSLSocketFromConnTimeout(conn, p.cfg, p.clientTimeout), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the socket is listening.
|
||||||
|
func (p *TSSLServerSocket) IsListening() bool {
|
||||||
|
return p.listener != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connects the socket, creating a new socket object if necessary.
|
||||||
|
func (p *TSSLServerSocket) Open() error {
|
||||||
|
if p.IsListening() {
|
||||||
|
return NewTTransportException(ALREADY_OPEN, "Server socket already open")
|
||||||
|
}
|
||||||
|
if l, err := tls.Listen(p.addr.Network(), p.addr.String(), p.cfg); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
p.listener = l
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLServerSocket) Addr() net.Addr {
|
||||||
|
return p.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLServerSocket) Close() error {
|
||||||
|
defer func() {
|
||||||
|
p.listener = nil
|
||||||
|
}()
|
||||||
|
if p.IsListening() {
|
||||||
|
return p.listener.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLServerSocket) Interrupt() error {
|
||||||
|
p.interrupted = true
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TSSLSocket struct {
|
||||||
|
conn *socketConn
|
||||||
|
// hostPort contains host:port (e.g. "asdf.com:12345"). The field is
|
||||||
|
// only valid if addr is nil.
|
||||||
|
hostPort string
|
||||||
|
// addr is nil when hostPort is not "", and is only used when the
|
||||||
|
// TSSLSocket is constructed from a net.Addr.
|
||||||
|
addr net.Addr
|
||||||
|
|
||||||
|
cfg *TConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSSLSocketConf creates a net.Conn-backed TTransport, given a host and port.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// trans, err := thrift.NewTSSLSocketConf("localhost:9090", nil, &TConfiguration{
|
||||||
|
// ConnectTimeout: time.Second, // Use 0 for no timeout
|
||||||
|
// SocketTimeout: time.Second, // Use 0 for no timeout
|
||||||
|
// })
|
||||||
|
func NewTSSLSocketConf(hostPort string, conf *TConfiguration) (*TSSLSocket, error) {
|
||||||
|
if cfg := conf.GetTLSConfig(); cfg != nil && cfg.MinVersion == 0 {
|
||||||
|
cfg.MinVersion = tls.VersionTLS10
|
||||||
|
}
|
||||||
|
return &TSSLSocket{
|
||||||
|
hostPort: hostPort,
|
||||||
|
cfg: conf,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSSLSocketConf instead.
|
||||||
|
func NewTSSLSocket(hostPort string, cfg *tls.Config) (*TSSLSocket, error) {
|
||||||
|
return NewTSSLSocketConf(hostPort, &TConfiguration{
|
||||||
|
TLSConfig: cfg,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSSLSocketConf instead.
|
||||||
|
func NewTSSLSocketTimeout(hostPort string, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) (*TSSLSocket, error) {
|
||||||
|
return NewTSSLSocketConf(hostPort, &TConfiguration{
|
||||||
|
ConnectTimeout: connectTimeout,
|
||||||
|
SocketTimeout: socketTimeout,
|
||||||
|
TLSConfig: cfg,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSSLSocketFromAddrConf creates a TSSLSocket from a net.Addr.
|
||||||
|
func NewTSSLSocketFromAddrConf(addr net.Addr, conf *TConfiguration) *TSSLSocket {
|
||||||
|
return &TSSLSocket{
|
||||||
|
addr: addr,
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSSLSocketFromAddrConf instead.
|
||||||
|
func NewTSSLSocketFromAddrTimeout(addr net.Addr, cfg *tls.Config, connectTimeout, socketTimeout time.Duration) *TSSLSocket {
|
||||||
|
return NewTSSLSocketFromAddrConf(addr, &TConfiguration{
|
||||||
|
ConnectTimeout: connectTimeout,
|
||||||
|
SocketTimeout: socketTimeout,
|
||||||
|
TLSConfig: cfg,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTSSLSocketFromConnConf creates a TSSLSocket from an existing net.Conn.
|
||||||
|
func NewTSSLSocketFromConnConf(conn net.Conn, conf *TConfiguration) *TSSLSocket {
|
||||||
|
return &TSSLSocket{
|
||||||
|
conn: wrapSocketConn(conn),
|
||||||
|
addr: conn.RemoteAddr(),
|
||||||
|
cfg: conf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use NewTSSLSocketFromConnConf instead.
|
||||||
|
func NewTSSLSocketFromConnTimeout(conn net.Conn, cfg *tls.Config, socketTimeout time.Duration) *TSSLSocket {
|
||||||
|
return NewTSSLSocketFromConnConf(conn, &TConfiguration{
|
||||||
|
SocketTimeout: socketTimeout,
|
||||||
|
TLSConfig: cfg,
|
||||||
|
|
||||||
|
noPropagation: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter.
|
||||||
|
//
|
||||||
|
// It can be used to change connect and socket timeouts.
|
||||||
|
func (p *TSSLSocket) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
p.cfg = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the connect timeout
|
||||||
|
func (p *TSSLSocket) SetConnTimeout(timeout time.Duration) error {
|
||||||
|
if p.cfg == nil {
|
||||||
|
p.cfg = &TConfiguration{}
|
||||||
|
}
|
||||||
|
p.cfg.ConnectTimeout = timeout
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the socket timeout
|
||||||
|
func (p *TSSLSocket) SetSocketTimeout(timeout time.Duration) error {
|
||||||
|
if p.cfg == nil {
|
||||||
|
p.cfg = &TConfiguration{}
|
||||||
|
}
|
||||||
|
p.cfg.SocketTimeout = timeout
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLSocket) pushDeadline(read, write bool) {
|
||||||
|
var t time.Time
|
||||||
|
if timeout := p.cfg.GetSocketTimeout(); timeout > 0 {
|
||||||
|
t = time.Now().Add(time.Duration(timeout))
|
||||||
|
}
|
||||||
|
if read && write {
|
||||||
|
p.conn.SetDeadline(t)
|
||||||
|
} else if read {
|
||||||
|
p.conn.SetReadDeadline(t)
|
||||||
|
} else if write {
|
||||||
|
p.conn.SetWriteDeadline(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connects the socket, creating a new socket object if necessary.
|
||||||
|
func (p *TSSLSocket) Open() error {
|
||||||
|
var err error
|
||||||
|
// If we have a hostname, we need to pass the hostname to tls.Dial for
|
||||||
|
// certificate hostname checks.
|
||||||
|
if p.hostPort != "" {
|
||||||
|
if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer(
|
||||||
|
&net.Dialer{
|
||||||
|
Timeout: p.cfg.GetConnectTimeout(),
|
||||||
|
},
|
||||||
|
"tcp",
|
||||||
|
p.hostPort,
|
||||||
|
p.cfg.GetTLSConfig(),
|
||||||
|
)); err != nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if p.conn.isValid() {
|
||||||
|
return NewTTransportException(ALREADY_OPEN, "Socket already connected.")
|
||||||
|
}
|
||||||
|
if p.addr == nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot open nil address.")
|
||||||
|
}
|
||||||
|
if len(p.addr.Network()) == 0 {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot open bad network name.")
|
||||||
|
}
|
||||||
|
if len(p.addr.String()) == 0 {
|
||||||
|
return NewTTransportException(NOT_OPEN, "Cannot open bad address.")
|
||||||
|
}
|
||||||
|
if p.conn, err = createSocketConnFromReturn(tls.DialWithDialer(
|
||||||
|
&net.Dialer{
|
||||||
|
Timeout: p.cfg.GetConnectTimeout(),
|
||||||
|
},
|
||||||
|
p.addr.Network(),
|
||||||
|
p.addr.String(),
|
||||||
|
p.cfg.GetTLSConfig(),
|
||||||
|
)); err != nil {
|
||||||
|
return NewTTransportException(NOT_OPEN, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the underlying net.Conn
|
||||||
|
func (p *TSSLSocket) Conn() net.Conn {
|
||||||
|
return p.conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the connection is open
|
||||||
|
func (p *TSSLSocket) IsOpen() bool {
|
||||||
|
return p.conn.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closes the socket.
|
||||||
|
func (p *TSSLSocket) Close() error {
|
||||||
|
// Close the socket
|
||||||
|
if p.conn != nil {
|
||||||
|
err := p.conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.conn = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLSocket) Read(buf []byte) (int, error) {
|
||||||
|
if !p.conn.isValid() {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||||
|
}
|
||||||
|
p.pushDeadline(true, false)
|
||||||
|
// NOTE: Calling any of p.IsOpen, p.conn.read0, or p.conn.IsOpen between
|
||||||
|
// p.pushDeadline and p.conn.Read could cause the deadline set inside
|
||||||
|
// p.pushDeadline being reset, thus need to be avoided.
|
||||||
|
n, err := p.conn.Read(buf)
|
||||||
|
return n, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLSocket) Write(buf []byte) (int, error) {
|
||||||
|
if !p.conn.isValid() {
|
||||||
|
return 0, NewTTransportException(NOT_OPEN, "Connection not open")
|
||||||
|
}
|
||||||
|
p.pushDeadline(false, true)
|
||||||
|
return p.conn.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLSocket) Flush(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLSocket) Interrupt() error {
|
||||||
|
if !p.conn.isValid() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *TSSLSocket) RemainingBytes() (num_bytes uint64) {
|
||||||
|
const maxSize = ^uint64(0)
|
||||||
|
return maxSize // the truth is, we just don't know unless framed is used
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*TSSLSocket)(nil)
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errTransportInterrupted = errors.New("Transport Interrupted")
|
||||||
|
|
||||||
|
type Flusher interface {
|
||||||
|
Flush() (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContextFlusher interface {
|
||||||
|
Flush(ctx context.Context) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReadSizeProvider interface {
|
||||||
|
RemainingBytes() (num_bytes uint64)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulates the I/O layer
|
||||||
|
type TTransport interface {
|
||||||
|
io.ReadWriteCloser
|
||||||
|
ContextFlusher
|
||||||
|
ReadSizeProvider
|
||||||
|
|
||||||
|
// Opens the transport for communication
|
||||||
|
Open() error
|
||||||
|
|
||||||
|
// Returns true if the transport is open
|
||||||
|
IsOpen() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringWriter interface {
|
||||||
|
WriteString(s string) (n int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is "enchanced" transport with extra capabilities. You need to use one of these
|
||||||
|
// to construct protocol.
|
||||||
|
// Notably, TSocket does not implement this interface, and it is always a mistake to use
|
||||||
|
// TSocket directly in protocol.
|
||||||
|
type TRichTransport interface {
|
||||||
|
io.ReadWriter
|
||||||
|
io.ByteReader
|
||||||
|
io.ByteWriter
|
||||||
|
stringWriter
|
||||||
|
ContextFlusher
|
||||||
|
ReadSizeProvider
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type timeoutable interface {
|
||||||
|
Timeout() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thrift Transport exception
|
||||||
|
type TTransportException interface {
|
||||||
|
TException
|
||||||
|
TypeId() int
|
||||||
|
Err() error
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
UNKNOWN_TRANSPORT_EXCEPTION = 0
|
||||||
|
NOT_OPEN = 1
|
||||||
|
ALREADY_OPEN = 2
|
||||||
|
TIMED_OUT = 3
|
||||||
|
END_OF_FILE = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type tTransportException struct {
|
||||||
|
typeId int
|
||||||
|
err error
|
||||||
|
msg string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TTransportException = (*tTransportException)(nil)
|
||||||
|
|
||||||
|
func (tTransportException) TExceptionType() TExceptionType {
|
||||||
|
return TExceptionTypeTransport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tTransportException) TypeId() int {
|
||||||
|
return p.typeId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tTransportException) Error() string {
|
||||||
|
return p.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tTransportException) Err() error {
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tTransportException) Unwrap() error {
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *tTransportException) Timeout() bool {
|
||||||
|
return p.typeId == TIMED_OUT
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTTransportException(t int, e string) TTransportException {
|
||||||
|
return &tTransportException{
|
||||||
|
typeId: t,
|
||||||
|
err: errors.New(e),
|
||||||
|
msg: e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTTransportExceptionFromError(e error) TTransportException {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if t, ok := e.(TTransportException); ok {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
te := &tTransportException{
|
||||||
|
typeId: UNKNOWN_TRANSPORT_EXCEPTION,
|
||||||
|
err: e,
|
||||||
|
msg: e.Error(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeoutError(e) {
|
||||||
|
te.typeId = TIMED_OUT
|
||||||
|
return te
|
||||||
|
}
|
||||||
|
|
||||||
|
if errors.Is(e, io.EOF) {
|
||||||
|
te.typeId = END_OF_FILE
|
||||||
|
return te
|
||||||
|
}
|
||||||
|
|
||||||
|
return te
|
||||||
|
}
|
||||||
|
|
||||||
|
func prependTTransportException(prepend string, e TTransportException) TTransportException {
|
||||||
|
return &tTransportException{
|
||||||
|
typeId: e.TypeId(),
|
||||||
|
err: e,
|
||||||
|
msg: prepend + e.Error(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTimeoutError returns true when err is an error caused by timeout.
|
||||||
|
//
|
||||||
|
// Note that this also includes TTransportException wrapped timeout errors.
|
||||||
|
func isTimeoutError(err error) bool {
|
||||||
|
var t timeoutable
|
||||||
|
if errors.As(err, &t) {
|
||||||
|
return t.Timeout()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
// Factory class used to create wrapped instance of Transports.
|
||||||
|
// This is used primarily in servers, which get Transports from
|
||||||
|
// a ServerTransport and then may want to mutate them (i.e. create
|
||||||
|
// a BufferedTransport from the underlying base transport)
|
||||||
|
type TTransportFactory interface {
|
||||||
|
GetTransport(trans TTransport) (TTransport, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tTransportFactory struct{}
|
||||||
|
|
||||||
|
// Return a wrapped instance of the base Transport.
|
||||||
|
func (p *tTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
return trans, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTTransportFactory() TTransportFactory {
|
||||||
|
return &tTransportFactory{}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
// Type constants in the Thrift protocol
|
||||||
|
type TType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
STOP = 0
|
||||||
|
VOID = 1
|
||||||
|
BOOL = 2
|
||||||
|
BYTE = 3
|
||||||
|
I08 = 3
|
||||||
|
DOUBLE = 4
|
||||||
|
I16 = 6
|
||||||
|
I32 = 8
|
||||||
|
I64 = 10
|
||||||
|
STRING = 11
|
||||||
|
UTF7 = 11
|
||||||
|
STRUCT = 12
|
||||||
|
MAP = 13
|
||||||
|
SET = 14
|
||||||
|
LIST = 15
|
||||||
|
UTF8 = 16
|
||||||
|
UTF16 = 17
|
||||||
|
//BINARY = 18 wrong and unusued
|
||||||
|
)
|
||||||
|
|
||||||
|
var typeNames = map[int]string{
|
||||||
|
STOP: "STOP",
|
||||||
|
VOID: "VOID",
|
||||||
|
BOOL: "BOOL",
|
||||||
|
BYTE: "BYTE",
|
||||||
|
DOUBLE: "DOUBLE",
|
||||||
|
I16: "I16",
|
||||||
|
I32: "I32",
|
||||||
|
I64: "I64",
|
||||||
|
STRING: "STRING",
|
||||||
|
STRUCT: "STRUCT",
|
||||||
|
MAP: "MAP",
|
||||||
|
SET: "SET",
|
||||||
|
LIST: "LIST",
|
||||||
|
UTF8: "UTF8",
|
||||||
|
UTF16: "UTF16",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p TType) String() string {
|
||||||
|
if s, ok := typeNames[int(p)]; ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return "Unknown"
|
||||||
|
}
|
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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 thrift
|
||||||
|
|
||||||
|
import (
|
||||||
|
"compress/zlib"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TZlibTransportFactory is a factory for TZlibTransport instances
|
||||||
|
type TZlibTransportFactory struct {
|
||||||
|
level int
|
||||||
|
factory TTransportFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
// TZlibTransport is a TTransport implementation that makes use of zlib compression.
|
||||||
|
type TZlibTransport struct {
|
||||||
|
reader io.ReadCloser
|
||||||
|
transport TTransport
|
||||||
|
writer *zlib.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTransport constructs a new instance of NewTZlibTransport
|
||||||
|
func (p *TZlibTransportFactory) GetTransport(trans TTransport) (TTransport, error) {
|
||||||
|
if p.factory != nil {
|
||||||
|
// wrap other factory
|
||||||
|
var err error
|
||||||
|
trans, err = p.factory.GetTransport(trans)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NewTZlibTransport(trans, p.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTZlibTransportFactory constructs a new instance of NewTZlibTransportFactory
|
||||||
|
func NewTZlibTransportFactory(level int) *TZlibTransportFactory {
|
||||||
|
return &TZlibTransportFactory{level: level, factory: nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTZlibTransportFactory constructs a new instance of TZlibTransportFactory
|
||||||
|
// as a wrapper over existing transport factory
|
||||||
|
func NewTZlibTransportFactoryWithFactory(level int, factory TTransportFactory) *TZlibTransportFactory {
|
||||||
|
return &TZlibTransportFactory{level: level, factory: factory}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTZlibTransport constructs a new instance of TZlibTransport
|
||||||
|
func NewTZlibTransport(trans TTransport, level int) (*TZlibTransport, error) {
|
||||||
|
w, err := zlib.NewWriterLevel(trans, level)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TZlibTransport{
|
||||||
|
writer: w,
|
||||||
|
transport: trans,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the reader and writer (flushing any unwritten data) and closes
|
||||||
|
// the underlying transport.
|
||||||
|
func (z *TZlibTransport) Close() error {
|
||||||
|
if z.reader != nil {
|
||||||
|
if err := z.reader.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := z.writer.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return z.transport.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush flushes the writer and its underlying transport.
|
||||||
|
func (z *TZlibTransport) Flush(ctx context.Context) error {
|
||||||
|
if err := z.writer.Flush(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return z.transport.Flush(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOpen returns true if the transport is open
|
||||||
|
func (z *TZlibTransport) IsOpen() bool {
|
||||||
|
return z.transport.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens the transport for communication
|
||||||
|
func (z *TZlibTransport) Open() error {
|
||||||
|
return z.transport.Open()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *TZlibTransport) Read(p []byte) (int, error) {
|
||||||
|
if z.reader == nil {
|
||||||
|
r, err := zlib.NewReader(z.transport)
|
||||||
|
if err != nil {
|
||||||
|
return 0, NewTTransportExceptionFromError(err)
|
||||||
|
}
|
||||||
|
z.reader = r
|
||||||
|
}
|
||||||
|
|
||||||
|
return z.reader.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemainingBytes returns the size in bytes of the data that is still to be
|
||||||
|
// read.
|
||||||
|
func (z *TZlibTransport) RemainingBytes() uint64 {
|
||||||
|
return z.transport.RemainingBytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *TZlibTransport) Write(p []byte) (int, error) {
|
||||||
|
return z.writer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTConfiguration implements TConfigurationSetter for propagation.
|
||||||
|
func (z *TZlibTransport) SetTConfiguration(conf *TConfiguration) {
|
||||||
|
PropagateTConfiguration(z.transport, conf)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TConfigurationSetter = (*TZlibTransport)(nil)
|
@ -0,0 +1,360 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/codes"
|
||||||
|
gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
keyInstrumentationLibraryName = "otel.library.name"
|
||||||
|
keyInstrumentationLibraryVersion = "otel.library.version"
|
||||||
|
keyError = "error"
|
||||||
|
keySpanKind = "span.kind"
|
||||||
|
keyStatusCode = "otel.status_code"
|
||||||
|
keyStatusMessage = "otel.status_description"
|
||||||
|
keyDroppedAttributeCount = "otel.event.dropped_attributes_count"
|
||||||
|
keyEventName = "event"
|
||||||
|
)
|
||||||
|
|
||||||
|
// New returns an OTel Exporter implementation that exports the collected
|
||||||
|
// spans to Jaeger.
|
||||||
|
func New(endpointOption EndpointOption) (*Exporter, error) {
|
||||||
|
uploader, err := endpointOption.newBatchUploader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch default service.name from default resource for backup
|
||||||
|
var defaultServiceName string
|
||||||
|
defaultResource := resource.Default()
|
||||||
|
if value, exists := defaultResource.Set().Value(semconv.ServiceNameKey); exists {
|
||||||
|
defaultServiceName = value.AsString()
|
||||||
|
}
|
||||||
|
if defaultServiceName == "" {
|
||||||
|
return nil, fmt.Errorf("failed to get service name from default resource")
|
||||||
|
}
|
||||||
|
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
e := &Exporter{
|
||||||
|
uploader: uploader,
|
||||||
|
stopCh: stopCh,
|
||||||
|
defaultServiceName: defaultServiceName,
|
||||||
|
}
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exporter exports OpenTelemetry spans to a Jaeger agent or collector.
|
||||||
|
type Exporter struct {
|
||||||
|
uploader batchUploader
|
||||||
|
stopOnce sync.Once
|
||||||
|
stopCh chan struct{}
|
||||||
|
defaultServiceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ sdktrace.SpanExporter = (*Exporter)(nil)
|
||||||
|
|
||||||
|
// ExportSpans transforms and exports OpenTelemetry spans to Jaeger.
|
||||||
|
func (e *Exporter) ExportSpans(ctx context.Context, spans []sdktrace.ReadOnlySpan) error {
|
||||||
|
// Return fast if context is already canceled or Exporter shutdown.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
case <-e.stopCh:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel export if Exporter is shutdown.
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
go func(ctx context.Context, cancel context.CancelFunc) {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
case <-e.stopCh:
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}(ctx, cancel)
|
||||||
|
|
||||||
|
for _, batch := range jaegerBatchList(spans, e.defaultServiceName) {
|
||||||
|
if err := e.uploader.upload(ctx, batch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown stops the Exporter. This will close all connections and release
|
||||||
|
// all resources held by the Exporter.
|
||||||
|
func (e *Exporter) Shutdown(ctx context.Context) error {
|
||||||
|
// Stop any active and subsequent exports.
|
||||||
|
e.stopOnce.Do(func() { close(e.stopCh) })
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return e.uploader.shutdown(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalLog is the marshaling function used by the logging system to represent this exporter.
|
||||||
|
func (e *Exporter) MarshalLog() interface{} {
|
||||||
|
return struct {
|
||||||
|
Type string
|
||||||
|
}{
|
||||||
|
Type: "jaeger",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func spanToThrift(ss sdktrace.ReadOnlySpan) *gen.Span {
|
||||||
|
attr := ss.Attributes()
|
||||||
|
tags := make([]*gen.Tag, 0, len(attr))
|
||||||
|
for _, kv := range attr {
|
||||||
|
tag := keyValueToTag(kv)
|
||||||
|
if tag != nil {
|
||||||
|
tags = append(tags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is := ss.InstrumentationScope(); is.Name != "" {
|
||||||
|
tags = append(tags, getStringTag(keyInstrumentationLibraryName, is.Name))
|
||||||
|
if is.Version != "" {
|
||||||
|
tags = append(tags, getStringTag(keyInstrumentationLibraryVersion, is.Version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ss.SpanKind() != trace.SpanKindInternal {
|
||||||
|
tags = append(tags,
|
||||||
|
getStringTag(keySpanKind, ss.SpanKind().String()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ss.Status().Code != codes.Unset {
|
||||||
|
switch ss.Status().Code {
|
||||||
|
case codes.Ok:
|
||||||
|
tags = append(tags, getStringTag(keyStatusCode, "OK"))
|
||||||
|
case codes.Error:
|
||||||
|
tags = append(tags, getBoolTag(keyError, true))
|
||||||
|
tags = append(tags, getStringTag(keyStatusCode, "ERROR"))
|
||||||
|
}
|
||||||
|
if ss.Status().Description != "" {
|
||||||
|
tags = append(tags, getStringTag(keyStatusMessage, ss.Status().Description))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var logs []*gen.Log
|
||||||
|
for _, a := range ss.Events() {
|
||||||
|
nTags := len(a.Attributes)
|
||||||
|
if a.Name != "" {
|
||||||
|
nTags++
|
||||||
|
}
|
||||||
|
if a.DroppedAttributeCount != 0 {
|
||||||
|
nTags++
|
||||||
|
}
|
||||||
|
fields := make([]*gen.Tag, 0, nTags)
|
||||||
|
if a.Name != "" {
|
||||||
|
// If an event contains an attribute with the same key, it needs
|
||||||
|
// to be given precedence and overwrite this.
|
||||||
|
fields = append(fields, getStringTag(keyEventName, a.Name))
|
||||||
|
}
|
||||||
|
for _, kv := range a.Attributes {
|
||||||
|
tag := keyValueToTag(kv)
|
||||||
|
if tag != nil {
|
||||||
|
fields = append(fields, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if a.DroppedAttributeCount != 0 {
|
||||||
|
fields = append(fields, getInt64Tag(keyDroppedAttributeCount, int64(a.DroppedAttributeCount)))
|
||||||
|
}
|
||||||
|
logs = append(logs, &gen.Log{
|
||||||
|
Timestamp: a.Time.UnixNano() / 1000,
|
||||||
|
Fields: fields,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var refs []*gen.SpanRef
|
||||||
|
for _, link := range ss.Links() {
|
||||||
|
tid := link.SpanContext.TraceID()
|
||||||
|
sid := link.SpanContext.SpanID()
|
||||||
|
refs = append(refs, &gen.SpanRef{
|
||||||
|
TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])),
|
||||||
|
TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])),
|
||||||
|
SpanId: int64(binary.BigEndian.Uint64(sid[:])),
|
||||||
|
RefType: gen.SpanRefType_FOLLOWS_FROM,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := ss.SpanContext().TraceID()
|
||||||
|
sid := ss.SpanContext().SpanID()
|
||||||
|
psid := ss.Parent().SpanID()
|
||||||
|
return &gen.Span{
|
||||||
|
TraceIdHigh: int64(binary.BigEndian.Uint64(tid[0:8])),
|
||||||
|
TraceIdLow: int64(binary.BigEndian.Uint64(tid[8:16])),
|
||||||
|
SpanId: int64(binary.BigEndian.Uint64(sid[:])),
|
||||||
|
ParentSpanId: int64(binary.BigEndian.Uint64(psid[:])),
|
||||||
|
OperationName: ss.Name(), // TODO: if span kind is added then add prefix "Sent"/"Recv"
|
||||||
|
Flags: int32(ss.SpanContext().TraceFlags()),
|
||||||
|
StartTime: ss.StartTime().UnixNano() / 1000,
|
||||||
|
Duration: ss.EndTime().Sub(ss.StartTime()).Nanoseconds() / 1000,
|
||||||
|
Tags: tags,
|
||||||
|
Logs: logs,
|
||||||
|
References: refs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func keyValueToTag(keyValue attribute.KeyValue) *gen.Tag {
|
||||||
|
var tag *gen.Tag
|
||||||
|
switch keyValue.Value.Type() {
|
||||||
|
case attribute.STRING:
|
||||||
|
s := keyValue.Value.AsString()
|
||||||
|
tag = &gen.Tag{
|
||||||
|
Key: string(keyValue.Key),
|
||||||
|
VStr: &s,
|
||||||
|
VType: gen.TagType_STRING,
|
||||||
|
}
|
||||||
|
case attribute.BOOL:
|
||||||
|
b := keyValue.Value.AsBool()
|
||||||
|
tag = &gen.Tag{
|
||||||
|
Key: string(keyValue.Key),
|
||||||
|
VBool: &b,
|
||||||
|
VType: gen.TagType_BOOL,
|
||||||
|
}
|
||||||
|
case attribute.INT64:
|
||||||
|
i := keyValue.Value.AsInt64()
|
||||||
|
tag = &gen.Tag{
|
||||||
|
Key: string(keyValue.Key),
|
||||||
|
VLong: &i,
|
||||||
|
VType: gen.TagType_LONG,
|
||||||
|
}
|
||||||
|
case attribute.FLOAT64:
|
||||||
|
f := keyValue.Value.AsFloat64()
|
||||||
|
tag = &gen.Tag{
|
||||||
|
Key: string(keyValue.Key),
|
||||||
|
VDouble: &f,
|
||||||
|
VType: gen.TagType_DOUBLE,
|
||||||
|
}
|
||||||
|
case attribute.BOOLSLICE,
|
||||||
|
attribute.INT64SLICE,
|
||||||
|
attribute.FLOAT64SLICE,
|
||||||
|
attribute.STRINGSLICE:
|
||||||
|
data, _ := json.Marshal(keyValue.Value.AsInterface())
|
||||||
|
a := (string)(data)
|
||||||
|
tag = &gen.Tag{
|
||||||
|
Key: string(keyValue.Key),
|
||||||
|
VStr: &a,
|
||||||
|
VType: gen.TagType_STRING,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func getInt64Tag(k string, i int64) *gen.Tag {
|
||||||
|
return &gen.Tag{
|
||||||
|
Key: k,
|
||||||
|
VLong: &i,
|
||||||
|
VType: gen.TagType_LONG,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStringTag(k, s string) *gen.Tag {
|
||||||
|
return &gen.Tag{
|
||||||
|
Key: k,
|
||||||
|
VStr: &s,
|
||||||
|
VType: gen.TagType_STRING,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBoolTag(k string, b bool) *gen.Tag {
|
||||||
|
return &gen.Tag{
|
||||||
|
Key: k,
|
||||||
|
VBool: &b,
|
||||||
|
VType: gen.TagType_BOOL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jaegerBatchList transforms a slice of spans into a slice of jaeger Batch.
|
||||||
|
func jaegerBatchList(ssl []sdktrace.ReadOnlySpan, defaultServiceName string) []*gen.Batch {
|
||||||
|
if len(ssl) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
batchDict := make(map[attribute.Distinct]*gen.Batch)
|
||||||
|
|
||||||
|
for _, ss := range ssl {
|
||||||
|
if ss == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceKey := ss.Resource().Equivalent()
|
||||||
|
batch, bOK := batchDict[resourceKey]
|
||||||
|
if !bOK {
|
||||||
|
batch = &gen.Batch{
|
||||||
|
Process: process(ss.Resource(), defaultServiceName),
|
||||||
|
Spans: []*gen.Span{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
batch.Spans = append(batch.Spans, spanToThrift(ss))
|
||||||
|
batchDict[resourceKey] = batch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform the categorized map into a slice
|
||||||
|
batchList := make([]*gen.Batch, 0, len(batchDict))
|
||||||
|
for _, batch := range batchDict {
|
||||||
|
batchList = append(batchList, batch)
|
||||||
|
}
|
||||||
|
return batchList
|
||||||
|
}
|
||||||
|
|
||||||
|
// process transforms an OTel Resource into a jaeger Process.
|
||||||
|
func process(res *resource.Resource, defaultServiceName string) *gen.Process {
|
||||||
|
var process gen.Process
|
||||||
|
|
||||||
|
var serviceName attribute.KeyValue
|
||||||
|
if res != nil {
|
||||||
|
for iter := res.Iter(); iter.Next(); {
|
||||||
|
if iter.Attribute().Key == semconv.ServiceNameKey {
|
||||||
|
serviceName = iter.Attribute()
|
||||||
|
// Don't convert service.name into tag.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tag := keyValueToTag(iter.Attribute()); tag != nil {
|
||||||
|
process.Tags = append(process.Tags, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no service.name is contained in a Span's Resource,
|
||||||
|
// that field MUST be populated from the default Resource.
|
||||||
|
if serviceName.Value.AsString() == "" {
|
||||||
|
serviceName = semconv.ServiceName(defaultServiceName)
|
||||||
|
}
|
||||||
|
process.ServiceName = serviceName.Value.AsString()
|
||||||
|
|
||||||
|
return &process
|
||||||
|
}
|
@ -0,0 +1,204 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// reconnectingUDPConn is an implementation of udpConn that resolves hostPort every resolveTimeout, if the resolved address is
|
||||||
|
// different than the current conn then the new address is dialed and the conn is swapped.
|
||||||
|
type reconnectingUDPConn struct {
|
||||||
|
// `sync/atomic` expects the first word in an allocated struct to be 64-bit
|
||||||
|
// aligned on both ARM and x86-32. See https://goo.gl/zW7dgq for more details.
|
||||||
|
bufferBytes int64
|
||||||
|
hostPort string
|
||||||
|
resolveFunc resolveFunc
|
||||||
|
dialFunc dialFunc
|
||||||
|
logger logr.Logger
|
||||||
|
|
||||||
|
connMtx sync.RWMutex
|
||||||
|
conn *net.UDPConn
|
||||||
|
destAddr *net.UDPAddr
|
||||||
|
closeChan chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type resolveFunc func(network string, hostPort string) (*net.UDPAddr, error)
|
||||||
|
type dialFunc func(network string, laddr, raddr *net.UDPAddr) (*net.UDPConn, error)
|
||||||
|
|
||||||
|
// newReconnectingUDPConn returns a new udpConn that resolves hostPort every resolveTimeout, if the resolved address is
|
||||||
|
// different than the current conn then the new address is dialed and the conn is swapped.
|
||||||
|
func newReconnectingUDPConn(hostPort string, bufferBytes int, resolveTimeout time.Duration, resolveFunc resolveFunc, dialFunc dialFunc, logger logr.Logger) (*reconnectingUDPConn, error) {
|
||||||
|
conn := &reconnectingUDPConn{
|
||||||
|
hostPort: hostPort,
|
||||||
|
resolveFunc: resolveFunc,
|
||||||
|
dialFunc: dialFunc,
|
||||||
|
logger: logger,
|
||||||
|
closeChan: make(chan struct{}),
|
||||||
|
bufferBytes: int64(bufferBytes),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conn.attemptResolveAndDial(); err != nil {
|
||||||
|
conn.logf("failed resolving destination address on connection startup, with err: %q. retrying in %s", err.Error(), resolveTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
go conn.reconnectLoop(resolveTimeout)
|
||||||
|
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *reconnectingUDPConn) logf(format string, args ...interface{}) {
|
||||||
|
if c.logger != emptyLogger {
|
||||||
|
c.logger.Info(format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *reconnectingUDPConn) reconnectLoop(resolveTimeout time.Duration) {
|
||||||
|
ticker := time.NewTicker(resolveTimeout)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-c.closeChan:
|
||||||
|
return
|
||||||
|
case <-ticker.C:
|
||||||
|
if err := c.attemptResolveAndDial(); err != nil {
|
||||||
|
c.logf("%s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *reconnectingUDPConn) attemptResolveAndDial() error {
|
||||||
|
newAddr, err := c.resolveFunc("udp", c.hostPort)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve new addr for host %q, with err: %w", c.hostPort, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.connMtx.RLock()
|
||||||
|
curAddr := c.destAddr
|
||||||
|
c.connMtx.RUnlock()
|
||||||
|
|
||||||
|
// dont attempt dial if an addr was successfully dialed previously and, resolved addr is the same as current conn
|
||||||
|
if curAddr != nil && newAddr.String() == curAddr.String() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.attemptDialNewAddr(newAddr); err != nil {
|
||||||
|
return fmt.Errorf("failed to dial newly resolved addr '%s', with err: %w", newAddr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *reconnectingUDPConn) attemptDialNewAddr(newAddr *net.UDPAddr) error {
|
||||||
|
connUDP, err := c.dialFunc(newAddr.Network(), nil, newAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bufferBytes := int(atomic.LoadInt64(&c.bufferBytes)); bufferBytes != 0 {
|
||||||
|
if err = connUDP.SetWriteBuffer(bufferBytes); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.connMtx.Lock()
|
||||||
|
c.destAddr = newAddr
|
||||||
|
// store prev to close later
|
||||||
|
prevConn := c.conn
|
||||||
|
c.conn = connUDP
|
||||||
|
c.connMtx.Unlock()
|
||||||
|
|
||||||
|
if prevConn != nil {
|
||||||
|
return prevConn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write calls net.udpConn.Write, if it fails an attempt is made to connect to a new addr, if that succeeds the write is retried before returning.
|
||||||
|
func (c *reconnectingUDPConn) Write(b []byte) (int, error) {
|
||||||
|
var bytesWritten int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
c.connMtx.RLock()
|
||||||
|
conn := c.conn
|
||||||
|
c.connMtx.RUnlock()
|
||||||
|
|
||||||
|
if conn == nil {
|
||||||
|
// if connection is not initialized indicate this with err in order to hook into retry logic
|
||||||
|
err = fmt.Errorf("UDP connection not yet initialized, an address has not been resolved")
|
||||||
|
} else {
|
||||||
|
bytesWritten, err = conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
return bytesWritten, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// attempt to resolve and dial new address in case that's the problem, if resolve and dial succeeds, try write again
|
||||||
|
if reconnErr := c.attemptResolveAndDial(); reconnErr == nil {
|
||||||
|
c.connMtx.RLock()
|
||||||
|
conn := c.conn
|
||||||
|
c.connMtx.RUnlock()
|
||||||
|
|
||||||
|
return conn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return original error if reconn fails
|
||||||
|
return bytesWritten, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close stops the reconnectLoop, then closes the connection via net.udpConn 's implementation.
|
||||||
|
func (c *reconnectingUDPConn) Close() error {
|
||||||
|
close(c.closeChan)
|
||||||
|
|
||||||
|
// acquire rw lock before closing conn to ensure calls to Write drain
|
||||||
|
c.connMtx.Lock()
|
||||||
|
defer c.connMtx.Unlock()
|
||||||
|
|
||||||
|
if c.conn != nil {
|
||||||
|
return c.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriteBuffer defers to the net.udpConn SetWriteBuffer implementation wrapped with a RLock. if no conn is currently held
|
||||||
|
// and SetWriteBuffer is called store bufferBytes to be set for new conns.
|
||||||
|
func (c *reconnectingUDPConn) SetWriteBuffer(bytes int) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
c.connMtx.RLock()
|
||||||
|
conn := c.conn
|
||||||
|
c.connMtx.RUnlock()
|
||||||
|
|
||||||
|
if conn != nil {
|
||||||
|
err = c.conn.SetWriteBuffer(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
atomic.StoreInt64(&c.bufferBytes, int64(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
@ -0,0 +1,339 @@
|
|||||||
|
// Copyright The OpenTelemetry Authors
|
||||||
|
//
|
||||||
|
// 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 jaeger // import "go.opentelemetry.io/otel/exporters/jaeger"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-logr/logr"
|
||||||
|
"github.com/go-logr/stdr"
|
||||||
|
|
||||||
|
gen "go.opentelemetry.io/otel/exporters/jaeger/internal/gen-go/jaeger"
|
||||||
|
"go.opentelemetry.io/otel/exporters/jaeger/internal/third_party/thrift/lib/go/thrift"
|
||||||
|
)
|
||||||
|
|
||||||
|
// batchUploader send a batch of spans to Jaeger.
|
||||||
|
type batchUploader interface {
|
||||||
|
upload(context.Context, *gen.Batch) error
|
||||||
|
shutdown(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointOption configures a Jaeger endpoint.
|
||||||
|
type EndpointOption interface {
|
||||||
|
newBatchUploader() (batchUploader, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type endpointOptionFunc func() (batchUploader, error)
|
||||||
|
|
||||||
|
func (fn endpointOptionFunc) newBatchUploader() (batchUploader, error) {
|
||||||
|
return fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAgentEndpoint configures the Jaeger exporter to send spans to a Jaeger agent
|
||||||
|
// over compact thrift protocol. This will use the following environment variables for
|
||||||
|
// configuration if no explicit option is provided:
|
||||||
|
//
|
||||||
|
// - OTEL_EXPORTER_JAEGER_AGENT_HOST is used for the agent address host
|
||||||
|
// - OTEL_EXPORTER_JAEGER_AGENT_PORT is used for the agent address port
|
||||||
|
//
|
||||||
|
// The passed options will take precedence over any environment variables and default values
|
||||||
|
// will be used if neither are provided.
|
||||||
|
func WithAgentEndpoint(options ...AgentEndpointOption) EndpointOption {
|
||||||
|
return endpointOptionFunc(func() (batchUploader, error) {
|
||||||
|
cfg := agentEndpointConfig{
|
||||||
|
agentClientUDPParams{
|
||||||
|
AttemptReconnecting: true,
|
||||||
|
Host: envOr(envAgentHost, "localhost"),
|
||||||
|
Port: envOr(envAgentPort, "6831"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, opt := range options {
|
||||||
|
cfg = opt.apply(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := newAgentClientUDP(cfg.agentClientUDPParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &agentUploader{client: client}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AgentEndpointOption configures a Jaeger agent endpoint.
|
||||||
|
type AgentEndpointOption interface {
|
||||||
|
apply(agentEndpointConfig) agentEndpointConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type agentEndpointConfig struct {
|
||||||
|
agentClientUDPParams
|
||||||
|
}
|
||||||
|
|
||||||
|
type agentEndpointOptionFunc func(agentEndpointConfig) agentEndpointConfig
|
||||||
|
|
||||||
|
func (fn agentEndpointOptionFunc) apply(cfg agentEndpointConfig) agentEndpointConfig {
|
||||||
|
return fn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAgentHost sets a host to be used in the agent client endpoint.
|
||||||
|
// This option overrides any value set for the
|
||||||
|
// OTEL_EXPORTER_JAEGER_AGENT_HOST environment variable.
|
||||||
|
// If this option is not passed and the env var is not set, "localhost" will be used by default.
|
||||||
|
func WithAgentHost(host string) AgentEndpointOption {
|
||||||
|
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
|
||||||
|
o.Host = host
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAgentPort sets a port to be used in the agent client endpoint.
|
||||||
|
// This option overrides any value set for the
|
||||||
|
// OTEL_EXPORTER_JAEGER_AGENT_PORT environment variable.
|
||||||
|
// If this option is not passed and the env var is not set, "6831" will be used by default.
|
||||||
|
func WithAgentPort(port string) AgentEndpointOption {
|
||||||
|
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
|
||||||
|
o.Port = port
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyLogger = logr.Logger{}
|
||||||
|
|
||||||
|
// WithLogger sets a logger to be used by agent client.
|
||||||
|
// WithLogger and WithLogr will overwrite each other.
|
||||||
|
func WithLogger(logger *log.Logger) AgentEndpointOption {
|
||||||
|
return WithLogr(stdr.New(logger))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogr sets a logr.Logger to be used by agent client.
|
||||||
|
// WithLogr and WithLogger will overwrite each other.
|
||||||
|
func WithLogr(logger logr.Logger) AgentEndpointOption {
|
||||||
|
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
|
||||||
|
o.Logger = logger
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDisableAttemptReconnecting sets option to disable reconnecting udp client.
|
||||||
|
func WithDisableAttemptReconnecting() AgentEndpointOption {
|
||||||
|
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
|
||||||
|
o.AttemptReconnecting = false
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAttemptReconnectingInterval sets the interval between attempts to re resolve agent endpoint.
|
||||||
|
func WithAttemptReconnectingInterval(interval time.Duration) AgentEndpointOption {
|
||||||
|
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
|
||||||
|
o.AttemptReconnectInterval = interval
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMaxPacketSize sets the maximum UDP packet size for transport to the Jaeger agent.
|
||||||
|
func WithMaxPacketSize(size int) AgentEndpointOption {
|
||||||
|
return agentEndpointOptionFunc(func(o agentEndpointConfig) agentEndpointConfig {
|
||||||
|
o.MaxPacketSize = size
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCollectorEndpoint defines the full URL to the Jaeger HTTP Thrift collector. This will
|
||||||
|
// use the following environment variables for configuration if no explicit option is provided:
|
||||||
|
//
|
||||||
|
// - OTEL_EXPORTER_JAEGER_ENDPOINT is the HTTP endpoint for sending spans directly to a collector.
|
||||||
|
// - OTEL_EXPORTER_JAEGER_USER is the username to be sent as authentication to the collector endpoint.
|
||||||
|
// - OTEL_EXPORTER_JAEGER_PASSWORD is the password to be sent as authentication to the collector endpoint.
|
||||||
|
//
|
||||||
|
// The passed options will take precedence over any environment variables.
|
||||||
|
// If neither values are provided for the endpoint, the default value of "http://localhost:14268/api/traces" will be used.
|
||||||
|
// If neither values are provided for the username or the password, they will not be set since there is no default.
|
||||||
|
func WithCollectorEndpoint(options ...CollectorEndpointOption) EndpointOption {
|
||||||
|
return endpointOptionFunc(func() (batchUploader, error) {
|
||||||
|
cfg := collectorEndpointConfig{
|
||||||
|
endpoint: envOr(envEndpoint, "http://localhost:14268/api/traces"),
|
||||||
|
username: envOr(envUser, ""),
|
||||||
|
password: envOr(envPassword, ""),
|
||||||
|
httpClient: http.DefaultClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range options {
|
||||||
|
cfg = opt.apply(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &collectorUploader{
|
||||||
|
endpoint: cfg.endpoint,
|
||||||
|
username: cfg.username,
|
||||||
|
password: cfg.password,
|
||||||
|
httpClient: cfg.httpClient,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectorEndpointOption configures a Jaeger collector endpoint.
|
||||||
|
type CollectorEndpointOption interface {
|
||||||
|
apply(collectorEndpointConfig) collectorEndpointConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type collectorEndpointConfig struct {
|
||||||
|
// endpoint for sending spans directly to a collector.
|
||||||
|
endpoint string
|
||||||
|
|
||||||
|
// username to be used for authentication with the collector endpoint.
|
||||||
|
username string
|
||||||
|
|
||||||
|
// password to be used for authentication with the collector endpoint.
|
||||||
|
password string
|
||||||
|
|
||||||
|
// httpClient to be used to make requests to the collector endpoint.
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
type collectorEndpointOptionFunc func(collectorEndpointConfig) collectorEndpointConfig
|
||||||
|
|
||||||
|
func (fn collectorEndpointOptionFunc) apply(cfg collectorEndpointConfig) collectorEndpointConfig {
|
||||||
|
return fn(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEndpoint is the URL for the Jaeger collector that spans are sent to.
|
||||||
|
// This option overrides any value set for the
|
||||||
|
// OTEL_EXPORTER_JAEGER_ENDPOINT environment variable.
|
||||||
|
// If this option is not passed and the environment variable is not set,
|
||||||
|
// "http://localhost:14268/api/traces" will be used by default.
|
||||||
|
func WithEndpoint(endpoint string) CollectorEndpointOption {
|
||||||
|
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
|
||||||
|
o.endpoint = endpoint
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUsername sets the username to be used in the authorization header sent for all requests to the collector.
|
||||||
|
// This option overrides any value set for the
|
||||||
|
// OTEL_EXPORTER_JAEGER_USER environment variable.
|
||||||
|
// If this option is not passed and the environment variable is not set, no username will be set.
|
||||||
|
func WithUsername(username string) CollectorEndpointOption {
|
||||||
|
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
|
||||||
|
o.username = username
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPassword sets the password to be used in the authorization header sent for all requests to the collector.
|
||||||
|
// This option overrides any value set for the
|
||||||
|
// OTEL_EXPORTER_JAEGER_PASSWORD environment variable.
|
||||||
|
// If this option is not passed and the environment variable is not set, no password will be set.
|
||||||
|
func WithPassword(password string) CollectorEndpointOption {
|
||||||
|
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
|
||||||
|
o.password = password
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithHTTPClient sets the http client to be used to make request to the collector endpoint.
|
||||||
|
func WithHTTPClient(client *http.Client) CollectorEndpointOption {
|
||||||
|
return collectorEndpointOptionFunc(func(o collectorEndpointConfig) collectorEndpointConfig {
|
||||||
|
o.httpClient = client
|
||||||
|
return o
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// agentUploader implements batchUploader interface sending batches to
|
||||||
|
// Jaeger through the UDP agent.
|
||||||
|
type agentUploader struct {
|
||||||
|
client *agentClientUDP
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ batchUploader = (*agentUploader)(nil)
|
||||||
|
|
||||||
|
func (a *agentUploader) shutdown(ctx context.Context) error {
|
||||||
|
done := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
done <- a.client.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
// Prioritize not blocking the calling thread and just leak the
|
||||||
|
// spawned goroutine to close the client.
|
||||||
|
return ctx.Err()
|
||||||
|
case err := <-done:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *agentUploader) upload(ctx context.Context, batch *gen.Batch) error {
|
||||||
|
return a.client.EmitBatch(ctx, batch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectorUploader implements batchUploader interface sending batches to
|
||||||
|
// Jaeger through the collector http endpoint.
|
||||||
|
type collectorUploader struct {
|
||||||
|
endpoint string
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ batchUploader = (*collectorUploader)(nil)
|
||||||
|
|
||||||
|
func (c *collectorUploader) shutdown(ctx context.Context) error {
|
||||||
|
// The Exporter will cancel any active exports and will prevent all
|
||||||
|
// subsequent exports, so nothing to do here.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collectorUploader) upload(ctx context.Context, batch *gen.Batch) error {
|
||||||
|
body, err := serialize(batch)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", c.endpoint, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if c.username != "" && c.password != "" {
|
||||||
|
req.SetBasicAuth(c.username, c.password)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-thrift")
|
||||||
|
|
||||||
|
resp, err := c.httpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = io.Copy(io.Discard, resp.Body)
|
||||||
|
if err = resp.Body.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
return fmt.Errorf("failed to upload traces; HTTP status code: %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serialize(obj thrift.TStruct) (*bytes.Buffer, error) {
|
||||||
|
buf := thrift.NewTMemoryBuffer()
|
||||||
|
if err := obj.Write(context.Background(), thrift.NewTBinaryProtocolConf(buf, &thrift.TConfiguration{})); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Buffer, nil
|
||||||
|
}
|
Loading…
Reference in New Issue