1// +build go1.13
2
3// Copyright (c) Microsoft Corporation. All rights reserved.
4// Licensed under the MIT License.
5
6package azcore
7
8import (
9	"bytes"
10	"fmt"
11	"os"
12	"runtime"
13	"strings"
14)
15
16// TelemetryOptions configures the telemetry policy's behavior.
17type TelemetryOptions struct {
18	// Value is a string prepended to each request's User-Agent and sent to the service.
19	// The service records the user-agent in logs for diagnostics and tracking of client requests.
20	Value string
21
22	// ApplicationID is an application-specific identification string used in telemetry.
23	// It has a maximum length of 24 characters and must not contain any spaces.
24	ApplicationID string
25
26	// Disabled will prevent the addition of any telemetry data to the User-Agent.
27	Disabled bool
28}
29
30type telemetryPolicy struct {
31	telemetryValue string
32}
33
34// NewTelemetryPolicy creates a telemetry policy object that adds telemetry information to outgoing HTTP requests.
35// The format is [<application_id> ]azsdk-<sdk_language>-<package_name>/<package_version> <platform_info> [<custom>].
36// Pass nil to accept the default values; this is the same as passing a zero-value options.
37func NewTelemetryPolicy(o *TelemetryOptions) Policy {
38	if o == nil {
39		o = &TelemetryOptions{}
40	}
41	tp := telemetryPolicy{}
42	if o.Disabled {
43		return &tp
44	}
45	b := &bytes.Buffer{}
46	// normalize ApplicationID
47	if o.ApplicationID != "" {
48		o.ApplicationID = strings.ReplaceAll(o.ApplicationID, " ", "/")
49		if len(o.ApplicationID) > 24 {
50			o.ApplicationID = o.ApplicationID[:24]
51		}
52		b.WriteString(o.ApplicationID)
53		b.WriteRune(' ')
54	}
55	// write out telemetry string
56	if o.Value != "" {
57		b.WriteString(o.Value)
58		b.WriteRune(' ')
59	}
60	b.WriteString(UserAgent)
61	b.WriteRune(' ')
62	b.WriteString(platformInfo)
63	tp.telemetryValue = b.String()
64	return &tp
65}
66
67func (p telemetryPolicy) Do(req *Request) (*Response, error) {
68	if p.telemetryValue == "" {
69		return req.Next()
70	}
71	// preserve the existing User-Agent string
72	if ua := req.Request.Header.Get(HeaderUserAgent); ua != "" {
73		p.telemetryValue = fmt.Sprintf("%s %s", p.telemetryValue, ua)
74	}
75	var rt requestTelemetry
76	if req.OperationValue(&rt) {
77		p.telemetryValue = fmt.Sprintf("%s %s", string(rt), p.telemetryValue)
78	}
79	req.Request.Header.Set(HeaderUserAgent, p.telemetryValue)
80	return req.Next()
81}
82
83// NOTE: the ONLY function that should write to this variable is this func
84var platformInfo = func() string {
85	operatingSystem := runtime.GOOS // Default OS string
86	switch operatingSystem {
87	case "windows":
88		operatingSystem = os.Getenv("OS") // Get more specific OS information
89	case "linux": // accept default OS info
90	case "freebsd": //  accept default OS info
91	}
92	return fmt.Sprintf("(%s; %s)", runtime.Version(), operatingSystem)
93}()
94
95// used for adding per-request telemetry
96type requestTelemetry string
97