feat(protocol): negotiate display and native input
This commit is contained in:
+143
-23
@@ -13,7 +13,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const SchemaSHA256 = "a86cbdaf2cfb884e3d98467968007e731ca55c6f6eb6dbd5cd6b95e062a9b058"
|
||||
const SchemaSHA256 = "b2bb0a8ac8ef56dbc0e1443eeb5b3028be9e71ec2f5fd8e73928d71b7cd9340c"
|
||||
const ProtocolVersion = "1.0.0"
|
||||
const CurrentWireVersion = "1"
|
||||
const NMinus1WireVersion = "0"
|
||||
@@ -50,21 +50,23 @@ type AssignedDesktop struct {
|
||||
}
|
||||
|
||||
type BrokerSession struct {
|
||||
ID string `json:"id"`
|
||||
PrincipalID string `json:"principal_id"`
|
||||
PoolID string `json:"pool_id"`
|
||||
AssignmentID string `json:"assignment_id,omitempty"`
|
||||
State string `json:"state"`
|
||||
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
|
||||
ReconnectDeadline string `json:"reconnect_deadline,omitempty"`
|
||||
Outcome string `json:"outcome,omitempty"`
|
||||
FailureCode string `json:"failure_code,omitempty"`
|
||||
CleanupState string `json:"cleanup_state"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
CorrelationID string `json:"correlation_id"`
|
||||
RequestedAt string `json:"requested_at"`
|
||||
EndedAt string `json:"ended_at,omitempty"`
|
||||
Version int64 `json:"version"`
|
||||
ID string `json:"id"`
|
||||
PrincipalID string `json:"principal_id"`
|
||||
PoolID string `json:"pool_id"`
|
||||
AssignmentID string `json:"assignment_id,omitempty"`
|
||||
State string `json:"state"`
|
||||
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
|
||||
ReconnectDeadline string `json:"reconnect_deadline,omitempty"`
|
||||
Outcome string `json:"outcome,omitempty"`
|
||||
FailureCode string `json:"failure_code,omitempty"`
|
||||
CleanupState string `json:"cleanup_state"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
CorrelationID string `json:"correlation_id"`
|
||||
RequestedAt string `json:"requested_at"`
|
||||
EndedAt string `json:"ended_at,omitempty"`
|
||||
Version int64 `json:"version"`
|
||||
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
|
||||
EffectiveDisplayMode *DisplayMode `json:"effective_display_mode,omitempty"`
|
||||
}
|
||||
|
||||
type CapabilityProfile struct {
|
||||
@@ -134,6 +136,12 @@ type DeviceRegistrationRequest struct {
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
type DisplayMode struct {
|
||||
ResolutionWidth int64 `json:"resolution_width"`
|
||||
ResolutionHeight int64 `json:"resolution_height"`
|
||||
Fps int64 `json:"fps"`
|
||||
}
|
||||
|
||||
type EntitledPool struct {
|
||||
PoolID string `json:"pool_id"`
|
||||
Name string `json:"name"`
|
||||
@@ -263,8 +271,9 @@ type ManifestGateway struct {
|
||||
}
|
||||
|
||||
type ManifestProfile struct {
|
||||
ID string `json:"id"`
|
||||
Bounds ManifestBounds `json:"bounds"`
|
||||
ID string `json:"id"`
|
||||
Bounds ManifestBounds `json:"bounds"`
|
||||
DisplayMode *DisplayMode `json:"display_mode,omitempty"`
|
||||
}
|
||||
|
||||
type ManifestTunnel struct {
|
||||
@@ -383,11 +392,12 @@ type SessionAuthority struct {
|
||||
}
|
||||
|
||||
type SessionRequest struct {
|
||||
ClientDeviceID string `json:"client_device_id"`
|
||||
DeviceKeyID string `json:"device_key_id"`
|
||||
PoolID string `json:"pool_id"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
|
||||
ClientDeviceID string `json:"client_device_id"`
|
||||
DeviceKeyID string `json:"device_key_id"`
|
||||
PoolID string `json:"pool_id"`
|
||||
IdempotencyKey string `json:"idempotency_key"`
|
||||
PolicySnapshot AllocationPolicy `json:"policy_snapshot"`
|
||||
RequestedDisplayMode *DisplayMode `json:"requested_display_mode,omitempty"`
|
||||
}
|
||||
|
||||
type StableError struct {
|
||||
@@ -767,6 +777,16 @@ func (v BrokerSession) Validate() error {
|
||||
if v.Version != 0 && v.Version < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "version", Code: "minimum"})
|
||||
}
|
||||
if v.RequestedDisplayMode != nil {
|
||||
if err := v.RequestedDisplayMode.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "requested_display_mode", Code: "invalid_object"})
|
||||
}
|
||||
}
|
||||
if v.EffectiveDisplayMode != nil {
|
||||
if err := v.EffectiveDisplayMode.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "effective_display_mode", Code: "invalid_object"})
|
||||
}
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
@@ -812,6 +832,12 @@ func DecodeBrokerSession(data []byte) (BrokerSession, error) {
|
||||
if raw, ok := fields["version"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "version", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["requested_display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_display_mode", Code: "invalid_object"}}}
|
||||
}
|
||||
if raw, ok := fields["effective_display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "effective_display_mode", Code: "invalid_object"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
@@ -1625,6 +1651,84 @@ func EncodeDeviceRegistrationRequest(value DeviceRegistrationRequest) ([]byte, e
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v DisplayMode) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.ResolutionWidth == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "required"})
|
||||
}
|
||||
if v.ResolutionWidth != 0 && v.ResolutionWidth < 320 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "minimum"})
|
||||
}
|
||||
if v.ResolutionWidth > 16384 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_width", Code: "maximum"})
|
||||
}
|
||||
if v.ResolutionHeight == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "required"})
|
||||
}
|
||||
if v.ResolutionHeight != 0 && v.ResolutionHeight < 200 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "minimum"})
|
||||
}
|
||||
if v.ResolutionHeight > 8640 {
|
||||
violations = append(violations, FieldViolation{Field: "resolution_height", Code: "maximum"})
|
||||
}
|
||||
if v.Fps == 0 {
|
||||
violations = append(violations, FieldViolation{Field: "fps", Code: "required"})
|
||||
}
|
||||
if v.Fps != 0 && v.Fps < 1 {
|
||||
violations = append(violations, FieldViolation{Field: "fps", Code: "minimum"})
|
||||
}
|
||||
if v.Fps > 240 {
|
||||
violations = append(violations, FieldViolation{Field: "fps", Code: "maximum"})
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DecodeDisplayMode(data []byte) (DisplayMode, error) {
|
||||
var value DisplayMode
|
||||
if len(data) > 1024*1024 {
|
||||
return value, errors.New("protocol payload exceeds limit")
|
||||
}
|
||||
var fields map[string]json.RawMessage
|
||||
if err := json.Unmarshal(data, &fields); err != nil {
|
||||
return value, err
|
||||
}
|
||||
if raw, ok := fields["fps"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "fps", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["resolution_height"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution_height", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["resolution_width"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "resolution_width", Code: "required"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
return value, err
|
||||
}
|
||||
var trailing any
|
||||
if err := decoder.Decode(&trailing); err != io.EOF {
|
||||
if err == nil {
|
||||
return value, errors.New("trailing JSON value")
|
||||
}
|
||||
return value, err
|
||||
}
|
||||
if err := value.Validate(); err != nil {
|
||||
return value, err
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
|
||||
func EncodeDisplayMode(value DisplayMode) ([]byte, error) {
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(value)
|
||||
}
|
||||
|
||||
func (v EntitledPool) Validate() error {
|
||||
var violations []FieldViolation
|
||||
if v.PoolID == "" {
|
||||
@@ -3202,6 +3306,11 @@ func (v ManifestProfile) Validate() error {
|
||||
if err := v.Bounds.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "bounds", Code: "invalid_object"})
|
||||
}
|
||||
if v.DisplayMode != nil {
|
||||
if err := v.DisplayMode.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "display_mode", Code: "invalid_object"})
|
||||
}
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
@@ -3223,6 +3332,9 @@ func DecodeManifestProfile(data []byte) (ManifestProfile, error) {
|
||||
if raw, ok := fields["id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "id", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "display_mode", Code: "invalid_object"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
@@ -4655,6 +4767,11 @@ func (v SessionRequest) Validate() error {
|
||||
if err := v.PolicySnapshot.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "policy_snapshot", Code: "invalid_object"})
|
||||
}
|
||||
if v.RequestedDisplayMode != nil {
|
||||
if err := v.RequestedDisplayMode.Validate(); err != nil {
|
||||
violations = append(violations, FieldViolation{Field: "requested_display_mode", Code: "invalid_object"})
|
||||
}
|
||||
}
|
||||
if len(violations) > 0 {
|
||||
return ValidationError{Violations: violations}
|
||||
}
|
||||
@@ -4685,6 +4802,9 @@ func DecodeSessionRequest(data []byte) (SessionRequest, error) {
|
||||
if raw, ok := fields["pool_id"]; !ok || bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "pool_id", Code: "required"}}}
|
||||
}
|
||||
if raw, ok := fields["requested_display_mode"]; ok && bytes.Equal(bytes.TrimSpace(raw), []byte("null")) {
|
||||
return value, ValidationError{Violations: []FieldViolation{{Field: "requested_display_mode", Code: "invalid_object"}}}
|
||||
}
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.DisallowUnknownFields()
|
||||
if err := decoder.Decode(&value); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user