-
Notifications
You must be signed in to change notification settings - Fork 8.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Expose new Admin endpoint on orderer for the channel participation API (
#1939) * Split out common server logic for http endpoints Move the common logic for http server/handler and TLS authentication used by the operations.System into a separate fabhttp.Server and refactor the operations.System to embed the server. This is being done to allow reuse of the common code for the orderer Admin endpoint, which will host the Channel Participation API. FAB-18246 Signed-off-by: Will Lahti <wtlahti@us.ibm.com> * Expose new Admin endpoint on orderer Move the channel participation API to use this endpoint. FAB-18246 #done Signed-off-by: Will Lahti <wtlahti@us.ibm.com>
- Loading branch information
Showing
16 changed files
with
716 additions
and
155 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* | ||
Copyright IBM Corp All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package fabhttp_test | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"io/ioutil" | ||
"net/http" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/hyperledger/fabric/common/crypto/tlsgen" | ||
. "github.com/onsi/ginkgo" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestFabHTTP(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "FabHTTP Suite") | ||
} | ||
|
||
func generateCertificates(tempDir string) { | ||
serverCA, err := tlsgen.NewCA() | ||
Expect(err).NotTo(HaveOccurred()) | ||
err = ioutil.WriteFile(filepath.Join(tempDir, "server-ca.pem"), serverCA.CertBytes(), 0640) | ||
Expect(err).NotTo(HaveOccurred()) | ||
serverKeyPair, err := serverCA.NewServerCertKeyPair("127.0.0.1") | ||
Expect(err).NotTo(HaveOccurred()) | ||
err = ioutil.WriteFile(filepath.Join(tempDir, "server-cert.pem"), serverKeyPair.Cert, 0640) | ||
Expect(err).NotTo(HaveOccurred()) | ||
err = ioutil.WriteFile(filepath.Join(tempDir, "server-key.pem"), serverKeyPair.Key, 0640) | ||
Expect(err).NotTo(HaveOccurred()) | ||
|
||
clientCA, err := tlsgen.NewCA() | ||
Expect(err).NotTo(HaveOccurred()) | ||
err = ioutil.WriteFile(filepath.Join(tempDir, "client-ca.pem"), clientCA.CertBytes(), 0640) | ||
Expect(err).NotTo(HaveOccurred()) | ||
clientKeyPair, err := clientCA.NewClientCertKeyPair() | ||
Expect(err).NotTo(HaveOccurred()) | ||
err = ioutil.WriteFile(filepath.Join(tempDir, "client-cert.pem"), clientKeyPair.Cert, 0640) | ||
Expect(err).NotTo(HaveOccurred()) | ||
err = ioutil.WriteFile(filepath.Join(tempDir, "client-key.pem"), clientKeyPair.Key, 0640) | ||
Expect(err).NotTo(HaveOccurred()) | ||
} | ||
|
||
func newHTTPClient(tlsDir string, withClientCert bool) *http.Client { | ||
clientCertPool := x509.NewCertPool() | ||
caCert, err := ioutil.ReadFile(filepath.Join(tlsDir, "server-ca.pem")) | ||
Expect(err).NotTo(HaveOccurred()) | ||
clientCertPool.AppendCertsFromPEM(caCert) | ||
|
||
tlsClientConfig := &tls.Config{ | ||
RootCAs: clientCertPool, | ||
} | ||
if withClientCert { | ||
clientCert, err := tls.LoadX509KeyPair( | ||
filepath.Join(tlsDir, "client-cert.pem"), | ||
filepath.Join(tlsDir, "client-key.pem"), | ||
) | ||
Expect(err).NotTo(HaveOccurred()) | ||
tlsClientConfig.Certificates = []tls.Certificate{clientCert} | ||
} | ||
|
||
return &http.Client{ | ||
Transport: &http.Transport{ | ||
TLSClientConfig: tlsClientConfig, | ||
}, | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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 @@ | ||
/* | ||
Copyright IBM Corp All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package fabhttp | ||
|
||
import ( | ||
"context" | ||
"crypto/tls" | ||
"net" | ||
"net/http" | ||
"os" | ||
"time" | ||
|
||
"github.com/hyperledger/fabric/common/flogging" | ||
"github.com/hyperledger/fabric/common/util" | ||
"github.com/hyperledger/fabric/core/middleware" | ||
) | ||
|
||
//go:generate counterfeiter -o fakes/logger.go -fake-name Logger . Logger | ||
|
||
type Logger interface { | ||
Warn(args ...interface{}) | ||
Warnf(template string, args ...interface{}) | ||
} | ||
|
||
type Options struct { | ||
Logger Logger | ||
ListenAddress string | ||
TLS TLS | ||
} | ||
|
||
type Server struct { | ||
logger Logger | ||
options Options | ||
httpServer *http.Server | ||
mux *http.ServeMux | ||
addr string | ||
} | ||
|
||
func NewServer(o Options) *Server { | ||
logger := o.Logger | ||
if logger == nil { | ||
logger = flogging.MustGetLogger("fabhttp") | ||
} | ||
|
||
server := &Server{ | ||
logger: logger, | ||
options: o, | ||
} | ||
|
||
server.initializeServer() | ||
|
||
return server | ||
} | ||
|
||
func (s *Server) Run(signals <-chan os.Signal, ready chan<- struct{}) error { | ||
err := s.Start() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
close(ready) | ||
|
||
<-signals | ||
return s.Stop() | ||
} | ||
|
||
func (s *Server) Start() error { | ||
listener, err := s.Listen() | ||
if err != nil { | ||
return err | ||
} | ||
s.addr = listener.Addr().String() | ||
|
||
go s.httpServer.Serve(listener) | ||
|
||
return nil | ||
} | ||
|
||
func (s *Server) Stop() error { | ||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
defer cancel() | ||
|
||
return s.httpServer.Shutdown(ctx) | ||
} | ||
|
||
func (s *Server) initializeServer() { | ||
s.mux = http.NewServeMux() | ||
s.httpServer = &http.Server{ | ||
Addr: s.options.ListenAddress, | ||
Handler: s.mux, | ||
ReadTimeout: 10 * time.Second, | ||
WriteTimeout: 2 * time.Minute, | ||
} | ||
} | ||
|
||
func (s *Server) HandlerChain(h http.Handler, secure bool) http.Handler { | ||
if secure { | ||
return middleware.NewChain(middleware.RequireCert(), middleware.WithRequestID(util.GenerateUUID)).Handler(h) | ||
} | ||
return middleware.NewChain(middleware.WithRequestID(util.GenerateUUID)).Handler(h) | ||
} | ||
|
||
// RegisterHandler registers into the ServeMux a handler chain that borrows | ||
// its security properties from the fabhttp.Server. This method is thread | ||
// safe because ServeMux.Handle() is thread safe, and options are immutable. | ||
// This method can be called either before or after Server.Start(). If the | ||
// pattern exists the method panics. | ||
func (s *Server) RegisterHandler(pattern string, handler http.Handler, secure bool) { | ||
s.mux.Handle( | ||
pattern, | ||
s.HandlerChain( | ||
handler, | ||
secure, | ||
), | ||
) | ||
} | ||
|
||
func (s *Server) Listen() (net.Listener, error) { | ||
listener, err := net.Listen("tcp", s.options.ListenAddress) | ||
if err != nil { | ||
return nil, err | ||
} | ||
tlsConfig, err := s.options.TLS.Config() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if tlsConfig != nil { | ||
listener = tls.NewListener(listener, tlsConfig) | ||
} | ||
return listener, nil | ||
} | ||
|
||
func (s *Server) Addr() string { | ||
return s.addr | ||
} | ||
|
||
func (s *Server) Log(keyvals ...interface{}) error { | ||
s.logger.Warn(keyvals...) | ||
return nil | ||
} |
Oops, something went wrong.