github.com/stackdocker/rkt@v0.10.1-0.20151109095037-1aa827478248/Godeps/_workspace/src/google.golang.org/grpc/call.go (about)

     1  /*
     2   *
     3   * Copyright 2014, Google Inc.
     4   * All rights reserved.
     5   *
     6   * Redistribution and use in source and binary forms, with or without
     7   * modification, are permitted provided that the following conditions are
     8   * met:
     9   *
    10   *     * Redistributions of source code must retain the above copyright
    11   * notice, this list of conditions and the following disclaimer.
    12   *     * Redistributions in binary form must reproduce the above
    13   * copyright notice, this list of conditions and the following disclaimer
    14   * in the documentation and/or other materials provided with the
    15   * distribution.
    16   *     * Neither the name of Google Inc. nor the names of its
    17   * contributors may be used to endorse or promote products derived from
    18   * this software without specific prior written permission.
    19   *
    20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    21   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    22   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    23   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    24   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    25   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    26   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    28   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    29   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    30   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    31   *
    32   */
    33  
    34  package grpc
    35  
    36  import (
    37  	"io"
    38  	"time"
    39  
    40  	"github.com/coreos/rkt/Godeps/_workspace/src/golang.org/x/net/context"
    41  	"github.com/coreos/rkt/Godeps/_workspace/src/golang.org/x/net/trace"
    42  	"github.com/coreos/rkt/Godeps/_workspace/src/google.golang.org/grpc/codes"
    43  	"github.com/coreos/rkt/Godeps/_workspace/src/google.golang.org/grpc/metadata"
    44  	"github.com/coreos/rkt/Godeps/_workspace/src/google.golang.org/grpc/transport"
    45  )
    46  
    47  // recvResponse receives and parses an RPC response.
    48  // On error, it returns the error and indicates whether the call should be retried.
    49  //
    50  // TODO(zhaoq): Check whether the received message sequence is valid.
    51  func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error {
    52  	// Try to acquire header metadata from the server if there is any.
    53  	var err error
    54  	c.headerMD, err = stream.Header()
    55  	if err != nil {
    56  		return err
    57  	}
    58  	p := &parser{s: stream}
    59  	for {
    60  		if err = recv(p, codec, reply); err != nil {
    61  			if err == io.EOF {
    62  				break
    63  			}
    64  			return err
    65  		}
    66  	}
    67  	c.trailerMD = stream.Trailer()
    68  	return nil
    69  }
    70  
    71  // sendRequest writes out various information of an RPC such as Context and Message.
    72  func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
    73  	stream, err := t.NewStream(ctx, callHdr)
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	defer func() {
    78  		if err != nil {
    79  			if _, ok := err.(transport.ConnectionError); !ok {
    80  				t.CloseStream(stream, err)
    81  			}
    82  		}
    83  	}()
    84  	// TODO(zhaoq): Support compression.
    85  	outBuf, err := encode(codec, args, compressionNone)
    86  	if err != nil {
    87  		return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err)
    88  	}
    89  	err = t.Write(stream, outBuf, opts)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  	// Sent successfully.
    94  	return stream, nil
    95  }
    96  
    97  // callInfo contains all related configuration and information about an RPC.
    98  type callInfo struct {
    99  	failFast  bool
   100  	headerMD  metadata.MD
   101  	trailerMD metadata.MD
   102  	traceInfo traceInfo // in trace.go
   103  }
   104  
   105  // Invoke is called by the generated code. It sends the RPC request on the
   106  // wire and returns after response is received.
   107  func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) {
   108  	var c callInfo
   109  	for _, o := range opts {
   110  		if err := o.before(&c); err != nil {
   111  			return toRPCErr(err)
   112  		}
   113  	}
   114  	defer func() {
   115  		for _, o := range opts {
   116  			o.after(&c)
   117  		}
   118  	}()
   119  
   120  	if EnableTracing {
   121  		c.traceInfo.tr = trace.New("grpc.Sent."+methodFamily(method), method)
   122  		defer c.traceInfo.tr.Finish()
   123  		c.traceInfo.firstLine.client = true
   124  		if deadline, ok := ctx.Deadline(); ok {
   125  			c.traceInfo.firstLine.deadline = deadline.Sub(time.Now())
   126  		}
   127  		c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
   128  		// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
   129  		defer func() {
   130  			if err != nil {
   131  				c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
   132  				c.traceInfo.tr.SetError()
   133  			}
   134  		}()
   135  	}
   136  	callHdr := &transport.CallHdr{
   137  		Host:   cc.authority,
   138  		Method: method,
   139  	}
   140  	topts := &transport.Options{
   141  		Last:  true,
   142  		Delay: false,
   143  	}
   144  	var (
   145  		ts      int   // track the transport sequence number
   146  		lastErr error // record the error that happened
   147  	)
   148  	for {
   149  		var (
   150  			err    error
   151  			t      transport.ClientTransport
   152  			stream *transport.Stream
   153  		)
   154  		// TODO(zhaoq): Need a formal spec of retry strategy for non-failfast rpcs.
   155  		if lastErr != nil && c.failFast {
   156  			return toRPCErr(lastErr)
   157  		}
   158  		t, ts, err = cc.wait(ctx, ts)
   159  		if err != nil {
   160  			if lastErr != nil {
   161  				// This was a retry; return the error from the last attempt.
   162  				return toRPCErr(lastErr)
   163  			}
   164  			return toRPCErr(err)
   165  		}
   166  		if c.traceInfo.tr != nil {
   167  			c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
   168  		}
   169  		stream, err = sendRequest(ctx, cc.dopts.codec, callHdr, t, args, topts)
   170  		if err != nil {
   171  			if _, ok := err.(transport.ConnectionError); ok {
   172  				lastErr = err
   173  				continue
   174  			}
   175  			if lastErr != nil {
   176  				return toRPCErr(lastErr)
   177  			}
   178  			return toRPCErr(err)
   179  		}
   180  		// Receive the response
   181  		lastErr = recvResponse(cc.dopts.codec, t, &c, stream, reply)
   182  		if _, ok := lastErr.(transport.ConnectionError); ok {
   183  			continue
   184  		}
   185  		if c.traceInfo.tr != nil {
   186  			c.traceInfo.tr.LazyLog(&payload{sent: false, msg: reply}, true)
   187  		}
   188  		t.CloseStream(stream, lastErr)
   189  		if lastErr != nil {
   190  			return toRPCErr(lastErr)
   191  		}
   192  		return Errorf(stream.StatusCode(), stream.StatusDesc())
   193  	}
   194  }