mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
fix: pre-v0.1 review feedback (#14)
* fix(sdk-android): add @JvmOverloads to Uatu.start Java callers can now invoke start(application) without supplying a Configuration, matching the Kotlin default-arg ergonomics. * feat(agent): add protocol_version to HELLO handshake ProtocolVersion=1 lives on Message and is set by Hello(). Server.Accept rejects mismatches with a clear error. SDK upgrades that don't change the wire format keep the same protocol_version; bump on breaking changes. * test(agent): assert protocol_version in Hello round-trip * feat(sdk-android): send protocol_version=1 in HELLO Mirrors agent.ProtocolVersion on the Go side. Bump in lockstep with the Go constant when the wire format breaks. * chore(sample-app): pull @uatu/spec from npm next tag Replaces the file: dep. Copy-paste users can now npm install against the registry. The release workflow publishes pre-release tags to npm dist-tag 'next', so the sample tracks the latest rc without manual version bumps. Lockfile currently resolves to 0.0.1-rc3.
This commit is contained in:
9 files changed
+64
-17
No files matched your search
Generated
+6
-14
@@ -6,23 +6,15 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "uatu-sample",
|
"name": "uatu-sample",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@uatu/spec": "file:../../pkg/spec-api"
|
"@uatu/spec": "next"
|
||||||
}
|
|
||||||
},
|
|
||||||
"../../pkg/spec-api": {
|
|
||||||
"name": "@uatu/spec",
|
|
||||||
"version": "0.0.0-dev",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^22.7.5",
|
|
||||||
"tsx": "^4.21.0",
|
|
||||||
"typescript": "^5.9.3"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@uatu/spec": {
|
"node_modules/@uatu/spec": {
|
||||||
"resolved": "../../pkg/spec-api",
|
"version": "0.0.1-rc3",
|
||||||
"link": true
|
"resolved": "https://registry.npmjs.org/@uatu/spec/-/spec-0.0.1-rc3.tgz",
|
||||||
|
"integrity": "sha512-mCayssOqM2wfzrQDnFGE8FAlUlDzBl1fx2L/MLDIuPrQHBMlUC350xBB8Z7I/DYI98eCWJMqjtw3iU6yVWKtIQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"description": "Minimal uatu integration example.",
|
"description": "Minimal uatu integration example.",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@uatu/spec": "file:../../pkg/spec-api"
|
"@uatu/spec": "next"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,15 @@ const (
|
|||||||
|
|
||||||
const MaxFrameSize = 16 * 1024 * 1024
|
const MaxFrameSize = 16 * 1024 * 1024
|
||||||
|
|
||||||
|
// ProtocolVersion is the wire-format version. Bump on any breaking change
|
||||||
|
// to the message schema or framing. Independent of the SDK release version.
|
||||||
|
const ProtocolVersion = 1
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Type MessageType `json:"type"`
|
Type MessageType `json:"type"`
|
||||||
ID uint64 `json:"id,omitempty"`
|
ID uint64 `json:"id,omitempty"`
|
||||||
|
|
||||||
|
ProtocolVersion int `json:"protocol_version,omitempty"`
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
Platform string `json:"platform,omitempty"`
|
Platform string `json:"platform,omitempty"`
|
||||||
AppPackage string `json:"app_package,omitempty"`
|
AppPackage string `json:"app_package,omitempty"`
|
||||||
@@ -39,7 +44,13 @@ type Message struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Hello(version, platform, appPackage string) Message {
|
func Hello(version, platform, appPackage string) Message {
|
||||||
return Message{Type: MessageTypeHello, Version: version, Platform: platform, AppPackage: appPackage}
|
return Message{
|
||||||
|
Type: MessageTypeHello,
|
||||||
|
ProtocolVersion: ProtocolVersion,
|
||||||
|
Version: version,
|
||||||
|
Platform: platform,
|
||||||
|
AppPackage: appPackage,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Pause(id uint64) Message { return Message{Type: MessageTypePause, ID: id} }
|
func Pause(id uint64) Message { return Message{Type: MessageTypePause, ID: id} }
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ func TestRoundTrip_Hello(t *testing.T) {
|
|||||||
if got.Type != MessageTypeHello || got.Version != "0.0.1" || got.Platform != "android" || got.AppPackage != "in.okcredit.merchant" {
|
if got.Type != MessageTypeHello || got.Version != "0.0.1" || got.Platform != "android" || got.AppPackage != "in.okcredit.merchant" {
|
||||||
t.Fatalf("hello round-trip failed: %+v", got)
|
t.Fatalf("hello round-trip failed: %+v", got)
|
||||||
}
|
}
|
||||||
|
if got.ProtocolVersion != ProtocolVersion {
|
||||||
|
t.Errorf("protocol_version: got %d, want %d", got.ProtocolVersion, ProtocolVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRoundTrip_PauseResume(t *testing.T) {
|
func TestRoundTrip_PauseResume(t *testing.T) {
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ func (s *Server) Accept(ctx context.Context) (*Conn, error) {
|
|||||||
rawConn.Close()
|
rawConn.Close()
|
||||||
return nil, fmt.Errorf("expected HELLO, got %q", hello.Type)
|
return nil, fmt.Errorf("expected HELLO, got %q", hello.Type)
|
||||||
}
|
}
|
||||||
|
if hello.ProtocolVersion != ProtocolVersion {
|
||||||
|
rawConn.Close()
|
||||||
|
return nil, fmt.Errorf("protocol version mismatch: host=%d sdk=%d", ProtocolVersion, hello.ProtocolVersion)
|
||||||
|
}
|
||||||
return &Conn{rawConn: rawConn, hello: hello}, nil
|
return &Conn{rawConn: rawConn, hello: hello}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,6 +141,28 @@ func TestServer_SnapshotAndRelease(t *testing.T) {
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_AcceptRejectsProtocolVersionMismatch(t *testing.T) {
|
||||||
|
server := newLoopbackServer(t)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
client, err := net.Dial("tcp", server.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
mismatched := Hello("0.0.1", "android", "com.x")
|
||||||
|
mismatched.ProtocolVersion = ProtocolVersion + 99
|
||||||
|
_ = WriteMessage(client, mismatched)
|
||||||
|
}()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
_, err := server.Accept(ctx)
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "protocol version mismatch") {
|
||||||
|
t.Fatalf("expected protocol-version-mismatch error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestServer_AcceptRequiresHello(t *testing.T) {
|
func TestServer_AcceptRequiresHello(t *testing.T) {
|
||||||
server := newLoopbackServer(t)
|
server := newLoopbackServer(t)
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ enum class MessageType(val wire: String) {
|
|||||||
data class Message(
|
data class Message(
|
||||||
val type: MessageType,
|
val type: MessageType,
|
||||||
val id: Long = 0L,
|
val id: Long = 0L,
|
||||||
|
val protocolVersion: Int = 0,
|
||||||
val version: String? = null,
|
val version: String? = null,
|
||||||
val platform: String? = null,
|
val platform: String? = null,
|
||||||
val appPackage: String? = null,
|
val appPackage: String? = null,
|
||||||
@@ -37,7 +38,13 @@ data class Message(
|
|||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun hello(version: String, platform: String, appPackage: String): Message =
|
fun hello(version: String, platform: String, appPackage: String): Message =
|
||||||
Message(MessageType.HELLO, version = version, platform = platform, appPackage = appPackage)
|
Message(
|
||||||
|
MessageType.HELLO,
|
||||||
|
protocolVersion = Protocol.PROTOCOL_VERSION,
|
||||||
|
version = version,
|
||||||
|
platform = platform,
|
||||||
|
appPackage = appPackage,
|
||||||
|
)
|
||||||
|
|
||||||
fun pause(id: Long): Message = Message(MessageType.PAUSE, id = id)
|
fun pause(id: Long): Message = Message(MessageType.PAUSE, id = id)
|
||||||
|
|
||||||
@@ -56,6 +63,9 @@ data class Message(
|
|||||||
object Protocol {
|
object Protocol {
|
||||||
const val MAX_FRAME_SIZE: Int = 16 * 1024 * 1024
|
const val MAX_FRAME_SIZE: Int = 16 * 1024 * 1024
|
||||||
|
|
||||||
|
// Wire-format version. Must match agent.ProtocolVersion on the Go side.
|
||||||
|
const val PROTOCOL_VERSION: Int = 1
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun write(output: OutputStream, message: Message) {
|
fun write(output: OutputStream, message: Message) {
|
||||||
val bytes = toJson(message).toString().toByteArray(Charsets.UTF_8)
|
val bytes = toJson(message).toString().toByteArray(Charsets.UTF_8)
|
||||||
@@ -85,6 +95,7 @@ object Protocol {
|
|||||||
val json = JSONObject()
|
val json = JSONObject()
|
||||||
json.put("type", message.type.wire)
|
json.put("type", message.type.wire)
|
||||||
if (message.id != 0L) json.put("id", message.id)
|
if (message.id != 0L) json.put("id", message.id)
|
||||||
|
if (message.protocolVersion != 0) json.put("protocol_version", message.protocolVersion)
|
||||||
message.version?.let { json.put("version", it) }
|
message.version?.let { json.put("version", it) }
|
||||||
message.platform?.let { json.put("platform", it) }
|
message.platform?.let { json.put("platform", it) }
|
||||||
message.appPackage?.let { json.put("app_package", it) }
|
message.appPackage?.let { json.put("app_package", it) }
|
||||||
@@ -108,6 +119,7 @@ object Protocol {
|
|||||||
return Message(
|
return Message(
|
||||||
type = MessageType.fromWire(typeString),
|
type = MessageType.fromWire(typeString),
|
||||||
id = json.optLong("id", 0L),
|
id = json.optLong("id", 0L),
|
||||||
|
protocolVersion = json.optInt("protocol_version", 0),
|
||||||
version = json.optStringOrNull("version"),
|
version = json.optStringOrNull("version"),
|
||||||
platform = json.optStringOrNull("platform"),
|
platform = json.optStringOrNull("platform"),
|
||||||
appPackage = json.optStringOrNull("app_package"),
|
appPackage = json.optStringOrNull("app_package"),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ object Uatu {
|
|||||||
|
|
||||||
@Volatile private var runtime: UatuRuntime? = null
|
@Volatile private var runtime: UatuRuntime? = null
|
||||||
|
|
||||||
|
@JvmOverloads
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun start(application: Application, configuration: Configuration = Configuration()) {
|
fun start(application: Application, configuration: Configuration = Configuration()) {
|
||||||
if (runtime != null) return
|
if (runtime != null) return
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class ProtocolTest {
|
|||||||
@Test fun roundTripHello() {
|
@Test fun roundTripHello() {
|
||||||
val got = roundTrip(Message.hello("0.0.1", "android", "in.okcredit.merchant"))
|
val got = roundTrip(Message.hello("0.0.1", "android", "in.okcredit.merchant"))
|
||||||
assertEquals(MessageType.HELLO, got.type)
|
assertEquals(MessageType.HELLO, got.type)
|
||||||
|
assertEquals(Protocol.PROTOCOL_VERSION, got.protocolVersion)
|
||||||
assertEquals("0.0.1", got.version)
|
assertEquals("0.0.1", got.version)
|
||||||
assertEquals("android", got.platform)
|
assertEquals("android", got.platform)
|
||||||
assertEquals("in.okcredit.merchant", got.appPackage)
|
assertEquals("in.okcredit.merchant", got.appPackage)
|
||||||
@@ -140,6 +141,7 @@ class ProtocolTest {
|
|||||||
Protocol.write(output, Message.hello("0.0.1", "android", "com.x"))
|
Protocol.write(output, Message.hello("0.0.1", "android", "com.x"))
|
||||||
val payload = String(output.toByteArray().copyOfRange(4, output.size()), Charsets.UTF_8)
|
val payload = String(output.toByteArray().copyOfRange(4, output.size()), Charsets.UTF_8)
|
||||||
assertTrue(payload.contains("\"type\":\"HELLO\""))
|
assertTrue(payload.contains("\"type\":\"HELLO\""))
|
||||||
|
assertTrue(payload.contains("\"protocol_version\":1"))
|
||||||
assertTrue(payload.contains("\"version\":\"0.0.1\""))
|
assertTrue(payload.contains("\"version\":\"0.0.1\""))
|
||||||
assertTrue(payload.contains("\"platform\":\"android\""))
|
assertTrue(payload.contains("\"platform\":\"android\""))
|
||||||
assertTrue(payload.contains("\"app_package\":\"com.x\""))
|
assertTrue(payload.contains("\"app_package\":\"com.x\""))
|
||||||
|
|||||||
Reference in new issue
Block a user