// UnmarshalBody extracts the Body from a soap.Envelope and unmarshals to the corresponding govmomi type func UnmarshalBody(data []byte) (*Method, error) { body := struct { Content string `xml:",innerxml"` }{} req := soap.Envelope{ Body: &body, } err := xml.Unmarshal(data, &req) if err != nil { return nil, fmt.Errorf("xml.Unmarshal: %s", err) } decoder := xml.NewDecoder(bytes.NewReader([]byte(body.Content))) decoder.TypeFunc = typeFunc // required to decode interface types var start *xml.StartElement for { tok, derr := decoder.Token() if derr != nil { return nil, fmt.Errorf("decoding body: %s", err) } if t, ok := tok.(xml.StartElement); ok { start = &t break } } kind := start.Name.Local rtype, ok := typeFunc(kind) if !ok { return nil, fmt.Errorf("no vmomi type defined for '%s'", kind) } var val interface{} if rtype != nil { val = reflect.New(rtype).Interface() } err = decoder.DecodeElement(val, start) if err != nil { return nil, fmt.Errorf("decoding %s: %s", kind, err) } method := &Method{Name: kind, Body: val} field := reflect.ValueOf(val).Elem().FieldByName("This") method.This = field.Interface().(types.ManagedObjectReference) return method, nil }
func load(name string) *Response { f, err := os.Open(name) if err != nil { panic(err) } defer f.Close() var b Response dec := xml.NewDecoder(f) dec.TypeFunc = types.TypeFunc() if err := dec.Decode(&b); err != nil { panic(err) } return &b }
func TestFaultDetail(t *testing.T) { body := TestBody{} env := soap.Envelope{Body: &body} dec := xml.NewDecoder(bytes.NewReader([]byte(invalidLoginFault))) dec.TypeFunc = types.TypeFunc() err := dec.Decode(&env) if err != nil { t.Fatalf("Decode: %s", err) } if body.Fault == nil { t.Fatal("Expected fault") } if _, ok := body.Fault.Detail.Fault.(types.InvalidLogin); !ok { t.Fatalf("Expected InvalidLogin, got: %#v", body.Fault.Detail.Fault) } }
func TestAnyType(t *testing.T) { x := func(s string) []byte { s = `<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">` + s s += `</root>` return []byte(s) } tests := []struct { Input []byte Value interface{} }{ { Input: x(`<name xsi:type="xsd:string">test</name>`), Value: "test", }, { Input: x(`<name xsi:type="ArrayOfString"><string>AA</string><string>BB</string></name>`), Value: ArrayOfString{String: []string{"AA", "BB"}}, }, } for _, test := range tests { var r struct { A interface{} `xml:"name,typeattr"` } dec := xml.NewDecoder(bytes.NewReader(test.Input)) dec.TypeFunc = TypeFunc() err := dec.Decode(&r) if err != nil { t.Fatalf("Decode: %s", err) } if !reflect.DeepEqual(r.A, test.Value) { t.Errorf("Expected: %#v, actual: %#v", r.A, test.Value) } } }
func (c *Client) RoundTrip(ctx context.Context, reqBody, resBody HasFault) error { var err error reqEnv := Envelope{Body: reqBody} resEnv := Envelope{Body: resBody} // Create debugging context for this round trip d := c.d.newRoundTrip() if d.enabled() { defer d.done() } b, err := xml.Marshal(reqEnv) if err != nil { panic(err) } rawReqBody := io.MultiReader(strings.NewReader(xml.Header), bytes.NewReader(b)) req, err := http.NewRequest("POST", c.u.String(), rawReqBody) if err != nil { panic(err) } req.Header.Set(`Content-Type`, `text/xml; charset="utf-8"`) soapAction := fmt.Sprintf("%s/%s", c.Namespace, c.Version) req.Header.Set(`SOAPAction`, soapAction) if c.UserAgent != "" { req.Header.Set(`User-Agent`, c.UserAgent) } if d.enabled() { d.debugRequest(req) } tstart := time.Now() res, err := c.do(ctx, req) tstop := time.Now() if d.enabled() { d.logf("%6dms (%T)", tstop.Sub(tstart)/time.Millisecond, resBody) } if err != nil { return err } if d.enabled() { d.debugResponse(res) } // Close response regardless of what happens next defer res.Body.Close() switch res.StatusCode { case http.StatusOK: // OK case http.StatusInternalServerError: // Error, but typically includes a body explaining the error default: return errors.New(res.Status) } dec := xml.NewDecoder(res.Body) dec.TypeFunc = types.TypeFunc() err = dec.Decode(&resEnv) if err != nil { return err } if f := resBody.Fault(); f != nil { return WrapSoapFault(f) } return err }