<commit_before>package server

import (
	"net"
	"net/http"

	"github.com/gorilla/context"

	"github.com/getlantern/golog"

	"github.com/getlantern/http-proxy/listeners"
)

var (
	testingLocal = false
	log          = golog.LoggerFor("server")
)

type listenerGenerator func(net.Listener) net.Listener

type Server struct {
	Addr net.Addr
	Tls  bool

	handler            http.Handler
	httpServer         http.Server
	listenerGenerators []listenerGenerator
	listeners          []*net.Listener
}

func NewServer(handler http.Handler) *Server {
	server := &Server{
		handler: handler,
	}

	return server
}

func (s *Server) AddListenerWrappers(listenerGens ...listenerGenerator) {
	for _, g := range listenerGens {
		s.listenerGenerators = append(s.listenerGenerators, g)
	}
}

func (s *Server) ServeHTTP(addr string, readyCb func(addr string)) error {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	s.Tls = false
	log.Debugf("Listen http on %s", addr)
	return s.doServe(listener, readyCb)
}

func (s *Server) ServeHTTPS(addr, keyfile, certfile string, readyCb func(addr string)) error {
	listener, err := listenTLS(addr, keyfile, certfile)
	if err != nil {
		return err
	}
	s.Tls = true
	log.Debugf("Listen https on %s", addr)
	return s.doServe(listener, readyCb)
}

func (s *Server) doServe(listener net.Listener, readyCb func(addr string)) error {
	cb := NewConnBag()

	proxy := http.HandlerFunc(
		func(w http.ResponseWriter, req *http.Request) {
			c := cb.Withdraw(req.RemoteAddr)
			context.Set(req, "conn", c)
			s.handler.ServeHTTP(w, req)
		})

	s.httpServer = http.Server{Handler: proxy,
		ConnState: func(c net.Conn, state http.ConnState) {
			wconn, ok := c.(listeners.WrapConn)
			if !ok {
				panic("Should be of type WrapConn")
			}

			wconn.OnState(state)

			switch state {
			case http.StateActive:
				cb.Put(wconn)
			case http.StateClosed:
				// When go server encounters abnormal request, it
				// will transit to StateClosed directly without
				// the handler being invoked, hence the connection
				// will not be withdrawed. Purge it in such case.
				cb.Purge(c.RemoteAddr().String())
			}
		},
	}
<commit_msg>Fix Request Context memory leak closes #66<commit_after>package server

import (
	"net"
	"net/http"

	"github.com/gorilla/context"

	"github.com/getlantern/golog"

	"github.com/getlantern/http-proxy/listeners"
)

var (
	testingLocal = false
	log          = golog.LoggerFor("server")
)

type listenerGenerator func(net.Listener) net.Listener

type Server struct {
	Addr net.Addr
	Tls  bool

	handler            http.Handler
	httpServer         http.Server
	listenerGenerators []listenerGenerator
	listeners          []*net.Listener
}

func NewServer(handler http.Handler) *Server {
	server := &Server{
		handler: handler,
	}

	return server
}

func (s *Server) AddListenerWrappers(listenerGens ...listenerGenerator) {
	for _, g := range listenerGens {
		s.listenerGenerators = append(s.listenerGenerators, g)
	}
}

func (s *Server) ServeHTTP(addr string, readyCb func(addr string)) error {
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	s.Tls = false
	log.Debugf("Listen http on %s", addr)
	return s.doServe(listener, readyCb)
}

func (s *Server) ServeHTTPS(addr, keyfile, certfile string, readyCb func(addr string)) error {
	listener, err := listenTLS(addr, keyfile, certfile)
	if err != nil {
		return err
	}
	s.Tls = true
	log.Debugf("Listen https on %s", addr)
	return s.doServe(listener, readyCb)
}

func (s *Server) doServe(listener net.Listener, readyCb func(addr string)) error {
	cb := NewConnBag()

	proxy := http.HandlerFunc(
		func(w http.ResponseWriter, req *http.Request) {
			c := cb.Withdraw(req.RemoteAddr)
			context.Set(req, "conn", c)
			s.handler.ServeHTTP(w, req)
			context.Clear(req)
		})

	s.httpServer = http.Server{Handler: proxy,
		ConnState: func(c net.Conn, state http.ConnState) {
			wconn, ok := c.(listeners.WrapConn)
			if !ok {
				panic("Should be of type WrapConn")
			}

			wconn.OnState(state)

			switch state {
			case http.StateActive:
				cb.Put(wconn)
			case http.StateClosed:
				// When go server encounters abnormal request, it
				// will transit to StateClosed directly without
				// the handler being invoked, hence the connection
				// will not be withdrawed. Purge it in such case.
				cb.Purge(c.RemoteAddr().String())
			}
		},
	}

{'fraction_non_alphanumeric': 0.12188997945674504, 'fraction_numerical': 0.00045651677699155445, 'mean_word_length': 3.545643153526971, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1019722', 'n_tokens_mistral': 1626, 'n_tokens_neox': 1489, 'n_words': 393}