simulator.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. /*
  2. Copyright (c) 2017-2018 VMware, Inc. All Rights Reserved.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package simulator
  14. import (
  15. "bytes"
  16. "context"
  17. "crypto/tls"
  18. "crypto/x509"
  19. "encoding/base64"
  20. "encoding/json"
  21. "encoding/pem"
  22. "flag"
  23. "fmt"
  24. "io"
  25. "io/ioutil"
  26. "log"
  27. "math/rand"
  28. "net"
  29. "net/http"
  30. "net/http/httptest"
  31. "net/url"
  32. "os"
  33. "path"
  34. "reflect"
  35. "sort"
  36. "strconv"
  37. "strings"
  38. "time"
  39. "github.com/vmware/govmomi/find"
  40. "github.com/vmware/govmomi/object"
  41. "github.com/vmware/govmomi/vim25"
  42. "github.com/vmware/govmomi/vim25/mo"
  43. "github.com/vmware/govmomi/vim25/soap"
  44. "github.com/vmware/govmomi/vim25/types"
  45. "github.com/vmware/govmomi/vim25/xml"
  46. )
  47. // Trace when set to true, writes SOAP traffic to stderr
  48. var Trace = false
  49. // Method encapsulates a decoded SOAP client request
  50. type Method struct {
  51. Name string
  52. This types.ManagedObjectReference
  53. Header soap.Header
  54. Body types.AnyType
  55. }
  56. // Service decodes incoming requests and dispatches to a Handler
  57. type Service struct {
  58. client *vim25.Client
  59. sm *SessionManager
  60. sdk map[string]*Registry
  61. delay *DelayConfig
  62. readAll func(io.Reader) ([]byte, error)
  63. TLS *tls.Config
  64. ServeMux *http.ServeMux
  65. }
  66. // Server provides a simulator Service over HTTP
  67. type Server struct {
  68. *httptest.Server
  69. URL *url.URL
  70. Tunnel int
  71. caFile string
  72. }
  73. // New returns an initialized simulator Service instance
  74. func New(instance *ServiceInstance) *Service {
  75. s := &Service{
  76. readAll: ioutil.ReadAll,
  77. sm: Map.SessionManager(),
  78. sdk: make(map[string]*Registry),
  79. }
  80. s.client, _ = vim25.NewClient(context.Background(), s)
  81. return s
  82. }
  83. type serverFaultBody struct {
  84. Reason *soap.Fault `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault,omitempty"`
  85. }
  86. func (b *serverFaultBody) Fault() *soap.Fault { return b.Reason }
  87. func serverFault(msg string) soap.HasFault {
  88. return &serverFaultBody{Reason: Fault(msg, &types.InvalidRequest{})}
  89. }
  90. // Fault wraps the given message and fault in a soap.Fault
  91. func Fault(msg string, fault types.BaseMethodFault) *soap.Fault {
  92. f := &soap.Fault{
  93. Code: "ServerFaultCode",
  94. String: msg,
  95. }
  96. f.Detail.Fault = fault
  97. return f
  98. }
  99. func (s *Service) call(ctx *Context, method *Method) soap.HasFault {
  100. handler := ctx.Map.Get(method.This)
  101. session := ctx.Session
  102. if session == nil {
  103. switch method.Name {
  104. case "RetrieveServiceContent", "PbmRetrieveServiceContent", "List", "Login", "LoginByToken", "LoginExtensionByCertificate", "RetrieveProperties", "RetrievePropertiesEx", "CloneSession":
  105. // ok for now, TODO: authz
  106. default:
  107. fault := &types.NotAuthenticated{
  108. NoPermission: types.NoPermission{
  109. Object: method.This,
  110. PrivilegeId: "System.View",
  111. },
  112. }
  113. return &serverFaultBody{Reason: Fault("", fault)}
  114. }
  115. } else {
  116. // Prefer the Session.Registry, ServiceContent.PropertyCollector filter field for example is per-session
  117. if h := session.Get(method.This); h != nil {
  118. handler = h
  119. }
  120. }
  121. if handler == nil {
  122. msg := fmt.Sprintf("managed object not found: %s", method.This)
  123. log.Print(msg)
  124. fault := &types.ManagedObjectNotFound{Obj: method.This}
  125. return &serverFaultBody{Reason: Fault(msg, fault)}
  126. }
  127. // Lowercase methods can't be accessed outside their package
  128. name := strings.Title(method.Name)
  129. if strings.HasSuffix(name, vTaskSuffix) {
  130. // Make golint happy renaming "Foo_Task" -> "FooTask"
  131. name = name[:len(name)-len(vTaskSuffix)] + sTaskSuffix
  132. }
  133. m := reflect.ValueOf(handler).MethodByName(name)
  134. if !m.IsValid() {
  135. msg := fmt.Sprintf("%s does not implement: %s", method.This, method.Name)
  136. log.Print(msg)
  137. fault := &types.MethodNotFound{Receiver: method.This, Method: method.Name}
  138. return &serverFaultBody{Reason: Fault(msg, fault)}
  139. }
  140. if e, ok := handler.(mo.Entity); ok {
  141. for _, dm := range e.Entity().DisabledMethod {
  142. if name == dm {
  143. msg := fmt.Sprintf("%s method is disabled: %s", method.This, method.Name)
  144. fault := &types.MethodDisabled{}
  145. return &serverFaultBody{Reason: Fault(msg, fault)}
  146. }
  147. }
  148. }
  149. // We have a valid call. Introduce a delay if requested
  150. //
  151. if s.delay != nil {
  152. d := 0
  153. if s.delay.Delay > 0 {
  154. d = s.delay.Delay
  155. }
  156. if md, ok := s.delay.MethodDelay[method.Name]; ok {
  157. d += md
  158. }
  159. if s.delay.DelayJitter > 0 {
  160. d += int(rand.NormFloat64() * s.delay.DelayJitter * float64(d))
  161. }
  162. if d > 0 {
  163. //fmt.Printf("Delaying method %s %d ms\n", name, d)
  164. time.Sleep(time.Duration(d) * time.Millisecond)
  165. }
  166. }
  167. var args, res []reflect.Value
  168. if m.Type().NumIn() == 2 {
  169. args = append(args, reflect.ValueOf(ctx))
  170. }
  171. args = append(args, reflect.ValueOf(method.Body))
  172. ctx.Map.WithLock(handler, func() {
  173. res = m.Call(args)
  174. })
  175. return res[0].Interface().(soap.HasFault)
  176. }
  177. // RoundTrip implements the soap.RoundTripper interface in process.
  178. // Rather than encode/decode SOAP over HTTP, this implementation uses reflection.
  179. func (s *Service) RoundTrip(ctx context.Context, request, response soap.HasFault) error {
  180. field := func(r soap.HasFault, name string) reflect.Value {
  181. return reflect.ValueOf(r).Elem().FieldByName(name)
  182. }
  183. // Every struct passed to soap.RoundTrip has "Req" and "Res" fields
  184. req := field(request, "Req")
  185. // Every request has a "This" field.
  186. this := req.Elem().FieldByName("This")
  187. method := &Method{
  188. Name: req.Elem().Type().Name(),
  189. This: this.Interface().(types.ManagedObjectReference),
  190. Body: req.Interface(),
  191. }
  192. res := s.call(&Context{
  193. Map: Map,
  194. Context: ctx,
  195. Session: internalContext.Session,
  196. }, method)
  197. if err := res.Fault(); err != nil {
  198. return soap.WrapSoapFault(err)
  199. }
  200. field(response, "Res").Set(field(res, "Res"))
  201. return nil
  202. }
  203. // soapEnvelope is a copy of soap.Envelope, with namespace changed to "soapenv",
  204. // and additional namespace attributes required by some client libraries.
  205. // Go still has issues decoding with such a namespace, but encoding is ok.
  206. type soapEnvelope struct {
  207. XMLName xml.Name `xml:"soapenv:Envelope"`
  208. Enc string `xml:"xmlns:soapenc,attr"`
  209. Env string `xml:"xmlns:soapenv,attr"`
  210. XSD string `xml:"xmlns:xsd,attr"`
  211. XSI string `xml:"xmlns:xsi,attr"`
  212. Body interface{} `xml:"soapenv:Body"`
  213. }
  214. type faultDetail struct {
  215. Fault types.AnyType
  216. }
  217. // soapFault is a copy of soap.Fault, with the same changes as soapEnvelope
  218. type soapFault struct {
  219. XMLName xml.Name `xml:"soapenv:Fault"`
  220. Code string `xml:"faultcode"`
  221. String string `xml:"faultstring"`
  222. Detail struct {
  223. Fault *faultDetail
  224. } `xml:"detail"`
  225. }
  226. // MarshalXML renames the start element from "Fault" to "${Type}Fault"
  227. func (d *faultDetail) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
  228. kind := reflect.TypeOf(d.Fault).Elem().Name()
  229. start.Name.Local = kind + "Fault"
  230. start.Attr = append(start.Attr,
  231. xml.Attr{
  232. Name: xml.Name{Local: "xmlns"},
  233. Value: "urn:" + vim25.Namespace,
  234. },
  235. xml.Attr{
  236. Name: xml.Name{Local: "xsi:type"},
  237. Value: kind,
  238. })
  239. return e.EncodeElement(d.Fault, start)
  240. }
  241. // About generates some info about the simulator.
  242. func (s *Service) About(w http.ResponseWriter, r *http.Request) {
  243. var about struct {
  244. Methods []string
  245. Types []string
  246. }
  247. seen := make(map[string]bool)
  248. f := reflect.TypeOf((*soap.HasFault)(nil)).Elem()
  249. for _, obj := range Map.objects {
  250. kind := obj.Reference().Type
  251. if seen[kind] {
  252. continue
  253. }
  254. seen[kind] = true
  255. about.Types = append(about.Types, kind)
  256. t := reflect.TypeOf(obj)
  257. for i := 0; i < t.NumMethod(); i++ {
  258. m := t.Method(i)
  259. if seen[m.Name] {
  260. continue
  261. }
  262. seen[m.Name] = true
  263. in := m.Type.NumIn()
  264. if in < 2 || in > 3 { // at least 2 params (receiver and request), optionally a 3rd param (context)
  265. continue
  266. }
  267. if m.Type.NumOut() != 1 || m.Type.Out(0) != f { // all methods return soap.HasFault
  268. continue
  269. }
  270. about.Methods = append(about.Methods, strings.Replace(m.Name, "Task", "_Task", 1))
  271. }
  272. }
  273. sort.Strings(about.Methods)
  274. sort.Strings(about.Types)
  275. w.Header().Set("Content-Type", "application/json")
  276. enc := json.NewEncoder(w)
  277. enc.SetIndent("", " ")
  278. _ = enc.Encode(&about)
  279. }
  280. // Handle registers the handler for the given pattern with Service.ServeMux.
  281. func (s *Service) Handle(pattern string, handler http.Handler) {
  282. s.ServeMux.Handle(pattern, handler)
  283. // Not ideal, but avoids having to add yet another registration mechanism
  284. // so we can optionally use vapi/simulator internally.
  285. if m, ok := handler.(tagManager); ok {
  286. s.sdk[vim25.Path].tagManager = m
  287. }
  288. }
  289. // RegisterSDK adds an HTTP handler for the Registry's Path and Namespace.
  290. func (s *Service) RegisterSDK(r *Registry) {
  291. if s.ServeMux == nil {
  292. s.ServeMux = http.NewServeMux()
  293. }
  294. s.sdk[r.Path] = r
  295. s.ServeMux.HandleFunc(r.Path, s.ServeSDK)
  296. }
  297. // ServeSDK implements the http.Handler interface
  298. func (s *Service) ServeSDK(w http.ResponseWriter, r *http.Request) {
  299. if r.Method != "POST" {
  300. w.WriteHeader(http.StatusMethodNotAllowed)
  301. return
  302. }
  303. body, err := s.readAll(r.Body)
  304. _ = r.Body.Close()
  305. if err != nil {
  306. log.Printf("error reading body: %s", err)
  307. w.WriteHeader(http.StatusBadRequest)
  308. return
  309. }
  310. if Trace {
  311. fmt.Fprintf(os.Stderr, "Request: %s\n", string(body))
  312. }
  313. ctx := &Context{
  314. req: r,
  315. res: w,
  316. svc: s,
  317. Map: s.sdk[r.URL.Path],
  318. Context: context.Background(),
  319. }
  320. ctx.Map.WithLock(s.sm, ctx.mapSession)
  321. var res soap.HasFault
  322. var soapBody interface{}
  323. method, err := UnmarshalBody(ctx.Map.typeFunc, body)
  324. if err != nil {
  325. res = serverFault(err.Error())
  326. } else {
  327. ctx.Header = method.Header
  328. res = s.call(ctx, method)
  329. }
  330. if f := res.Fault(); f != nil {
  331. w.WriteHeader(http.StatusInternalServerError)
  332. // the generated method/*Body structs use the '*soap.Fault' type,
  333. // so we need our own Body type to use the modified '*soapFault' type.
  334. soapBody = struct {
  335. Fault *soapFault
  336. }{
  337. &soapFault{
  338. Code: f.Code,
  339. String: f.String,
  340. Detail: struct {
  341. Fault *faultDetail
  342. }{&faultDetail{f.Detail.Fault}},
  343. },
  344. }
  345. } else {
  346. w.WriteHeader(http.StatusOK)
  347. soapBody = res
  348. }
  349. var out bytes.Buffer
  350. fmt.Fprint(&out, xml.Header)
  351. e := xml.NewEncoder(&out)
  352. err = e.Encode(&soapEnvelope{
  353. Enc: "http://schemas.xmlsoap.org/soap/encoding/",
  354. Env: "http://schemas.xmlsoap.org/soap/envelope/",
  355. XSD: "http://www.w3.org/2001/XMLSchema",
  356. XSI: "http://www.w3.org/2001/XMLSchema-instance",
  357. Body: soapBody,
  358. })
  359. if err == nil {
  360. err = e.Flush()
  361. }
  362. if err != nil {
  363. log.Printf("error encoding %s response: %s", method.Name, err)
  364. return
  365. }
  366. if Trace {
  367. fmt.Fprintf(os.Stderr, "Response: %s\n", out.String())
  368. }
  369. _, _ = w.Write(out.Bytes())
  370. }
  371. func (s *Service) findDatastore(query url.Values) (*Datastore, error) {
  372. ctx := context.Background()
  373. finder := find.NewFinder(s.client, false)
  374. dc, err := finder.DatacenterOrDefault(ctx, query.Get("dcPath"))
  375. if err != nil {
  376. return nil, err
  377. }
  378. finder.SetDatacenter(dc)
  379. ds, err := finder.DatastoreOrDefault(ctx, query.Get("dsName"))
  380. if err != nil {
  381. return nil, err
  382. }
  383. return Map.Get(ds.Reference()).(*Datastore), nil
  384. }
  385. const folderPrefix = "/folder/"
  386. // ServeDatastore handler for Datastore access via /folder path.
  387. func (s *Service) ServeDatastore(w http.ResponseWriter, r *http.Request) {
  388. ds, ferr := s.findDatastore(r.URL.Query())
  389. if ferr != nil {
  390. log.Printf("failed to locate datastore with query params: %s", r.URL.RawQuery)
  391. w.WriteHeader(http.StatusNotFound)
  392. return
  393. }
  394. r.URL.Path = strings.TrimPrefix(r.URL.Path, folderPrefix)
  395. p := path.Join(ds.Info.GetDatastoreInfo().Url, r.URL.Path)
  396. switch r.Method {
  397. case "POST":
  398. _, err := os.Stat(p)
  399. if err == nil {
  400. // File exists
  401. w.WriteHeader(http.StatusConflict)
  402. return
  403. }
  404. // File does not exist, fallthrough to create via PUT logic
  405. fallthrough
  406. case "PUT":
  407. dir := path.Dir(p)
  408. _ = os.MkdirAll(dir, 0700)
  409. f, err := os.Create(p)
  410. if err != nil {
  411. log.Printf("failed to %s '%s': %s", r.Method, p, err)
  412. w.WriteHeader(http.StatusInternalServerError)
  413. return
  414. }
  415. defer f.Close()
  416. _, _ = io.Copy(f, r.Body)
  417. default:
  418. fs := http.FileServer(http.Dir(ds.Info.GetDatastoreInfo().Url))
  419. fs.ServeHTTP(w, r)
  420. }
  421. }
  422. // ServiceVersions handler for the /sdk/vimServiceVersions.xml path.
  423. func (*Service) ServiceVersions(w http.ResponseWriter, r *http.Request) {
  424. // pyvmomi depends on this
  425. const versions = xml.Header + `<namespaces version="1.0">
  426. <namespace>
  427. <name>urn:vim25</name>
  428. <version>6.5</version>
  429. <priorVersions>
  430. <version>6.0</version>
  431. <version>5.5</version>
  432. </priorVersions>
  433. </namespace>
  434. </namespaces>
  435. `
  436. fmt.Fprint(w, versions)
  437. }
  438. // defaultIP returns addr.IP if specified, otherwise attempts to find a non-loopback ipv4 IP
  439. func defaultIP(addr *net.TCPAddr) string {
  440. if !addr.IP.IsUnspecified() {
  441. return addr.IP.String()
  442. }
  443. nics, err := net.Interfaces()
  444. if err != nil {
  445. return addr.IP.String()
  446. }
  447. for _, nic := range nics {
  448. if nic.Name == "docker0" || strings.HasPrefix(nic.Name, "vmnet") {
  449. continue
  450. }
  451. addrs, aerr := nic.Addrs()
  452. if aerr != nil {
  453. continue
  454. }
  455. for _, addr := range addrs {
  456. if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() {
  457. if ip.IP.To4() != nil {
  458. return ip.IP.String()
  459. }
  460. }
  461. }
  462. }
  463. return addr.IP.String()
  464. }
  465. // NewServer returns an http Server instance for the given service
  466. func (s *Service) NewServer() *Server {
  467. s.RegisterSDK(Map)
  468. mux := s.ServeMux
  469. mux.HandleFunc(Map.Path+"/vimServiceVersions.xml", s.ServiceVersions)
  470. mux.HandleFunc(folderPrefix, s.ServeDatastore)
  471. mux.HandleFunc("/about", s.About)
  472. // Using NewUnstartedServer() instead of NewServer(),
  473. // for use in main.go, where Start() blocks, we can still set ServiceHostName
  474. ts := httptest.NewUnstartedServer(mux)
  475. addr := ts.Listener.Addr().(*net.TCPAddr)
  476. port := strconv.Itoa(addr.Port)
  477. u := &url.URL{
  478. Scheme: "http",
  479. Host: net.JoinHostPort(defaultIP(addr), port),
  480. Path: Map.Path,
  481. }
  482. // Redirect clients to this http server, rather than HostSystem.Name
  483. Map.SessionManager().ServiceHostName = u.Host
  484. if f := flag.Lookup("httptest.serve"); f != nil {
  485. // Avoid the blocking behaviour of httptest.Server.Start() when this flag is set
  486. _ = f.Value.Set("")
  487. }
  488. cert := ""
  489. if s.TLS == nil {
  490. ts.Start()
  491. } else {
  492. ts.TLS = s.TLS
  493. ts.TLS.ClientAuth = tls.RequestClientCert // Used by SessionManager.LoginExtensionByCertificate
  494. ts.StartTLS()
  495. u.Scheme += "s"
  496. cert = base64.StdEncoding.EncodeToString(ts.TLS.Certificates[0].Certificate[0])
  497. }
  498. // Add vcsim config to OptionManager for use by SDK handlers (see lookup/simulator for example)
  499. m := Map.OptionManager()
  500. m.Setting = append(m.Setting,
  501. &types.OptionValue{
  502. Key: "vcsim.server.url",
  503. Value: u.String(),
  504. },
  505. &types.OptionValue{
  506. Key: "vcsim.server.cert",
  507. Value: cert,
  508. },
  509. )
  510. u.User = url.UserPassword("user", "pass")
  511. return &Server{
  512. Server: ts,
  513. URL: u,
  514. }
  515. }
  516. // Certificate returns the TLS certificate for the Server if started with TLS enabled.
  517. // This method will panic if TLS is not enabled for the server.
  518. func (s *Server) Certificate() *x509.Certificate {
  519. // By default httptest.StartTLS uses http/internal.LocalhostCert, which we can access here:
  520. cert, _ := x509.ParseCertificate(s.TLS.Certificates[0].Certificate[0])
  521. return cert
  522. }
  523. // CertificateInfo returns Server.Certificate() as object.HostCertificateInfo
  524. func (s *Server) CertificateInfo() *object.HostCertificateInfo {
  525. info := new(object.HostCertificateInfo)
  526. info.FromCertificate(s.Certificate())
  527. return info
  528. }
  529. // CertificateFile returns a file name, where the file contains the PEM encoded Server.Certificate.
  530. // The temporary file is removed when Server.Close() is called.
  531. func (s *Server) CertificateFile() (string, error) {
  532. if s.caFile != "" {
  533. return s.caFile, nil
  534. }
  535. f, err := ioutil.TempFile("", "vcsim-")
  536. if err != nil {
  537. return "", err
  538. }
  539. defer f.Close()
  540. s.caFile = f.Name()
  541. cert := s.Certificate()
  542. return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
  543. }
  544. // proxy tunnels SDK requests
  545. func (s *Server) proxy(w http.ResponseWriter, r *http.Request) {
  546. if r.Method != http.MethodConnect {
  547. http.Error(w, "", http.StatusMethodNotAllowed)
  548. return
  549. }
  550. dst, err := net.Dial("tcp", s.URL.Host)
  551. if err != nil {
  552. http.Error(w, err.Error(), http.StatusBadGateway)
  553. return
  554. }
  555. w.WriteHeader(http.StatusOK)
  556. src, _, err := w.(http.Hijacker).Hijack()
  557. if err != nil {
  558. http.Error(w, err.Error(), http.StatusBadRequest)
  559. return
  560. }
  561. go io.Copy(src, dst)
  562. go func() {
  563. _, _ = io.Copy(dst, src)
  564. _ = dst.Close()
  565. _ = src.Close()
  566. }()
  567. }
  568. // StartTunnel runs an HTTP proxy for tunneling SDK requests that require TLS client certificate authentication.
  569. func (s *Server) StartTunnel() error {
  570. tunnel := &http.Server{
  571. Addr: fmt.Sprintf("%s:%d", s.URL.Hostname(), s.Tunnel),
  572. Handler: http.HandlerFunc(s.proxy),
  573. }
  574. l, err := net.Listen("tcp", tunnel.Addr)
  575. if err != nil {
  576. return err
  577. }
  578. if s.Tunnel == 0 {
  579. s.Tunnel = l.Addr().(*net.TCPAddr).Port
  580. }
  581. // Set client proxy port (defaults to vCenter host port 80 in real life)
  582. q := s.URL.Query()
  583. q.Set("GOVMOMI_TUNNEL_PROXY_PORT", strconv.Itoa(s.Tunnel))
  584. s.URL.RawQuery = q.Encode()
  585. go tunnel.Serve(l)
  586. return nil
  587. }
  588. // Close shuts down the server and blocks until all outstanding
  589. // requests on this server have completed.
  590. func (s *Server) Close() {
  591. s.Server.Close()
  592. if s.caFile != "" {
  593. _ = os.Remove(s.caFile)
  594. }
  595. }
  596. var (
  597. vim25MapType = types.TypeFunc()
  598. )
  599. func defaultMapType(name string) (reflect.Type, bool) {
  600. typ, ok := vim25MapType(name)
  601. if !ok {
  602. // See TestIssue945, in which case Go does not resolve the namespace and name == "ns1:TraversalSpec"
  603. // Without this hack, the SelectSet would be all nil's
  604. kind := strings.SplitN(name, ":", 2)
  605. if len(kind) == 2 {
  606. typ, ok = vim25MapType(kind[1])
  607. }
  608. }
  609. return typ, ok
  610. }
  611. // Element can be used to defer decoding of an XML node.
  612. type Element struct {
  613. start xml.StartElement
  614. inner struct {
  615. Content string `xml:",innerxml"`
  616. }
  617. typeFunc func(string) (reflect.Type, bool)
  618. }
  619. func (e *Element) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
  620. e.start = start
  621. return d.DecodeElement(&e.inner, &start)
  622. }
  623. func (e *Element) decoder() *xml.Decoder {
  624. decoder := xml.NewDecoder(strings.NewReader(e.inner.Content))
  625. decoder.TypeFunc = e.typeFunc // required to decode interface types
  626. return decoder
  627. }
  628. func (e *Element) Decode(val interface{}) error {
  629. return e.decoder().DecodeElement(val, &e.start)
  630. }
  631. // UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type
  632. func UnmarshalBody(typeFunc func(string) (reflect.Type, bool), data []byte) (*Method, error) {
  633. body := &Element{typeFunc: typeFunc}
  634. req := soap.Envelope{
  635. Header: &soap.Header{
  636. Security: new(Element),
  637. },
  638. Body: body,
  639. }
  640. err := xml.Unmarshal(data, &req)
  641. if err != nil {
  642. return nil, fmt.Errorf("xml.Unmarshal: %s", err)
  643. }
  644. var start xml.StartElement
  645. var ok bool
  646. decoder := body.decoder()
  647. for {
  648. tok, derr := decoder.Token()
  649. if derr != nil {
  650. return nil, fmt.Errorf("decoding: %s", derr)
  651. }
  652. if start, ok = tok.(xml.StartElement); ok {
  653. break
  654. }
  655. }
  656. if !ok {
  657. return nil, fmt.Errorf("decoding: method token not found")
  658. }
  659. kind := start.Name.Local
  660. rtype, ok := typeFunc(kind)
  661. if !ok {
  662. return nil, fmt.Errorf("no vmomi type defined for '%s'", kind)
  663. }
  664. val := reflect.New(rtype).Interface()
  665. err = decoder.DecodeElement(val, &start)
  666. if err != nil {
  667. return nil, fmt.Errorf("decoding %s: %s", kind, err)
  668. }
  669. method := &Method{Name: kind, Header: *req.Header, Body: val}
  670. field := reflect.ValueOf(val).Elem().FieldByName("This")
  671. method.This = field.Interface().(types.ManagedObjectReference)
  672. return method, nil
  673. }