mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-04 23:16:57 +08:00
feat: create free port for WDA HTTP driver
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
v4.3.0-beta-09301627
|
v4.3.0-beta-09302036
|
||||||
+4
-6
@@ -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,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
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user