VideoTools/vendor/github.com/fredbi/uri/ip.go
Stu Leak 68df790d27 Fix player frame generation and video playback
Major improvements to UnifiedPlayer:

1. GetFrameImage() now works when paused for responsive UI updates
2. Play() method properly starts FFmpeg process
3. Frame display loop runs continuously for smooth video display
4. Disabled audio temporarily to fix video playback fundamentals
5. Simplified FFmpeg command to focus on video stream only

Player now:
- Generates video frames correctly
- Shows video when paused
- Has responsive progress tracking
- Starts playback properly

Next steps: Re-enable audio playback once video is stable
2026-01-07 22:20:00 -05:00

230 lines
6.0 KiB
Go

package uri
import (
"fmt"
"net/netip"
"net/url"
"strings"
"unicode/utf8"
)
// IsIP indicates if the URI host was specified using an IP address (v4 or v6).
func (a authorityInfo) IsIP() bool {
// IPvFuture won't parse as a netip.Addr
return a.isIPv4 || (a.isIPv6 && !a.isIPvFuture)
}
// IPAddr returns the parsed netip.Addr whenever IsIP is true (or the zero value whenever false).
func (a authorityInfo) IPAddr() netip.Addr {
if !a.IsIP() {
return netip.Addr{}
}
unescapedHost, _ := url.PathUnescape(a.host) // TODO
addr, _ := netip.ParseAddr(unescapedHost)
return addr
}
//nolint:dupword,mnd // false positive in the BNF comment
//nolint:mnd // straightforward interpretation, no need to define a constant
func validateIPv4(host string) error {
//
// check for IPv4 address
//
// The host SHOULD check
// the string syntactically for a dotted-decimal number before
// looking it up in the Domain Name System.
//
// IPv4 may **NOT** contain percent-encoded escaped characters, e.g. 192.168.0.%31 is not valid.
// Reference: https://www.rfc-editor.org/rfc/rfc3986#appendix-A
//
// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
// dec-octet = DIGIT ; 0-9
// / %x31-39 DIGIT ; 10-99
// / "1" 2DIGIT ; 100-199
// / "2" %x30-34 DIGIT ; 200-249
// / "25" %x30-35 ; 250-255
var (
partNum, digitNum uint8
currentPart [3]byte
)
for i := 0; i < len(host); i++ {
b := host[i]
switch {
case isDigit(b):
switch digitNum {
case 0:
if b > '2' {
return errValueGreater255
}
case 1:
if currentPart[0] == '2' && b > '5' {
return errValueGreater255
}
case 2:
if currentPart[0] == '2' && currentPart[1] == '5' && b > '5' {
return errValueGreater255
}
default:
return errValueGreater255
}
currentPart[digitNum] = b
digitNum++
case b == '.':
if digitNum == 0 {
return errAtLeastOneDigit
}
if digitNum > 1 && currentPart[0] == '0' {
return errLeadingZero
}
partNum++
if partNum > 3 {
return errTooLong
}
digitNum = 0
default:
// optimize the default path to return a value error
return errInvalidCharacter
}
}
if partNum < 3 {
return errTooShort
}
return nil
}
func validateIPv6(host string) error {
// * check IPv6
// IPv6address = 6( h16 ":" ) ls32
// / "::" 5( h16 ":" ) ls32
// / [ h16 ] "::" 4( h16 ":" ) ls32
// / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
// / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
// / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
// / [ *4( h16 ":" ) h16 ] "::" ls32
// / [ *5( h16 ":" ) h16 ] "::" h16
// / [ *6( h16 ":" ) h16 ] "::"
//
// ls32 = ( h16 ":" h16 ) / IPv4address
// ; least-significant 32 bits of address
//
// h16 = 1*4HEXDIG
// ; 16 bits of address represented in hexadecimal
// In addition to RFC 8986, RFC6874 states that IPv6 may include a zone ID like so:
// IPv6address *("%25" ZoneID}
// The zone ID may be percent-encoded
// ZoneID = 1*( unreserved / pct-encoded )
//
var ipv6WithoutZone, zoneID string
idx := strings.IndexRune(host, '%')
switch {
case idx == 0:
return errorsJoin(
ErrInvalidHostAddress,
fmt.Errorf("only the zoneID of an IPv6 literal may be percent-encoded: %w", ErrURI),
)
case idx > 0:
ipv6WithoutZone = host[:idx]
zoneID = host[idx:]
default:
ipv6WithoutZone = host
}
// check for IPv6 address
// IPv6 may contain a percent-encoded escaped zone identifier: unescape it first if present
addr, err := netip.ParseAddr(ipv6WithoutZone)
if err != nil {
return errorsJoin(
ErrInvalidHostAddress,
fmt.Errorf("a square-bracketed host part should be a valid IPv6 address: %q: %w", host, ErrURI),
)
}
if !addr.Is6() {
// RFC3986 stipulates that only IPv6 addresses are within square brackets
return errorsJoin(
ErrInvalidHostAddress,
fmt.Errorf("a square-bracketed host part should not contain an IPv4 address: %q: %w", host, ErrURI),
)
}
if zoneID == "" {
return nil
}
// check the escaping of the zoneID. Notice than if a zone is added, it must not be empty.
if len(zoneID) < 4 || zoneID[1] != '2' || zoneID[2] != '5' {
return errorsJoin(
ErrInvalidHostAddress,
fmt.Errorf("IPv6 zoneID separator in URI must be percent-encoded with %%25, but got: %q: %w", zoneID, ErrURI),
)
}
if err := validateUnreservedWithExtra(zoneID, nil); err != nil {
return errorsJoin(
ErrInvalidHostAddress,
fmt.Errorf("invalid IPv6 zoneID %q: %w", zoneID, err),
ErrURI,
)
}
return nil
}
// validateIPvFuture covers the special provision in the RFC for future IP scheme.
// The passed argument removes the heading "v" character.
//
// Example: http://[v6.fe80::a_en1]
//
// Reference: https://www.rfc-editor.org/rfc/rfc3986#section-3.2.2
//
// IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
func validateIPvFuture(address string) error {
var (
offset int
foundHexDigits, foundDot bool
)
for offset < len(address) {
r, size := utf8.DecodeRuneInString(address[offset:])
if r == utf8.RuneError {
return fmt.Errorf("invalid UTF8 rune near: %q: %w", address[offset:], ErrURI)
}
offset += size
if r == dotSeparator {
foundDot = true
break
}
if !isHex(r) {
return fmt.Errorf(
"invalid IP vFuture format: expect an hexadecimal version tag: %w", ErrURI,
)
}
foundHexDigits = true
}
if !foundHexDigits || !foundDot {
return fmt.Errorf(
"invalid IP vFuture format: expect a '.' after an hexadecimal version tag: %w", ErrURI,
)
}
if offset >= len(address) {
return fmt.Errorf("invalid IP vFuture format: expect a non-empty address after the version tag: %w", ErrURI)
}
// TODO: wrong because IpvFuture is not escaped
return validateUnreservedWithExtra(address[offset:], userInfoExtraRunes)
}