Copyright 2020 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package middleware

import (
	
	
	
	
	
	
)
statsKey is the type of the context key for stats.
type statsKey struct{}
Stats returns a Middleware that, instead of serving the page, serves statistics about the page.
func () Middleware {
	return func( http.Handler) http.Handler {
		return http.HandlerFunc(func( http.ResponseWriter,  *http.Request) {
			 := newStatsResponseWriter()
			 := context.WithValue(.Context(), statsKey{}, .stats.Other)
			.ServeHTTP(, .WithContext())
			.WriteStats(, )
		})
	}
}
SetStat sets a stat named key in the current context. If key already has a value, the old and new value are both stored in a slice.
func ( context.Context,  string,  interface{}) {
	 := .Value(statsKey{})
	if  == nil {
		return
	}
	 := .(map[string]interface{})
	,  := []
	if ! {
		[] = 
	} else if ,  := .([]interface{});  {
		[] = append(, )
	} else {
		[] = []interface{}{, }
	}
}
ElapsedStat records as a stat the elapsed time for a function execution. Invoke like so: defer ElapsedStat(ctx, "FunctionName")() The resulting stat will be called "FunctionName ms" and will be the wall-clock execution time of the function in milliseconds.
func ( context.Context,  string) func() {
	 := time.Now()
	return func() {
		SetStat(, +" ms", time.Since().Milliseconds())
	}
}
statsResponseWriter is an http.ResponseWriter that tracks statistics about the page being written.
type statsResponseWriter struct {
	header http.Header // required for a ResponseWriter; ignored
	start  time.Time   // start time of request
	hasher hash.Hash64
	stats  PageStats
}

type PageStats struct {
	MillisToFirstByte int64
	MillisToLastByte  int64
	Hash              uint64 // hash of page contents
	Size              int    // total size of data written
	StatusCode        int    // HTTP status
	Other             map[string]interface{}
}

func () *statsResponseWriter {
	return &statsResponseWriter{
		header: http.Header{},
		start:  time.Now(),
		hasher: fnv.New64a(),
		stats:  PageStats{Other: map[string]interface{}{}},
	}
}
Header implements http.ResponseWriter.Header.
func ( *statsResponseWriter) () http.Header { return .header }
WriteHeader implements http.ResponseWriter.WriteHeader.
func ( *statsResponseWriter) ( int) {
	.stats.StatusCode = 
}
Write implements http.ResponseWriter.Write by tracking statistics about the data being written.
func ( *statsResponseWriter) ( []byte) (int, error) {
	if .stats.Size == 0 {
		.stats.MillisToFirstByte = time.Since(.start).Milliseconds()
	}
	if .stats.StatusCode == 0 {
		.WriteHeader(http.StatusOK)
	}
	.stats.Size += len()
	.hasher.Write()
	return len(), nil
}
WriteStats writes the statistics to w.
func ( *statsResponseWriter) ( context.Context,  http.ResponseWriter) {
	.stats.MillisToLastByte = time.Since(.start).Milliseconds()
	.stats.Hash = .hasher.Sum64()
	,  := json.Marshal(.stats)
	if  != nil {
		http.Error(, .Error(), http.StatusInternalServerError)
	} else {
		_, _ = .Write()
	}