diff --git a/google/example/endpointsapis/goapp/main.go b/google/example/endpointsapis/goapp/main.go index 84603559..3bbc4acf 100644 --- a/google/example/endpointsapis/goapp/main.go +++ b/google/example/endpointsapis/goapp/main.go @@ -19,15 +19,12 @@ import ( // Check calls Service Control API v2 for admission control. // Name specifies the target resource name. Permission specifies -// the required permission on the target resource. -func check(w http.ResponseWriter, r *http.Request, name string, permission string) (string, error) { - client, err := servicecontrol.NewService(r.Context()) - if err != nil { - return "", err - } +// the required permission on the target resource. Received +// specifies the timestamp when the request is received. +func check(w http.ResponseWriter, r *http.Request, name string, permission string, received time.Time, client *servicecontrol.Service) (string, error) { // Construct CheckRequest from the incoming HTTP request. // The code assumes the incoming request processed by App Engine ingress. - checkRequest := &servicecontrol.CheckRequest{ + req := &servicecontrol.CheckRequest{ ServiceConfigId: "latest", Attributes: &servicecontrol.AttributeContext{ Origin: &servicecontrol.Peer{ @@ -41,7 +38,7 @@ func check(w http.ResponseWriter, r *http.Request, name string, permission strin }, Request: &servicecontrol.Request{ Id: r.Header.Get("x-appengine-request-log-id"), - Time: time.Now().UTC().Format(time.RFC3339), + Time: received.UTC().Format(time.RFC3339), Method: r.Method, Scheme: r.Header.Get("x-forwarded-proto"), Host: r.Host, @@ -53,6 +50,9 @@ func check(w http.ResponseWriter, r *http.Request, name string, permission strin "referer": r.Header.Get("referer"), }, }, + Resource: &servicecontrol.Resource{ + Name: name, + }, }, Resources: []*servicecontrol.ResourceInfo{ { @@ -62,18 +62,64 @@ func check(w http.ResponseWriter, r *http.Request, name string, permission strin }, }, } - response, err := client.Services.Check("endpointsapis.appspot.com", checkRequest).Do() + resp, err := client.Services.Check("endpointsapis.appspot.com", req).Do() if err != nil { return "", err } - responseJSON, err := response.MarshalJSON() + json, err := resp.MarshalJSON() if err != nil { return "", err } - return string(responseJSON), nil + return string(json), nil } -func admission(w http.ResponseWriter, r *http.Request) (string, error) { +// Report calls Service Control API v2 for telemetry reporting. +// Name specifies the target resource name. ResponseCode specifies +// the response code returned to user. Received specifies the +// timestamp when the request is received. +func report(w http.ResponseWriter, r *http.Request, name string, responseCode int64, received time.Time, client *servicecontrol.Service) (string, error) { + // Construct ReportRequest from the incoming HTTP request. + // The code assumes the incoming request processed by App Engine ingress. + req := &servicecontrol.ReportRequest{ + ServiceConfigId: "latest", + Operations: []*servicecontrol.AttributeContext{ + { + Api: &servicecontrol.Api{ + Service: "endpointsapis.appspot.com", + Operation: "google.example.endpointsapis.v1.Workspaces.GetWorkspace", + Version: "v1", + Protocol: r.Header.Get("x-forwarded-proto"), + }, + Request: &servicecontrol.Request{ + Size: r.ContentLength, + Time: received.UTC().Format(time.RFC3339), + }, + Response: &servicecontrol.Response{ + Time: time.Now().UTC().Format(time.RFC3339), + Code: responseCode, + Headers: map[string]string{ + "x-backend-latency": "0.007", + }, + }, + Destination: &servicecontrol.Peer{ + RegionCode: "us-central1", + }, + Resource: &servicecontrol.Resource{ + Name: name, + }, + }, + }, + } + _, err := client.Services.Report("endpointsapis.appspot.com", req).Do() + if err != nil { + return "", err + } + return "{}", nil +} + +// Parse processes the request path and extract the resource name and +// permissions. +func parse(r *http.Request) (string, string, error) { // Split the request path. segments := strings.Split(r.URL.Path, "/") @@ -81,25 +127,45 @@ func admission(w http.ResponseWriter, r *http.Request) (string, error) { // "/v1/projects/*/locations/*/workspaces". They correspond to the // GetWorkspace() and ListWorkspaces() methods defined in ../v1/workspace.proto. if segments[0] != "" || segments[1] != "v1" || segments[2] != "projects" || segments[4] != "locations" || segments[6] != "workspaces" || len(segments) > 8 { - return "", errors.New("Resource '" + r.URL.Path + "' not found.") + return "", "", errors.New("Resource '" + r.URL.Path + "' not found.") } + // Skip prefix "/v1/". resource := r.URL.Path[4:] permission := "endpointsapis.appspot.com/workspaces.list" if len(segments) == 8 { permission = "endpointsapis.appspot.com/workspaces.get" } - return check(w, r, resource, permission) + return resource, permission, nil } func indexHandler(w http.ResponseWriter, r *http.Request) { - // Perform admission control. - result, err := admission(w, r) + received := time.Now() + // Create a client for Service Control API v2. + client, err := servicecontrol.NewService(r.Context()) + if err != nil { + fmt.Fprintln(w, "Error:") + fmt.Fprintln(w, err.Error()) + return + } + + resource, permission, err := parse(r) + if err != nil { + fmt.Fprintln(w, "Error:") + fmt.Fprintln(w, err.Error()) + return + } + + // Perform admission control. + result, err := check(w, r, resource, permission, received, client) + + var responseCode int64 = 200 // Print the admission control result. if err != nil { fmt.Fprintln(w, "Error:") fmt.Fprintln(w, err.Error()) + responseCode = 403 } else { fmt.Fprintln(w, "CheckResponse:") fmt.Fprintln(w, result) @@ -116,6 +182,9 @@ func indexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "%v: %v\n", key, value) } } + + // Perform telemetry report. + report(w, r, resource, responseCode, received, client) } func main() {