-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.go
162 lines (139 loc) · 5.69 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright Josh Komoroske. All rights reserved.
// Use of this source code is governed by the MIT license,
// a copy of which can be found in the LICENSE.txt file.
// SPDX-License-Identifier: MIT
package main
import (
"fmt"
"log"
"net/http"
"net/http/httputil"
"net/url"
"os"
"strconv"
"github.com/joshdk/google-analytics-proxy/analytics"
)
// version is used to hold the version string. Is replaced at go build time
// with -ldflags.
var version = "development"
func main() {
if err := mainCmd(); err != nil {
fmt.Println("joshdk/google-analytics-proxy:", err) // nolint:forbidigo
os.Exit(1)
}
}
func mainCmd() error {
log.Printf("joshdk/google-analytics-proxy version %s", version)
// listenAddress is the host and port that the proxy will listen on.
// See net.Dial for details of the address format.
// Example: "localhost:8080" "0.0.0.0:8080" ":8080"
listenAddress := os.Getenv("LISTEN")
// tlsCertFile is optionally the path to a TLS certificate file, used for
// listening and serving HTTPS connections. Must always be configured with
// tlsKeyFile.
// Example: "/path/to/tls.pem"
tlsCertFile := os.Getenv("TLS_CERT_PATH")
// tlsKeyFile is optionally the path to a TLS private key file, used for
// listening and serving HTTPS connections. Must always be configured with
// tlsCertFile.
// Example: "/path/to/tls.key"
tlsKeyFile := os.Getenv("TLS_KEY_PATH")
// upstreamEndpoint is the address of the upstream service to be
// proxied.
// Example: "https://example.com" "http://:80"
upstreamEndpoint := os.Getenv("UPSTREAM_ENDPOINT")
// upstreamHostname optionally is the hostname to used when proxying
// requests to the upstream. Used for hostname based routing. If empty, the
// value of $GOOGLE_ANALYTICS_PROPERTY_NAME will be used.
// Example: "example.com"
upstreamHostname := os.Getenv("UPSTREAM_HOSTNAME")
// googleAnalyticsTrackingID is the tracking id for the Google
// Analytics property that you want to track pageview events for. This
// can be found in your Google Analytics dashboard.
// Example: "UA-123456789-1"
googleAnalyticsTrackingID := os.Getenv("GOOGLE_ANALYTICS_TRACKING_ID")
// googleAnalyticsPropertyName is the name for the Google Analytics
// property that you want to track pageview events for. This can be
// found in your Google Analytics dashboard. Will be used as the upstream
// hostname in proxied requests if $UPSTREAM_HOSTNAME is empty.
// Example: "example.com"
googleAnalyticsPropertyName := os.Getenv("GOOGLE_ANALYTICS_PROPERTY_NAME")
// googleAnalyticsDryRun can optionally be used to disable reporting
// pageview events with Google Analytics. See strconv.ParseBool() for
// acceptable values.
// Example: "true"
googleAnalyticsDryRun := os.Getenv("GOOGLE_ANALYTICS_DRY_RUN")
// Validate that the required settings are not empty.
switch {
case googleAnalyticsTrackingID == "":
return fmt.Errorf("GOOGLE_ANALYTICS_TRACKING_ID was not provided")
case googleAnalyticsPropertyName == "":
return fmt.Errorf("GOOGLE_ANALYTICS_PROPERTY_NAME was not provided")
case upstreamEndpoint == "":
return fmt.Errorf("UPSTREAM_ENDPOINT was not provided")
}
// Validate the TLS settings, and set sane defaults.
switch {
// Validate HTTP listen mode.
case tlsCertFile == "" && tlsKeyFile == "":
if listenAddress == "" {
// Set a default listen address if none was given.
listenAddress = "0.0.0.0:8080"
}
// Validate HTTPS listen mode.
case tlsCertFile != "" && tlsKeyFile != "":
if listenAddress == "" {
// Set a default listen address if none was given.
listenAddress = "0.0.0.0:8443"
}
default:
// HTTPS listen mode was only partially (mis)configured.
return fmt.Errorf("TLS_CERT_PATH and TLS_KEY_PATH were not both provided")
}
// Parse the upstream endpoint address to ensure that it's valid.
upstreamURL, err := url.Parse(upstreamEndpoint)
if err != nil {
return err
}
// Use the property name for the upstream hostname, if one was not
// explicitly given.
if upstreamHostname == "" {
upstreamHostname = googleAnalyticsPropertyName
}
// Create a reverse proxy HTTP handler for our upstream. This handler is
// responsible for relaying all downstream client requests to the upstream
// service, and the upstream service responses back to the downstream
// client.
log.Printf("proxying traffic to %s (%s)", upstreamEndpoint, upstreamHostname)
proxy := httputil.NewSingleHostReverseProxy(upstreamURL)
// Modify the original proxy director function, only updating the request
// hostname so that any hostname base routing that is performed by the
// upstream service continues to work correctly.
original := proxy.Director
proxy.Director = func(request *http.Request) {
request.Host = upstreamHostname
original(request)
}
// Parse the Google Analytics dry run value. Intentionally ignore all
// errors and default to false.
googleAnalyticsDryRunBool, _ := strconv.ParseBool(googleAnalyticsDryRun)
// Create a tracker for sending pageviews to Google Analytics.
if !googleAnalyticsDryRunBool {
log.Printf("tracking analytics for %s (%s)", googleAnalyticsTrackingID, googleAnalyticsPropertyName)
} else {
log.Printf("skipping analytics for %s (%s)", googleAnalyticsTrackingID, googleAnalyticsPropertyName)
}
tracker := &analytics.Tracker{
TrackingID: googleAnalyticsTrackingID,
PropertyName: googleAnalyticsPropertyName,
DryRun: googleAnalyticsDryRunBool,
Handler: proxy,
}
// Start the server and listen for incoming requests!
if tlsCertFile != "" && tlsKeyFile != "" {
log.Printf("serving HTTPS on %s", listenAddress)
return http.ListenAndServeTLS(listenAddress, tlsCertFile, tlsKeyFile, tracker)
}
log.Printf("serving HTTP on %s", listenAddress)
return http.ListenAndServe(listenAddress, tracker)
}