github.com/vmware/govmomi@v0.37.1/vim25/soap/debug.go (about)

     1  /*
     2  Copyright (c) 2015 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package soap
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"net/http"
    23  	"net/http/httputil"
    24  	"sync/atomic"
    25  
    26  	"github.com/vmware/govmomi/vim25/debug"
    27  )
    28  
    29  var (
    30  	// Trace reads an http request or response from rc and writes to w.
    31  	// The content type (kind) should be one of "xml" or "json".
    32  	Trace = func(rc io.ReadCloser, w io.Writer, kind string) io.ReadCloser {
    33  		return debug.NewTeeReader(rc, w)
    34  	}
    35  )
    36  
    37  // debugRoundTrip contains state and logic needed to debug a single round trip.
    38  type debugRoundTrip struct {
    39  	cn uint64      // Client number
    40  	rn uint64      // Request number
    41  	cs []io.Closer // Files that need closing when done
    42  }
    43  
    44  func (d *debugRoundTrip) enabled() bool {
    45  	return d != nil
    46  }
    47  
    48  func (d *debugRoundTrip) done() {
    49  	for _, c := range d.cs {
    50  		c.Close()
    51  	}
    52  }
    53  
    54  func (d *debugRoundTrip) newFile(suffix string) io.WriteCloser {
    55  	return debug.NewFile(fmt.Sprintf("%d-%04d.%s", d.cn, d.rn, suffix))
    56  }
    57  
    58  func (d *debugRoundTrip) ext(h http.Header) string {
    59  	const json = "application/json"
    60  	ext := "xml"
    61  	if h.Get("Accept") == json || h.Get("Content-Type") == json {
    62  		ext = "json"
    63  	}
    64  	return ext
    65  }
    66  
    67  func (d *debugRoundTrip) debugRequest(req *http.Request) string {
    68  	if d == nil {
    69  		return ""
    70  	}
    71  
    72  	// Capture headers
    73  	var wc io.WriteCloser = d.newFile("req.headers")
    74  	b, _ := httputil.DumpRequest(req, false)
    75  	wc.Write(b)
    76  	wc.Close()
    77  
    78  	ext := d.ext(req.Header)
    79  	// Capture body
    80  	wc = d.newFile("req." + ext)
    81  	if req.Body != nil {
    82  		req.Body = Trace(req.Body, wc, ext)
    83  	}
    84  
    85  	// Delay closing until marked done
    86  	d.cs = append(d.cs, wc)
    87  
    88  	return ext
    89  }
    90  
    91  func (d *debugRoundTrip) debugResponse(res *http.Response, ext string) {
    92  	if d == nil {
    93  		return
    94  	}
    95  
    96  	// Capture headers
    97  	var wc io.WriteCloser = d.newFile("res.headers")
    98  	b, _ := httputil.DumpResponse(res, false)
    99  	wc.Write(b)
   100  	wc.Close()
   101  
   102  	// Capture body
   103  	wc = d.newFile("res." + ext)
   104  	res.Body = Trace(res.Body, wc, ext)
   105  
   106  	// Delay closing until marked done
   107  	d.cs = append(d.cs, wc)
   108  }
   109  
   110  var cn uint64 // Client counter
   111  
   112  // debugContainer wraps the debugging state for a single client.
   113  type debugContainer struct {
   114  	cn uint64 // Client number
   115  	rn uint64 // Request counter
   116  }
   117  
   118  func newDebug() *debugContainer {
   119  	d := debugContainer{
   120  		cn: atomic.AddUint64(&cn, 1),
   121  		rn: 0,
   122  	}
   123  
   124  	if !debug.Enabled() {
   125  		return nil
   126  	}
   127  	return &d
   128  }
   129  
   130  func (d *debugContainer) newRoundTrip() *debugRoundTrip {
   131  	if d == nil {
   132  		return nil
   133  	}
   134  
   135  	drt := debugRoundTrip{
   136  		cn: d.cn,
   137  		rn: atomic.AddUint64(&d.rn, 1),
   138  	}
   139  
   140  	return &drt
   141  }