github.com/google/martian/v3@v3.3.3/trafficshape/listener.go (about)

     1  // Copyright 2015 Google Inc. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package trafficshape
    16  
    17  import (
    18  	"net"
    19  	"sync"
    20  	"time"
    21  
    22  	"github.com/google/martian/v3/log"
    23  )
    24  
    25  // DefaultBitrate represents the bitrate that will be for all url regexs for which a shape
    26  // has not been specified.
    27  var DefaultBitrate int64 = 500000000000 // 500Gbps (unlimited)
    28  
    29  // ErrForceClose is an error that communicates the need to close the connection.
    30  type ErrForceClose struct {
    31  	message string
    32  }
    33  
    34  func (efc *ErrForceClose) Error() string {
    35  	return efc.message
    36  }
    37  
    38  // urlShape contains a rw lock protected shape of a url_regex.
    39  type urlShape struct {
    40  	sync.RWMutex
    41  	Shape *Shape
    42  }
    43  
    44  // urlShapes contains a rw lock protected map of url regexs to their URLShapes.
    45  type urlShapes struct {
    46  	sync.RWMutex
    47  	M                map[string]*urlShape
    48  	LastModifiedTime time.Time
    49  }
    50  
    51  // Buckets contains the read and write buckets for a url_regex.
    52  type Buckets struct {
    53  	ReadBucket  *Bucket
    54  	WriteBucket *Bucket
    55  }
    56  
    57  // NewBuckets returns a *Buckets with the specified up and down bandwidths.
    58  func NewBuckets(up int64, down int64) *Buckets {
    59  	return &Buckets{
    60  		ReadBucket:  NewBucket(up, time.Second),
    61  		WriteBucket: NewBucket(down, time.Second),
    62  	}
    63  }
    64  
    65  // ThrottleContext represents whether we are currently in a throttle interval for a particular
    66  // url_regex. If ThrottleNow is true, only then will the current throttle 'Bandwidth' be set
    67  // correctly.
    68  type ThrottleContext struct {
    69  	ThrottleNow bool
    70  	Bandwidth   int64
    71  }
    72  
    73  // NextActionInfo represents whether there is an upcoming action. Only if ActionNext is true will the
    74  // Index and ByteOffset be set correctly.
    75  type NextActionInfo struct {
    76  	ActionNext bool
    77  	Index      int64
    78  	ByteOffset int64
    79  }
    80  
    81  // Context represents the current information that is needed while writing back to the client.
    82  // Only if Shaping is true, that is we are currently writing back a response that matches a certain
    83  // url_regex will the other values be set correctly. If so, the Buckets represent the buckets
    84  // to be used for the current url_regex. NextActionInfo tells us whether there is an upcoming action
    85  // that needs to be performed, and ThrottleContext tells us whether we are currently in a throttle
    86  // interval (according to the RangeStart). Note, the ThrottleContext is only used once in the start
    87  // to determine the beginning bandwidth. It need not be updated after that. This
    88  // is because the subsequent throttles are captured in the upcoming ChangeBandwidth actions.
    89  // Byte Offset represents the absolute byte offset of response data that we are currently writing back.
    90  // It does not account for the header data.
    91  type Context struct {
    92  	Shaping            bool
    93  	RangeStart         int64
    94  	URLRegex           string
    95  	Buckets            *Buckets
    96  	GlobalBucket       *Bucket
    97  	ThrottleContext    *ThrottleContext
    98  	NextActionInfo     *NextActionInfo
    99  	ByteOffset         int64
   100  	HeaderLen          int64
   101  	HeaderBytesWritten int64
   102  }
   103  
   104  // Listener wraps a net.Listener and simulates connection latency and bandwidth
   105  // constraints.
   106  type Listener struct {
   107  	net.Listener
   108  
   109  	ReadBucket  *Bucket
   110  	WriteBucket *Bucket
   111  
   112  	mu            sync.RWMutex
   113  	latency       time.Duration
   114  	GlobalBuckets map[string]*Bucket
   115  	Shapes        *urlShapes
   116  	defaults      *Default
   117  }
   118  
   119  // NewListener returns a new bandwidth constrained listener. Defaults to
   120  // DefaultBitrate (uncapped).
   121  func NewListener(l net.Listener) *Listener {
   122  	return &Listener{
   123  		Listener:      l,
   124  		ReadBucket:    NewBucket(DefaultBitrate/8, time.Second),
   125  		WriteBucket:   NewBucket(DefaultBitrate/8, time.Second),
   126  		Shapes:        &urlShapes{M: make(map[string]*urlShape)},
   127  		GlobalBuckets: make(map[string]*Bucket),
   128  		defaults: &Default{
   129  			Bandwidth: Bandwidth{
   130  				Up:   DefaultBitrate / 8,
   131  				Down: DefaultBitrate / 8,
   132  			},
   133  			Latency: 0,
   134  		},
   135  	}
   136  }
   137  
   138  // ReadBitrate returns the bitrate in bits per second for reads.
   139  func (l *Listener) ReadBitrate() int64 {
   140  	return l.ReadBucket.Capacity() * 8
   141  }
   142  
   143  // SetReadBitrate sets the bitrate in bits per second for reads.
   144  func (l *Listener) SetReadBitrate(bitrate int64) {
   145  	l.ReadBucket.SetCapacity(bitrate / 8)
   146  }
   147  
   148  // WriteBitrate returns the bitrate in bits per second for writes.
   149  func (l *Listener) WriteBitrate() int64 {
   150  	return l.WriteBucket.Capacity() * 8
   151  }
   152  
   153  // SetWriteBitrate sets the bitrate in bits per second for writes.
   154  func (l *Listener) SetWriteBitrate(bitrate int64) {
   155  	l.WriteBucket.SetCapacity(bitrate / 8)
   156  }
   157  
   158  // SetDefaults sets the default traffic shaping parameters for the listener.
   159  func (l *Listener) SetDefaults(defaults *Default) {
   160  	l.mu.Lock()
   161  	defer l.mu.Unlock()
   162  
   163  	l.defaults = defaults
   164  }
   165  
   166  // Defaults returns the default traffic shaping parameters for the listener.
   167  func (l *Listener) Defaults() *Default {
   168  	l.mu.RLock()
   169  	defer l.mu.RUnlock()
   170  
   171  	return l.defaults
   172  }
   173  
   174  // Latency returns the latency for connections.
   175  func (l *Listener) Latency() time.Duration {
   176  	l.mu.Lock()
   177  	defer l.mu.Unlock()
   178  
   179  	return l.latency
   180  }
   181  
   182  // SetLatency sets the initial latency for connections.
   183  func (l *Listener) SetLatency(latency time.Duration) {
   184  	l.mu.Lock()
   185  	defer l.mu.Unlock()
   186  
   187  	l.latency = latency
   188  }
   189  
   190  // GetTrafficShapedConn takes in a normal connection and returns a traffic shaped connection.
   191  func (l *Listener) GetTrafficShapedConn(oc net.Conn) *Conn {
   192  	if tsconn, ok := oc.(*Conn); ok {
   193  		return tsconn
   194  	}
   195  	urlbuckets := make(map[string]*Buckets)
   196  	globalurlbuckets := make(map[string]*Bucket)
   197  
   198  	l.Shapes.RLock()
   199  	defaults := l.Defaults()
   200  	latency := l.Latency()
   201  	defaultBandwidth := defaults.Bandwidth
   202  	for regex, shape := range l.Shapes.M {
   203  		// It should be ok to not acquire the read lock on shape, since WriteBucket is never mutated.
   204  		globalurlbuckets[regex] = shape.Shape.WriteBucket
   205  		urlbuckets[regex] = NewBuckets(DefaultBitrate/8, shape.Shape.MaxBandwidth)
   206  	}
   207  
   208  	l.Shapes.RUnlock()
   209  
   210  	curinfo := &Context{}
   211  
   212  	lc := &Conn{
   213  		conn:             oc,
   214  		latency:          latency,
   215  		ReadBucket:       l.ReadBucket,
   216  		WriteBucket:      l.WriteBucket,
   217  		Shapes:           l.Shapes,
   218  		GlobalBuckets:    globalurlbuckets,
   219  		LocalBuckets:     urlbuckets,
   220  		Context:          curinfo,
   221  		Established:      time.Now(),
   222  		DefaultBandwidth: defaultBandwidth,
   223  		Listener:         l,
   224  	}
   225  	return lc
   226  }
   227  
   228  // Accept waits for and returns the next connection to the listener.
   229  func (l *Listener) Accept() (net.Conn, error) {
   230  	oc, err := l.Listener.Accept()
   231  	if err != nil {
   232  		log.Errorf("trafficshape: failed accepting connection: %v", err)
   233  		return nil, err
   234  	}
   235  
   236  	if tconn, ok := oc.(*net.TCPConn); ok {
   237  		log.Debugf("trafficshape: setting keep-alive for TCP connection")
   238  		tconn.SetKeepAlive(true)
   239  		tconn.SetKeepAlivePeriod(3 * time.Minute)
   240  	}
   241  	return l.GetTrafficShapedConn(oc), nil
   242  }
   243  
   244  // Close closes the read and write buckets along with the underlying listener.
   245  func (l *Listener) Close() error {
   246  	defer log.Debugf("trafficshape: closed read/write buckets and connection")
   247  
   248  	l.ReadBucket.Close()
   249  	l.WriteBucket.Close()
   250  
   251  	return l.Listener.Close()
   252  }