Blame vendor/github.com/vmware/govmomi/vim25/soap/debug.go

Packit 63bb0d
/*
Packit 63bb0d
Copyright (c) 2015 VMware, Inc. All Rights Reserved.
Packit 63bb0d
Packit 63bb0d
Licensed under the Apache License, Version 2.0 (the "License");
Packit 63bb0d
you may not use this file except in compliance with the License.
Packit 63bb0d
You may obtain a copy of the License at
Packit 63bb0d
Packit 63bb0d
    http://www.apache.org/licenses/LICENSE-2.0
Packit 63bb0d
Packit 63bb0d
Unless required by applicable law or agreed to in writing, software
Packit 63bb0d
distributed under the License is distributed on an "AS IS" BASIS,
Packit 63bb0d
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 63bb0d
See the License for the specific language governing permissions and
Packit 63bb0d
limitations under the License.
Packit 63bb0d
*/
Packit 63bb0d
Packit 63bb0d
package soap
Packit 63bb0d
Packit 63bb0d
import (
Packit 63bb0d
	"fmt"
Packit 63bb0d
	"io"
Packit 63bb0d
	"net/http"
Packit 63bb0d
	"net/http/httputil"
Packit 63bb0d
	"sync/atomic"
Packit 63bb0d
	"time"
Packit 63bb0d
Packit 63bb0d
	"github.com/vmware/govmomi/vim25/debug"
Packit 63bb0d
)
Packit 63bb0d
Packit 63bb0d
// teeReader wraps io.TeeReader and patches through the Close() function.
Packit 63bb0d
type teeReader struct {
Packit 63bb0d
	io.Reader
Packit 63bb0d
	io.Closer
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func newTeeReader(rc io.ReadCloser, w io.Writer) io.ReadCloser {
Packit 63bb0d
	return teeReader{
Packit 63bb0d
		Reader: io.TeeReader(rc, w),
Packit 63bb0d
		Closer: rc,
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
// debugRoundTrip contains state and logic needed to debug a single round trip.
Packit 63bb0d
type debugRoundTrip struct {
Packit 63bb0d
	cn  uint64         // Client number
Packit 63bb0d
	rn  uint64         // Request number
Packit 63bb0d
	log io.WriteCloser // Request log
Packit 63bb0d
	cs  []io.Closer    // Files that need closing when done
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) logf(format string, a ...interface{}) {
Packit 63bb0d
	now := time.Now().Format("2006-01-02T15-04-05.000000000")
Packit 63bb0d
	fmt.Fprintf(d.log, "%s - %04d: ", now, d.rn)
Packit 63bb0d
	fmt.Fprintf(d.log, format, a...)
Packit 63bb0d
	fmt.Fprintf(d.log, "\n")
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) enabled() bool {
Packit 63bb0d
	return d != nil
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) done() {
Packit 63bb0d
	for _, c := range d.cs {
Packit 63bb0d
		c.Close()
Packit 63bb0d
	}
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) newFile(suffix string) io.WriteCloser {
Packit 63bb0d
	return debug.NewFile(fmt.Sprintf("%d-%04d.%s", d.cn, d.rn, suffix))
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) ext(h http.Header) string {
Packit 63bb0d
	const json = "application/json"
Packit 63bb0d
	ext := "xml"
Packit 63bb0d
	if h.Get("Accept") == json || h.Get("Content-Type") == json {
Packit 63bb0d
		ext = "json"
Packit 63bb0d
	}
Packit 63bb0d
	return ext
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) debugRequest(req *http.Request) string {
Packit 63bb0d
	if d == nil {
Packit 63bb0d
		return ""
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// Capture headers
Packit 63bb0d
	var wc io.WriteCloser = d.newFile("req.headers")
Packit 63bb0d
	b, _ := httputil.DumpRequest(req, false)
Packit 63bb0d
	wc.Write(b)
Packit 63bb0d
	wc.Close()
Packit 63bb0d
Packit 63bb0d
	ext := d.ext(req.Header)
Packit 63bb0d
	// Capture body
Packit 63bb0d
	wc = d.newFile("req." + ext)
Packit 63bb0d
	req.Body = newTeeReader(req.Body, wc)
Packit 63bb0d
Packit 63bb0d
	// Delay closing until marked done
Packit 63bb0d
	d.cs = append(d.cs, wc)
Packit 63bb0d
Packit 63bb0d
	return ext
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugRoundTrip) debugResponse(res *http.Response, ext string) {
Packit 63bb0d
	if d == nil {
Packit 63bb0d
		return
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	// Capture headers
Packit 63bb0d
	var wc io.WriteCloser = d.newFile("res.headers")
Packit 63bb0d
	b, _ := httputil.DumpResponse(res, false)
Packit 63bb0d
	wc.Write(b)
Packit 63bb0d
	wc.Close()
Packit 63bb0d
Packit 63bb0d
	// Capture body
Packit 63bb0d
	wc = d.newFile("res." + ext)
Packit 63bb0d
	res.Body = newTeeReader(res.Body, wc)
Packit 63bb0d
Packit 63bb0d
	// Delay closing until marked done
Packit 63bb0d
	d.cs = append(d.cs, wc)
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
var cn uint64 // Client counter
Packit 63bb0d
Packit 63bb0d
// debugContainer wraps the debugging state for a single client.
Packit 63bb0d
type debugContainer struct {
Packit 63bb0d
	cn  uint64         // Client number
Packit 63bb0d
	rn  uint64         // Request counter
Packit 63bb0d
	log io.WriteCloser // Request log
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func newDebug() *debugContainer {
Packit 63bb0d
	d := debugContainer{
Packit 63bb0d
		cn: atomic.AddUint64(&cn, 1),
Packit 63bb0d
		rn: 0,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	if !debug.Enabled() {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	d.log = debug.NewFile(fmt.Sprintf("%d-client.log", d.cn))
Packit 63bb0d
	return &d
Packit 63bb0d
}
Packit 63bb0d
Packit 63bb0d
func (d *debugContainer) newRoundTrip() *debugRoundTrip {
Packit 63bb0d
	if d == nil {
Packit 63bb0d
		return nil
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	drt := debugRoundTrip{
Packit 63bb0d
		cn:  d.cn,
Packit 63bb0d
		rn:  atomic.AddUint64(&d.rn, 1),
Packit 63bb0d
		log: d.log,
Packit 63bb0d
	}
Packit 63bb0d
Packit 63bb0d
	return &drt
Packit 63bb0d
}