This repository has been archived by the owner on Mar 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from im-kulikov/prepare-new-release
Prepare new release v0.13.0
- Loading branch information
Showing
8 changed files
with
695 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
package web | ||
|
||
import ( | ||
"fmt" | ||
"net" | ||
"time" | ||
|
||
"github.com/im-kulikov/helium/internal" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
type ( | ||
gRPC struct { | ||
skipErrors bool | ||
address string | ||
network string | ||
server *grpc.Server | ||
shutdownTimeout time.Duration | ||
} | ||
|
||
// GRPCOption allows changing default gRPC | ||
// service settings. | ||
GRPCOption func(g *gRPC) | ||
) | ||
|
||
const ( | ||
// ErrEmptyGRPC is raised when called NewGRPCService | ||
// or gRPC methods with empty grpc.Server. | ||
ErrEmptyGRPC = internal.Error("empty gRPC server") | ||
|
||
// ErrEmptyGRPCAddress is raised when passed empty address to NewGRPCService. | ||
ErrEmptyGRPCAddress = internal.Error("empty gRPC address") | ||
) | ||
|
||
// GRPCSkipErrors allows to skip any errors | ||
func GRPCSkipErrors() GRPCOption { | ||
return func(g *gRPC) { | ||
g.skipErrors = true | ||
} | ||
} | ||
|
||
// GRPCListenAddress allows to change network for net.Listener. | ||
func GRPCListenAddress(addr string) GRPCOption { | ||
return func(g *gRPC) { | ||
g.address = addr | ||
} | ||
} | ||
|
||
// GRPCListenNetwork allows to change default (tcp) | ||
// network for net.Listener. | ||
func GRPCListenNetwork(network string) GRPCOption { | ||
return func(g *gRPC) { | ||
g.network = network | ||
} | ||
} | ||
|
||
// GRPCShutdownTimeout changes default shutdown timeout. | ||
func GRPCShutdownTimeout(v time.Duration) GRPCOption { | ||
return func(g *gRPC) { | ||
g.shutdownTimeout = v | ||
} | ||
} | ||
|
||
// NewGRPCService creates gRPC service with passed gRPC options. | ||
// If something went wrong it returns an error. | ||
func NewGRPCService(serve *grpc.Server, opts ...GRPCOption) (Service, error) { | ||
if serve == nil { | ||
return nil, ErrEmptyGRPC | ||
} | ||
|
||
s := &gRPC{ | ||
server: serve, | ||
network: "tcp", | ||
skipErrors: false, | ||
shutdownTimeout: time.Second * 30, | ||
} | ||
|
||
for i := range opts { | ||
opts[i](s) | ||
} | ||
|
||
if s.address == "" { | ||
return nil, ErrEmptyGRPCAddress | ||
} | ||
|
||
return s, nil | ||
} | ||
|
||
// Start tries to start gRPC service. | ||
// If something went wrong it returns an error. | ||
// If could not start server panics. | ||
func (g *gRPC) Start() error { | ||
var ( | ||
err error | ||
lis net.Listener | ||
) | ||
|
||
if g.server == nil { | ||
return g.catch(ErrEmptyHTTPServer) | ||
} else if lis, err = net.Listen(g.network, g.address); err != nil { | ||
return g.catch(err) | ||
} | ||
|
||
go func() { | ||
if err := g.catch(g.server.Serve(lis)); err != nil { | ||
panic(fmt.Sprintf("could not start grpc.Server: %v", err)) | ||
} | ||
}() | ||
|
||
return nil | ||
} | ||
|
||
// Stop tries to stop gRPC service. | ||
func (g *gRPC) Stop() error { | ||
if g.server == nil { | ||
return g.catch(ErrEmptyHTTPServer) | ||
} | ||
|
||
g.server.GracefulStop() | ||
return nil | ||
} | ||
|
||
func (g *gRPC) catch(err error) error { | ||
if g.skipErrors || err == grpc.ErrServerStopped { | ||
return nil | ||
} | ||
|
||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package web | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/im-kulikov/helium/internal" | ||
) | ||
|
||
type ( | ||
httpService struct { | ||
skipErrors bool | ||
address string | ||
network string | ||
server *http.Server | ||
shutdownTimeout time.Duration | ||
} | ||
|
||
// HTTPOption interface that allows | ||
// to change default http-service options. | ||
HTTPOption func(s *httpService) | ||
) | ||
|
||
const ( | ||
// ErrEmptyHTTPServer is raised when called New | ||
// or httpService methods with empty http.Server. | ||
ErrEmptyHTTPServer = internal.Error("empty http server") | ||
|
||
// ErrEmptyHTTPAddress is raised when passed empty address to NewHTTPService. | ||
ErrEmptyHTTPAddress = internal.Error("empty http address") | ||
) | ||
|
||
// HTTPShutdownTimeout changes default shutdown timeout. | ||
func HTTPShutdownTimeout(v time.Duration) HTTPOption { | ||
return func(s *httpService) { | ||
s.shutdownTimeout = v | ||
} | ||
} | ||
|
||
// HTTPListenNetwork allows to change default (tcp) | ||
// network for net.Listener. | ||
func HTTPListenNetwork(network string) HTTPOption { | ||
return func(s *httpService) { | ||
s.network = network | ||
} | ||
} | ||
|
||
// HTTPListenAddress allows to change network for net.Listener. | ||
// By default it takes address from http.Server. | ||
func HTTPListenAddress(address string) HTTPOption { | ||
return func(s *httpService) { | ||
s.address = address | ||
} | ||
} | ||
|
||
// HTTPSkipErrors allows to skip any errors | ||
func HTTPSkipErrors() HTTPOption { | ||
return func(s *httpService) { | ||
s.skipErrors = true | ||
} | ||
} | ||
|
||
// NewHTTPService creates Service from http.Server and HTTPOption's. | ||
func NewHTTPService(serve *http.Server, opts ...HTTPOption) (Service, error) { | ||
if serve == nil { | ||
return nil, ErrEmptyHTTPServer | ||
} | ||
|
||
s := &httpService{ | ||
skipErrors: false, | ||
server: serve, | ||
network: "tcp", | ||
address: serve.Addr, | ||
shutdownTimeout: time.Second * 30, | ||
} | ||
|
||
for i := range opts { | ||
opts[i](s) | ||
} | ||
|
||
if s.address == "" { | ||
return nil, ErrEmptyHTTPAddress | ||
} | ||
|
||
return s, nil | ||
} | ||
|
||
// Start runs http.Server and returns error | ||
// if something went wrong. | ||
func (s *httpService) Start() error { | ||
var ( | ||
err error | ||
lis net.Listener | ||
) | ||
|
||
if s.server == nil { | ||
return s.catch(ErrEmptyHTTPServer) | ||
} else if lis, err = net.Listen(s.network, s.address); err != nil { | ||
return s.catch(err) | ||
} | ||
|
||
go func() { | ||
var err error | ||
|
||
switch { | ||
case s.server.TLSConfig == nil: | ||
err = s.server.Serve(lis) | ||
default: | ||
// provide cert and key from TLSConfig | ||
err = s.server.ServeTLS(lis, "", "") | ||
} | ||
|
||
// ignores known error | ||
if err = s.catch(err); err != nil { | ||
panic(fmt.Sprintf("could not start http.Server: %v", err)) | ||
} | ||
}() | ||
|
||
return nil | ||
} | ||
|
||
// Stop tries to stop http.Server and returns error | ||
// if something went wrong. | ||
func (s *httpService) Stop() error { | ||
if s.server == nil { | ||
return s.catch(ErrEmptyHTTPServer) | ||
} | ||
|
||
ctx, cancel := context.WithTimeout(context.Background(), s.shutdownTimeout) | ||
defer cancel() | ||
|
||
return s.catch(s.server.Shutdown(ctx)) | ||
} | ||
|
||
func (s *httpService) catch(err error) error { | ||
if s.skipErrors || err == http.ErrServerClosed { | ||
return nil | ||
} | ||
|
||
return err | ||
} |
Oops, something went wrong.