Sync vendor folder.
This commit is contained in:
parent
4c0f4cc9d9
commit
1eb00476c8
|
@ -0,0 +1,20 @@
|
||||||
|
Copyright (C) 2013 Blake Mizerany
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,292 @@
|
||||||
|
// Package quantile computes approximate quantiles over an unbounded data
|
||||||
|
// stream within low memory and CPU bounds.
|
||||||
|
//
|
||||||
|
// A small amount of accuracy is traded to achieve the above properties.
|
||||||
|
//
|
||||||
|
// Multiple streams can be merged before calling Query to generate a single set
|
||||||
|
// of results. This is meaningful when the streams represent the same type of
|
||||||
|
// data. See Merge and Samples.
|
||||||
|
//
|
||||||
|
// For more detailed information about the algorithm used, see:
|
||||||
|
//
|
||||||
|
// Effective Computation of Biased Quantiles over Data Streams
|
||||||
|
//
|
||||||
|
// http://www.cs.rutgers.edu/~muthu/bquant.pdf
|
||||||
|
package quantile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sample holds an observed value and meta information for compression. JSON
|
||||||
|
// tags have been added for convenience.
|
||||||
|
type Sample struct {
|
||||||
|
Value float64 `json:",string"`
|
||||||
|
Width float64 `json:",string"`
|
||||||
|
Delta float64 `json:",string"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples represents a slice of samples. It implements sort.Interface.
|
||||||
|
type Samples []Sample
|
||||||
|
|
||||||
|
func (a Samples) Len() int { return len(a) }
|
||||||
|
func (a Samples) Less(i, j int) bool { return a[i].Value < a[j].Value }
|
||||||
|
func (a Samples) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
type invariant func(s *stream, r float64) float64
|
||||||
|
|
||||||
|
// NewLowBiased returns an initialized Stream for low-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the lower ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within (1±Epsilon)*Quantile.
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewLowBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * r
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHighBiased returns an initialized Stream for high-biased quantiles
|
||||||
|
// (e.g. 0.01, 0.1, 0.5) where the needed quantiles are not known a priori, but
|
||||||
|
// error guarantees can still be given even for the higher ranks of the data
|
||||||
|
// distribution.
|
||||||
|
//
|
||||||
|
// The provided epsilon is a relative error, i.e. the true quantile of a value
|
||||||
|
// returned by a query is guaranteed to be within 1-(1±Epsilon)*(1-Quantile).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error
|
||||||
|
// properties.
|
||||||
|
func NewHighBiased(epsilon float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
return 2 * epsilon * (s.n - r)
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTargeted returns an initialized Stream concerned with a particular set of
|
||||||
|
// quantile values that are supplied a priori. Knowing these a priori reduces
|
||||||
|
// space and computation time. The targets map maps the desired quantiles to
|
||||||
|
// their absolute errors, i.e. the true quantile of a value returned by a query
|
||||||
|
// is guaranteed to be within (Quantile±Epsilon).
|
||||||
|
//
|
||||||
|
// See http://www.cs.rutgers.edu/~muthu/bquant.pdf for time, space, and error properties.
|
||||||
|
func NewTargeted(targets map[float64]float64) *Stream {
|
||||||
|
ƒ := func(s *stream, r float64) float64 {
|
||||||
|
var m = math.MaxFloat64
|
||||||
|
var f float64
|
||||||
|
for quantile, epsilon := range targets {
|
||||||
|
if quantile*s.n <= r {
|
||||||
|
f = (2 * epsilon * r) / quantile
|
||||||
|
} else {
|
||||||
|
f = (2 * epsilon * (s.n - r)) / (1 - quantile)
|
||||||
|
}
|
||||||
|
if f < m {
|
||||||
|
m = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return newStream(ƒ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stream computes quantiles for a stream of float64s. It is not thread-safe by
|
||||||
|
// design. Take care when using across multiple goroutines.
|
||||||
|
type Stream struct {
|
||||||
|
*stream
|
||||||
|
b Samples
|
||||||
|
sorted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStream(ƒ invariant) *Stream {
|
||||||
|
x := &stream{ƒ: ƒ}
|
||||||
|
return &Stream{x, make(Samples, 0, 500), true}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert inserts v into the stream.
|
||||||
|
func (s *Stream) Insert(v float64) {
|
||||||
|
s.insert(Sample{Value: v, Width: 1})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) insert(sample Sample) {
|
||||||
|
s.b = append(s.b, sample)
|
||||||
|
s.sorted = false
|
||||||
|
if len(s.b) == cap(s.b) {
|
||||||
|
s.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query returns the computed qth percentiles value. If s was created with
|
||||||
|
// NewTargeted, and q is not in the set of quantiles provided a priori, Query
|
||||||
|
// will return an unspecified result.
|
||||||
|
func (s *Stream) Query(q float64) float64 {
|
||||||
|
if !s.flushed() {
|
||||||
|
// Fast path when there hasn't been enough data for a flush;
|
||||||
|
// this also yields better accuracy for small sets of data.
|
||||||
|
l := len(s.b)
|
||||||
|
if l == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
i := int(math.Ceil(float64(l) * q))
|
||||||
|
if i > 0 {
|
||||||
|
i -= 1
|
||||||
|
}
|
||||||
|
s.maybeSort()
|
||||||
|
return s.b[i].Value
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.query(q)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge merges samples into the underlying streams samples. This is handy when
|
||||||
|
// merging multiple streams from separate threads, database shards, etc.
|
||||||
|
//
|
||||||
|
// ATTENTION: This method is broken and does not yield correct results. The
|
||||||
|
// underlying algorithm is not capable of merging streams correctly.
|
||||||
|
func (s *Stream) Merge(samples Samples) {
|
||||||
|
sort.Sort(samples)
|
||||||
|
s.stream.merge(samples)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset reinitializes and clears the list reusing the samples buffer memory.
|
||||||
|
func (s *Stream) Reset() {
|
||||||
|
s.stream.reset()
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Samples returns stream samples held by s.
|
||||||
|
func (s *Stream) Samples() Samples {
|
||||||
|
if !s.flushed() {
|
||||||
|
return s.b
|
||||||
|
}
|
||||||
|
s.flush()
|
||||||
|
return s.stream.samples()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the total number of samples observed in the stream
|
||||||
|
// since initialization.
|
||||||
|
func (s *Stream) Count() int {
|
||||||
|
return len(s.b) + s.stream.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flush() {
|
||||||
|
s.maybeSort()
|
||||||
|
s.stream.merge(s.b)
|
||||||
|
s.b = s.b[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) maybeSort() {
|
||||||
|
if !s.sorted {
|
||||||
|
s.sorted = true
|
||||||
|
sort.Sort(s.b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) flushed() bool {
|
||||||
|
return len(s.stream.l) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type stream struct {
|
||||||
|
n float64
|
||||||
|
l []Sample
|
||||||
|
ƒ invariant
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) reset() {
|
||||||
|
s.l = s.l[:0]
|
||||||
|
s.n = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) insert(v float64) {
|
||||||
|
s.merge(Samples{{v, 1, 0}})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) merge(samples Samples) {
|
||||||
|
// TODO(beorn7): This tries to merge not only individual samples, but
|
||||||
|
// whole summaries. The paper doesn't mention merging summaries at
|
||||||
|
// all. Unittests show that the merging is inaccurate. Find out how to
|
||||||
|
// do merges properly.
|
||||||
|
var r float64
|
||||||
|
i := 0
|
||||||
|
for _, sample := range samples {
|
||||||
|
for ; i < len(s.l); i++ {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Value > sample.Value {
|
||||||
|
// Insert at position i.
|
||||||
|
s.l = append(s.l, Sample{})
|
||||||
|
copy(s.l[i+1:], s.l[i:])
|
||||||
|
s.l[i] = Sample{
|
||||||
|
sample.Value,
|
||||||
|
sample.Width,
|
||||||
|
math.Max(sample.Delta, math.Floor(s.ƒ(s, r))-1),
|
||||||
|
// TODO(beorn7): How to calculate delta correctly?
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
goto inserted
|
||||||
|
}
|
||||||
|
r += c.Width
|
||||||
|
}
|
||||||
|
s.l = append(s.l, Sample{sample.Value, sample.Width, 0})
|
||||||
|
i++
|
||||||
|
inserted:
|
||||||
|
s.n += sample.Width
|
||||||
|
r += sample.Width
|
||||||
|
}
|
||||||
|
s.compress()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) count() int {
|
||||||
|
return int(s.n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) query(q float64) float64 {
|
||||||
|
t := math.Ceil(q * s.n)
|
||||||
|
t += math.Ceil(s.ƒ(s, t) / 2)
|
||||||
|
p := s.l[0]
|
||||||
|
var r float64
|
||||||
|
for _, c := range s.l[1:] {
|
||||||
|
r += p.Width
|
||||||
|
if r+c.Width+c.Delta > t {
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
p = c
|
||||||
|
}
|
||||||
|
return p.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) compress() {
|
||||||
|
if len(s.l) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x := s.l[len(s.l)-1]
|
||||||
|
xi := len(s.l) - 1
|
||||||
|
r := s.n - 1 - x.Width
|
||||||
|
|
||||||
|
for i := len(s.l) - 2; i >= 0; i-- {
|
||||||
|
c := s.l[i]
|
||||||
|
if c.Width+x.Width+x.Delta <= s.ƒ(s, r) {
|
||||||
|
x.Width += c.Width
|
||||||
|
s.l[xi] = x
|
||||||
|
// Remove element at i.
|
||||||
|
copy(s.l[i:], s.l[i+1:])
|
||||||
|
s.l = s.l[:len(s.l)-1]
|
||||||
|
xi -= 1
|
||||||
|
} else {
|
||||||
|
x = c
|
||||||
|
xi = i
|
||||||
|
}
|
||||||
|
r -= c.Width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stream) samples() Samples {
|
||||||
|
samples := make(Samples, len(s.l))
|
||||||
|
copy(samples, s.l)
|
||||||
|
return samples
|
||||||
|
}
|
|
@ -1,202 +0,0 @@
|
||||||
|
|
||||||
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 @@
|
||||||
|
# Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
#
|
||||||
|
# Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
# https://github.com/golang/protobuf
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * 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.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "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 COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS 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.
|
||||||
|
|
||||||
|
|
||||||
|
include ../../Make.protobuf
|
||||||
|
|
||||||
|
all: regenerate
|
||||||
|
|
||||||
|
regenerate:
|
||||||
|
rm -f test.pb.go
|
||||||
|
make test.pb.go
|
||||||
|
|
||||||
|
# The following rules are just aids to development. Not needed for typical testing.
|
||||||
|
|
||||||
|
diff: regenerate
|
||||||
|
git diff test.pb.go
|
||||||
|
|
||||||
|
restore:
|
||||||
|
cp test.pb.go.golden test.pb.go
|
||||||
|
|
||||||
|
preserve:
|
||||||
|
cp test.pb.go test.pb.go.golden
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,548 @@
|
||||||
|
// Go support for Protocol Buffers - Google's data interchange format
|
||||||
|
//
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// https://github.com/golang/protobuf
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * 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.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "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 COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS 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.
|
||||||
|
|
||||||
|
// A feature-rich test file for the protocol compiler and libraries.
|
||||||
|
|
||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package testdata;
|
||||||
|
|
||||||
|
enum FOO { FOO1 = 1; };
|
||||||
|
|
||||||
|
message GoEnum {
|
||||||
|
required FOO foo = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GoTestField {
|
||||||
|
required string Label = 1;
|
||||||
|
required string Type = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GoTest {
|
||||||
|
// An enum, for completeness.
|
||||||
|
enum KIND {
|
||||||
|
VOID = 0;
|
||||||
|
|
||||||
|
// Basic types
|
||||||
|
BOOL = 1;
|
||||||
|
BYTES = 2;
|
||||||
|
FINGERPRINT = 3;
|
||||||
|
FLOAT = 4;
|
||||||
|
INT = 5;
|
||||||
|
STRING = 6;
|
||||||
|
TIME = 7;
|
||||||
|
|
||||||
|
// Groupings
|
||||||
|
TUPLE = 8;
|
||||||
|
ARRAY = 9;
|
||||||
|
MAP = 10;
|
||||||
|
|
||||||
|
// Table types
|
||||||
|
TABLE = 11;
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
FUNCTION = 12; // last tag
|
||||||
|
};
|
||||||
|
|
||||||
|
// Some typical parameters
|
||||||
|
required KIND Kind = 1;
|
||||||
|
optional string Table = 2;
|
||||||
|
optional int32 Param = 3;
|
||||||
|
|
||||||
|
// Required, repeated and optional foreign fields.
|
||||||
|
required GoTestField RequiredField = 4;
|
||||||
|
repeated GoTestField RepeatedField = 5;
|
||||||
|
optional GoTestField OptionalField = 6;
|
||||||
|
|
||||||
|
// Required fields of all basic types
|
||||||
|
required bool F_Bool_required = 10;
|
||||||
|
required int32 F_Int32_required = 11;
|
||||||
|
required int64 F_Int64_required = 12;
|
||||||
|
required fixed32 F_Fixed32_required = 13;
|
||||||
|
required fixed64 F_Fixed64_required = 14;
|
||||||
|
required uint32 F_Uint32_required = 15;
|
||||||
|
required uint64 F_Uint64_required = 16;
|
||||||
|
required float F_Float_required = 17;
|
||||||
|
required double F_Double_required = 18;
|
||||||
|
required string F_String_required = 19;
|
||||||
|
required bytes F_Bytes_required = 101;
|
||||||
|
required sint32 F_Sint32_required = 102;
|
||||||
|
required sint64 F_Sint64_required = 103;
|
||||||
|
|
||||||
|
// Repeated fields of all basic types
|
||||||
|
repeated bool F_Bool_repeated = 20;
|
||||||
|
repeated int32 F_Int32_repeated = 21;
|
||||||
|
repeated int64 F_Int64_repeated = 22;
|
||||||
|
repeated fixed32 F_Fixed32_repeated = 23;
|
||||||
|
repeated fixed64 F_Fixed64_repeated = 24;
|
||||||
|
repeated uint32 F_Uint32_repeated = 25;
|
||||||
|
repeated uint64 F_Uint64_repeated = 26;
|
||||||
|
repeated float F_Float_repeated = 27;
|
||||||
|
repeated double F_Double_repeated = 28;
|
||||||
|
repeated string F_String_repeated = 29;
|
||||||
|
repeated bytes F_Bytes_repeated = 201;
|
||||||
|
repeated sint32 F_Sint32_repeated = 202;
|
||||||
|
repeated sint64 F_Sint64_repeated = 203;
|
||||||
|
|
||||||
|
// Optional fields of all basic types
|
||||||
|
optional bool F_Bool_optional = 30;
|
||||||
|
optional int32 F_Int32_optional = 31;
|
||||||
|
optional int64 F_Int64_optional = 32;
|
||||||
|
optional fixed32 F_Fixed32_optional = 33;
|
||||||
|
optional fixed64 F_Fixed64_optional = 34;
|
||||||
|
optional uint32 F_Uint32_optional = 35;
|
||||||
|
optional uint64 F_Uint64_optional = 36;
|
||||||
|
optional float F_Float_optional = 37;
|
||||||
|
optional double F_Double_optional = 38;
|
||||||
|
optional string F_String_optional = 39;
|
||||||
|
optional bytes F_Bytes_optional = 301;
|
||||||
|
optional sint32 F_Sint32_optional = 302;
|
||||||
|
optional sint64 F_Sint64_optional = 303;
|
||||||
|
|
||||||
|
// Default-valued fields of all basic types
|
||||||
|
optional bool F_Bool_defaulted = 40 [default=true];
|
||||||
|
optional int32 F_Int32_defaulted = 41 [default=32];
|
||||||
|
optional int64 F_Int64_defaulted = 42 [default=64];
|
||||||
|
optional fixed32 F_Fixed32_defaulted = 43 [default=320];
|
||||||
|
optional fixed64 F_Fixed64_defaulted = 44 [default=640];
|
||||||
|
optional uint32 F_Uint32_defaulted = 45 [default=3200];
|
||||||
|
optional uint64 F_Uint64_defaulted = 46 [default=6400];
|
||||||
|
optional float F_Float_defaulted = 47 [default=314159.];
|
||||||
|
optional double F_Double_defaulted = 48 [default=271828.];
|
||||||
|
optional string F_String_defaulted = 49 [default="hello, \"world!\"\n"];
|
||||||
|
optional bytes F_Bytes_defaulted = 401 [default="Bignose"];
|
||||||
|
optional sint32 F_Sint32_defaulted = 402 [default = -32];
|
||||||
|
optional sint64 F_Sint64_defaulted = 403 [default = -64];
|
||||||
|
|
||||||
|
// Packed repeated fields (no string or bytes).
|
||||||
|
repeated bool F_Bool_repeated_packed = 50 [packed=true];
|
||||||
|
repeated int32 F_Int32_repeated_packed = 51 [packed=true];
|
||||||
|
repeated int64 F_Int64_repeated_packed = 52 [packed=true];
|
||||||
|
repeated fixed32 F_Fixed32_repeated_packed = 53 [packed=true];
|
||||||
|
repeated fixed64 F_Fixed64_repeated_packed = 54 [packed=true];
|
||||||
|
repeated uint32 F_Uint32_repeated_packed = 55 [packed=true];
|
||||||
|
repeated uint64 F_Uint64_repeated_packed = 56 [packed=true];
|
||||||
|
repeated float F_Float_repeated_packed = 57 [packed=true];
|
||||||
|
repeated double F_Double_repeated_packed = 58 [packed=true];
|
||||||
|
repeated sint32 F_Sint32_repeated_packed = 502 [packed=true];
|
||||||
|
repeated sint64 F_Sint64_repeated_packed = 503 [packed=true];
|
||||||
|
|
||||||
|
// Required, repeated, and optional groups.
|
||||||
|
required group RequiredGroup = 70 {
|
||||||
|
required string RequiredField = 71;
|
||||||
|
};
|
||||||
|
|
||||||
|
repeated group RepeatedGroup = 80 {
|
||||||
|
required string RequiredField = 81;
|
||||||
|
};
|
||||||
|
|
||||||
|
optional group OptionalGroup = 90 {
|
||||||
|
required string RequiredField = 91;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing a group containing a required field.
|
||||||
|
message GoTestRequiredGroupField {
|
||||||
|
required group Group = 1 {
|
||||||
|
required int32 Field = 2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing skipping of unrecognized fields.
|
||||||
|
// Numbers are all big, larger than tag numbers in GoTestField,
|
||||||
|
// the message used in the corresponding test.
|
||||||
|
message GoSkipTest {
|
||||||
|
required int32 skip_int32 = 11;
|
||||||
|
required fixed32 skip_fixed32 = 12;
|
||||||
|
required fixed64 skip_fixed64 = 13;
|
||||||
|
required string skip_string = 14;
|
||||||
|
required group SkipGroup = 15 {
|
||||||
|
required int32 group_int32 = 16;
|
||||||
|
required string group_string = 17;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing packed/non-packed decoder switching.
|
||||||
|
// A serialized instance of one should be deserializable as the other.
|
||||||
|
message NonPackedTest {
|
||||||
|
repeated int32 a = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PackedTest {
|
||||||
|
repeated int32 b = 1 [packed=true];
|
||||||
|
}
|
||||||
|
|
||||||
|
message MaxTag {
|
||||||
|
// Maximum possible tag number.
|
||||||
|
optional string last_field = 536870911;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OldMessage {
|
||||||
|
message Nested {
|
||||||
|
optional string name = 1;
|
||||||
|
}
|
||||||
|
optional Nested nested = 1;
|
||||||
|
|
||||||
|
optional int32 num = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMessage is wire compatible with OldMessage;
|
||||||
|
// imagine it as a future version.
|
||||||
|
message NewMessage {
|
||||||
|
message Nested {
|
||||||
|
optional string name = 1;
|
||||||
|
optional string food_group = 2;
|
||||||
|
}
|
||||||
|
optional Nested nested = 1;
|
||||||
|
|
||||||
|
// This is an int32 in OldMessage.
|
||||||
|
optional int64 num = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smaller tests for ASCII formatting.
|
||||||
|
|
||||||
|
message InnerMessage {
|
||||||
|
required string host = 1;
|
||||||
|
optional int32 port = 2 [default=4000];
|
||||||
|
optional bool connected = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OtherMessage {
|
||||||
|
optional int64 key = 1;
|
||||||
|
optional bytes value = 2;
|
||||||
|
optional float weight = 3;
|
||||||
|
optional InnerMessage inner = 4;
|
||||||
|
|
||||||
|
extensions 100 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequiredInnerMessage {
|
||||||
|
required InnerMessage leo_finally_won_an_oscar = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MyMessage {
|
||||||
|
required int32 count = 1;
|
||||||
|
optional string name = 2;
|
||||||
|
optional string quote = 3;
|
||||||
|
repeated string pet = 4;
|
||||||
|
optional InnerMessage inner = 5;
|
||||||
|
repeated OtherMessage others = 6;
|
||||||
|
optional RequiredInnerMessage we_must_go_deeper = 13;
|
||||||
|
repeated InnerMessage rep_inner = 12;
|
||||||
|
|
||||||
|
enum Color {
|
||||||
|
RED = 0;
|
||||||
|
GREEN = 1;
|
||||||
|
BLUE = 2;
|
||||||
|
};
|
||||||
|
optional Color bikeshed = 7;
|
||||||
|
|
||||||
|
optional group SomeGroup = 8 {
|
||||||
|
optional int32 group_field = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This field becomes [][]byte in the generated code.
|
||||||
|
repeated bytes rep_bytes = 10;
|
||||||
|
|
||||||
|
optional double bigfloat = 11;
|
||||||
|
|
||||||
|
extensions 100 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ext {
|
||||||
|
extend MyMessage {
|
||||||
|
optional Ext more = 103;
|
||||||
|
optional string text = 104;
|
||||||
|
optional int32 number = 105;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional string data = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend MyMessage {
|
||||||
|
repeated string greeting = 106;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ComplexExtension {
|
||||||
|
optional int32 first = 1;
|
||||||
|
optional int32 second = 2;
|
||||||
|
repeated int32 third = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend OtherMessage {
|
||||||
|
optional ComplexExtension complex = 200;
|
||||||
|
repeated ComplexExtension r_complex = 201;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DefaultsMessage {
|
||||||
|
enum DefaultsEnum {
|
||||||
|
ZERO = 0;
|
||||||
|
ONE = 1;
|
||||||
|
TWO = 2;
|
||||||
|
};
|
||||||
|
extensions 100 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend DefaultsMessage {
|
||||||
|
optional double no_default_double = 101;
|
||||||
|
optional float no_default_float = 102;
|
||||||
|
optional int32 no_default_int32 = 103;
|
||||||
|
optional int64 no_default_int64 = 104;
|
||||||
|
optional uint32 no_default_uint32 = 105;
|
||||||
|
optional uint64 no_default_uint64 = 106;
|
||||||
|
optional sint32 no_default_sint32 = 107;
|
||||||
|
optional sint64 no_default_sint64 = 108;
|
||||||
|
optional fixed32 no_default_fixed32 = 109;
|
||||||
|
optional fixed64 no_default_fixed64 = 110;
|
||||||
|
optional sfixed32 no_default_sfixed32 = 111;
|
||||||
|
optional sfixed64 no_default_sfixed64 = 112;
|
||||||
|
optional bool no_default_bool = 113;
|
||||||
|
optional string no_default_string = 114;
|
||||||
|
optional bytes no_default_bytes = 115;
|
||||||
|
optional DefaultsMessage.DefaultsEnum no_default_enum = 116;
|
||||||
|
|
||||||
|
optional double default_double = 201 [default = 3.1415];
|
||||||
|
optional float default_float = 202 [default = 3.14];
|
||||||
|
optional int32 default_int32 = 203 [default = 42];
|
||||||
|
optional int64 default_int64 = 204 [default = 43];
|
||||||
|
optional uint32 default_uint32 = 205 [default = 44];
|
||||||
|
optional uint64 default_uint64 = 206 [default = 45];
|
||||||
|
optional sint32 default_sint32 = 207 [default = 46];
|
||||||
|
optional sint64 default_sint64 = 208 [default = 47];
|
||||||
|
optional fixed32 default_fixed32 = 209 [default = 48];
|
||||||
|
optional fixed64 default_fixed64 = 210 [default = 49];
|
||||||
|
optional sfixed32 default_sfixed32 = 211 [default = 50];
|
||||||
|
optional sfixed64 default_sfixed64 = 212 [default = 51];
|
||||||
|
optional bool default_bool = 213 [default = true];
|
||||||
|
optional string default_string = 214 [default = "Hello, string"];
|
||||||
|
optional bytes default_bytes = 215 [default = "Hello, bytes"];
|
||||||
|
optional DefaultsMessage.DefaultsEnum default_enum = 216 [default = ONE];
|
||||||
|
}
|
||||||
|
|
||||||
|
message MyMessageSet {
|
||||||
|
option message_set_wire_format = true;
|
||||||
|
extensions 100 to max;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Empty {
|
||||||
|
}
|
||||||
|
|
||||||
|
extend MyMessageSet {
|
||||||
|
optional Empty x201 = 201;
|
||||||
|
optional Empty x202 = 202;
|
||||||
|
optional Empty x203 = 203;
|
||||||
|
optional Empty x204 = 204;
|
||||||
|
optional Empty x205 = 205;
|
||||||
|
optional Empty x206 = 206;
|
||||||
|
optional Empty x207 = 207;
|
||||||
|
optional Empty x208 = 208;
|
||||||
|
optional Empty x209 = 209;
|
||||||
|
optional Empty x210 = 210;
|
||||||
|
optional Empty x211 = 211;
|
||||||
|
optional Empty x212 = 212;
|
||||||
|
optional Empty x213 = 213;
|
||||||
|
optional Empty x214 = 214;
|
||||||
|
optional Empty x215 = 215;
|
||||||
|
optional Empty x216 = 216;
|
||||||
|
optional Empty x217 = 217;
|
||||||
|
optional Empty x218 = 218;
|
||||||
|
optional Empty x219 = 219;
|
||||||
|
optional Empty x220 = 220;
|
||||||
|
optional Empty x221 = 221;
|
||||||
|
optional Empty x222 = 222;
|
||||||
|
optional Empty x223 = 223;
|
||||||
|
optional Empty x224 = 224;
|
||||||
|
optional Empty x225 = 225;
|
||||||
|
optional Empty x226 = 226;
|
||||||
|
optional Empty x227 = 227;
|
||||||
|
optional Empty x228 = 228;
|
||||||
|
optional Empty x229 = 229;
|
||||||
|
optional Empty x230 = 230;
|
||||||
|
optional Empty x231 = 231;
|
||||||
|
optional Empty x232 = 232;
|
||||||
|
optional Empty x233 = 233;
|
||||||
|
optional Empty x234 = 234;
|
||||||
|
optional Empty x235 = 235;
|
||||||
|
optional Empty x236 = 236;
|
||||||
|
optional Empty x237 = 237;
|
||||||
|
optional Empty x238 = 238;
|
||||||
|
optional Empty x239 = 239;
|
||||||
|
optional Empty x240 = 240;
|
||||||
|
optional Empty x241 = 241;
|
||||||
|
optional Empty x242 = 242;
|
||||||
|
optional Empty x243 = 243;
|
||||||
|
optional Empty x244 = 244;
|
||||||
|
optional Empty x245 = 245;
|
||||||
|
optional Empty x246 = 246;
|
||||||
|
optional Empty x247 = 247;
|
||||||
|
optional Empty x248 = 248;
|
||||||
|
optional Empty x249 = 249;
|
||||||
|
optional Empty x250 = 250;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageList {
|
||||||
|
repeated group Message = 1 {
|
||||||
|
required string name = 2;
|
||||||
|
required int32 count = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Strings {
|
||||||
|
optional string string_field = 1;
|
||||||
|
optional bytes bytes_field = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Defaults {
|
||||||
|
enum Color {
|
||||||
|
RED = 0;
|
||||||
|
GREEN = 1;
|
||||||
|
BLUE = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default-valued fields of all basic types.
|
||||||
|
// Same as GoTest, but copied here to make testing easier.
|
||||||
|
optional bool F_Bool = 1 [default=true];
|
||||||
|
optional int32 F_Int32 = 2 [default=32];
|
||||||
|
optional int64 F_Int64 = 3 [default=64];
|
||||||
|
optional fixed32 F_Fixed32 = 4 [default=320];
|
||||||
|
optional fixed64 F_Fixed64 = 5 [default=640];
|
||||||
|
optional uint32 F_Uint32 = 6 [default=3200];
|
||||||
|
optional uint64 F_Uint64 = 7 [default=6400];
|
||||||
|
optional float F_Float = 8 [default=314159.];
|
||||||
|
optional double F_Double = 9 [default=271828.];
|
||||||
|
optional string F_String = 10 [default="hello, \"world!\"\n"];
|
||||||
|
optional bytes F_Bytes = 11 [default="Bignose"];
|
||||||
|
optional sint32 F_Sint32 = 12 [default=-32];
|
||||||
|
optional sint64 F_Sint64 = 13 [default=-64];
|
||||||
|
optional Color F_Enum = 14 [default=GREEN];
|
||||||
|
|
||||||
|
// More fields with crazy defaults.
|
||||||
|
optional float F_Pinf = 15 [default=inf];
|
||||||
|
optional float F_Ninf = 16 [default=-inf];
|
||||||
|
optional float F_Nan = 17 [default=nan];
|
||||||
|
|
||||||
|
// Sub-message.
|
||||||
|
optional SubDefaults sub = 18;
|
||||||
|
|
||||||
|
// Redundant but explicit defaults.
|
||||||
|
optional string str_zero = 19 [default=""];
|
||||||
|
}
|
||||||
|
|
||||||
|
message SubDefaults {
|
||||||
|
optional int64 n = 1 [default=7];
|
||||||
|
}
|
||||||
|
|
||||||
|
message RepeatedEnum {
|
||||||
|
enum Color {
|
||||||
|
RED = 1;
|
||||||
|
}
|
||||||
|
repeated Color color = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MoreRepeated {
|
||||||
|
repeated bool bools = 1;
|
||||||
|
repeated bool bools_packed = 2 [packed=true];
|
||||||
|
repeated int32 ints = 3;
|
||||||
|
repeated int32 ints_packed = 4 [packed=true];
|
||||||
|
repeated int64 int64s_packed = 7 [packed=true];
|
||||||
|
repeated string strings = 5;
|
||||||
|
repeated fixed32 fixeds = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupOld and GroupNew have the same wire format.
|
||||||
|
// GroupNew has a new field inside a group.
|
||||||
|
|
||||||
|
message GroupOld {
|
||||||
|
optional group G = 101 {
|
||||||
|
optional int32 x = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message GroupNew {
|
||||||
|
optional group G = 101 {
|
||||||
|
optional int32 x = 2;
|
||||||
|
optional int32 y = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message FloatingPoint {
|
||||||
|
required double f = 1;
|
||||||
|
optional bool exact = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MessageWithMap {
|
||||||
|
map<int32, string> name_mapping = 1;
|
||||||
|
map<sint64, FloatingPoint> msg_mapping = 2;
|
||||||
|
map<bool, bytes> byte_mapping = 3;
|
||||||
|
map<string, string> str_to_str = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Oneof {
|
||||||
|
oneof union {
|
||||||
|
bool F_Bool = 1;
|
||||||
|
int32 F_Int32 = 2;
|
||||||
|
int64 F_Int64 = 3;
|
||||||
|
fixed32 F_Fixed32 = 4;
|
||||||
|
fixed64 F_Fixed64 = 5;
|
||||||
|
uint32 F_Uint32 = 6;
|
||||||
|
uint64 F_Uint64 = 7;
|
||||||
|
float F_Float = 8;
|
||||||
|
double F_Double = 9;
|
||||||
|
string F_String = 10;
|
||||||
|
bytes F_Bytes = 11;
|
||||||
|
sint32 F_Sint32 = 12;
|
||||||
|
sint64 F_Sint64 = 13;
|
||||||
|
MyMessage.Color F_Enum = 14;
|
||||||
|
GoTestField F_Message = 15;
|
||||||
|
group F_Group = 16 {
|
||||||
|
optional int32 x = 17;
|
||||||
|
}
|
||||||
|
int32 F_Largest_Tag = 536870911;
|
||||||
|
}
|
||||||
|
|
||||||
|
oneof tormato {
|
||||||
|
int32 value = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message Communique {
|
||||||
|
optional bool make_me_cry = 1;
|
||||||
|
|
||||||
|
// This is a oneof, called "union".
|
||||||
|
oneof union {
|
||||||
|
int32 number = 5;
|
||||||
|
string name = 6;
|
||||||
|
bytes data = 7;
|
||||||
|
double temp_c = 8;
|
||||||
|
MyMessage.Color col = 9;
|
||||||
|
Strings msg = 10;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* 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.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"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 COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS 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.
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code refers to The Go Authors for copyright purposes.
|
||||||
|
# The master list of authors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/AUTHORS.
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Contributing to Go
|
||||||
|
|
||||||
|
Go is an open source project.
|
||||||
|
|
||||||
|
It is the work of hundreds of contributors. We appreciate your help!
|
||||||
|
|
||||||
|
|
||||||
|
## Filing issues
|
||||||
|
|
||||||
|
When [filing an issue](https://github.com/golang/oauth2/issues), make sure to answer these five questions:
|
||||||
|
|
||||||
|
1. What version of Go are you using (`go version`)?
|
||||||
|
2. What operating system and processor architecture are you using?
|
||||||
|
3. What did you do?
|
||||||
|
4. What did you expect to see?
|
||||||
|
5. What did you see instead?
|
||||||
|
|
||||||
|
General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker.
|
||||||
|
The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||||
|
|
||||||
|
## Contributing code
|
||||||
|
|
||||||
|
Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html)
|
||||||
|
before sending patches.
|
||||||
|
|
||||||
|
**We do not accept GitHub pull requests**
|
||||||
|
(we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review).
|
||||||
|
|
||||||
|
Unless otherwise noted, the Go source files are distributed under
|
||||||
|
the BSD-style license found in the LICENSE file.
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
# This source code was written by the Go contributors.
|
||||||
|
# The master list of contributors is in the main Go distribution,
|
||||||
|
# visible at http://tip.golang.org/CONTRIBUTORS.
|
|
@ -0,0 +1,65 @@
|
||||||
|
# OAuth2 for Go
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/golang/oauth2.svg?branch=master)](https://travis-ci.org/golang/oauth2)
|
||||||
|
[![GoDoc](https://godoc.org/golang.org/x/oauth2?status.svg)](https://godoc.org/golang.org/x/oauth2)
|
||||||
|
|
||||||
|
oauth2 package contains a client implementation for OAuth 2.0 spec.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
~~~~
|
||||||
|
go get golang.org/x/oauth2
|
||||||
|
~~~~
|
||||||
|
|
||||||
|
See godoc for further documentation and examples.
|
||||||
|
|
||||||
|
* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
|
||||||
|
* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
|
||||||
|
|
||||||
|
|
||||||
|
## App Engine
|
||||||
|
|
||||||
|
In change 96e89be (March 2015) we removed the `oauth2.Context2` type in favor
|
||||||
|
of the [`context.Context`](https://golang.org/x/net/context#Context) type from
|
||||||
|
the `golang.org/x/net/context` package
|
||||||
|
|
||||||
|
This means its no longer possible to use the "Classic App Engine"
|
||||||
|
`appengine.Context` type with the `oauth2` package. (You're using
|
||||||
|
Classic App Engine if you import the package `"appengine"`.)
|
||||||
|
|
||||||
|
To work around this, you may use the new `"google.golang.org/appengine"`
|
||||||
|
package. This package has almost the same API as the `"appengine"` package,
|
||||||
|
but it can be fetched with `go get` and used on "Managed VMs" and well as
|
||||||
|
Classic App Engine.
|
||||||
|
|
||||||
|
See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
|
||||||
|
for information on updating your app.
|
||||||
|
|
||||||
|
If you don't want to update your entire app to use the new App Engine packages,
|
||||||
|
you may use both sets of packages in parallel, using only the new packages
|
||||||
|
with the `oauth2` package.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/oauth2/google"
|
||||||
|
newappengine "google.golang.org/appengine"
|
||||||
|
newurlfetch "google.golang.org/appengine/urlfetch"
|
||||||
|
|
||||||
|
"appengine"
|
||||||
|
)
|
||||||
|
|
||||||
|
func handler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var c appengine.Context = appengine.NewContext(r)
|
||||||
|
c.Infof("Logging a message with the old package")
|
||||||
|
|
||||||
|
var ctx context.Context = newappengine.NewContext(r)
|
||||||
|
client := &http.Client{
|
||||||
|
Transport: &oauth2.Transport{
|
||||||
|
Source: google.AppEngineTokenSource(ctx, "scope"),
|
||||||
|
Base: &newurlfetch.Transport{Context: ctx},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
client.Get("...")
|
||||||
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// appengineFlex is set at init time by appengineflex_hook.go. If true, we are on App Engine Flex.
|
// Set at init time by appenginevm_hook.go. If true, we are on App Engine Managed VMs.
|
||||||
var appengineFlex bool
|
var appengineVM bool
|
||||||
|
|
||||||
// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
|
// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
|
||||||
var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)
|
var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build appengine appenginevm
|
// +build appengine
|
||||||
|
|
||||||
package google
|
package google
|
||||||
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2015 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// +build appenginevm
|
|
||||||
|
|
||||||
package google
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
appengineFlex = true // Flex doesn't support appengine.AccessToken; depend on metadata server.
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
// Copyright 2015 The oauth2 Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build appenginevm
|
||||||
|
|
||||||
|
package google
|
||||||
|
|
||||||
|
import "google.golang.org/appengine"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
appengineVM = true
|
||||||
|
appengineTokenFunc = appengine.AccessToken
|
||||||
|
appengineAppIDFunc = appengine.AppID
|
||||||
|
}
|
|
@ -81,7 +81,7 @@ func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCrede
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third, if we're on Google App Engine use those credentials.
|
// Third, if we're on Google App Engine use those credentials.
|
||||||
if appengineTokenFunc != nil && !appengineFlex {
|
if appengineTokenFunc != nil && !appengineVM {
|
||||||
return &DefaultCredentials{
|
return &DefaultCredentials{
|
||||||
ProjectID: appengineAppIDFunc(ctx),
|
ProjectID: appengineAppIDFunc(ctx),
|
||||||
TokenSource: AppEngineTokenSource(ctx, scope...),
|
TokenSource: AppEngineTokenSource(ctx, scope...),
|
||||||
|
|
|
@ -180,6 +180,7 @@ func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
|
||||||
"grant_type": {"authorization_code"},
|
"grant_type": {"authorization_code"},
|
||||||
"code": {code},
|
"code": {code},
|
||||||
"redirect_uri": internal.CondVal(c.RedirectURL),
|
"redirect_uri": internal.CondVal(c.RedirectURL),
|
||||||
|
"scope": internal.CondVal(strings.Join(c.Scopes, " ")),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2011 Google Inc. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* 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.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"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 COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS 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.
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
# How to contribute
|
||||||
|
|
||||||
|
We definitely welcome patches and contribution to grpc! Here are some guidelines
|
||||||
|
and information about how to do so.
|
||||||
|
|
||||||
|
## Sending patches
|
||||||
|
|
||||||
|
### Getting started
|
||||||
|
|
||||||
|
1. Check out the code:
|
||||||
|
|
||||||
|
$ go get google.golang.org/grpc
|
||||||
|
$ cd $GOPATH/src/google.golang.org/grpc
|
||||||
|
|
||||||
|
1. Create a fork of the grpc-go repository.
|
||||||
|
1. Add your fork as a remote:
|
||||||
|
|
||||||
|
$ git remote add fork git@github.com:$YOURGITHUBUSERNAME/grpc-go.git
|
||||||
|
|
||||||
|
1. Make changes, commit them.
|
||||||
|
1. Run the test suite:
|
||||||
|
|
||||||
|
$ make test
|
||||||
|
|
||||||
|
1. Push your changes to your fork:
|
||||||
|
|
||||||
|
$ git push fork ...
|
||||||
|
|
||||||
|
1. Open a pull request.
|
||||||
|
|
||||||
|
## Legal requirements
|
||||||
|
|
||||||
|
In order to protect both you and ourselves, you will need to sign the
|
||||||
|
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
||||||
|
|
||||||
|
## Filing Issues
|
||||||
|
When filing an issue, make sure to answer these five questions:
|
||||||
|
|
||||||
|
1. What version of Go are you using (`go version`)?
|
||||||
|
2. What operating system and processor architecture are you using?
|
||||||
|
3. What did you do?
|
||||||
|
4. What did you expect to see?
|
||||||
|
5. What did you see instead?
|
||||||
|
|
||||||
|
### Contributing code
|
||||||
|
Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file.
|
|
@ -0,0 +1,52 @@
|
||||||
|
all: test testrace
|
||||||
|
|
||||||
|
deps:
|
||||||
|
go get -d -v google.golang.org/grpc/...
|
||||||
|
|
||||||
|
updatedeps:
|
||||||
|
go get -d -v -u -f google.golang.org/grpc/...
|
||||||
|
|
||||||
|
testdeps:
|
||||||
|
go get -d -v -t google.golang.org/grpc/...
|
||||||
|
|
||||||
|
updatetestdeps:
|
||||||
|
go get -d -v -t -u -f google.golang.org/grpc/...
|
||||||
|
|
||||||
|
build: deps
|
||||||
|
go build google.golang.org/grpc/...
|
||||||
|
|
||||||
|
proto:
|
||||||
|
@ if ! which protoc > /dev/null; then \
|
||||||
|
echo "error: protoc not installed" >&2; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
go get -u -v github.com/golang/protobuf/protoc-gen-go
|
||||||
|
# use $$dir as the root for all proto files in the same directory
|
||||||
|
for dir in $$(git ls-files '*.proto' | xargs -n1 dirname | uniq); do \
|
||||||
|
protoc -I $$dir --go_out=plugins=grpc:$$dir $$dir/*.proto; \
|
||||||
|
done
|
||||||
|
|
||||||
|
test: testdeps
|
||||||
|
go test -v -cpu 1,4 google.golang.org/grpc/...
|
||||||
|
|
||||||
|
testrace: testdeps
|
||||||
|
go test -v -race -cpu 1,4 google.golang.org/grpc/...
|
||||||
|
|
||||||
|
clean:
|
||||||
|
go clean -i google.golang.org/grpc/...
|
||||||
|
|
||||||
|
coverage: testdeps
|
||||||
|
./coverage.sh --coveralls
|
||||||
|
|
||||||
|
.PHONY: \
|
||||||
|
all \
|
||||||
|
deps \
|
||||||
|
updatedeps \
|
||||||
|
testdeps \
|
||||||
|
updatetestdeps \
|
||||||
|
build \
|
||||||
|
proto \
|
||||||
|
test \
|
||||||
|
testrace \
|
||||||
|
clean \
|
||||||
|
coverage
|
|
@ -0,0 +1,22 @@
|
||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the gRPC project.
|
||||||
|
|
||||||
|
Google 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,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of gRPC, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of gRPC. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of gRPC or any code incorporated within this
|
||||||
|
implementation of gRPC constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of gRPC
|
||||||
|
shall terminate as of the date such litigation is filed.
|
|
@ -0,0 +1,57 @@
|
||||||
|
#gRPC-Go
|
||||||
|
|
||||||
|
[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go) [![GoDoc](https://godoc.org/google.golang.org/grpc?status.svg)](https://godoc.org/google.golang.org/grpc)
|
||||||
|
|
||||||
|
The Go implementation of [gRPC](http://www.grpc.io/): A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. For more information see the [gRPC Quick Start](http://www.grpc.io/docs/) guide.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
To install this package, you need to install Go and setup your Go workspace on your computer. The simplest way to install the library is to run:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ go get google.golang.org/grpc
|
||||||
|
```
|
||||||
|
|
||||||
|
Prerequisites
|
||||||
|
-------------
|
||||||
|
|
||||||
|
This requires Go 1.5 or later.
|
||||||
|
|
||||||
|
A note on the version used: significant performance improvements in benchmarks
|
||||||
|
of grpc-go have been seen by upgrading the go version from 1.5 to the latest
|
||||||
|
1.7.1.
|
||||||
|
|
||||||
|
From https://golang.org/doc/install, one way to install the latest version of go is:
|
||||||
|
```
|
||||||
|
$ GO_VERSION=1.7.1
|
||||||
|
$ OS=linux
|
||||||
|
$ ARCH=amd64
|
||||||
|
$ curl -O https://storage.googleapis.com/golang/go${GO_VERSION}.${OS}-${ARCH}.tar.gz
|
||||||
|
$ sudo tar -C /usr/local -xzf go$GO_VERSION.$OS-$ARCH.tar.gz
|
||||||
|
$ # Put go on the PATH, keep the usual installation dir
|
||||||
|
$ sudo ln -s /usr/local/go/bin/go /usr/bin/go
|
||||||
|
$ rm go$GO_VERSION.$OS-$ARCH.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Constraints
|
||||||
|
-----------
|
||||||
|
The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the [list](http://godoc.org/google.golang.org/grpc?imports), you need a discussion with gRPC-Go authors and consultants.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
See [API documentation](https://godoc.org/google.golang.org/grpc) for package and API descriptions and find examples in the [examples directory](examples/).
|
||||||
|
|
||||||
|
Status
|
||||||
|
------
|
||||||
|
GA
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Compiling error, undefined: grpc.SupportPackageIsVersion
|
||||||
|
|
||||||
|
Please update proto package, gRPC package and rebuild the proto files:
|
||||||
|
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
|
||||||
|
- `go get -u google.golang.org/grpc`
|
||||||
|
- `protoc --go_out=plugins=grpc:. *.proto`
|
|
@ -36,14 +36,13 @@ package grpc
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/trace"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/peer"
|
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,27 +72,28 @@ func recvResponse(ctx context.Context, dopts dialOptions, t transport.ClientTran
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
if err = recv(p, dopts.codec, stream, dopts.dc, reply, dopts.maxMsgSize, inPayload); err != nil {
|
if err = recv(p, dopts.codec, stream, dopts.dc, reply, math.MaxInt32, inPayload); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if inPayload != nil && err == io.EOF && stream.Status().Code() == codes.OK {
|
if inPayload != nil && err == io.EOF && stream.StatusCode() == codes.OK {
|
||||||
// TODO in the current implementation, inTrailer may be handled before inPayload in some cases.
|
// TODO in the current implementation, inTrailer may be handled before inPayload in some cases.
|
||||||
// Fix the order if necessary.
|
// Fix the order if necessary.
|
||||||
dopts.copts.StatsHandler.HandleRPC(ctx, inPayload)
|
dopts.copts.StatsHandler.HandleRPC(ctx, inPayload)
|
||||||
}
|
}
|
||||||
c.trailerMD = stream.Trailer()
|
c.trailerMD = stream.Trailer()
|
||||||
if peer, ok := peer.FromContext(stream.Context()); ok {
|
|
||||||
c.peer = peer
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendRequest writes out various information of an RPC such as Context and Message.
|
// sendRequest writes out various information of an RPC such as Context and Message.
|
||||||
func sendRequest(ctx context.Context, dopts dialOptions, compressor Compressor, callHdr *transport.CallHdr, stream *transport.Stream, t transport.ClientTransport, args interface{}, opts *transport.Options) (err error) {
|
func sendRequest(ctx context.Context, dopts dialOptions, compressor Compressor, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
|
||||||
|
stream, err := t.NewStream(ctx, callHdr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If err is connection error, t will be closed, no need to close stream here.
|
// If err is connection error, t will be closed, no need to close stream here.
|
||||||
|
@ -116,7 +116,7 @@ func sendRequest(ctx context.Context, dopts dialOptions, compressor Compressor,
|
||||||
}
|
}
|
||||||
outBuf, err := encode(dopts.codec, args, compressor, cbuf, outPayload)
|
outBuf, err := encode(dopts.codec, args, compressor, cbuf, outPayload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Errorf(codes.Internal, "grpc: %v", err)
|
return nil, Errorf(codes.Internal, "grpc: %v", err)
|
||||||
}
|
}
|
||||||
err = t.Write(stream, outBuf, opts)
|
err = t.Write(stream, outBuf, opts)
|
||||||
if err == nil && outPayload != nil {
|
if err == nil && outPayload != nil {
|
||||||
|
@ -127,10 +127,10 @@ func sendRequest(ctx context.Context, dopts dialOptions, compressor Compressor,
|
||||||
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
// does not exist.) so that t.Write could get io.EOF from wait(...). Leave the following
|
||||||
// recvResponse to get the final status.
|
// recvResponse to get the final status.
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Sent successfully.
|
// Sent successfully.
|
||||||
return nil
|
return stream, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke sends the RPC request on the wire and returns after response is received.
|
// Invoke sends the RPC request on the wire and returns after response is received.
|
||||||
|
@ -179,7 +179,6 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = newContextWithRPCInfo(ctx)
|
|
||||||
sh := cc.dopts.copts.StatsHandler
|
sh := cc.dopts.copts.StatsHandler
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method})
|
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method})
|
||||||
|
@ -228,7 +227,7 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
t, put, err = cc.getTransport(ctx, gopts)
|
t, put, err = cc.getTransport(ctx, gopts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(zhaoq): Probably revisit the error handling.
|
// TODO(zhaoq): Probably revisit the error handling.
|
||||||
if _, ok := status.FromError(err); ok {
|
if _, ok := err.(*rpcError); ok {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err == errConnClosing || err == errConnUnavailable {
|
if err == errConnClosing || err == errConnUnavailable {
|
||||||
|
@ -243,35 +242,19 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
if c.traceInfo.tr != nil {
|
if c.traceInfo.tr != nil {
|
||||||
c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
|
c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
|
||||||
}
|
}
|
||||||
stream, err = t.NewStream(ctx, callHdr)
|
stream, err = sendRequest(ctx, cc.dopts, cc.dopts.cp, callHdr, t, args, topts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
if _, ok := err.(transport.ConnectionError); ok {
|
|
||||||
// If error is connection error, transport was sending data on wire,
|
|
||||||
// and we are not sure if anything has been sent on wire.
|
|
||||||
// If error is not connection error, we are sure nothing has been sent.
|
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: true, bytesReceived: false})
|
|
||||||
}
|
|
||||||
put()
|
|
||||||
}
|
|
||||||
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return toRPCErr(err)
|
|
||||||
}
|
|
||||||
err = sendRequest(ctx, cc.dopts, cc.dopts.cp, callHdr, stream, t, args, topts)
|
|
||||||
if err != nil {
|
|
||||||
if put != nil {
|
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{
|
|
||||||
bytesSent: stream.BytesSent(),
|
|
||||||
bytesReceived: stream.BytesReceived(),
|
|
||||||
})
|
|
||||||
put()
|
put()
|
||||||
|
put = nil
|
||||||
}
|
}
|
||||||
// Retry a non-failfast RPC when
|
// Retry a non-failfast RPC when
|
||||||
// i) there is a connection error; or
|
// i) there is a connection error; or
|
||||||
// ii) the server started to drain before this RPC was initiated.
|
// ii) the server started to drain before this RPC was initiated.
|
||||||
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
if _, ok := err.(transport.ConnectionError); ok || err == transport.ErrStreamDrain {
|
||||||
|
if c.failFast {
|
||||||
|
return toRPCErr(err)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
|
@ -279,13 +262,13 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
err = recvResponse(ctx, cc.dopts, t, &c, stream, reply)
|
err = recvResponse(ctx, cc.dopts, t, &c, stream, reply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{
|
|
||||||
bytesSent: stream.BytesSent(),
|
|
||||||
bytesReceived: stream.BytesReceived(),
|
|
||||||
})
|
|
||||||
put()
|
put()
|
||||||
|
put = nil
|
||||||
|
}
|
||||||
|
if _, ok := err.(transport.ConnectionError); ok || err == transport.ErrStreamDrain {
|
||||||
|
if c.failFast {
|
||||||
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
|
@ -295,12 +278,9 @@ func invoke(ctx context.Context, method string, args, reply interface{}, cc *Cli
|
||||||
}
|
}
|
||||||
t.CloseStream(stream, nil)
|
t.CloseStream(stream, nil)
|
||||||
if put != nil {
|
if put != nil {
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{
|
|
||||||
bytesSent: stream.BytesSent(),
|
|
||||||
bytesReceived: stream.BytesReceived(),
|
|
||||||
})
|
|
||||||
put()
|
put()
|
||||||
|
put = nil
|
||||||
}
|
}
|
||||||
return stream.Status().Err()
|
return Errorf(stream.StatusCode(), "%s", stream.StatusDesc())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,8 @@ package grpc
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,7 +45,6 @@ import (
|
||||||
"golang.org/x/net/trace"
|
"golang.org/x/net/trace"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/keepalive"
|
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
@ -79,6 +78,7 @@ var (
|
||||||
errConnClosing = errors.New("grpc: the connection is closing")
|
errConnClosing = errors.New("grpc: the connection is closing")
|
||||||
// errConnUnavailable indicates that the connection is unavailable.
|
// errConnUnavailable indicates that the connection is unavailable.
|
||||||
errConnUnavailable = errors.New("grpc: the connection is unavailable")
|
errConnUnavailable = errors.New("grpc: the connection is unavailable")
|
||||||
|
errNoAddr = errors.New("grpc: there is no address available to dial")
|
||||||
// minimum time to give a connection to complete
|
// minimum time to give a connection to complete
|
||||||
minConnectTimeout = 20 * time.Second
|
minConnectTimeout = 20 * time.Second
|
||||||
)
|
)
|
||||||
|
@ -98,21 +98,11 @@ type dialOptions struct {
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
scChan <-chan ServiceConfig
|
scChan <-chan ServiceConfig
|
||||||
copts transport.ConnectOptions
|
copts transport.ConnectOptions
|
||||||
maxMsgSize int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultClientMaxMsgSize = math.MaxInt32
|
|
||||||
|
|
||||||
// DialOption configures how we set up the connection.
|
// DialOption configures how we set up the connection.
|
||||||
type DialOption func(*dialOptions)
|
type DialOption func(*dialOptions)
|
||||||
|
|
||||||
// WithMaxMsgSize returns a DialOption which sets the maximum message size the client can receive.
|
|
||||||
func WithMaxMsgSize(s int) DialOption {
|
|
||||||
return func(o *dialOptions) {
|
|
||||||
o.maxMsgSize = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
|
// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
|
||||||
func WithCodec(c Codec) DialOption {
|
func WithCodec(c Codec) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
|
@ -259,13 +249,6 @@ func WithUserAgent(s string) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithKeepaliveParams returns a DialOption that specifies keepalive paramaters for the client transport.
|
|
||||||
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
|
|
||||||
return func(o *dialOptions) {
|
|
||||||
o.copts.KeepaliveParams = kp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs.
|
// WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs.
|
||||||
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
|
func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption {
|
||||||
return func(o *dialOptions) {
|
return func(o *dialOptions) {
|
||||||
|
@ -280,15 +263,6 @@ func WithStreamInterceptor(f StreamClientInterceptor) DialOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithAuthority returns a DialOption that specifies the value to be used as
|
|
||||||
// the :authority pseudo-header. This value only works with WithInsecure and
|
|
||||||
// has no effect if TransportCredentials are present.
|
|
||||||
func WithAuthority(a string) DialOption {
|
|
||||||
return func(o *dialOptions) {
|
|
||||||
o.copts.Authority = a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial creates a client connection to the given target.
|
// Dial creates a client connection to the given target.
|
||||||
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
||||||
return DialContext(context.Background(), target, opts...)
|
return DialContext(context.Background(), target, opts...)
|
||||||
|
@ -305,26 +279,9 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
conns: make(map[Address]*addrConn),
|
conns: make(map[Address]*addrConn),
|
||||||
}
|
}
|
||||||
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
||||||
cc.dopts.maxMsgSize = defaultClientMaxMsgSize
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(&cc.dopts)
|
opt(&cc.dopts)
|
||||||
}
|
}
|
||||||
cc.mkp = cc.dopts.copts.KeepaliveParams
|
|
||||||
|
|
||||||
if cc.dopts.copts.Dialer == nil {
|
|
||||||
cc.dopts.copts.Dialer = newProxyDialer(
|
|
||||||
func(ctx context.Context, addr string) (net.Conn, error) {
|
|
||||||
return dialContext(ctx, "tcp", addr)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cc.dopts.copts.UserAgent != "" {
|
|
||||||
cc.dopts.copts.UserAgent += " " + grpcUA
|
|
||||||
} else {
|
|
||||||
cc.dopts.copts.UserAgent = grpcUA
|
|
||||||
}
|
|
||||||
|
|
||||||
if cc.dopts.timeout > 0 {
|
if cc.dopts.timeout > 0 {
|
||||||
var cancel context.CancelFunc
|
var cancel context.CancelFunc
|
||||||
ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)
|
ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout)
|
||||||
|
@ -364,18 +321,24 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
creds := cc.dopts.copts.TransportCredentials
|
creds := cc.dopts.copts.TransportCredentials
|
||||||
if creds != nil && creds.Info().ServerName != "" {
|
if creds != nil && creds.Info().ServerName != "" {
|
||||||
cc.authority = creds.Info().ServerName
|
cc.authority = creds.Info().ServerName
|
||||||
} else if cc.dopts.insecure && cc.dopts.copts.Authority != "" {
|
|
||||||
cc.authority = cc.dopts.copts.Authority
|
|
||||||
} else {
|
} else {
|
||||||
cc.authority = target
|
colonPos := strings.LastIndex(target, ":")
|
||||||
|
if colonPos == -1 {
|
||||||
|
colonPos = len(target)
|
||||||
}
|
}
|
||||||
|
cc.authority = target[:colonPos]
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
waitC := make(chan error, 1)
|
waitC := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(waitC)
|
var addrs []Address
|
||||||
if cc.dopts.balancer == nil && cc.sc.LB != nil {
|
if cc.dopts.balancer == nil && cc.sc.LB != nil {
|
||||||
cc.dopts.balancer = cc.sc.LB
|
cc.dopts.balancer = cc.sc.LB
|
||||||
}
|
}
|
||||||
if cc.dopts.balancer != nil {
|
if cc.dopts.balancer == nil {
|
||||||
|
// Connect to target directly if balancer is nil.
|
||||||
|
addrs = append(addrs, Address{Addr: target})
|
||||||
|
} else {
|
||||||
var credsClone credentials.TransportCredentials
|
var credsClone credentials.TransportCredentials
|
||||||
if creds != nil {
|
if creds != nil {
|
||||||
credsClone = creds.Clone()
|
credsClone = creds.Clone()
|
||||||
|
@ -388,22 +351,24 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ch := cc.dopts.balancer.Notify()
|
ch := cc.dopts.balancer.Notify()
|
||||||
if ch != nil {
|
if ch == nil {
|
||||||
if cc.dopts.block {
|
// There is no name resolver installed.
|
||||||
doneChan := make(chan struct{})
|
addrs = append(addrs, Address{Addr: target})
|
||||||
go cc.lbWatcher(doneChan)
|
|
||||||
<-doneChan
|
|
||||||
} else {
|
} else {
|
||||||
go cc.lbWatcher(nil)
|
addrs, ok = <-ch
|
||||||
}
|
if !ok || len(addrs) == 0 {
|
||||||
|
waitC <- errNoAddr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No balancer, or no resolver within the balancer. Connect directly.
|
}
|
||||||
if err := cc.resetAddrConn(Address{Addr: target}, cc.dopts.block, nil); err != nil {
|
for _, a := range addrs {
|
||||||
|
if err := cc.resetAddrConn(a, false, nil); err != nil {
|
||||||
waitC <- err
|
waitC <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
close(waitC)
|
||||||
}()
|
}()
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -414,10 +379,15 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If balancer is nil or balancer.Notify() is nil, ok will be false here.
|
||||||
|
// The lbWatcher goroutine will not be created.
|
||||||
|
if ok {
|
||||||
|
go cc.lbWatcher()
|
||||||
|
}
|
||||||
|
|
||||||
if cc.dopts.scChan != nil {
|
if cc.dopts.scChan != nil {
|
||||||
go cc.scWatcher()
|
go cc.scWatcher()
|
||||||
}
|
}
|
||||||
|
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,14 +436,9 @@ type ClientConn struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
sc ServiceConfig
|
sc ServiceConfig
|
||||||
conns map[Address]*addrConn
|
conns map[Address]*addrConn
|
||||||
// Keepalive parameter can be udated if a GoAway is received.
|
|
||||||
mkp keepalive.ClientParameters
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lbWatcher watches the Notify channel of the balancer in cc and manages
|
func (cc *ClientConn) lbWatcher() {
|
||||||
// connections accordingly. If doneChan is not nil, it is closed after the
|
|
||||||
// first successfull connection is made.
|
|
||||||
func (cc *ClientConn) lbWatcher(doneChan chan struct{}) {
|
|
||||||
for addrs := range cc.dopts.balancer.Notify() {
|
for addrs := range cc.dopts.balancer.Notify() {
|
||||||
var (
|
var (
|
||||||
add []Address // Addresses need to setup connections.
|
add []Address // Addresses need to setup connections.
|
||||||
|
@ -500,15 +465,7 @@ func (cc *ClientConn) lbWatcher(doneChan chan struct{}) {
|
||||||
}
|
}
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
for _, a := range add {
|
for _, a := range add {
|
||||||
if doneChan != nil {
|
cc.resetAddrConn(a, true, nil)
|
||||||
err := cc.resetAddrConn(a, true, nil)
|
|
||||||
if err == nil {
|
|
||||||
close(doneChan)
|
|
||||||
doneChan = nil
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cc.resetAddrConn(a, false, nil)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for _, c := range del {
|
for _, c := range del {
|
||||||
c.tearDown(errConnDrain)
|
c.tearDown(errConnDrain)
|
||||||
|
@ -537,15 +494,12 @@ func (cc *ClientConn) scWatcher() {
|
||||||
// resetAddrConn creates an addrConn for addr and adds it to cc.conns.
|
// resetAddrConn creates an addrConn for addr and adds it to cc.conns.
|
||||||
// If there is an old addrConn for addr, it will be torn down, using tearDownErr as the reason.
|
// If there is an old addrConn for addr, it will be torn down, using tearDownErr as the reason.
|
||||||
// If tearDownErr is nil, errConnDrain will be used instead.
|
// If tearDownErr is nil, errConnDrain will be used instead.
|
||||||
func (cc *ClientConn) resetAddrConn(addr Address, block bool, tearDownErr error) error {
|
func (cc *ClientConn) resetAddrConn(addr Address, skipWait bool, tearDownErr error) error {
|
||||||
ac := &addrConn{
|
ac := &addrConn{
|
||||||
cc: cc,
|
cc: cc,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
dopts: cc.dopts,
|
dopts: cc.dopts,
|
||||||
}
|
}
|
||||||
cc.mu.RLock()
|
|
||||||
ac.dopts.copts.KeepaliveParams = cc.mkp
|
|
||||||
cc.mu.RUnlock()
|
|
||||||
ac.ctx, ac.cancel = context.WithCancel(cc.ctx)
|
ac.ctx, ac.cancel = context.WithCancel(cc.ctx)
|
||||||
ac.stateCV = sync.NewCond(&ac.mu)
|
ac.stateCV = sync.NewCond(&ac.mu)
|
||||||
if EnableTracing {
|
if EnableTracing {
|
||||||
|
@ -590,7 +544,8 @@ func (cc *ClientConn) resetAddrConn(addr Address, block bool, tearDownErr error)
|
||||||
stale.tearDown(tearDownErr)
|
stale.tearDown(tearDownErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if block {
|
// skipWait may overwrite the decision in ac.dopts.block.
|
||||||
|
if ac.dopts.block && !skipWait {
|
||||||
if err := ac.resetTransport(false); err != nil {
|
if err := ac.resetTransport(false); err != nil {
|
||||||
if err != errConnClosing {
|
if err != errConnClosing {
|
||||||
// Tear down ac and delete it from cc.conns.
|
// Tear down ac and delete it from cc.conns.
|
||||||
|
@ -669,7 +624,6 @@ func (cc *ClientConn) getTransport(ctx context.Context, opts BalancerGetOptions)
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: false, bytesReceived: false})
|
|
||||||
put()
|
put()
|
||||||
}
|
}
|
||||||
return nil, nil, errConnClosing
|
return nil, nil, errConnClosing
|
||||||
|
@ -677,7 +631,6 @@ func (cc *ClientConn) getTransport(ctx context.Context, opts BalancerGetOptions)
|
||||||
t, err := ac.wait(ctx, cc.dopts.balancer != nil, !opts.BlockingWait)
|
t, err := ac.wait(ctx, cc.dopts.balancer != nil, !opts.BlockingWait)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if put != nil {
|
if put != nil {
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: false, bytesReceived: false})
|
|
||||||
put()
|
put()
|
||||||
}
|
}
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -729,20 +682,6 @@ type addrConn struct {
|
||||||
tearDownErr error
|
tearDownErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// adjustParams updates parameters used to create transports upon
|
|
||||||
// receiving a GoAway.
|
|
||||||
func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
|
|
||||||
switch r {
|
|
||||||
case transport.TooManyPings:
|
|
||||||
v := 2 * ac.dopts.copts.KeepaliveParams.Time
|
|
||||||
ac.cc.mu.Lock()
|
|
||||||
if v > ac.cc.mkp.Time {
|
|
||||||
ac.cc.mkp.Time = v
|
|
||||||
}
|
|
||||||
ac.cc.mu.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf records an event in ac's event log, unless ac has been closed.
|
// printf records an event in ac's event log, unless ac has been closed.
|
||||||
// REQUIRES ac.mu is held.
|
// REQUIRES ac.mu is held.
|
||||||
func (ac *addrConn) printf(format string, a ...interface{}) {
|
func (ac *addrConn) printf(format string, a ...interface{}) {
|
||||||
|
@ -827,8 +766,6 @@ func (ac *addrConn) resetTransport(closeTransport bool) error {
|
||||||
Metadata: ac.addr.Metadata,
|
Metadata: ac.addr.Metadata,
|
||||||
}
|
}
|
||||||
newTransport, err := transport.NewClientTransport(ctx, sinfo, ac.dopts.copts)
|
newTransport, err := transport.NewClientTransport(ctx, sinfo, ac.dopts.copts)
|
||||||
// Don't call cancel in success path due to a race in Go 1.6:
|
|
||||||
// https://github.com/golang/go/issues/15078.
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
|
@ -899,7 +836,6 @@ func (ac *addrConn) transportMonitor() {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-t.GoAway():
|
case <-t.GoAway():
|
||||||
ac.adjustParams(t.GetGoAwayReason())
|
|
||||||
// If GoAway happens without any network I/O error, ac is closed without shutting down the
|
// If GoAway happens without any network I/O error, ac is closed without shutting down the
|
||||||
// underlying transport (the transport will be closed when all the pending RPCs finished or
|
// underlying transport (the transport will be closed when all the pending RPCs finished or
|
||||||
// failed.).
|
// failed.).
|
||||||
|
@ -908,9 +844,9 @@ func (ac *addrConn) transportMonitor() {
|
||||||
// In both cases, a new ac is created.
|
// In both cases, a new ac is created.
|
||||||
select {
|
select {
|
||||||
case <-t.Error():
|
case <-t.Error():
|
||||||
ac.cc.resetAddrConn(ac.addr, false, errNetworkIO)
|
ac.cc.resetAddrConn(ac.addr, true, errNetworkIO)
|
||||||
default:
|
default:
|
||||||
ac.cc.resetAddrConn(ac.addr, false, errConnDrain)
|
ac.cc.resetAddrConn(ac.addr, true, errConnDrain)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case <-t.Error():
|
case <-t.Error():
|
||||||
|
@ -919,8 +855,7 @@ func (ac *addrConn) transportMonitor() {
|
||||||
t.Close()
|
t.Close()
|
||||||
return
|
return
|
||||||
case <-t.GoAway():
|
case <-t.GoAway():
|
||||||
ac.adjustParams(t.GetGoAwayReason())
|
ac.cc.resetAddrConn(ac.addr, true, errNetworkIO)
|
||||||
ac.cc.resetAddrConn(ac.addr, false, errNetworkIO)
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2014, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * 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.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "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 COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Codec defines the interface gRPC uses to encode and decode messages.
|
|
||||||
// Note that implementations of this interface must be thread safe;
|
|
||||||
// a Codec's methods can be called from concurrent goroutines.
|
|
||||||
type Codec interface {
|
|
||||||
// Marshal returns the wire format of v.
|
|
||||||
Marshal(v interface{}) ([]byte, error)
|
|
||||||
// Unmarshal parses the wire format into v.
|
|
||||||
Unmarshal(data []byte, v interface{}) error
|
|
||||||
// String returns the name of the Codec implementation. The returned
|
|
||||||
// string will be used as part of content type in transmission.
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
// protoCodec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
|
||||||
type protoCodec struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
type cachedProtoBuffer struct {
|
|
||||||
lastMarshaledSize uint32
|
|
||||||
proto.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func capToMaxInt32(val int) uint32 {
|
|
||||||
if val > math.MaxInt32 {
|
|
||||||
return uint32(math.MaxInt32)
|
|
||||||
}
|
|
||||||
return uint32(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p protoCodec) marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) {
|
|
||||||
protoMsg := v.(proto.Message)
|
|
||||||
newSlice := make([]byte, 0, cb.lastMarshaledSize)
|
|
||||||
|
|
||||||
cb.SetBuf(newSlice)
|
|
||||||
cb.Reset()
|
|
||||||
if err := cb.Marshal(protoMsg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out := cb.Bytes()
|
|
||||||
cb.lastMarshaledSize = capToMaxInt32(len(out))
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p protoCodec) Marshal(v interface{}) ([]byte, error) {
|
|
||||||
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
|
||||||
out, err := p.marshal(v, cb)
|
|
||||||
|
|
||||||
// put back buffer and lose the ref to the slice
|
|
||||||
cb.SetBuf(nil)
|
|
||||||
protoBufferPool.Put(cb)
|
|
||||||
return out, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p protoCodec) Unmarshal(data []byte, v interface{}) error {
|
|
||||||
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
|
||||||
cb.SetBuf(data)
|
|
||||||
err := cb.Unmarshal(v.(proto.Message))
|
|
||||||
cb.SetBuf(nil)
|
|
||||||
protoBufferPool.Put(cb)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (protoCodec) String() string {
|
|
||||||
return "proto"
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
protoBufferPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return &cachedProtoBuffer{
|
|
||||||
Buffer: proto.Buffer{},
|
|
||||||
lastMarshaledSize: 16,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# This script serves as an example to demonstrate how to generate the gRPC-Go
|
||||||
|
# interface and the related messages from .proto file.
|
||||||
|
#
|
||||||
|
# It assumes the installation of i) Google proto buffer compiler at
|
||||||
|
# https://github.com/google/protobuf (after v2.6.1) and ii) the Go codegen
|
||||||
|
# plugin at https://github.com/golang/protobuf (after 2015-02-20). If you have
|
||||||
|
# not, please install them first.
|
||||||
|
#
|
||||||
|
# We recommend running this script at $GOPATH/src.
|
||||||
|
#
|
||||||
|
# If this is not what you need, feel free to make your own scripts. Again, this
|
||||||
|
# script is for demonstration purpose.
|
||||||
|
#
|
||||||
|
proto=$1
|
||||||
|
protoc --go_out=plugins=grpc:. $proto
|
|
@ -0,0 +1,48 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
workdir=.cover
|
||||||
|
profile="$workdir/cover.out"
|
||||||
|
mode=set
|
||||||
|
end2endtest="google.golang.org/grpc/test"
|
||||||
|
|
||||||
|
generate_cover_data() {
|
||||||
|
rm -rf "$workdir"
|
||||||
|
mkdir "$workdir"
|
||||||
|
|
||||||
|
for pkg in "$@"; do
|
||||||
|
if [ $pkg == "google.golang.org/grpc" -o $pkg == "google.golang.org/grpc/transport" -o $pkg == "google.golang.org/grpc/metadata" -o $pkg == "google.golang.org/grpc/credentials" ]
|
||||||
|
then
|
||||||
|
f="$workdir/$(echo $pkg | tr / -)"
|
||||||
|
go test -covermode="$mode" -coverprofile="$f.cover" "$pkg"
|
||||||
|
go test -covermode="$mode" -coverpkg "$pkg" -coverprofile="$f.e2e.cover" "$end2endtest"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "mode: $mode" >"$profile"
|
||||||
|
grep -h -v "^mode:" "$workdir"/*.cover >>"$profile"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_cover_report() {
|
||||||
|
go tool cover -${1}="$profile"
|
||||||
|
}
|
||||||
|
|
||||||
|
push_to_coveralls() {
|
||||||
|
goveralls -coverprofile="$profile"
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_cover_data $(go list ./...)
|
||||||
|
show_cover_report func
|
||||||
|
case "$1" in
|
||||||
|
"")
|
||||||
|
;;
|
||||||
|
--html)
|
||||||
|
show_cover_report html ;;
|
||||||
|
--coveralls)
|
||||||
|
push_to_coveralls ;;
|
||||||
|
*)
|
||||||
|
echo >&2 "error: invalid option: $1" ;;
|
||||||
|
esac
|
||||||
|
rm -rf "$workdir"
|
|
@ -102,10 +102,6 @@ type TransportCredentials interface {
|
||||||
// authentication protocol on rawConn for clients. It returns the authenticated
|
// authentication protocol on rawConn for clients. It returns the authenticated
|
||||||
// connection and the corresponding auth information about the connection.
|
// connection and the corresponding auth information about the connection.
|
||||||
// Implementations must use the provided context to implement timely cancellation.
|
// Implementations must use the provided context to implement timely cancellation.
|
||||||
// gRPC will try to reconnect if the error returned is a temporary error
|
|
||||||
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
|
|
||||||
// If the returned error is a wrapper error, implementations should make sure that
|
|
||||||
// the error implements Temporary() to have the correct retry behaviors.
|
|
||||||
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
||||||
// ServerHandshake does the authentication handshake for servers. It returns
|
// ServerHandshake does the authentication handshake for servers. It returns
|
||||||
// the authenticated connection and the corresponding auth information about
|
// the authenticated connection and the corresponding auth information about
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// +build go1.7
|
// +build go1.7
|
||||||
// +build !go1.8
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
|
@ -45,6 +44,8 @@ import (
|
||||||
// contains a mutex and must not be copied.
|
// contains a mutex and must not be copied.
|
||||||
//
|
//
|
||||||
// If cfg is nil, a new zero tls.Config is returned.
|
// If cfg is nil, a new zero tls.Config is returned.
|
||||||
|
//
|
||||||
|
// TODO replace this function with official clone function.
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return &tls.Config{}
|
return &tls.Config{}
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
// +build go1.8
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2017, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * 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.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "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 COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package credentials
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
)
|
|
||||||
|
|
||||||
// cloneTLSConfig returns a shallow clone of the exported
|
|
||||||
// fields of cfg, ignoring the unexported sync.Once, which
|
|
||||||
// contains a mutex and must not be copied.
|
|
||||||
//
|
|
||||||
// If cfg is nil, a new zero tls.Config is returned.
|
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
|
||||||
if cfg == nil {
|
|
||||||
return &tls.Config{}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg.Clone()
|
|
||||||
}
|
|
|
@ -44,6 +44,8 @@ import (
|
||||||
// contains a mutex and must not be copied.
|
// contains a mutex and must not be copied.
|
||||||
//
|
//
|
||||||
// If cfg is nil, a new zero tls.Config is returned.
|
// If cfg is nil, a new zero tls.Config is returned.
|
||||||
|
//
|
||||||
|
// TODO replace this function with official clone function.
|
||||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
return &tls.Config{}
|
return &tls.Config{}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
// +build go1.6,!go1.7
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2016, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * 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.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "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 COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// dialContext connects to the address on the named network.
|
|
||||||
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
return (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
|
||||||
req.Cancel = ctx.Done()
|
|
||||||
if err := req.Write(conn); err != nil {
|
|
||||||
return fmt.Errorf("failed to write the HTTP request: %v", err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
// +build go1.7
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2016, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * 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.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "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 COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// dialContext connects to the address on the named network.
|
|
||||||
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
|
||||||
return (&net.Dialer{}).DialContext(ctx, network, address)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
if err := req.Write(conn); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,749 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2016, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * 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.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "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 COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/metadata"
|
|
||||||
"google.golang.org/grpc/naming"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Client API for LoadBalancer service.
|
|
||||||
// Mostly copied from generated pb.go file.
|
|
||||||
// To avoid circular dependency.
|
|
||||||
type loadBalancerClient struct {
|
|
||||||
cc *ClientConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loadBalancerClient) BalanceLoad(ctx context.Context, opts ...CallOption) (*balanceLoadClientStream, error) {
|
|
||||||
desc := &StreamDesc{
|
|
||||||
StreamName: "BalanceLoad",
|
|
||||||
ServerStreams: true,
|
|
||||||
ClientStreams: true,
|
|
||||||
}
|
|
||||||
stream, err := NewClientStream(ctx, desc, c.cc, "/grpc.lb.v1.LoadBalancer/BalanceLoad", opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
x := &balanceLoadClientStream{stream}
|
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type balanceLoadClientStream struct {
|
|
||||||
ClientStream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *balanceLoadClientStream) Send(m *lbpb.LoadBalanceRequest) error {
|
|
||||||
return x.ClientStream.SendMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *balanceLoadClientStream) Recv() (*lbpb.LoadBalanceResponse, error) {
|
|
||||||
m := new(lbpb.LoadBalanceResponse)
|
|
||||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddressType indicates the address type returned by name resolution.
|
|
||||||
type AddressType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Backend indicates the server is a backend server.
|
|
||||||
Backend AddressType = iota
|
|
||||||
// GRPCLB indicates the server is a grpclb load balancer.
|
|
||||||
GRPCLB
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddrMetadataGRPCLB contains the information the name resolution for grpclb should provide. The
|
|
||||||
// name resolver used by grpclb balancer is required to provide this type of metadata in
|
|
||||||
// its address updates.
|
|
||||||
type AddrMetadataGRPCLB struct {
|
|
||||||
// AddrType is the type of server (grpc load balancer or backend).
|
|
||||||
AddrType AddressType
|
|
||||||
// ServerName is the name of the grpc load balancer. Used for authentication.
|
|
||||||
ServerName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGRPCLBBalancer creates a grpclb load balancer.
|
|
||||||
func NewGRPCLBBalancer(r naming.Resolver) Balancer {
|
|
||||||
return &balancer{
|
|
||||||
r: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type remoteBalancerInfo struct {
|
|
||||||
addr string
|
|
||||||
// the server name used for authentication with the remote LB server.
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
// grpclbAddrInfo consists of the information of a backend server.
|
|
||||||
type grpclbAddrInfo struct {
|
|
||||||
addr Address
|
|
||||||
connected bool
|
|
||||||
// dropForRateLimiting indicates whether this particular request should be
|
|
||||||
// dropped by the client for rate limiting.
|
|
||||||
dropForRateLimiting bool
|
|
||||||
// dropForLoadBalancing indicates whether this particular request should be
|
|
||||||
// dropped by the client for load balancing.
|
|
||||||
dropForLoadBalancing bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type balancer struct {
|
|
||||||
r naming.Resolver
|
|
||||||
target string
|
|
||||||
mu sync.Mutex
|
|
||||||
seq int // a sequence number to make sure addrCh does not get stale addresses.
|
|
||||||
w naming.Watcher
|
|
||||||
addrCh chan []Address
|
|
||||||
rbs []remoteBalancerInfo
|
|
||||||
addrs []*grpclbAddrInfo
|
|
||||||
next int
|
|
||||||
waitCh chan struct{}
|
|
||||||
done bool
|
|
||||||
expTimer *time.Timer
|
|
||||||
rand *rand.Rand
|
|
||||||
|
|
||||||
clientStats lbpb.ClientStats
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) watchAddrUpdates(w naming.Watcher, ch chan []remoteBalancerInfo) error {
|
|
||||||
updates, err := w.Next()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
if b.done {
|
|
||||||
return ErrClientConnClosing
|
|
||||||
}
|
|
||||||
for _, update := range updates {
|
|
||||||
switch update.Op {
|
|
||||||
case naming.Add:
|
|
||||||
var exist bool
|
|
||||||
for _, v := range b.rbs {
|
|
||||||
// TODO: Is the same addr with different server name a different balancer?
|
|
||||||
if update.Addr == v.addr {
|
|
||||||
exist = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if exist {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
md, ok := update.Metadata.(*AddrMetadataGRPCLB)
|
|
||||||
if !ok {
|
|
||||||
// TODO: Revisit the handling here and may introduce some fallback mechanism.
|
|
||||||
grpclog.Printf("The name resolution contains unexpected metadata %v", update.Metadata)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch md.AddrType {
|
|
||||||
case Backend:
|
|
||||||
// TODO: Revisit the handling here and may introduce some fallback mechanism.
|
|
||||||
grpclog.Printf("The name resolution does not give grpclb addresses")
|
|
||||||
continue
|
|
||||||
case GRPCLB:
|
|
||||||
b.rbs = append(b.rbs, remoteBalancerInfo{
|
|
||||||
addr: update.Addr,
|
|
||||||
name: md.ServerName,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
grpclog.Printf("Received unknow address type %d", md.AddrType)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case naming.Delete:
|
|
||||||
for i, v := range b.rbs {
|
|
||||||
if update.Addr == v.addr {
|
|
||||||
copy(b.rbs[i:], b.rbs[i+1:])
|
|
||||||
b.rbs = b.rbs[:len(b.rbs)-1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
grpclog.Println("Unknown update.Op ", update.Op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: Fall back to the basic round-robin load balancing if the resulting address is
|
|
||||||
// not a load balancer.
|
|
||||||
select {
|
|
||||||
case <-ch:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
ch <- b.rbs
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) serverListExpire(seq int) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
// TODO: gRPC interanls do not clear the connections when the server list is stale.
|
|
||||||
// This means RPCs will keep using the existing server list until b receives new
|
|
||||||
// server list even though the list is expired. Revisit this behavior later.
|
|
||||||
if b.done || seq < b.seq {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.next = 0
|
|
||||||
b.addrs = nil
|
|
||||||
// Ask grpc internals to close all the corresponding connections.
|
|
||||||
b.addrCh <- nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertDuration(d *lbpb.Duration) time.Duration {
|
|
||||||
if d == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) processServerList(l *lbpb.ServerList, seq int) {
|
|
||||||
if l == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
servers := l.GetServers()
|
|
||||||
expiration := convertDuration(l.GetExpirationInterval())
|
|
||||||
var (
|
|
||||||
sl []*grpclbAddrInfo
|
|
||||||
addrs []Address
|
|
||||||
)
|
|
||||||
for _, s := range servers {
|
|
||||||
md := metadata.Pairs("lb-token", s.LoadBalanceToken)
|
|
||||||
addr := Address{
|
|
||||||
Addr: fmt.Sprintf("%s:%d", net.IP(s.IpAddress), s.Port),
|
|
||||||
Metadata: &md,
|
|
||||||
}
|
|
||||||
sl = append(sl, &grpclbAddrInfo{
|
|
||||||
addr: addr,
|
|
||||||
dropForRateLimiting: s.DropForRateLimiting,
|
|
||||||
dropForLoadBalancing: s.DropForLoadBalancing,
|
|
||||||
})
|
|
||||||
addrs = append(addrs, addr)
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
if b.done || seq < b.seq {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(sl) > 0 {
|
|
||||||
// reset b.next to 0 when replacing the server list.
|
|
||||||
b.next = 0
|
|
||||||
b.addrs = sl
|
|
||||||
b.addrCh <- addrs
|
|
||||||
if b.expTimer != nil {
|
|
||||||
b.expTimer.Stop()
|
|
||||||
b.expTimer = nil
|
|
||||||
}
|
|
||||||
if expiration > 0 {
|
|
||||||
b.expTimer = time.AfterFunc(expiration, func() {
|
|
||||||
b.serverListExpire(seq)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) sendLoadReport(s *balanceLoadClientStream, interval time.Duration, done <-chan struct{}) {
|
|
||||||
ticker := time.NewTicker(interval)
|
|
||||||
defer ticker.Stop()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
case <-done:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
stats := b.clientStats
|
|
||||||
b.clientStats = lbpb.ClientStats{} // Clear the stats.
|
|
||||||
b.mu.Unlock()
|
|
||||||
t := time.Now()
|
|
||||||
stats.Timestamp = &lbpb.Timestamp{
|
|
||||||
Seconds: t.Unix(),
|
|
||||||
Nanos: int32(t.Nanosecond()),
|
|
||||||
}
|
|
||||||
if err := s.Send(&lbpb.LoadBalanceRequest{
|
|
||||||
LoadBalanceRequestType: &lbpb.LoadBalanceRequest_ClientStats{
|
|
||||||
ClientStats: &stats,
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) callRemoteBalancer(lbc *loadBalancerClient, seq int) (retry bool) {
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
stream, err := lbc.BalanceLoad(ctx)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Printf("Failed to perform RPC to the remote balancer %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.done {
|
|
||||||
b.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
initReq := &lbpb.LoadBalanceRequest{
|
|
||||||
LoadBalanceRequestType: &lbpb.LoadBalanceRequest_InitialRequest{
|
|
||||||
InitialRequest: &lbpb.InitialLoadBalanceRequest{
|
|
||||||
Name: b.target,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if err := stream.Send(initReq); err != nil {
|
|
||||||
// TODO: backoff on retry?
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
reply, err := stream.Recv()
|
|
||||||
if err != nil {
|
|
||||||
// TODO: backoff on retry?
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
initResp := reply.GetInitialResponse()
|
|
||||||
if initResp == nil {
|
|
||||||
grpclog.Println("Failed to receive the initial response from the remote balancer.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// TODO: Support delegation.
|
|
||||||
if initResp.LoadBalancerDelegate != "" {
|
|
||||||
// delegation
|
|
||||||
grpclog.Println("TODO: Delegation is not supported yet.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
streamDone := make(chan struct{})
|
|
||||||
defer close(streamDone)
|
|
||||||
b.mu.Lock()
|
|
||||||
b.clientStats = lbpb.ClientStats{} // Clear client stats.
|
|
||||||
b.mu.Unlock()
|
|
||||||
if d := convertDuration(initResp.ClientStatsReportInterval); d > 0 {
|
|
||||||
go b.sendLoadReport(stream, d, streamDone)
|
|
||||||
}
|
|
||||||
// Retrieve the server list.
|
|
||||||
for {
|
|
||||||
reply, err := stream.Recv()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.done || seq < b.seq {
|
|
||||||
b.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.seq++ // tick when receiving a new list of servers.
|
|
||||||
seq = b.seq
|
|
||||||
b.mu.Unlock()
|
|
||||||
if serverList := reply.GetServerList(); serverList != nil {
|
|
||||||
b.processServerList(serverList, seq)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) Start(target string, config BalancerConfig) error {
|
|
||||||
b.rand = rand.New(rand.NewSource(time.Now().Unix()))
|
|
||||||
// TODO: Fall back to the basic direct connection if there is no name resolver.
|
|
||||||
if b.r == nil {
|
|
||||||
return errors.New("there is no name resolver installed")
|
|
||||||
}
|
|
||||||
b.target = target
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.done {
|
|
||||||
b.mu.Unlock()
|
|
||||||
return ErrClientConnClosing
|
|
||||||
}
|
|
||||||
b.addrCh = make(chan []Address)
|
|
||||||
w, err := b.r.Resolve(target)
|
|
||||||
if err != nil {
|
|
||||||
b.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.w = w
|
|
||||||
b.mu.Unlock()
|
|
||||||
balancerAddrsCh := make(chan []remoteBalancerInfo, 1)
|
|
||||||
// Spawn a goroutine to monitor the name resolution of remote load balancer.
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if err := b.watchAddrUpdates(w, balancerAddrsCh); err != nil {
|
|
||||||
grpclog.Printf("grpc: the naming watcher stops working due to %v.\n", err)
|
|
||||||
close(balancerAddrsCh)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// Spawn a goroutine to talk to the remote load balancer.
|
|
||||||
go func() {
|
|
||||||
var (
|
|
||||||
cc *ClientConn
|
|
||||||
// ccError is closed when there is an error in the current cc.
|
|
||||||
// A new rb should be picked from rbs and connected.
|
|
||||||
ccError chan struct{}
|
|
||||||
rb *remoteBalancerInfo
|
|
||||||
rbs []remoteBalancerInfo
|
|
||||||
rbIdx int
|
|
||||||
)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if ccError != nil {
|
|
||||||
select {
|
|
||||||
case <-ccError:
|
|
||||||
default:
|
|
||||||
close(ccError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cc != nil {
|
|
||||||
cc.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
var ok bool
|
|
||||||
select {
|
|
||||||
case rbs, ok = <-balancerAddrsCh:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
foundIdx := -1
|
|
||||||
if rb != nil {
|
|
||||||
for i, trb := range rbs {
|
|
||||||
if trb == *rb {
|
|
||||||
foundIdx = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if foundIdx >= 0 {
|
|
||||||
if foundIdx >= 1 {
|
|
||||||
// Move the address in use to the beginning of the list.
|
|
||||||
b.rbs[0], b.rbs[foundIdx] = b.rbs[foundIdx], b.rbs[0]
|
|
||||||
rbIdx = 0
|
|
||||||
}
|
|
||||||
continue // If found, don't dial new cc.
|
|
||||||
} else if len(rbs) > 0 {
|
|
||||||
// Pick a random one from the list, instead of always using the first one.
|
|
||||||
if l := len(rbs); l > 1 && rb != nil {
|
|
||||||
tmpIdx := b.rand.Intn(l - 1)
|
|
||||||
b.rbs[0], b.rbs[tmpIdx] = b.rbs[tmpIdx], b.rbs[0]
|
|
||||||
}
|
|
||||||
rbIdx = 0
|
|
||||||
rb = &rbs[0]
|
|
||||||
} else {
|
|
||||||
// foundIdx < 0 && len(rbs) <= 0.
|
|
||||||
rb = nil
|
|
||||||
}
|
|
||||||
case <-ccError:
|
|
||||||
ccError = nil
|
|
||||||
if rbIdx < len(rbs)-1 {
|
|
||||||
rbIdx++
|
|
||||||
rb = &rbs[rbIdx]
|
|
||||||
} else {
|
|
||||||
rb = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if rb == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if cc != nil {
|
|
||||||
cc.Close()
|
|
||||||
}
|
|
||||||
// Talk to the remote load balancer to get the server list.
|
|
||||||
var err error
|
|
||||||
creds := config.DialCreds
|
|
||||||
ccError = make(chan struct{})
|
|
||||||
if creds == nil {
|
|
||||||
cc, err = Dial(rb.addr, WithInsecure())
|
|
||||||
} else {
|
|
||||||
if rb.name != "" {
|
|
||||||
if err := creds.OverrideServerName(rb.name); err != nil {
|
|
||||||
grpclog.Printf("Failed to override the server name in the credentials: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cc, err = Dial(rb.addr, WithTransportCredentials(creds))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Printf("Failed to setup a connection to the remote balancer %v: %v", rb.addr, err)
|
|
||||||
close(ccError)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
b.seq++ // tick when getting a new balancer address
|
|
||||||
seq := b.seq
|
|
||||||
b.next = 0
|
|
||||||
b.mu.Unlock()
|
|
||||||
go func(cc *ClientConn, ccError chan struct{}) {
|
|
||||||
lbc := &loadBalancerClient{cc}
|
|
||||||
b.callRemoteBalancer(lbc, seq)
|
|
||||||
cc.Close()
|
|
||||||
select {
|
|
||||||
case <-ccError:
|
|
||||||
default:
|
|
||||||
close(ccError)
|
|
||||||
}
|
|
||||||
}(cc, ccError)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) down(addr Address, err error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
for _, a := range b.addrs {
|
|
||||||
if addr == a.addr {
|
|
||||||
a.connected = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) Up(addr Address) func(error) {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
if b.done {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var cnt int
|
|
||||||
for _, a := range b.addrs {
|
|
||||||
if a.addr == addr {
|
|
||||||
if a.connected {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
a.connected = true
|
|
||||||
}
|
|
||||||
if a.connected && !a.dropForRateLimiting && !a.dropForLoadBalancing {
|
|
||||||
cnt++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// addr is the only one which is connected. Notify the Get() callers who are blocking.
|
|
||||||
if cnt == 1 && b.waitCh != nil {
|
|
||||||
close(b.waitCh)
|
|
||||||
b.waitCh = nil
|
|
||||||
}
|
|
||||||
return func(err error) {
|
|
||||||
b.down(addr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) {
|
|
||||||
var ch chan struct{}
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.done {
|
|
||||||
b.mu.Unlock()
|
|
||||||
err = ErrClientConnClosing
|
|
||||||
return
|
|
||||||
}
|
|
||||||
seq := b.seq
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
put = func() {
|
|
||||||
s, ok := rpcInfoFromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
if b.done || seq < b.seq {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
if !s.bytesSent {
|
|
||||||
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
|
||||||
} else if s.bytesReceived {
|
|
||||||
b.clientStats.NumCallsFinishedKnownReceived++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
b.clientStats.NumCallsStarted++
|
|
||||||
if len(b.addrs) > 0 {
|
|
||||||
if b.next >= len(b.addrs) {
|
|
||||||
b.next = 0
|
|
||||||
}
|
|
||||||
next := b.next
|
|
||||||
for {
|
|
||||||
a := b.addrs[next]
|
|
||||||
next = (next + 1) % len(b.addrs)
|
|
||||||
if a.connected {
|
|
||||||
if !a.dropForRateLimiting && !a.dropForLoadBalancing {
|
|
||||||
addr = a.addr
|
|
||||||
b.next = next
|
|
||||||
b.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !opts.BlockingWait {
|
|
||||||
b.next = next
|
|
||||||
if a.dropForLoadBalancing {
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithDropForLoadBalancing++
|
|
||||||
} else if a.dropForRateLimiting {
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithDropForRateLimiting++
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
err = Errorf(codes.Unavailable, "%s drops requests", a.addr.Addr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if next == b.next {
|
|
||||||
// Has iterated all the possible address but none is connected.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !opts.BlockingWait {
|
|
||||||
if len(b.addrs) == 0 {
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
|
||||||
b.mu.Unlock()
|
|
||||||
err = Errorf(codes.Unavailable, "there is no address available")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Returns the next addr on b.addrs for a failfast RPC.
|
|
||||||
addr = b.addrs[b.next].addr
|
|
||||||
b.next++
|
|
||||||
b.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Wait on b.waitCh for non-failfast RPCs.
|
|
||||||
if b.waitCh == nil {
|
|
||||||
ch = make(chan struct{})
|
|
||||||
b.waitCh = ch
|
|
||||||
} else {
|
|
||||||
ch = b.waitCh
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
b.mu.Lock()
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
|
||||||
b.mu.Unlock()
|
|
||||||
err = ctx.Err()
|
|
||||||
return
|
|
||||||
case <-ch:
|
|
||||||
b.mu.Lock()
|
|
||||||
if b.done {
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithClientFailedToSend++
|
|
||||||
b.mu.Unlock()
|
|
||||||
err = ErrClientConnClosing
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(b.addrs) > 0 {
|
|
||||||
if b.next >= len(b.addrs) {
|
|
||||||
b.next = 0
|
|
||||||
}
|
|
||||||
next := b.next
|
|
||||||
for {
|
|
||||||
a := b.addrs[next]
|
|
||||||
next = (next + 1) % len(b.addrs)
|
|
||||||
if a.connected {
|
|
||||||
if !a.dropForRateLimiting && !a.dropForLoadBalancing {
|
|
||||||
addr = a.addr
|
|
||||||
b.next = next
|
|
||||||
b.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !opts.BlockingWait {
|
|
||||||
b.next = next
|
|
||||||
if a.dropForLoadBalancing {
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithDropForLoadBalancing++
|
|
||||||
} else if a.dropForRateLimiting {
|
|
||||||
b.clientStats.NumCallsFinished++
|
|
||||||
b.clientStats.NumCallsFinishedWithDropForRateLimiting++
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
err = Errorf(codes.Unavailable, "drop requests for the addreess %s", a.addr.Addr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if next == b.next {
|
|
||||||
// Has iterated all the possible address but none is connected.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The newly added addr got removed by Down() again.
|
|
||||||
if b.waitCh == nil {
|
|
||||||
ch = make(chan struct{})
|
|
||||||
b.waitCh = ch
|
|
||||||
} else {
|
|
||||||
ch = b.waitCh
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) Notify() <-chan []Address {
|
|
||||||
return b.addrCh
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *balancer) Close() error {
|
|
||||||
b.mu.Lock()
|
|
||||||
defer b.mu.Unlock()
|
|
||||||
b.done = true
|
|
||||||
if b.expTimer != nil {
|
|
||||||
b.expTimer.Stop()
|
|
||||||
}
|
|
||||||
if b.waitCh != nil {
|
|
||||||
close(b.waitCh)
|
|
||||||
}
|
|
||||||
if b.addrCh != nil {
|
|
||||||
close(b.addrCh)
|
|
||||||
}
|
|
||||||
if b.w != nil {
|
|
||||||
b.w.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -40,7 +40,7 @@ import (
|
||||||
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
||||||
type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error
|
type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error
|
||||||
|
|
||||||
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client. invoker is the handler to complete the RPC
|
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client. inovker is the handler to complete the RPC
|
||||||
// and it is the responsibility of the interceptor to call it.
|
// and it is the responsibility of the interceptor to call it.
|
||||||
// This is the EXPERIMENTAL API.
|
// This is the EXPERIMENTAL API.
|
||||||
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
|
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2017, Google Inc.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are
|
|
||||||
* met:
|
|
||||||
*
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * 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.
|
|
||||||
* * Neither the name of Google Inc. nor the names of its
|
|
||||||
* contributors may be used to endorse or promote products derived from
|
|
||||||
* this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "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 COPYRIGHT
|
|
||||||
* OWNER OR CONTRIBUTORS 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httputil"
|
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// errDisabled indicates that proxy is disabled for the address.
|
|
||||||
errDisabled = errors.New("proxy is disabled for the address")
|
|
||||||
// The following variable will be overwritten in the tests.
|
|
||||||
httpProxyFromEnvironment = http.ProxyFromEnvironment
|
|
||||||
)
|
|
||||||
|
|
||||||
func mapAddress(ctx context.Context, address string) (string, error) {
|
|
||||||
req := &http.Request{
|
|
||||||
URL: &url.URL{
|
|
||||||
Scheme: "https",
|
|
||||||
Host: address,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
url, err := httpProxyFromEnvironment(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if url == nil {
|
|
||||||
return "", errDisabled
|
|
||||||
}
|
|
||||||
return url.Host, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader.
|
|
||||||
// It's possible that this reader reads more than what's need for the response and stores
|
|
||||||
// those bytes in the buffer.
|
|
||||||
// bufConn wraps the original net.Conn and the bufio.Reader to make sure we don't lose the
|
|
||||||
// bytes in the buffer.
|
|
||||||
type bufConn struct {
|
|
||||||
net.Conn
|
|
||||||
r io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *bufConn) Read(b []byte) (int, error) {
|
|
||||||
return c.r.Read(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_ net.Conn, err error) {
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
conn.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
req := (&http.Request{
|
|
||||||
Method: http.MethodConnect,
|
|
||||||
URL: &url.URL{Host: addr},
|
|
||||||
Header: map[string][]string{"User-Agent": {grpcUA}},
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := sendHTTPRequest(ctx, req, conn); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to write the HTTP request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := bufio.NewReader(conn)
|
|
||||||
resp, err := http.ReadResponse(r, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading server HTTP response: %v", err)
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
dump, err := httputil.DumpResponse(resp, true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to do connect handshake, status code: %s", resp.Status)
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &bufConn{Conn: conn, r: r}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// newProxyDialer returns a dialer that connects to proxy first if necessary.
|
|
||||||
// The returned dialer checks if a proxy is necessary, dial to the proxy with the
|
|
||||||
// provided dialer, does HTTP CONNECT handshake and returns the connection.
|
|
||||||
func newProxyDialer(dialer func(context.Context, string) (net.Conn, error)) func(context.Context, string) (net.Conn, error) {
|
|
||||||
return func(ctx context.Context, addr string) (conn net.Conn, err error) {
|
|
||||||
var skipHandshake bool
|
|
||||||
newAddr, err := mapAddress(ctx, addr)
|
|
||||||
if err != nil {
|
|
||||||
if err != errDisabled {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
skipHandshake = true
|
|
||||||
newAddr = addr
|
|
||||||
}
|
|
||||||
|
|
||||||
conn, err = dialer(ctx, newAddr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !skipHandshake {
|
|
||||||
conn, err = doHTTPConnectHandshake(ctx, conn, addr)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,21 +37,47 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Codec defines the interface gRPC uses to encode and decode messages.
|
||||||
|
type Codec interface {
|
||||||
|
// Marshal returns the wire format of v.
|
||||||
|
Marshal(v interface{}) ([]byte, error)
|
||||||
|
// Unmarshal parses the wire format into v.
|
||||||
|
Unmarshal(data []byte, v interface{}) error
|
||||||
|
// String returns the name of the Codec implementation. The returned
|
||||||
|
// string will be used as part of content type in transmission.
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// protoCodec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
||||||
|
type protoCodec struct{}
|
||||||
|
|
||||||
|
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return proto.Marshal(v.(proto.Message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return proto.Unmarshal(data, v.(proto.Message))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protoCodec) String() string {
|
||||||
|
return "proto"
|
||||||
|
}
|
||||||
|
|
||||||
// Compressor defines the interface gRPC uses to compress a message.
|
// Compressor defines the interface gRPC uses to compress a message.
|
||||||
type Compressor interface {
|
type Compressor interface {
|
||||||
// Do compresses p into w.
|
// Do compresses p into w.
|
||||||
|
@ -114,7 +140,6 @@ type callInfo struct {
|
||||||
failFast bool
|
failFast bool
|
||||||
headerMD metadata.MD
|
headerMD metadata.MD
|
||||||
trailerMD metadata.MD
|
trailerMD metadata.MD
|
||||||
peer *peer.Peer
|
|
||||||
traceInfo traceInfo // in trace.go
|
traceInfo traceInfo // in trace.go
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,22 +183,12 @@ func Trailer(md *metadata.MD) CallOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer returns a CallOption that retrieves peer information for a
|
|
||||||
// unary RPC.
|
|
||||||
func Peer(peer *peer.Peer) CallOption {
|
|
||||||
return afterCall(func(c *callInfo) {
|
|
||||||
if c.peer != nil {
|
|
||||||
*peer = *c.peer
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// FailFast configures the action to take when an RPC is attempted on broken
|
// FailFast configures the action to take when an RPC is attempted on broken
|
||||||
// connections or unreachable servers. If failfast is true, the RPC will fail
|
// connections or unreachable servers. If failfast is true, the RPC will fail
|
||||||
// immediately. Otherwise, the RPC client will block the call until a
|
// immediately. Otherwise, the RPC client will block the call until a
|
||||||
// connection is available (or the call is canceled or times out) and will retry
|
// connection is available (or the call is canceled or times out) and will retry
|
||||||
// the call if it fails due to a transient error. Please refer to
|
// the call if it fails due to a transient error. Please refer to
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/fail_fast.md. Note: failFast is default to true.
|
// https://github.com/grpc/grpc/blob/master/doc/fail_fast.md
|
||||||
func FailFast(failFast bool) CallOption {
|
func FailFast(failFast bool) CallOption {
|
||||||
return beforeCall(func(c *callInfo) error {
|
return beforeCall(func(c *callInfo) error {
|
||||||
c.failFast = failFast
|
c.failFast = failFast
|
||||||
|
@ -345,80 +360,88 @@ func recv(p *parser, c Codec, s *transport.Stream, dc Decompressor, m interface{
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcInfo struct {
|
// rpcError defines the status from an RPC.
|
||||||
bytesSent bool
|
type rpcError struct {
|
||||||
bytesReceived bool
|
code codes.Code
|
||||||
|
desc string
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcInfoContextKey struct{}
|
func (e *rpcError) Error() string {
|
||||||
|
return fmt.Sprintf("rpc error: code = %d desc = %s", e.code, e.desc)
|
||||||
func newContextWithRPCInfo(ctx context.Context) context.Context {
|
|
||||||
return context.WithValue(ctx, rpcInfoContextKey{}, &rpcInfo{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcInfoFromContext(ctx context.Context) (s *rpcInfo, ok bool) {
|
|
||||||
s, ok = ctx.Value(rpcInfoContextKey{}).(*rpcInfo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateRPCInfoInContext(ctx context.Context, s rpcInfo) {
|
|
||||||
if ss, ok := rpcInfoFromContext(ctx); ok {
|
|
||||||
*ss = s
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code returns the error code for err if it was produced by the rpc system.
|
// Code returns the error code for err if it was produced by the rpc system.
|
||||||
// Otherwise, it returns codes.Unknown.
|
// Otherwise, it returns codes.Unknown.
|
||||||
//
|
|
||||||
// Deprecated; use status.FromError and Code method instead.
|
|
||||||
func Code(err error) codes.Code {
|
func Code(err error) codes.Code {
|
||||||
if s, ok := status.FromError(err); ok {
|
if err == nil {
|
||||||
return s.Code()
|
return codes.OK
|
||||||
|
}
|
||||||
|
if e, ok := err.(*rpcError); ok {
|
||||||
|
return e.code
|
||||||
}
|
}
|
||||||
return codes.Unknown
|
return codes.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorDesc returns the error description of err if it was produced by the rpc system.
|
// ErrorDesc returns the error description of err if it was produced by the rpc system.
|
||||||
// Otherwise, it returns err.Error() or empty string when err is nil.
|
// Otherwise, it returns err.Error() or empty string when err is nil.
|
||||||
//
|
|
||||||
// Deprecated; use status.FromError and Message method instead.
|
|
||||||
func ErrorDesc(err error) string {
|
func ErrorDesc(err error) string {
|
||||||
if s, ok := status.FromError(err); ok {
|
if err == nil {
|
||||||
return s.Message()
|
return ""
|
||||||
|
}
|
||||||
|
if e, ok := err.(*rpcError); ok {
|
||||||
|
return e.desc
|
||||||
}
|
}
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf returns an error containing an error code and a description;
|
// Errorf returns an error containing an error code and a description;
|
||||||
// Errorf returns nil if c is OK.
|
// Errorf returns nil if c is OK.
|
||||||
//
|
|
||||||
// Deprecated; use status.Errorf instead.
|
|
||||||
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
||||||
return status.Errorf(c, format, a...)
|
if c == codes.OK {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &rpcError{
|
||||||
|
code: c,
|
||||||
|
desc: fmt.Sprintf(format, a...),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// toRPCErr converts an error into an error from the status package.
|
// toRPCErr converts an error into a rpcError.
|
||||||
func toRPCErr(err error) error {
|
func toRPCErr(err error) error {
|
||||||
if _, ok := status.FromError(err); ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
|
case *rpcError:
|
||||||
|
return err
|
||||||
case transport.StreamError:
|
case transport.StreamError:
|
||||||
return status.Error(e.Code, e.Desc)
|
return &rpcError{
|
||||||
|
code: e.Code,
|
||||||
|
desc: e.Desc,
|
||||||
|
}
|
||||||
case transport.ConnectionError:
|
case transport.ConnectionError:
|
||||||
return status.Error(codes.Internal, e.Desc)
|
return &rpcError{
|
||||||
|
code: codes.Internal,
|
||||||
|
desc: e.Desc,
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
switch err {
|
switch err {
|
||||||
case context.DeadlineExceeded:
|
case context.DeadlineExceeded:
|
||||||
return status.Error(codes.DeadlineExceeded, err.Error())
|
return &rpcError{
|
||||||
|
code: codes.DeadlineExceeded,
|
||||||
|
desc: err.Error(),
|
||||||
|
}
|
||||||
case context.Canceled:
|
case context.Canceled:
|
||||||
return status.Error(codes.Canceled, err.Error())
|
return &rpcError{
|
||||||
|
code: codes.Canceled,
|
||||||
|
desc: err.Error(),
|
||||||
|
}
|
||||||
case ErrClientConnClosing:
|
case ErrClientConnClosing:
|
||||||
return status.Error(codes.FailedPrecondition, err.Error())
|
return &rpcError{
|
||||||
|
code: codes.FailedPrecondition,
|
||||||
|
desc: err.Error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return status.Error(codes.Unknown, err.Error())
|
|
||||||
|
}
|
||||||
|
return Errorf(codes.Unknown, "%v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// convertCode converts a standard Go error into its canonical code. Note that
|
// convertCode converts a standard Go error into its canonical code. Note that
|
||||||
|
@ -463,17 +486,17 @@ type MethodConfig struct {
|
||||||
// then the other will be used. If neither is set, then the RPC has no deadline.
|
// then the other will be used. If neither is set, then the RPC has no deadline.
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
// MaxReqSize is the maximum allowed payload size for an individual request in a
|
// MaxReqSize is the maximum allowed payload size for an individual request in a
|
||||||
// stream (client->server) in bytes. The size which is measured is the serialized
|
// stream (client->server) in bytes. The size which is measured is the serialized,
|
||||||
// payload after per-message compression (but before stream compression) in bytes.
|
// uncompressed payload in bytes. The actual value used is the minumum of the value
|
||||||
// The actual value used is the minumum of the value specified here and the value set
|
// specified here and the value set by the application via the gRPC client API. If
|
||||||
// by the application via the gRPC client API. If either one is not set, then the other
|
// either one is not set, then the other will be used. If neither is set, then the
|
||||||
// will be used. If neither is set, then the built-in default is used.
|
// built-in default is used.
|
||||||
// TODO: support this.
|
// TODO: support this.
|
||||||
MaxReqSize uint32
|
MaxReqSize uint64
|
||||||
// MaxRespSize is the maximum allowed payload size for an individual response in a
|
// MaxRespSize is the maximum allowed payload size for an individual response in a
|
||||||
// stream (server->client) in bytes.
|
// stream (server->client) in bytes.
|
||||||
// TODO: support this.
|
// TODO: support this.
|
||||||
MaxRespSize uint32
|
MaxRespSize uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceConfig is provided by the service provider and contains parameters for how
|
// ServiceConfig is provided by the service provider and contains parameters for how
|
||||||
|
@ -494,8 +517,3 @@ type ServiceConfig struct {
|
||||||
// requires a synchronised update of grpc-go and protoc-gen-go. This constant
|
// requires a synchronised update of grpc-go and protoc-gen-go. This constant
|
||||||
// should not be referenced from any other code.
|
// should not be referenced from any other code.
|
||||||
const SupportPackageIsVersion4 = true
|
const SupportPackageIsVersion4 = true
|
||||||
|
|
||||||
// Version is the current grpc version.
|
|
||||||
const Version = "1.4.0-dev"
|
|
||||||
|
|
||||||
const grpcUA = "grpc-go/" + Version
|
|
||||||
|
|
|
@ -53,10 +53,8 @@ import (
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/keepalive"
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/grpc/tap"
|
"google.golang.org/grpc/tap"
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
@ -118,9 +116,6 @@ type options struct {
|
||||||
statsHandler stats.Handler
|
statsHandler stats.Handler
|
||||||
maxConcurrentStreams uint32
|
maxConcurrentStreams uint32
|
||||||
useHandlerImpl bool // use http.Handler-based server
|
useHandlerImpl bool // use http.Handler-based server
|
||||||
unknownStreamDesc *StreamDesc
|
|
||||||
keepaliveParams keepalive.ServerParameters
|
|
||||||
keepalivePolicy keepalive.EnforcementPolicy
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size limit
|
var defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size limit
|
||||||
|
@ -128,20 +123,6 @@ var defaultMaxMsgSize = 1024 * 1024 * 4 // use 4MB as the default message size l
|
||||||
// A ServerOption sets options.
|
// A ServerOption sets options.
|
||||||
type ServerOption func(*options)
|
type ServerOption func(*options)
|
||||||
|
|
||||||
// KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server.
|
|
||||||
func KeepaliveParams(kp keepalive.ServerParameters) ServerOption {
|
|
||||||
return func(o *options) {
|
|
||||||
o.keepaliveParams = kp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeepaliveEnforcementPolicy returns a ServerOption that sets keepalive enforcement policy for the server.
|
|
||||||
func KeepaliveEnforcementPolicy(kep keepalive.EnforcementPolicy) ServerOption {
|
|
||||||
return func(o *options) {
|
|
||||||
o.keepalivePolicy = kep
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
|
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
|
||||||
func CustomCodec(codec Codec) ServerOption {
|
func CustomCodec(codec Codec) ServerOption {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
|
@ -227,24 +208,6 @@ func StatsHandler(h stats.Handler) ServerOption {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnknownServiceHandler returns a ServerOption that allows for adding a custom
|
|
||||||
// unknown service handler. The provided method is a bidi-streaming RPC service
|
|
||||||
// handler that will be invoked instead of returning the the "unimplemented" gRPC
|
|
||||||
// error whenever a request is received for an unregistered service or method.
|
|
||||||
// The handling function has full access to the Context of the request and the
|
|
||||||
// stream, and the invocation passes through interceptors.
|
|
||||||
func UnknownServiceHandler(streamHandler StreamHandler) ServerOption {
|
|
||||||
return func(o *options) {
|
|
||||||
o.unknownStreamDesc = &StreamDesc{
|
|
||||||
StreamName: "unknown_service_handler",
|
|
||||||
Handler: streamHandler,
|
|
||||||
// We need to assume that the users of the streamHandler will want to use both.
|
|
||||||
ClientStreams: true,
|
|
||||||
ServerStreams: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServer creates a gRPC server which has no service registered and has not
|
// NewServer creates a gRPC server which has no service registered and has not
|
||||||
// started to accept requests yet.
|
// started to accept requests yet.
|
||||||
func NewServer(opt ...ServerOption) *Server {
|
func NewServer(opt ...ServerOption) *Server {
|
||||||
|
@ -487,8 +450,6 @@ func (s *Server) serveHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo)
|
||||||
AuthInfo: authInfo,
|
AuthInfo: authInfo,
|
||||||
InTapHandle: s.opts.inTapHandle,
|
InTapHandle: s.opts.inTapHandle,
|
||||||
StatsHandler: s.opts.statsHandler,
|
StatsHandler: s.opts.statsHandler,
|
||||||
KeepaliveParams: s.opts.keepaliveParams,
|
|
||||||
KeepalivePolicy: s.opts.keepalivePolicy,
|
|
||||||
}
|
}
|
||||||
st, err := transport.NewServerTransport("http2", c, config)
|
st, err := transport.NewServerTransport("http2", c, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -672,7 +633,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
stream.SetSendCompress(s.opts.cp.Type())
|
stream.SetSendCompress(s.opts.cp.Type())
|
||||||
}
|
}
|
||||||
p := &parser{r: stream}
|
p := &parser{r: stream}
|
||||||
for { // TODO: delete
|
for {
|
||||||
pf, req, err := p.recvMsg(s.opts.maxMsgSize)
|
pf, req, err := p.recvMsg(s.opts.maxMsgSize)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
// The entire stream is done (for unary RPC only).
|
// The entire stream is done (for unary RPC only).
|
||||||
|
@ -682,44 +643,45 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
err = Errorf(codes.Internal, io.ErrUnexpectedEOF.Error())
|
err = Errorf(codes.Internal, io.ErrUnexpectedEOF.Error())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if st, ok := status.FromError(err); ok {
|
switch err := err.(type) {
|
||||||
if e := t.WriteStatus(stream, st); e != nil {
|
case *rpcError:
|
||||||
|
if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
switch st := err.(type) {
|
|
||||||
case transport.ConnectionError:
|
case transport.ConnectionError:
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
case transport.StreamError:
|
case transport.StreamError:
|
||||||
if e := t.WriteStatus(stream, status.New(st.Code, st.Desc)); e != nil {
|
if e := t.WriteStatus(stream, err.Code, err.Desc); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", st, st))
|
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
if err := checkRecvPayload(pf, stream.RecvCompress(), s.opts.dc); err != nil {
|
||||||
if st, ok := status.FromError(err); ok {
|
switch err := err.(type) {
|
||||||
if e := t.WriteStatus(stream, st); e != nil {
|
case *rpcError:
|
||||||
|
if e := t.WriteStatus(stream, err.code, err.desc); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
default:
|
||||||
if e := t.WriteStatus(stream, status.New(codes.Internal, err.Error())); e != nil {
|
if e := t.WriteStatus(stream, codes.Internal, err.Error()); e != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO checkRecvPayload always return RPC error. Add a return here if necessary.
|
// TODO checkRecvPayload always return RPC error. Add a return here if necessary.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var inPayload *stats.InPayload
|
var inPayload *stats.InPayload
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
inPayload = &stats.InPayload{
|
inPayload = &stats.InPayload{
|
||||||
RecvTime: time.Now(),
|
RecvTime: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
statusCode := codes.OK
|
||||||
|
statusDesc := ""
|
||||||
df := func(v interface{}) error {
|
df := func(v interface{}) error {
|
||||||
if inPayload != nil {
|
if inPayload != nil {
|
||||||
inPayload.WireLength = len(req)
|
inPayload.WireLength = len(req)
|
||||||
|
@ -728,16 +690,20 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
var err error
|
var err error
|
||||||
req, err = s.opts.dc.Do(bytes.NewReader(req))
|
req, err = s.opts.dc.Do(bytes.NewReader(req))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err := t.WriteStatus(stream, codes.Internal, err.Error()); err != nil {
|
||||||
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", err)
|
||||||
|
}
|
||||||
return Errorf(codes.Internal, err.Error())
|
return Errorf(codes.Internal, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(req) > s.opts.maxMsgSize {
|
if len(req) > s.opts.maxMsgSize {
|
||||||
// TODO: Revisit the error code. Currently keep it consistent with
|
// TODO: Revisit the error code. Currently keep it consistent with
|
||||||
// java implementation.
|
// java implementation.
|
||||||
return status.Errorf(codes.Internal, "grpc: server received a message of %d bytes exceeding %d limit", len(req), s.opts.maxMsgSize)
|
statusCode = codes.Internal
|
||||||
|
statusDesc = fmt.Sprintf("grpc: server received a message of %d bytes exceeding %d limit", len(req), s.opts.maxMsgSize)
|
||||||
}
|
}
|
||||||
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
if err := s.opts.codec.Unmarshal(req, v); err != nil {
|
||||||
return status.Errorf(codes.Internal, "grpc: error unmarshalling request: %v", err)
|
return err
|
||||||
}
|
}
|
||||||
if inPayload != nil {
|
if inPayload != nil {
|
||||||
inPayload.Payload = v
|
inPayload.Payload = v
|
||||||
|
@ -752,20 +718,21 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
}
|
}
|
||||||
reply, appErr := md.Handler(srv.server, stream.Context(), df, s.opts.unaryInt)
|
reply, appErr := md.Handler(srv.server, stream.Context(), df, s.opts.unaryInt)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
appStatus, ok := status.FromError(appErr)
|
if err, ok := appErr.(*rpcError); ok {
|
||||||
if !ok {
|
statusCode = err.code
|
||||||
// Convert appErr if it is not a grpc status error.
|
statusDesc = err.desc
|
||||||
appErr = status.Error(convertCode(appErr), appErr.Error())
|
} else {
|
||||||
appStatus, _ = status.FromError(appErr)
|
statusCode = convertCode(appErr)
|
||||||
|
statusDesc = appErr.Error()
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil && statusCode != codes.OK {
|
||||||
trInfo.tr.LazyLog(stringer(appStatus.Message()), true)
|
trInfo.tr.LazyLog(stringer(statusDesc), true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if e := t.WriteStatus(stream, appStatus); e != nil {
|
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", e)
|
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
|
||||||
}
|
}
|
||||||
return appErr
|
return Errorf(statusCode, statusDesc)
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(stringer("OK"), false)
|
trInfo.tr.LazyLog(stringer("OK"), false)
|
||||||
|
@ -775,35 +742,26 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
Delay: false,
|
Delay: false,
|
||||||
}
|
}
|
||||||
if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
|
if err := s.sendResponse(t, stream, reply, s.opts.cp, opts); err != nil {
|
||||||
if err == io.EOF {
|
switch err := err.(type) {
|
||||||
// The entire stream is done (for unary RPC only).
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if s, ok := status.FromError(err); ok {
|
|
||||||
if e := t.WriteStatus(stream, s); e != nil {
|
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch st := err.(type) {
|
|
||||||
case transport.ConnectionError:
|
case transport.ConnectionError:
|
||||||
// Nothing to do here.
|
// Nothing to do here.
|
||||||
case transport.StreamError:
|
case transport.StreamError:
|
||||||
if e := t.WriteStatus(stream, status.New(st.Code, st.Desc)); e != nil {
|
statusCode = err.Code
|
||||||
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
statusDesc = err.Desc
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("grpc: Unexpected error (%T) from sendResponse: %v", st, st))
|
statusCode = codes.Unknown
|
||||||
}
|
statusDesc = err.Error()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
|
||||||
}
|
}
|
||||||
// TODO: Should we be logging if writing status failed here, like above?
|
errWrite := t.WriteStatus(stream, statusCode, statusDesc)
|
||||||
// Should the logging be in WriteStatus? Should we ignore the WriteStatus
|
if statusCode != codes.OK {
|
||||||
// error or allow the stats handler to see it?
|
return Errorf(statusCode, statusDesc)
|
||||||
return t.WriteStatus(stream, status.New(codes.OK, ""))
|
}
|
||||||
|
return errWrite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,47 +815,43 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
var appErr error
|
var appErr error
|
||||||
var server interface{}
|
|
||||||
if srv != nil {
|
|
||||||
server = srv.server
|
|
||||||
}
|
|
||||||
if s.opts.streamInt == nil {
|
if s.opts.streamInt == nil {
|
||||||
appErr = sd.Handler(server, ss)
|
appErr = sd.Handler(srv.server, ss)
|
||||||
} else {
|
} else {
|
||||||
info := &StreamServerInfo{
|
info := &StreamServerInfo{
|
||||||
FullMethod: stream.Method(),
|
FullMethod: stream.Method(),
|
||||||
IsClientStream: sd.ClientStreams,
|
IsClientStream: sd.ClientStreams,
|
||||||
IsServerStream: sd.ServerStreams,
|
IsServerStream: sd.ServerStreams,
|
||||||
}
|
}
|
||||||
appErr = s.opts.streamInt(server, ss, info, sd.Handler)
|
appErr = s.opts.streamInt(srv.server, ss, info, sd.Handler)
|
||||||
}
|
}
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
appStatus, ok := status.FromError(appErr)
|
if err, ok := appErr.(*rpcError); ok {
|
||||||
if !ok {
|
ss.statusCode = err.code
|
||||||
switch err := appErr.(type) {
|
ss.statusDesc = err.desc
|
||||||
case transport.StreamError:
|
} else if err, ok := appErr.(transport.StreamError); ok {
|
||||||
appStatus = status.New(err.Code, err.Desc)
|
ss.statusCode = err.Code
|
||||||
default:
|
ss.statusDesc = err.Desc
|
||||||
appStatus = status.New(convertCode(appErr), appErr.Error())
|
} else {
|
||||||
|
ss.statusCode = convertCode(appErr)
|
||||||
|
ss.statusDesc = appErr.Error()
|
||||||
}
|
}
|
||||||
appErr = appStatus.Err()
|
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
ss.mu.Lock()
|
ss.mu.Lock()
|
||||||
ss.trInfo.tr.LazyLog(stringer(appStatus.Message()), true)
|
if ss.statusCode != codes.OK {
|
||||||
|
ss.trInfo.tr.LazyLog(stringer(ss.statusDesc), true)
|
||||||
ss.trInfo.tr.SetError()
|
ss.trInfo.tr.SetError()
|
||||||
ss.mu.Unlock()
|
} else {
|
||||||
}
|
|
||||||
t.WriteStatus(ss.s, appStatus)
|
|
||||||
// TODO: Should we log an error from WriteStatus here and below?
|
|
||||||
return appErr
|
|
||||||
}
|
|
||||||
if trInfo != nil {
|
|
||||||
ss.mu.Lock()
|
|
||||||
ss.trInfo.tr.LazyLog(stringer("OK"), false)
|
ss.trInfo.tr.LazyLog(stringer("OK"), false)
|
||||||
|
}
|
||||||
ss.mu.Unlock()
|
ss.mu.Unlock()
|
||||||
}
|
}
|
||||||
return t.WriteStatus(ss.s, status.New(codes.OK, ""))
|
errWrite := t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
|
||||||
|
if ss.statusCode != codes.OK {
|
||||||
|
return Errorf(ss.statusCode, ss.statusDesc)
|
||||||
|
}
|
||||||
|
return errWrite
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -913,7 +867,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
|
errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
|
||||||
if err := t.WriteStatus(stream, status.New(codes.InvalidArgument, errDesc)); err != nil {
|
if err := t.WriteStatus(stream, codes.InvalidArgument, errDesc); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
@ -929,16 +883,12 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
method := sm[pos+1:]
|
method := sm[pos+1:]
|
||||||
srv, ok := s.m[service]
|
srv, ok := s.m[service]
|
||||||
if !ok {
|
if !ok {
|
||||||
if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil {
|
|
||||||
s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
errDesc := fmt.Sprintf("unknown service %v", service)
|
errDesc := fmt.Sprintf("unknown service %v", service)
|
||||||
if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
|
if err := t.WriteStatus(stream, codes.Unimplemented, errDesc); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
@ -963,12 +913,8 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
}
|
}
|
||||||
if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil {
|
|
||||||
s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
errDesc := fmt.Sprintf("unknown method %v", method)
|
errDesc := fmt.Sprintf("unknown method %v", method)
|
||||||
if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
|
if err := t.WriteStatus(stream, codes.Unimplemented, errDesc); err != nil {
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
trInfo.tr.SetError()
|
trInfo.tr.SetError()
|
||||||
|
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -45,7 +46,6 @@ import (
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
"google.golang.org/grpc/transport"
|
"google.golang.org/grpc/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -151,7 +151,6 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
ctx = newContextWithRPCInfo(ctx)
|
|
||||||
sh := cc.dopts.copts.StatsHandler
|
sh := cc.dopts.copts.StatsHandler
|
||||||
if sh != nil {
|
if sh != nil {
|
||||||
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method})
|
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method})
|
||||||
|
@ -179,7 +178,7 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
t, put, err = cc.getTransport(ctx, gopts)
|
t, put, err = cc.getTransport(ctx, gopts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO(zhaoq): Probably revisit the error handling.
|
// TODO(zhaoq): Probably revisit the error handling.
|
||||||
if _, ok := status.FromError(err); ok {
|
if _, ok := err.(*rpcError); ok {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err == errConnClosing || err == errConnUnavailable {
|
if err == errConnClosing || err == errConnUnavailable {
|
||||||
|
@ -194,17 +193,14 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
|
|
||||||
s, err = t.NewStream(ctx, callHdr)
|
s, err = t.NewStream(ctx, callHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if _, ok := err.(transport.ConnectionError); ok && put != nil {
|
|
||||||
// If error is connection error, transport was sending data on wire,
|
|
||||||
// and we are not sure if anything has been sent on wire.
|
|
||||||
// If error is not connection error, we are sure nothing has been sent.
|
|
||||||
updateRPCInfoInContext(ctx, rpcInfo{bytesSent: true, bytesReceived: false})
|
|
||||||
}
|
|
||||||
if put != nil {
|
if put != nil {
|
||||||
put()
|
put()
|
||||||
put = nil
|
put = nil
|
||||||
}
|
}
|
||||||
if _, ok := err.(transport.ConnectionError); (ok || err == transport.ErrStreamDrain) && !c.failFast {
|
if _, ok := err.(transport.ConnectionError); ok || err == transport.ErrStreamDrain {
|
||||||
|
if c.failFast {
|
||||||
|
return nil, toRPCErr(err)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, toRPCErr(err)
|
return nil, toRPCErr(err)
|
||||||
|
@ -218,7 +214,6 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
codec: cc.dopts.codec,
|
codec: cc.dopts.codec,
|
||||||
cp: cc.dopts.cp,
|
cp: cc.dopts.cp,
|
||||||
dc: cc.dopts.dc,
|
dc: cc.dopts.dc,
|
||||||
maxMsgSize: cc.dopts.maxMsgSize,
|
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
|
|
||||||
put: put,
|
put: put,
|
||||||
|
@ -241,13 +236,14 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
select {
|
select {
|
||||||
case <-t.Error():
|
case <-t.Error():
|
||||||
// Incur transport error, simply exit.
|
// Incur transport error, simply exit.
|
||||||
case <-cc.ctx.Done():
|
|
||||||
cs.finish(ErrClientConnClosing)
|
|
||||||
cs.closeTransportStream(ErrClientConnClosing)
|
|
||||||
case <-s.Done():
|
case <-s.Done():
|
||||||
// TODO: The trace of the RPC is terminated here when there is no pending
|
// TODO: The trace of the RPC is terminated here when there is no pending
|
||||||
// I/O, which is probably not the optimal solution.
|
// I/O, which is probably not the optimal solution.
|
||||||
cs.finish(s.Status().Err())
|
if s.StatusCode() == codes.OK {
|
||||||
|
cs.finish(nil)
|
||||||
|
} else {
|
||||||
|
cs.finish(Errorf(s.StatusCode(), "%s", s.StatusDesc()))
|
||||||
|
}
|
||||||
cs.closeTransportStream(nil)
|
cs.closeTransportStream(nil)
|
||||||
case <-s.GoAway():
|
case <-s.GoAway():
|
||||||
cs.finish(errConnDrain)
|
cs.finish(errConnDrain)
|
||||||
|
@ -273,7 +269,6 @@ type clientStream struct {
|
||||||
cp Compressor
|
cp Compressor
|
||||||
cbuf *bytes.Buffer
|
cbuf *bytes.Buffer
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
maxMsgSize int
|
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
tracing bool // set to EnableTracing when the clientStream is created.
|
tracing bool // set to EnableTracing when the clientStream is created.
|
||||||
|
@ -281,7 +276,6 @@ type clientStream struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
put func()
|
put func()
|
||||||
closed bool
|
closed bool
|
||||||
finished bool
|
|
||||||
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
||||||
// and is set to nil when the clientStream's finish method is called.
|
// and is set to nil when the clientStream's finish method is called.
|
||||||
trInfo traceInfo
|
trInfo traceInfo
|
||||||
|
@ -367,13 +361,28 @@ func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil && cs.statsHandler != nil {
|
||||||
|
// Only generate End if err != nil.
|
||||||
|
// If err == nil, it's not the last RecvMsg.
|
||||||
|
// The last RecvMsg gets either an RPC error or io.EOF.
|
||||||
|
end := &stats.End{
|
||||||
|
Client: true,
|
||||||
|
EndTime: time.Now(),
|
||||||
|
}
|
||||||
|
if err != io.EOF {
|
||||||
|
end.Error = toRPCErr(err)
|
||||||
|
}
|
||||||
|
cs.statsHandler.HandleRPC(cs.statsCtx, end)
|
||||||
|
}
|
||||||
|
}()
|
||||||
var inPayload *stats.InPayload
|
var inPayload *stats.InPayload
|
||||||
if cs.statsHandler != nil {
|
if cs.statsHandler != nil {
|
||||||
inPayload = &stats.InPayload{
|
inPayload = &stats.InPayload{
|
||||||
Client: true,
|
Client: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, cs.maxMsgSize, inPayload)
|
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32, inPayload)
|
||||||
defer func() {
|
defer func() {
|
||||||
// err != nil indicates the termination of the stream.
|
// err != nil indicates the termination of the stream.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -396,30 +405,30 @@ func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||||
}
|
}
|
||||||
// Special handling for client streaming rpc.
|
// Special handling for client streaming rpc.
|
||||||
// This recv expects EOF or errors, so we don't collect inPayload.
|
// This recv expects EOF or errors, so we don't collect inPayload.
|
||||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, cs.maxMsgSize, nil)
|
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, math.MaxInt32, nil)
|
||||||
cs.closeTransportStream(err)
|
cs.closeTransportStream(err)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
|
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
if se := cs.s.Status().Err(); se != nil {
|
if cs.s.StatusCode() == codes.OK {
|
||||||
return se
|
|
||||||
}
|
|
||||||
cs.finish(err)
|
cs.finish(err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc())
|
||||||
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
if _, ok := err.(transport.ConnectionError); !ok {
|
if _, ok := err.(transport.ConnectionError); !ok {
|
||||||
cs.closeTransportStream(err)
|
cs.closeTransportStream(err)
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
if statusErr := cs.s.Status().Err(); statusErr != nil {
|
if cs.s.StatusCode() == codes.OK {
|
||||||
return statusErr
|
|
||||||
}
|
|
||||||
// Returns io.EOF to indicate the end of the stream.
|
// Returns io.EOF to indicate the end of the stream.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
return Errorf(cs.s.StatusCode(), "%s", cs.s.StatusDesc())
|
||||||
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,39 +461,20 @@ func (cs *clientStream) closeTransportStream(err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) finish(err error) {
|
func (cs *clientStream) finish(err error) {
|
||||||
cs.mu.Lock()
|
|
||||||
defer cs.mu.Unlock()
|
|
||||||
if cs.finished {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cs.finished = true
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if cs.cancel != nil {
|
if cs.cancel != nil {
|
||||||
cs.cancel()
|
cs.cancel()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
cs.mu.Lock()
|
||||||
|
defer cs.mu.Unlock()
|
||||||
for _, o := range cs.opts {
|
for _, o := range cs.opts {
|
||||||
o.after(&cs.c)
|
o.after(&cs.c)
|
||||||
}
|
}
|
||||||
if cs.put != nil {
|
if cs.put != nil {
|
||||||
updateRPCInfoInContext(cs.s.Context(), rpcInfo{
|
|
||||||
bytesSent: cs.s.BytesSent(),
|
|
||||||
bytesReceived: cs.s.BytesReceived(),
|
|
||||||
})
|
|
||||||
cs.put()
|
cs.put()
|
||||||
cs.put = nil
|
cs.put = nil
|
||||||
}
|
}
|
||||||
if cs.statsHandler != nil {
|
|
||||||
end := &stats.End{
|
|
||||||
Client: true,
|
|
||||||
EndTime: time.Now(),
|
|
||||||
}
|
|
||||||
if err != io.EOF {
|
|
||||||
// end.Error is nil if the RPC finished successfully.
|
|
||||||
end.Error = toRPCErr(err)
|
|
||||||
}
|
|
||||||
cs.statsHandler.HandleRPC(cs.statsCtx, end)
|
|
||||||
}
|
|
||||||
if !cs.tracing {
|
if !cs.tracing {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -529,6 +519,8 @@ type serverStream struct {
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
cbuf *bytes.Buffer
|
cbuf *bytes.Buffer
|
||||||
maxMsgSize int
|
maxMsgSize int
|
||||||
|
statusCode codes.Code
|
||||||
|
statusDesc string
|
||||||
trInfo *traceInfo
|
trInfo *traceInfo
|
||||||
|
|
||||||
statsHandler stats.Handler
|
statsHandler stats.Handler
|
||||||
|
|
Loading…
Reference in New Issue