Capstan is a Golang web framework that shares some similarities with others in its segment.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
3.4KB

  1. package capstan
  2. import (
  3. "fmt"
  4. "net"
  5. "os"
  6. "sync"
  7. "syscall"
  8. )
  9. type PreLaunchFunc func() error
  10. type Listener interface {
  11. net.Listener
  12. syscall.Conn
  13. }
  14. type file struct {
  15. fp *os.File
  16. fd uintptr
  17. }
  18. type fdkey struct {
  19. network string
  20. addr string
  21. }
  22. func (k *fdkey) String() string {
  23. return k.network + ":" + k.addr
  24. }
  25. type fdencode struct {
  26. Name string `json:"fp"`
  27. FD uintptr `json:"fd"`
  28. }
  29. type fdenv struct {
  30. Sockets []*fdencode `json:"sockets"`
  31. ParentPID int `json:"parent_pid"`
  32. Addrs map[string]string `json:"addrs"`
  33. }
  34. type fdmap struct {
  35. sockets map[string]Listener
  36. descriptors map[string]uintptr
  37. addrmap map[string]string
  38. sync.RWMutex
  39. }
  40. func newfdmap() *fdmap {
  41. return &fdmap{
  42. sockets: make(map[string]Listener),
  43. descriptors: make(map[string]uintptr),
  44. addrmap: make(map[string]string),
  45. }
  46. }
  47. func (f *fdmap) Files() ([]*file, error) {
  48. i := 0
  49. files := make([]*file, len(f.sockets))
  50. for key, listener := range f.sockets {
  51. conn, ok := listener.(syscall.Conn)
  52. if !ok {
  53. return nil, fmt.Errorf("listener doesn't implement syscall.Conn")
  54. }
  55. fd, err := duplicate(conn)
  56. if err != nil {
  57. return nil, err
  58. }
  59. files[i] = &file{
  60. fp: os.NewFile(fd, key),
  61. fd: fd,
  62. }
  63. i++
  64. }
  65. return files, nil
  66. }
  67. var fds *fdmap
  68. var parentPID int
  69. var childPID int
  70. var prelaunchFunc PreLaunchFunc
  71. func listenerKey(listener net.Listener) string {
  72. if listener.Addr().Network() != "unix" {
  73. host, port, _ := net.SplitHostPort(listener.Addr().String())
  74. if host == "::" {
  75. host = ""
  76. }
  77. return netKey(listener.Addr().Network(), net.JoinHostPort(host, port))
  78. }
  79. return netKey(listener.Addr().Network(), listener.Addr().String())
  80. }
  81. func netKey(network, addr string) string {
  82. if network != "unix" {
  83. host, port, _ := net.SplitHostPort(addr)
  84. return network + "::" + net.JoinHostPort(host, port)
  85. }
  86. return addr
  87. }
  88. func AddPreLaunchFunc(fn PreLaunchFunc) {
  89. if prelaunchFunc != nil {
  90. oldfn := prelaunchFunc
  91. prelaunchFunc = func() error {
  92. if err := oldfn(); err != nil {
  93. return err
  94. }
  95. return fn()
  96. }
  97. }
  98. prelaunchFunc = fn
  99. }
  100. func (f fdmap) addAddrMap(key, fdkey string) {
  101. f.addrmap[key] = fdkey
  102. }
  103. func (f fdmap) addDescriptor(key string, fd uintptr) {
  104. f.descriptors[key] = fd
  105. }
  106. func (f fdmap) addListener(listener Listener, network, addr string) error {
  107. var fd uintptr
  108. conn, ok := listener.(syscall.Conn)
  109. if !ok {
  110. return fmt.Errorf("listener does not implement syscall.Conn")
  111. }
  112. raw, err := conn.SyscallConn()
  113. if err != nil {
  114. return err
  115. }
  116. raw.Control(func(ptr uintptr) {
  117. fd = ptr
  118. })
  119. f.Lock()
  120. f.sockets[listenerKey(listener)] = listener
  121. f.descriptors[listenerKey(listener)] = fd
  122. f.addrmap[addr] = listenerKey(listener)
  123. f.Unlock()
  124. return nil
  125. }
  126. func (f fdmap) getDescriptor(network, addr string) (uintptr, bool) {
  127. fd, ok := f.descriptors[netKey(network, addr)]
  128. if ok {
  129. return fd, true
  130. }
  131. mapped, ok := f.addrmap[addr]
  132. if ok {
  133. if fd, ok := f.descriptors[mapped]; ok {
  134. return fd, true
  135. }
  136. }
  137. return 0, false
  138. }
  139. // Removes the listener associated with the specified network and addr tuple.
  140. func (f fdmap) removeListener(listener net.Listener) {
  141. f.Lock()
  142. delete(f.sockets, listenerKey(listener))
  143. delete(f.descriptors, listenerKey(listener))
  144. f.Unlock()
  145. }
  146. // Resume and return a listener defined by the file descriptor `fd`.
  147. func resumeListener(network, addr string, fd uintptr) (net.Listener, error) {
  148. file := os.NewFile(fd, network+":"+addr)
  149. defer file.Close()
  150. return net.FileListener(file)
  151. }