Skip to content

Commit

Permalink
Add documentation to the main module
Browse files Browse the repository at this point in the history
  • Loading branch information
9seconds committed Dec 16, 2020
1 parent 4459d78 commit 3a8fe8b
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 28 deletions.
71 changes: 71 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// httransform is a framework to build a various MITM proxies.
//
// This framework is built using fasthttp
// (https://github.com/valyala/fasthttp) library to provide fast
// processing of HTTP requests. Also, as fasthttp it actively
// uses object pooling to reduce the pressure on GC and possibly
// save a little bit of memory. Unlike its alternative, goproxy
// (https://github.com/elazarl/goproxy), httransform does not have
// disadvantages of using net/http: we can keep header case and order.
//
// This framework provides features to build robust high-performant HTTP
// and HTTPS proxies (with the support of the CONNECT method and TLS
// certificate generation on-the-fly), it supports middleware layers and
// custom executors. It is also able to correctly process different http
// connection upgrades like websocket support.
//
// Layers are middlewares which do preprocess of the requests or
// postprocessing of the response. The thing about layers is that you
// have to define both directions: to executor and from executor. We
// encourage to create layers which know as less as possible about each
// other. If, for example, you raise an error in one layer, this error
// has to be processed only there, other layers should ignore that. This
// framework does not restrict any deviations from this rule, but you'll
// get more simple and clean design if you treat layers as independent
// as possible.
//
// An executor is some function which converts HTTP request to response.
// In the most simple case, an executor is HTTP client. But also, you
// can convert the request to JSON, send it somewhere, get ProtoBuf back
// and convert it to HTTP response. Or you can play with NATS. Or 0mq.
// Or RabbitMQ. You got the idea. Executor defines the function which
// converts HTTP request to HTTP response. If connection upgrade is
// required, this is a responsibility of the executor to make it.
//
// If any of your layers creates an error, executor won't be called.
// HTTP response will be converted to 500 response and error would be
// propagated by the chain of layers back.
//
// A Mental Scheme of the Request
//
// Just take a look here:
//
// HTTP interface Layer 1 Layer 2
// +----------------+ ************** **************
// | | * * * * ==============
// ---> | HTTP request | ===> * OnRequest * ===> * OnRequest * ===> = =
// | | * * * * = =
// +----------------+ ************** ************** = Executor =
// | | * * * * = =
// <--- | HTTP response | <=== * OnResponse * <=== * OnResponse * <=== = =
// | | * * * * ==============
// +----------------+ ************** **************
//
// As you see, the request goes through the all layers forward and
// backward. This is a contract of this package.
//
// Features
//
// 1. Interface of HTTP proxy
//
// 2. Interface of HTTPS proxy (e.g CONNECT method)
//
// 3. CONNECT method can be used to establish plain TCP tunnels. Upgrade
// to TLS is not mandatory.
//
// 4. Both HTTP request and responses can be processed: headers
// are added/removed/changed and so on.
//
// 5. TCP connection upgrade is supported. Websockets are supported. But
// by default you can't interfere: you can just watch.
package httransform
153 changes: 125 additions & 28 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,108 +10,198 @@ import (
)

const (
DefaultConcurrency = 65536
DefaultReadBufferSize = 16 * 1024
DefaultWriteBufferSize = 16 * 1024
DefaultReadTimeout = time.Minute
DefaultWriteTimeout = time.Minute
// DefaultConcurrency defines a default number of concurrently
// managed requests processed by a proxy.
DefaultConcurrency = 65536

// DefaultReadBufferSize defines a default size of the buffer to use
// on reading client requests.
DefaultReadBufferSize = 16 * 1024

// DefaultWriteBufferSize defines a default size of the buffer to
// use on writing to client socket.
DefaultWriteBufferSize = 16 * 1024

// DefaultReadTimeout defines a default timeout for reading of
// client request.
DefaultReadTimeout = time.Minute

// DefaultWriteTimeout defines a default timeout for writing
// to client.
DefaultWriteTimeout = time.Minute

// DefaultTCPKeepAlivePeriod defines a default time period for TCP
// keepalive probes.
DefaultTCPKeepAlivePeriod = 30 * time.Second

// DefaultMaxRequestBodySize defines a max size of the request body.
DefaultMaxRequestBodySize = 1024 * 1024 * 100
)

// ServerOpts defines server options to use.
//
// Please pay attention that each field is optional. We do provide sane
// defaults if you do not want to pass anything there.
type ServerOpts struct {
Concurrency uint
ReadBufferSize uint
WriteBufferSize uint
MaxRequestBodySize uint
ReadTimeout time.Duration
WriteTimeout time.Duration
TCPKeepAlivePeriod time.Duration
// Concurrency defines a number of concurrently managed requests.
Concurrency uint

// ReadBufferSize defines a size of the buffer allocated for reading
// from client socket.
ReadBufferSize uint

// WriteBufferSize defines a size of the buffer allocated for
// writing to a client socket.
WriteBufferSize uint

// MaxRequestBodySize defines a max size of the request body.
MaxRequestBodySize uint

// ReadTimeout defines a timeout for reading from client socket.
ReadTimeout time.Duration

// WriteTimeout defines a timeout for writing to client socket.
WriteTimeout time.Duration

// TCPKeepAlivePeriod defines a time period between 2 consequtive
// TCP keepalive probes.
TCPKeepAlivePeriod time.Duration

// EventProcessorFactory defines a factory method which produces
// event processors.
EventProcessorFactory events.ProcessorFactory
TLSCertCA []byte
TLSPrivateKey []byte
Layers []layers.Layer
Executor executor.Executor
Authenticator auth.Interface
TLSSkipVerify bool

// TLSCertCA is a bytes which contains TLS CA certificate. This
// certificate is required for generating fake TLS certifiates for
// websites on TLS connection upgrades.
TLSCertCA []byte

// TLSPrivateKey is a bytes which contains TLS private key. This
// certificate is required for generating fake TLS certifiates for
// websites on TLS connection upgrades.
TLSPrivateKey []byte

// Layers defines a list of layers, middleware which should be used
// by proxy.
Layers []layers.Layer

// Executor defines an executor function which should be used
// to terminate HTTP request and fill HTTP response.
Executor executor.Executor

// Authenticator is an interface which is used to authenticate a
// request.
Authenticator auth.Interface

// TLSSkipVerify defines if we need to verify TLS certifiates we have
// to deal with.
TLSSkipVerify bool
}

// GetConcurrency returns a concurrency paying attention to default
// value.
func (s *ServerOpts) GetConcurrency() int {
if s.Concurrency == 0 {
if s == nil || s.Concurrency == 0 {
return DefaultConcurrency
}

return int(s.Concurrency)
}

// GetReadBufferSize returns a read buffer size paying attention to
// default value.
func (s *ServerOpts) GetReadBufferSize() int {
if s.ReadBufferSize == 0 {
if s == nil || s.ReadBufferSize == 0 {
return DefaultReadBufferSize
}

return int(s.ReadBufferSize)
}

// GetWriteBufferSize returns a write buffer size paying attention to
// default value.
func (s *ServerOpts) GetWriteBufferSize() int {
if s.WriteBufferSize == 0 {
if s == nil || s.WriteBufferSize == 0 {
return DefaultWriteBufferSize
}

return int(s.WriteBufferSize)
}

// GetReadTimeout returns a read timeout paying attention to default
// value.
func (s *ServerOpts) GetReadTimeout() time.Duration {
if s.ReadTimeout == 0 {
if s == nil || s.ReadTimeout == 0 {
return DefaultReadTimeout
}

return s.ReadTimeout
}

// GetWriteTimeout returns a write timeout paying attention to default
// value.
func (s *ServerOpts) GetWriteTimeout() time.Duration {
if s.WriteTimeout == 0 {
if s == nil || s.WriteTimeout == 0 {
return DefaultWriteTimeout
}

return s.WriteTimeout
}

// GetTCPKeepAlivePeriod returns a period for TCP keepalive probes
// paying attention to default value.
func (s *ServerOpts) GetTCPKeepAlivePeriod() time.Duration {
if s.TCPKeepAlivePeriod == 0 {
if s == nil || s.TCPKeepAlivePeriod == 0 {
return DefaultTCPKeepAlivePeriod
}

return s.TCPKeepAlivePeriod
}

// GetMaxRequestBodySize returns max request body size paying attention
// to default value.
func (s *ServerOpts) GetMaxRequestBodySize() int {
if s.MaxRequestBodySize == 0 {
if s == nil || s.MaxRequestBodySize == 0 {
return DefaultMaxRequestBodySize
}

return int(s.MaxRequestBodySize)
}

// GetEventProcessorFactory returns an event factory paying attention to
// default value.
func (s *ServerOpts) GetEventProcessorFactory() events.ProcessorFactory {
if s.EventProcessorFactory == nil {
if s == nil || s.EventProcessorFactory == nil {
return events.NoopProcessorFactory
}

return s.EventProcessorFactory
}

// GetTLSCertCA returns a given TLS CA certificate.
func (s *ServerOpts) GetTLSCertCA() []byte {
if s == nil {
return nil
}

return s.TLSCertCA
}

// GetTLSPrivateKey returns a given TLS private key.
func (s *ServerOpts) GetTLSPrivateKey() []byte {
if s == nil {
return nil
}

return s.TLSPrivateKey
}

// GetTLSSkipVerify returns a sign if we need to skip TLS verification.
func (s *ServerOpts) GetTLSSkipVerify() bool {
return s.TLSSkipVerify
return s != nil && s.TLSSkipVerify
}

// GetLayers returns a set of server layers to use.
func (s *ServerOpts) GetLayers() []layers.Layer {
toReturn := []layers.Layer{layerStartHeaders{}}
toReturn = append(toReturn, s.Layers...)
Expand All @@ -120,14 +210,21 @@ func (s *ServerOpts) GetLayers() []layers.Layer {
return toReturn
}

// GetLayers returns an authenticator instanse to use paying attention
// to default value (no auth).
func (s *ServerOpts) GetAuthenticator() auth.Interface {
if s.Authenticator == nil {
if s == nil || s.Authenticator == nil {
return auth.NoopAuth{}
}

return s.Authenticator
}

// GetExecutor returns an instance of executor to use.
func (s *ServerOpts) GetExecutor() executor.Executor {
if s == nil {
return nil
}

return s.Executor
}
7 changes: 7 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import (
"github.com/valyala/fasthttp"
)

// Server defines a MITM proxy instance. Please pay attention that it
// has its own context. If this context is cancelled, Server starts to
// gracefully terminate.
type Server struct {
ctx context.Context
ctxCancel context.CancelFunc
Expand All @@ -33,10 +36,12 @@ type Server struct {
server *fasthttp.Server
}

// Serve starts to serve on given net.Listener instance.
func (s *Server) Serve(ln net.Listener) error {
return s.server.Serve(ln)
}

// Close stops server.
func (s *Server) Close() error {
s.ctxCancel()

Expand Down Expand Up @@ -223,6 +228,8 @@ func (s *Server) completeRequestType(ctx *layers.Context) {
})
}

// NewServer creates a new instance of the server based on a given
// options.
func NewServer(ctx context.Context, opts ServerOpts) (*Server, error) { // nolint: funlen
ctx, cancel := context.WithCancel(ctx)
oopts := &opts
Expand Down

0 comments on commit 3a8fe8b

Please sign in to comment.