feat: create free port for WDA HTTP driver

This commit is contained in:
debugtalk
2022-09-30 20:37:34 +08:00
parent 508db771a2
commit a6e568e73b
6 changed files with 87 additions and 68 deletions
+4 -3
View File
@@ -13,9 +13,10 @@ import (
"syscall" "syscall"
"github.com/electricbubble/gadb" "github.com/electricbubble/gadb"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
) )
var ( var (
@@ -194,12 +195,12 @@ func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver *uiaD
func getFreePort() (int, error) { func getFreePort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0") addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil { if err != nil {
return 0, fmt.Errorf("free port: %w", err) return 0, errors.Wrap(err, "resolve tcp addr failed")
} }
l, err := net.ListenTCP("tcp", addr) l, err := net.ListenTCP("tcp", addr)
if err != nil { if err != nil {
return 0, fmt.Errorf("free port: %w", err) return 0, errors.Wrap(err, "listen tcp addr failed")
} }
defer func() { _ = l.Close() }() defer func() { _ = l.Close() }()
return l.Addr().(*net.TCPAddr).Port, nil return l.Addr().(*net.TCPAddr).Port, nil
+76 -56
View File
@@ -5,6 +5,7 @@ import (
"encoding/base64" "encoding/base64"
builtinJSON "encoding/json" builtinJSON "encoding/json"
"fmt" "fmt"
"io"
"mime" "mime"
"mime/multipart" "mime/multipart"
"net" "net"
@@ -12,8 +13,8 @@ import (
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
"strconv"
"strings" "strings"
"time"
giDevice "github.com/electricbubble/gidevice" giDevice "github.com/electricbubble/gidevice"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -53,7 +54,9 @@ func InitWDAClient(device *IOSDevice) (*DriverExt, error) {
capabilities := NewCapabilities() capabilities := NewCapabilities()
capabilities.WithDefaultAlertAction(AlertActionAccept) capabilities.WithDefaultAlertAction(AlertActionAccept)
var driver WebDriver var driver WebDriver
if iosDevice.LocalPort != 0 && iosDevice.LocalMjpegPort != 0 {
if os.Getenv("WDA_USB_DRIVER") == "" {
// default use http driver
driver, err = iosDevice.NewHTTPDriver(capabilities) driver, err = iosDevice.NewHTTPDriver(capabilities)
} else { } else {
driver, err = iosDevice.NewUSBDriver(capabilities) driver, err = iosDevice.NewUSBDriver(capabilities)
@@ -113,18 +116,6 @@ func WithWDAMjpegPort(port int) IOSDeviceOption {
} }
} }
func WithWDALocalPort(port int) IOSDeviceOption {
return func(device *IOSDevice) {
device.LocalPort = port
}
}
func WithWDALocalMjpegPort(port int) IOSDeviceOption {
return func(device *IOSDevice) {
device.LocalMjpegPort = port
}
}
func WithLogOn(logOn bool) IOSDeviceOption { func WithLogOn(logOn bool) IOSDeviceOption {
return func(device *IOSDevice) { return func(device *IOSDevice) {
device.LogOn = logOn device.LogOn = logOn
@@ -166,19 +157,62 @@ func NewIOSDevice(options ...IOSDeviceOption) (device *IOSDevice, err error) {
} }
type IOSDevice struct { type IOSDevice struct {
d giDevice.Device d giDevice.Device
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"` UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
Port int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port Port int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port
LocalPort int `json:"local_port,omitempty" yaml:"local_port,omitempty"` // WDA local port LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
LocalMjpegPort int `json:"local_mjpeg_port,omitempty" yaml:"local_mjpeg_port,omitempty"` // WDA local MJPEG port
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
} }
func (dev *IOSDevice) UUID() string { func (dev *IOSDevice) UUID() string {
return dev.UDID return dev.UDID
} }
func (dev *IOSDevice) forward(localPort, remotePort int) error {
log.Info().Int("localPort", localPort).Int("remotePort", remotePort).
Str("udid", dev.UDID).Msg("forward tcp port")
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", localPort))
if err != nil {
log.Error().Err(err).Msg("listen tcp error")
return err
}
go func(listener net.Listener, device giDevice.Device) {
for {
accept, err := listener.Accept()
if err != nil {
log.Error().Err(err).Msg("accept error")
continue
}
rInnerConn, err := device.NewConnect(remotePort)
if err != nil {
log.Error().Err(err).Msg("connect to device failed")
os.Exit(1)
}
rConn := rInnerConn.RawConn()
_ = rConn.SetDeadline(time.Time{})
go func(lConn net.Conn) {
go func(lConn, rConn net.Conn) {
if _, err := io.Copy(lConn, rConn); err != nil {
log.Error().Err(err).Msg("copy local -> remote")
}
}(lConn, rConn)
go func(lConn, rConn net.Conn) {
if _, err := io.Copy(rConn, lConn); err != nil {
log.Error().Err(err).Msg("copy local <- remote")
}
}(lConn, rConn)
}(accept)
}
}(listener, dev.d)
return nil
}
func (dev *IOSDevice) opitons() (deviceOptions []IOSDeviceOption) { func (dev *IOSDevice) opitons() (deviceOptions []IOSDeviceOption) {
if dev.UDID != "" { if dev.UDID != "" {
deviceOptions = append(deviceOptions, WithUDID(dev.UDID)) deviceOptions = append(deviceOptions, WithUDID(dev.UDID))
@@ -189,49 +223,35 @@ func (dev *IOSDevice) opitons() (deviceOptions []IOSDeviceOption) {
if dev.MjpegPort != 0 { if dev.MjpegPort != 0 {
deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.MjpegPort)) deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.MjpegPort))
} }
if wda_port := os.Getenv("WDA_LOCAL_PORT"); wda_port != "" {
if port, err := strconv.Atoi(wda_port); err == nil {
log.Info().Int("WDA_LOCAL_PORT", port).
Msg("override with environment variable")
dev.LocalPort = port
} else {
log.Error().Err(err).Str("WDA_LOCAL_PORT", wda_port).
Msg("invalid WDA_LOCAL_PORT, ignored")
}
}
if wda_mjpeg_port := os.Getenv("WDA_LOCAL_MJPEG_PORT"); wda_mjpeg_port != "" {
if mjpeg_port, err := strconv.Atoi(wda_mjpeg_port); err == nil {
log.Info().Int("WDA_LOCAL_MJPEG_PORT", mjpeg_port).
Msg("override with environment variable")
dev.LocalMjpegPort = mjpeg_port
} else {
log.Error().Err(err).Str("WDA_LOCAL_MJPEG_PORT", wda_mjpeg_port).
Msg("invalid WDA_LOCAL_MJPEG_PORT, ignored")
}
}
if dev.LocalPort != 0 {
deviceOptions = append(deviceOptions, WithWDALocalPort(dev.LocalPort))
}
if dev.LocalMjpegPort != 0 {
deviceOptions = append(deviceOptions, WithWDALocalMjpegPort(dev.LocalMjpegPort))
}
return return
} }
// NewHTTPDriver creates new remote HTTP client, this will also start a new session. // NewHTTPDriver creates new remote HTTP client, this will also start a new session.
// WDA port and mjpeg port must be proxied to local ports:
// iproxy -u UDID WDA_LOCAL_PORT WDA_PORT
// iproxy -u UDID WDA_LOCAL_MJPEG_PORT WDA_MJPEG_PORT
func (dev *IOSDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver, err error) { func (dev *IOSDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver, err error) {
host := "127.0.0.1" localPort, err := getFreePort()
if err != nil {
return nil, errors.Wrap(err, "get free port failed")
}
if err = dev.forward(localPort, dev.Port); err != nil {
return nil, errors.Wrap(err, "forward tcp port failed")
}
localMjpegPort, err := getFreePort()
if err != nil {
return nil, errors.Wrap(err, "get free port failed")
}
if err = dev.forward(localMjpegPort, dev.MjpegPort); err != nil {
return nil, errors.Wrap(err, "forward tcp port failed")
}
log.Info().Interface("capabilities", capabilities). log.Info().Interface("capabilities", capabilities).
Str("host", host).Msg("init WDA HTTP driver") Int("localPort", localPort).Int("localMjpegPort", localMjpegPort).
Msg("init WDA HTTP driver")
wd := new(wdaDriver) wd := new(wdaDriver)
wd.client = http.DefaultClient wd.client = http.DefaultClient
if wd.urlPrefix, err = url.Parse(fmt.Sprintf("http://%s:%d", host, dev.LocalPort)); err != nil { host := "127.0.0.1"
if wd.urlPrefix, err = url.Parse(fmt.Sprintf("http://%s:%d", host, localPort)); err != nil {
return nil, err return nil, err
} }
var sessionInfo SessionInfo var sessionInfo SessionInfo
@@ -242,7 +262,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities Capabilities) (driver WebDriver
if wd.mjpegHTTPConn, err = net.Dial( if wd.mjpegHTTPConn, err = net.Dial(
"tcp", "tcp",
fmt.Sprintf("%s:%d", host, dev.LocalMjpegPort), fmt.Sprintf("%s:%d", host, localMjpegPort),
); err != nil { ); err != nil {
return nil, err return nil, err
} }
+1 -1
View File
@@ -1 +1 @@
v4.3.0-beta-09301627 v4.3.0-beta-09302036
+4 -6
View File
@@ -11,12 +11,10 @@ import (
) )
var ( var (
WithUDID = uixt.WithUDID WithUDID = uixt.WithUDID
WithWDAPort = uixt.WithWDAPort WithWDAPort = uixt.WithWDAPort
WithWDAMjpegPort = uixt.WithWDAMjpegPort WithWDAMjpegPort = uixt.WithWDAMjpegPort
WithWDALocalPort = uixt.WithWDALocalPort WithLogOn = uixt.WithLogOn
WithWDALocalMjpegPort = uixt.WithWDALocalMjpegPort
WithLogOn = uixt.WithLogOn
) )
type IOSStep struct { type IOSStep struct {
+1 -1
View File
@@ -1,4 +1,4 @@
__version__ = "v4.3.0-beta-09301627" __version__ = "v4.3.0-beta-09302036"
__description__ = "One-stop solution for HTTP(S) testing." __description__ = "One-stop solution for HTTP(S) testing."
+1 -1
View File
@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "httprunner" name = "httprunner"
version = "v4.3.0-beta-09301627" version = "v4.3.0-beta-09302036"
description = "One-stop solution for HTTP(S) testing." description = "One-stop solution for HTTP(S) testing."
license = "Apache-2.0" license = "Apache-2.0"
readme = "README.md" readme = "README.md"