blob: 01cdedd21111bbdb10da73124318aae4423d9f5a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package grpc
import (
"context"
"crypto/tls"
"errors"
"net"
"net/http"
"github.com/go-chassis/go-chassis/core/client"
"github.com/go-chassis/go-chassis/core/common"
"github.com/go-chassis/go-chassis/core/invocation"
"github.com/go-chassis/go-chassis/pkg/util/httputil"
"golang.org/x/net/http2"
)
const (
//SchemaHTTP represents the http schema
SchemaHTTP = "http"
//SchemaHTTPS represents the https schema
SchemaHTTPS = "https"
)
//ErrInvalidResp invalid input
var (
ErrInvalidResp = errors.New("rest consumer response arg is not *http.Response type")
//ErrCanceled means Request is canceled by context management
ErrCanceled = errors.New("request cancelled")
)
//Client is a grpc client
type Client struct {
c *http.Client
opts client.Options
}
var protocolName = "grpc"
func init() {
client.InstallPlugin(protocolName, NewClient)
}
//NewClient return a new client of grpc
func NewClient(opts client.Options) (client.ProtocolClient, error) {
client := &http.Client{}
if opts.TLSConfig != nil {
client.Transport = &http2.Transport{
TLSClientConfig: opts.TLSConfig,
}
} else {
client.Transport = &http2.Transport{
AllowHTTP: true,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
}}
}
return &Client{
c: client,
opts: opts,
}, nil
}
func (c *Client) contextToHeader(ctx context.Context, req *http.Request) {
for k, v := range common.FromContext(ctx) {
req.Header.Set(k, v)
}
}
//Call is a method which uses grpc protocol to transfer invocation
func (c *Client) Call(ctx context.Context, addr string, inv *invocation.Invocation, rsp interface{}) error {
var err error
reqSend, err := httputil.HTTPRequest(inv)
if err != nil {
return err
}
resp, ok := rsp.(*http.Response)
if !ok {
return ErrInvalidResp
}
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if c.opts.TLSConfig != nil {
reqSend.URL.Scheme = SchemaHTTPS
} else {
reqSend.URL.Scheme = SchemaHTTP
}
if addr != "" {
reqSend.URL.Host = addr
}
var temp *http.Response
errChan := make(chan error, 1)
go func() {
temp, err = c.c.Do(reqSend)
errChan <- err
}()
select {
case <-ctx.Done():
err = ErrCanceled
case err = <-errChan:
if err == nil {
*resp = *temp
}
}
return err
}
//String return name
func (c *Client) String() string {
return protocolName
}
//Close close the conn
func (c *Client) Close() error {
return nil
}
// ReloadConfigs reload config
func (c *Client) ReloadConfigs(opts client.Options) {
c.opts = client.EqualOpts(c.opts, opts)
c.c.Timeout = opts.Timeout
}
// GetOptions return opts
func (c *Client) GetOptions() client.Options {
return c.opts
}