ghttp_server.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package ghttp
  7. import (
  8. "bytes"
  9. "context"
  10. "fmt"
  11. "net/http"
  12. "os"
  13. "runtime"
  14. "strings"
  15. "sync"
  16. "time"
  17. "github.com/olekukonko/tablewriter"
  18. "github.com/gogf/gf/v2/container/garray"
  19. "github.com/gogf/gf/v2/container/gtype"
  20. "github.com/gogf/gf/v2/debug/gdebug"
  21. "github.com/gogf/gf/v2/errors/gcode"
  22. "github.com/gogf/gf/v2/errors/gerror"
  23. "github.com/gogf/gf/v2/internal/intlog"
  24. "github.com/gogf/gf/v2/net/ghttp/internal/swaggerui"
  25. "github.com/gogf/gf/v2/net/goai"
  26. "github.com/gogf/gf/v2/os/gcache"
  27. "github.com/gogf/gf/v2/os/genv"
  28. "github.com/gogf/gf/v2/os/gfile"
  29. "github.com/gogf/gf/v2/os/glog"
  30. "github.com/gogf/gf/v2/os/gproc"
  31. "github.com/gogf/gf/v2/os/gsession"
  32. "github.com/gogf/gf/v2/os/gtimer"
  33. "github.com/gogf/gf/v2/text/gregex"
  34. "github.com/gogf/gf/v2/text/gstr"
  35. "github.com/gogf/gf/v2/util/gconv"
  36. )
  37. func init() {
  38. // Initialize the method map.
  39. for _, v := range strings.Split(supportedHttpMethods, ",") {
  40. methodsMap[v] = struct{}{}
  41. }
  42. }
  43. // serverProcessInit initializes some process configurations, which can only be done once.
  44. func serverProcessInit() {
  45. var ctx = context.TODO()
  46. if !serverProcessInitialized.Cas(false, true) {
  47. return
  48. }
  49. // This means it is a restart server. It should kill its parent before starting its listening,
  50. // to avoid duplicated port listening in two processes.
  51. if !genv.Get(adminActionRestartEnvKey).IsEmpty() {
  52. if p, err := os.FindProcess(gproc.PPid()); err == nil {
  53. if err = p.Kill(); err != nil {
  54. intlog.Errorf(ctx, `%+v`, err)
  55. }
  56. if _, err = p.Wait(); err != nil {
  57. intlog.Errorf(ctx, `%+v`, err)
  58. }
  59. } else {
  60. glog.Error(ctx, err)
  61. }
  62. }
  63. // Signal handler.
  64. go handleProcessSignal()
  65. // Process message handler.
  66. // It enabled only a graceful feature is enabled.
  67. if gracefulEnabled {
  68. intlog.Printf(ctx, "pid[%d]: graceful reload feature is enabled", gproc.Pid())
  69. go handleProcessMessage()
  70. } else {
  71. intlog.Printf(ctx, "pid[%d]: graceful reload feature is disabled", gproc.Pid())
  72. }
  73. // It's an ugly calling for better initializing the main package path
  74. // in source development environment. It is useful only be used in main goroutine.
  75. // It fails to retrieve the main package path in asynchronous goroutines.
  76. gfile.MainPkgPath()
  77. }
  78. // GetServer creates and returns a server instance using given name and default configurations.
  79. // Note that the parameter `name` should be unique for different servers. It returns an existing
  80. // server instance if given `name` is already existing in the server mapping.
  81. func GetServer(name ...interface{}) *Server {
  82. serverName := DefaultServerName
  83. if len(name) > 0 && name[0] != "" {
  84. serverName = gconv.String(name[0])
  85. }
  86. if s := serverMapping.Get(serverName); s != nil {
  87. return s.(*Server)
  88. }
  89. s := &Server{
  90. instance: serverName,
  91. plugins: make([]Plugin, 0),
  92. servers: make([]*gracefulServer, 0),
  93. closeChan: make(chan struct{}, 10000),
  94. serverCount: gtype.NewInt(),
  95. statusHandlerMap: make(map[string][]HandlerFunc),
  96. serveTree: make(map[string]interface{}),
  97. serveCache: gcache.New(),
  98. routesMap: make(map[string][]*HandlerItem),
  99. openapi: goai.New(),
  100. }
  101. // Initialize the server using default configurations.
  102. if err := s.SetConfig(NewConfig()); err != nil {
  103. panic(gerror.WrapCode(gcode.CodeInvalidConfiguration, err, ""))
  104. }
  105. // Record the server to internal server mapping by name.
  106. serverMapping.Set(serverName, s)
  107. // It enables OpenTelemetry for server in default.
  108. s.Use(internalMiddlewareServerTracing)
  109. return s
  110. }
  111. // Start starts listening on configured port.
  112. // This function does not block the process, you can use function Wait blocking the process.
  113. func (s *Server) Start() error {
  114. var ctx = context.TODO()
  115. // Swagger UI.
  116. if s.config.SwaggerPath != "" {
  117. swaggerui.Init()
  118. s.AddStaticPath(s.config.SwaggerPath, swaggerUIPackedPath)
  119. s.BindHookHandler(s.config.SwaggerPath+"/*", HookBeforeServe, s.swaggerUI)
  120. }
  121. // OpenApi specification json producing handler.
  122. if s.config.OpenApiPath != "" {
  123. s.BindHandler(s.config.OpenApiPath, s.openapiSpec)
  124. }
  125. // Register group routes.
  126. s.handlePreBindItems(ctx)
  127. // Server process initialization, which can only be initialized once.
  128. serverProcessInit()
  129. // Server can only be run once.
  130. if s.Status() == ServerStatusRunning {
  131. return gerror.NewCode(gcode.CodeInvalidOperation, "server is already running")
  132. }
  133. // Logging path setting check.
  134. if s.config.LogPath != "" && s.config.LogPath != s.config.Logger.GetPath() {
  135. if err := s.config.Logger.SetPath(s.config.LogPath); err != nil {
  136. return err
  137. }
  138. }
  139. // Default session storage.
  140. if s.config.SessionStorage == nil {
  141. sessionStoragePath := ""
  142. if s.config.SessionPath != "" {
  143. sessionStoragePath = gfile.Join(s.config.SessionPath, s.config.Name)
  144. if !gfile.Exists(sessionStoragePath) {
  145. if err := gfile.Mkdir(sessionStoragePath); err != nil {
  146. return gerror.Wrapf(err, `mkdir failed for "%s"`, sessionStoragePath)
  147. }
  148. }
  149. }
  150. s.config.SessionStorage = gsession.NewStorageFile(sessionStoragePath, s.config.SessionMaxAge)
  151. }
  152. // Initialize session manager when start running.
  153. s.sessionManager = gsession.New(
  154. s.config.SessionMaxAge,
  155. s.config.SessionStorage,
  156. )
  157. // PProf feature.
  158. if s.config.PProfEnabled {
  159. s.EnablePProf(s.config.PProfPattern)
  160. }
  161. // Default HTTP handler.
  162. if s.config.Handler == nil {
  163. s.config.Handler = s.ServeHTTP
  164. }
  165. // Install external plugins.
  166. for _, p := range s.plugins {
  167. if err := p.Install(s); err != nil {
  168. s.Logger().Fatalf(ctx, `%+v`, err)
  169. }
  170. }
  171. // Check the group routes again.
  172. s.handlePreBindItems(ctx)
  173. // If there's no route registered and no static service enabled,
  174. // it then returns an error of invalid usage of server.
  175. if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
  176. return gerror.NewCode(
  177. gcode.CodeInvalidOperation,
  178. `there's no route set or static feature enabled, did you forget import the router?`,
  179. )
  180. }
  181. // ================================================================================================
  182. // Start the HTTP server.
  183. // ================================================================================================
  184. reloaded := false
  185. fdMapStr := genv.Get(adminActionReloadEnvKey).String()
  186. if len(fdMapStr) > 0 {
  187. sfm := bufferToServerFdMap([]byte(fdMapStr))
  188. if v, ok := sfm[s.config.Name]; ok {
  189. s.startServer(v)
  190. reloaded = true
  191. }
  192. }
  193. if !reloaded {
  194. s.startServer(nil)
  195. }
  196. // Swagger UI info.
  197. if s.config.SwaggerPath != "" {
  198. s.Logger().Infof(
  199. ctx,
  200. `swagger ui is serving at address: %s%s/`,
  201. s.getLocalListenedAddress(),
  202. s.config.SwaggerPath,
  203. )
  204. }
  205. // OpenApi specification info.
  206. if s.config.OpenApiPath != "" {
  207. s.Logger().Infof(
  208. ctx,
  209. `openapi specification is serving at address: %s%s`,
  210. s.getLocalListenedAddress(),
  211. s.config.OpenApiPath,
  212. )
  213. } else {
  214. if s.config.SwaggerPath != "" {
  215. s.Logger().Warning(
  216. ctx,
  217. `openapi specification is disabled but swagger ui is serving, which might make no sense`,
  218. )
  219. } else {
  220. s.Logger().Info(
  221. ctx,
  222. `openapi specification is disabled`,
  223. )
  224. }
  225. }
  226. // If this is a child process, it then notifies its parent exit.
  227. if gproc.IsChild() {
  228. gtimer.SetTimeout(ctx, time.Duration(s.config.GracefulTimeout)*time.Second, func(ctx context.Context) {
  229. if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil {
  230. intlog.Errorf(ctx, `server error in process communication: %+v`, err)
  231. }
  232. })
  233. }
  234. s.initOpenApi()
  235. s.doServiceRegister()
  236. s.doRouterMapDump()
  237. return nil
  238. }
  239. func (s *Server) getLocalListenedAddress() string {
  240. return fmt.Sprintf(`http://127.0.0.1:%d`, s.GetListenedPort())
  241. }
  242. // doRouterMapDump checks and dumps the router map to the log.
  243. func (s *Server) doRouterMapDump() {
  244. var (
  245. ctx = context.TODO()
  246. routes = s.GetRoutes()
  247. isJustDefaultServerAndDomain = true
  248. headers = []string{
  249. "SERVER", "DOMAIN", "ADDRESS", "METHOD", "ROUTE", "HANDLER", "MIDDLEWARE",
  250. }
  251. )
  252. for _, item := range routes {
  253. if item.Server != DefaultServerName || item.Domain != DefaultDomainName {
  254. isJustDefaultServerAndDomain = false
  255. break
  256. }
  257. }
  258. if isJustDefaultServerAndDomain {
  259. headers = []string{"ADDRESS", "METHOD", "ROUTE", "HANDLER", "MIDDLEWARE"}
  260. }
  261. if s.config.DumpRouterMap && len(routes) > 0 {
  262. buffer := bytes.NewBuffer(nil)
  263. table := tablewriter.NewWriter(buffer)
  264. table.SetHeader(headers)
  265. table.SetRowLine(true)
  266. table.SetBorder(false)
  267. table.SetCenterSeparator("|")
  268. for _, item := range routes {
  269. var (
  270. data = make([]string, 0)
  271. handlerName = gstr.TrimRightStr(item.Handler.Name, "-fm")
  272. middlewares = gstr.SplitAndTrim(item.Middleware, ",")
  273. )
  274. for k, v := range middlewares {
  275. middlewares[k] = gstr.TrimRightStr(v, "-fm")
  276. }
  277. item.Middleware = gstr.Join(middlewares, "\n")
  278. if isJustDefaultServerAndDomain {
  279. data = append(
  280. data,
  281. item.Address,
  282. item.Method,
  283. item.Route,
  284. handlerName,
  285. item.Middleware,
  286. )
  287. } else {
  288. data = append(
  289. data,
  290. item.Server,
  291. item.Domain,
  292. item.Address,
  293. item.Method,
  294. item.Route,
  295. handlerName,
  296. item.Middleware,
  297. )
  298. }
  299. table.Append(data)
  300. }
  301. table.Render()
  302. s.config.Logger.Header(false).Printf(ctx, "\n%s", buffer.String())
  303. }
  304. }
  305. // GetOpenApi returns the OpenApi specification management object of current server.
  306. func (s *Server) GetOpenApi() *goai.OpenApiV3 {
  307. return s.openapi
  308. }
  309. // GetRoutes retrieves and returns the router array.
  310. func (s *Server) GetRoutes() []RouterItem {
  311. var (
  312. m = make(map[string]*garray.SortedArray)
  313. address = s.GetListenedAddress()
  314. )
  315. if s.config.HTTPSAddr != "" {
  316. if len(address) > 0 {
  317. address += ","
  318. }
  319. address += "tls" + s.config.HTTPSAddr
  320. }
  321. for k, handlerItems := range s.routesMap {
  322. array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, k)
  323. for index, handlerItem := range handlerItems {
  324. item := RouterItem{
  325. Server: s.config.Name,
  326. Address: address,
  327. Domain: array[4],
  328. Type: handlerItem.Type,
  329. Middleware: array[1],
  330. Method: array[2],
  331. Route: array[3],
  332. Priority: len(handlerItems) - index - 1,
  333. Handler: handlerItem,
  334. }
  335. switch item.Handler.Type {
  336. case HandlerTypeObject, HandlerTypeHandler:
  337. item.IsServiceHandler = true
  338. case HandlerTypeMiddleware:
  339. item.Middleware = "GLOBAL MIDDLEWARE"
  340. }
  341. if len(item.Handler.Middleware) > 0 {
  342. for _, v := range item.Handler.Middleware {
  343. if item.Middleware != "" {
  344. item.Middleware += ","
  345. }
  346. item.Middleware += gdebug.FuncName(v)
  347. }
  348. }
  349. // If the domain does not exist in the dump map, it creates the map.
  350. // The value of the map is a custom sorted array.
  351. if _, ok := m[item.Domain]; !ok {
  352. // Sort in ASC order.
  353. m[item.Domain] = garray.NewSortedArray(func(v1, v2 interface{}) int {
  354. item1 := v1.(RouterItem)
  355. item2 := v2.(RouterItem)
  356. r := 0
  357. if r = strings.Compare(item1.Domain, item2.Domain); r == 0 {
  358. if r = strings.Compare(item1.Route, item2.Route); r == 0 {
  359. if r = strings.Compare(item1.Method, item2.Method); r == 0 {
  360. if item1.Handler.Type == HandlerTypeMiddleware && item2.Handler.Type != HandlerTypeMiddleware {
  361. return -1
  362. } else if item1.Handler.Type == HandlerTypeMiddleware && item2.Handler.Type == HandlerTypeMiddleware {
  363. return 1
  364. } else if r = strings.Compare(item1.Middleware, item2.Middleware); r == 0 {
  365. r = item2.Priority - item1.Priority
  366. }
  367. }
  368. }
  369. }
  370. return r
  371. })
  372. }
  373. m[item.Domain].Add(item)
  374. }
  375. }
  376. routerArray := make([]RouterItem, 0, 128)
  377. for _, array := range m {
  378. for _, v := range array.Slice() {
  379. routerArray = append(routerArray, v.(RouterItem))
  380. }
  381. }
  382. return routerArray
  383. }
  384. // Run starts server listening in blocking way.
  385. // It's commonly used for single server situation.
  386. func (s *Server) Run() {
  387. var ctx = context.TODO()
  388. if err := s.Start(); err != nil {
  389. s.Logger().Fatalf(ctx, `%+v`, err)
  390. }
  391. // Blocking using channel.
  392. <-s.closeChan
  393. // Remove plugins.
  394. if len(s.plugins) > 0 {
  395. for _, p := range s.plugins {
  396. intlog.Printf(ctx, `remove plugin: %s`, p.Name())
  397. if err := p.Remove(); err != nil {
  398. intlog.Errorf(ctx, "%+v", err)
  399. }
  400. }
  401. }
  402. s.doServiceDeregister()
  403. s.Logger().Infof(ctx, "pid[%d]: all servers shutdown", gproc.Pid())
  404. }
  405. // Wait blocks to wait for all servers done.
  406. // It's commonly used in multiple server situation.
  407. func Wait() {
  408. var ctx = context.TODO()
  409. <-allShutdownChan
  410. // Remove plugins.
  411. serverMapping.Iterator(func(k string, v interface{}) bool {
  412. s := v.(*Server)
  413. if len(s.plugins) > 0 {
  414. for _, p := range s.plugins {
  415. intlog.Printf(ctx, `remove plugin: %s`, p.Name())
  416. if err := p.Remove(); err != nil {
  417. intlog.Errorf(ctx, `%+v`, err)
  418. }
  419. }
  420. }
  421. return true
  422. })
  423. glog.Infof(ctx, "pid[%d]: all servers shutdown", gproc.Pid())
  424. }
  425. // startServer starts the underlying server listening.
  426. func (s *Server) startServer(fdMap listenerFdMap) {
  427. var (
  428. ctx = context.TODO()
  429. httpsEnabled bool
  430. )
  431. // HTTPS
  432. if s.config.TLSConfig != nil || (s.config.HTTPSCertPath != "" && s.config.HTTPSKeyPath != "") {
  433. if len(s.config.HTTPSAddr) == 0 {
  434. if len(s.config.Address) > 0 {
  435. s.config.HTTPSAddr = s.config.Address
  436. s.config.Address = ""
  437. } else {
  438. s.config.HTTPSAddr = defaultHttpsAddr
  439. }
  440. }
  441. httpsEnabled = len(s.config.HTTPSAddr) > 0
  442. var array []string
  443. if v, ok := fdMap["https"]; ok && len(v) > 0 {
  444. array = strings.Split(v, ",")
  445. } else {
  446. array = strings.Split(s.config.HTTPSAddr, ",")
  447. }
  448. for _, v := range array {
  449. if len(v) == 0 {
  450. continue
  451. }
  452. var (
  453. fd = 0
  454. itemFunc = v
  455. addrAndFd = strings.Split(v, "#")
  456. )
  457. if len(addrAndFd) > 1 {
  458. itemFunc = addrAndFd[0]
  459. // The Windows OS does not support socket file descriptor passing
  460. // from parent process.
  461. if runtime.GOOS != "windows" {
  462. fd = gconv.Int(addrAndFd[1])
  463. }
  464. }
  465. if fd > 0 {
  466. s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
  467. } else {
  468. s.servers = append(s.servers, s.newGracefulServer(itemFunc))
  469. }
  470. s.servers[len(s.servers)-1].isHttps = true
  471. }
  472. }
  473. // HTTP
  474. if !httpsEnabled && len(s.config.Address) == 0 {
  475. s.config.Address = defaultHttpAddr
  476. }
  477. var array []string
  478. if v, ok := fdMap["http"]; ok && len(v) > 0 {
  479. array = strings.Split(v, ",")
  480. } else {
  481. array = strings.Split(s.config.Address, ",")
  482. }
  483. for _, v := range array {
  484. if len(v) == 0 {
  485. continue
  486. }
  487. var (
  488. fd = 0
  489. itemFunc = v
  490. addrAndFd = strings.Split(v, "#")
  491. )
  492. if len(addrAndFd) > 1 {
  493. itemFunc = addrAndFd[0]
  494. // The Window OS does not support socket file descriptor passing
  495. // from the parent process.
  496. if runtime.GOOS != "windows" {
  497. fd = gconv.Int(addrAndFd[1])
  498. }
  499. }
  500. if fd > 0 {
  501. s.servers = append(s.servers, s.newGracefulServer(itemFunc, fd))
  502. } else {
  503. s.servers = append(s.servers, s.newGracefulServer(itemFunc))
  504. }
  505. }
  506. // Start listening asynchronously.
  507. serverRunning.Add(1)
  508. var wg = sync.WaitGroup{}
  509. for _, v := range s.servers {
  510. wg.Add(1)
  511. go func(server *gracefulServer) {
  512. s.serverCount.Add(1)
  513. var err error
  514. // Create listener.
  515. if server.isHttps {
  516. err = server.CreateListenerTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath, s.config.TLSConfig)
  517. } else {
  518. err = server.CreateListener()
  519. }
  520. if err != nil {
  521. s.Logger().Fatalf(ctx, `%+v`, err)
  522. }
  523. wg.Done()
  524. // Start listening and serving in blocking way.
  525. err = server.Serve(ctx)
  526. // The process exits if the server is closed with none closing error.
  527. if err != nil && !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
  528. s.Logger().Fatalf(ctx, `%+v`, err)
  529. }
  530. // If all the underlying servers' shutdown, the process exits.
  531. if s.serverCount.Add(-1) < 1 {
  532. s.closeChan <- struct{}{}
  533. if serverRunning.Add(-1) < 1 {
  534. serverMapping.Remove(s.instance)
  535. allShutdownChan <- struct{}{}
  536. }
  537. }
  538. }(v)
  539. }
  540. wg.Wait()
  541. }
  542. // Status retrieves and returns the server status.
  543. func (s *Server) Status() int {
  544. if serverRunning.Val() == 0 {
  545. return ServerStatusStopped
  546. }
  547. // If any underlying server is running, the server status is running.
  548. for _, v := range s.servers {
  549. if v.status == ServerStatusRunning {
  550. return ServerStatusRunning
  551. }
  552. }
  553. return ServerStatusStopped
  554. }
  555. // getListenerFdMap retrieves and returns the socket file descriptors.
  556. // The key of the returned map is "http" and "https".
  557. func (s *Server) getListenerFdMap() map[string]string {
  558. m := map[string]string{
  559. "https": "",
  560. "http": "",
  561. }
  562. for _, v := range s.servers {
  563. str := v.address + "#" + gconv.String(v.Fd()) + ","
  564. if v.isHttps {
  565. if len(m["https"]) > 0 {
  566. m["https"] += ","
  567. }
  568. m["https"] += str
  569. } else {
  570. if len(m["http"]) > 0 {
  571. m["http"] += ","
  572. }
  573. m["http"] += str
  574. }
  575. }
  576. return m
  577. }
  578. // GetListenedPort retrieves and returns one port which is listened by current server.
  579. func (s *Server) GetListenedPort() int {
  580. ports := s.GetListenedPorts()
  581. if len(ports) > 0 {
  582. return ports[0]
  583. }
  584. return 0
  585. }
  586. // GetListenedPorts retrieves and returns the ports which are listened by current server.
  587. func (s *Server) GetListenedPorts() []int {
  588. ports := make([]int, 0)
  589. for _, server := range s.servers {
  590. ports = append(ports, server.GetListenedPort())
  591. }
  592. return ports
  593. }
  594. // GetListenedAddress retrieves and returns the address string which are listened by current server.
  595. func (s *Server) GetListenedAddress() string {
  596. if !gstr.Contains(s.config.Address, FreePortAddress) {
  597. return s.config.Address
  598. }
  599. var (
  600. address = s.config.Address
  601. listenedPorts = s.GetListenedPorts()
  602. )
  603. for _, listenedPort := range listenedPorts {
  604. address = gstr.Replace(address, FreePortAddress, fmt.Sprintf(`:%d`, listenedPort), 1)
  605. }
  606. return address
  607. }