Skip to content

Commit

Permalink
improve request logic and simplify protocol error handling logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasAlxDmy committed Dec 8, 2023
1 parent 12678f4 commit 3e6550c
Show file tree
Hide file tree
Showing 11 changed files with 630 additions and 501 deletions.
4 changes: 2 additions & 2 deletions examples/unlock/unlock.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func main() {

// Specify the user-agent header value used in HTTP requests to Tesla's servers. The default
// value is constructed from your package name and account.LibraryVersion.
account.UserAgent = "example-unlock/1.0.0"
userAgent := "example-unlock/1.0.0"

if vin == "" {
logger.Printf("Must specify VIN")
Expand Down Expand Up @@ -61,7 +61,7 @@ func main() {

// This example program sends commands over the Internet, which requires a Tesla account login
// token. The protocol can also work over BLE; see other programs in the example directory.
acct, err := account.New(string(oauthToken))
acct, err := account.New(string(oauthToken), userAgent)
if err != nil {
logger.Printf("Authentication error: %s", err)
return
Expand Down
13 changes: 6 additions & 7 deletions pkg/account/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,9 @@ import (
var (
//go:embed version.txt
libraryVersion string
// UserAgent is sent in HTTP requests.
//
// The default value is "<main package name>/<commit hash> tesla-sdk/<version>".
UserAgent = buildUserAgent()
)

func buildUserAgent() string {
func buildUserAgent(appVersion string) string {
library := strings.TrimSpace("tesla-sdk/" + libraryVersion)
build, ok := debug.ReadBuildInfo()
if !ok {
Expand All @@ -41,6 +37,9 @@ func buildUserAgent() string {
return library
}
app := path[len(path)-1]
if appVersion != "" {
app = fmt.Sprintf("%s-v%s", app, appVersion)
}

var version string
if build.Main.Version != "(devel)" && build.Main.Version != "" {
Expand Down Expand Up @@ -114,7 +113,7 @@ func (p *oauthPayload) domain() string {
}

// New returns an [Account] that can be used to fetch a [vehicle.Vehicle].
func New(oauthToken string) (*Account, error) {
func New(oauthToken, userAgentSuffix string) (*Account, error) {
parts := strings.Split(oauthToken, ".")
if len(parts) != 3 {
return nil, fmt.Errorf("client provided malformed OAuth token")
Expand All @@ -133,7 +132,7 @@ func New(oauthToken string) (*Account, error) {
return nil, fmt.Errorf("client provided OAuth token with invalid audiences")
}
return &Account{
UserAgent: UserAgent,
UserAgent: buildUserAgent(userAgentSuffix),
authHeader: "Bearer " + strings.TrimSpace(oauthToken),
Host: domain,
}, nil
Expand Down
20 changes: 10 additions & 10 deletions pkg/account/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,29 @@ func b64Encode(payload string) string {

func TestNewAccount(t *testing.T) {
validDomain := "fleet-api.example.tesla.com"
if _, err := New(""); err == nil {
if _, err := New("", ""); err == nil {
t.Error("Returned success empty JWT")
}
if _, err := New(b64Encode(validDomain)); err == nil {
if _, err := New(b64Encode(validDomain), ""); err == nil {
t.Error("Returned success on one-field JWT")
}
if _, err := New("x." + b64Encode(validDomain)); err == nil {
if _, err := New("x."+b64Encode(validDomain), ""); err == nil {
t.Error("Returned success on two-field JWT")
}
if _, err := New("x." + b64Encode(validDomain) + "y.z"); err == nil {
if _, err := New("x."+b64Encode(validDomain)+"y.z", ""); err == nil {
t.Error("Returned success on four-field JWT")
}
if _, err := New("x." + validDomain + ".y"); err == nil {
if _, err := New("x."+validDomain+".y", ""); err == nil {
t.Error("Returned success on non-base64 encoded JWT")
}
if _, err := New("x." + b64Encode("{\"aud\": \"example.com\"}") + ".y"); err == nil {
if _, err := New("x."+b64Encode("{\"aud\": \"example.com\"}")+".y", ""); err == nil {
t.Error("Returned success on untrusted domain")
}
if _, err := New("x." + b64Encode(fmt.Sprintf("{\"aud\": \"%s\"}", validDomain)) + ".y"); err == nil {
if _, err := New("x."+b64Encode(fmt.Sprintf("{\"aud\": \"%s\"}", validDomain))+".y", ""); err == nil {
t.Error("Returned when aud field not a list")
}

acct, err := New("x." + b64Encode(fmt.Sprintf("{\"aud\": [\"%s\"]}", validDomain)) + ".y")
acct, err := New("x."+b64Encode(fmt.Sprintf("{\"aud\": [\"%s\"]}", validDomain))+".y", "")
if err != nil {
t.Fatalf("Returned error on valid JWT: %s", err)
}
Expand All @@ -49,7 +49,7 @@ func TestDomainDefault(t *testing.T) {
Audiences: []string{"https://auth.tesla.com/nts"},
}

acct, err := New(makeTestJWT(payload))
acct, err := New(makeTestJWT(payload), "")
if err != nil {
t.Fatalf("Returned error on valid JWT: %s", err)
}
Expand All @@ -64,7 +64,7 @@ func TestDomainExtraction(t *testing.T) {
OUCode: "EU",
}

acct, err := New(makeTestJWT(payload))
acct, err := New(makeTestJWT(payload), "")
if err != nil {
t.Fatalf("Returned error on valid JWT: %s", err)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/account/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.1
0.0.2
2 changes: 1 addition & 1 deletion pkg/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ func (c *Config) Account() (*account.Account, error) {
if err != nil {
return nil, err
}
return account.New(token)
return account.New(token, "")
}

// SavePrivateKey writes skey to the system keyring or file, depending on what options are
Expand Down
6 changes: 2 additions & 4 deletions pkg/connector/inet/inet.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,8 @@ func SendFleetAPICommand(ctx context.Context, client *http.Client, userAgent, au
switch result.StatusCode {
case http.StatusOK:
return body, nil
case http.StatusUnprocessableEntity:
if bytes.Contains(body, []byte("vehicle does not support signed commands")) {
return nil, protocol.ErrProtocolNotSupported
}
case http.StatusUnprocessableEntity: // HTTP: 422 on commands endpoint means protocol is not supported (fallback to regular commands)
return nil, protocol.ErrProtocolNotSupported
case http.StatusServiceUnavailable:
return nil, ErrVehicleNotAwake
case http.StatusRequestTimeout:
Expand Down
Loading

0 comments on commit 3e6550c

Please sign in to comment.