func TestUnmarshalError(t *testing.T) { requests := []string{ "", // io.EOF `<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> </MissingEndTag </Envelope>`, `<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <UnknownType xmlns="urn:vim25"> <_this type="ServiceInstance">ServiceInstance</_this> </UnknownType> </Body> </Envelope>`, `<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <!-- no start tag --> </Body> </Envelope>`, `<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <RetrieveServiceContent xmlns="urn:vim25"> <_this type="ServiceInstance">ServiceInstance</_this> </RetrieveServiceContent> </Body> </Envelope>`, } defer func() { typeFunc = types.TypeFunc() // reset }() ttypes := map[string]reflect.Type{ // triggers xml.Decoder.DecodeElement error "RetrieveServiceContent": reflect.TypeOf(nil), } typeFunc = func(name string) (reflect.Type, bool) { typ, ok := ttypes[name] return typ, ok } for i, data := range requests { _, err := UnmarshalBody([]byte(data)) if err != nil { continue } t.Errorf("expected %d (%s) to return an error", i, data) } }
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 (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 }
func TestServeHTTPErrors(t *testing.T) { s := New(NewServiceInstance(esx.ServiceContent, esx.RootFolder)) ts := s.NewServer() defer ts.Close() ctx := context.Background() client, err := govmomi.NewClient(ctx, ts.URL, true) if err != nil { t.Fatal(err) } // unregister type, covering the ServeHTTP UnmarshalBody error path typeFunc = func(name string) (reflect.Type, bool) { return nil, false } _, err = methods.GetCurrentTime(ctx, client) if err == nil { t.Error("expected error") } typeFunc = types.TypeFunc() // reset // cover the does not implement method error path Map.objects[serviceInstance] = &errorNoSuchMethod{} _, err = methods.GetCurrentTime(ctx, client) if err == nil { t.Error("expected error") } // cover the xml encode error path Map.objects[serviceInstance] = &errorMarshal{} _, err = methods.GetCurrentTime(ctx, client) if err == nil { t.Error("expected error") } // cover the no such object path Map.Remove(serviceInstance) _, err = methods.GetCurrentTime(ctx, client) if err == nil { t.Error("expected error") } // verify we properly marshal the fault fault := soap.ToSoapFault(err).VimFault() f, ok := fault.(types.ManagedObjectNotFound) if !ok { t.Fatalf("fault=%#v", fault) } if f.Obj != serviceInstance.Reference() { t.Errorf("obj=%#v", f.Obj) } // cover the method not supported path res, err := http.Get(ts.URL.String()) if err != nil { log.Fatal(err) } if res.StatusCode != http.StatusMethodNotAllowed { t.Errorf("expected status %d, got %s", http.StatusMethodNotAllowed, res.Status) } // cover the ioutil.ReadAll error path s.readAll = func(io.Reader) ([]byte, error) { return nil, io.ErrShortBuffer } res, err = http.Post(ts.URL.String(), "none", nil) if err != nil { log.Fatal(err) } if res.StatusCode != http.StatusBadRequest { t.Errorf("expected status %d, got %s", http.StatusBadRequest, res.Status) } }
s.caFile = f.Name() cert := s.Certificate() return s.caFile, pem.Encode(f, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) } // Close shuts down the server and blocks until all outstanding // requests on this server have completed. func (s *Server) Close() { s.Server.Close() if s.caFile != "" { _ = os.Remove(s.caFile) } } var typeFunc = types.TypeFunc() // 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) }