// Unmarshal the raw bytes payload of this request (into a protobuf) func (self *Request) Unmarshal(into proto.Message) (err error) { switch self.delivery.ContentType { case "application/json": err = json.Unmarshal(self.delivery.Body, into) case "application/octetstream": err = proto.Unmarshal(self.delivery.Body, into) default: err = fmt.Errorf("Unknown content type: %s", self.delivery.ContentType) } return }
// Caller returns something that implements `Caller` - allowing us to use this as our // gateway to service calls - the returned `Caller` is thread safe func (m *Mock) Caller() Caller { return func(req *client.Request, rsp proto.Message) errors.Error { m.Lock() defer m.Unlock() for _, s := range m.stubs { if s.matches(req) { if s.Responder != nil { numMatched := len(s.matched) responderRsp, err := s.Responder(numMatched, s.matched[numMatched-1]) if err != nil { return err } // put the responderRsp INTO the rsp b, _ := proto.Marshal(responderRsp) proto.Unmarshal(b, rsp) return nil } if s.Error != nil { return s.Error } // put the response INTO the rsp b, _ := proto.Marshal(s.Response) proto.Unmarshal(b, rsp) return nil } } // no match found - do default action if m.proxy != nil { return m.proxy(req, rsp) } // no default - return error return errors.NotFound("mock.notfound", "No mocked service registered to handle request.") } }
// Unmarshal the raw bytes payload of this request (into a protobuf) func (r *Request) Unmarshal(into proto.Message) (err error) { if r == nil { err = fmt.Errorf("[Client] Cannot unmarshal request from nil Request") return } if into == nil { err = fmt.Errorf("[Client] Cannot unmarshal request into nil proto") return } switch r.contentType { case "application/json": err = json.Unmarshal(r.payload, into) case "application/octetstream": err = proto.Unmarshal(r.payload, into) default: err = fmt.Errorf("Unknown content type: %s", r.contentType) } return }
// Unmarshal the raw bytes payload of this request (into a protobuf) func (self *Response) Unmarshal(into proto.Message) (err error) { if self == nil { err = fmt.Errorf("[Client] Cannot unmarshal response from nil Response") return } if into == nil { err = fmt.Errorf("[Client] Cannot unmarshal response into nil proto") return } switch self.delivery.ContentType { case "application/json": err = json.Unmarshal(self.Body(), into) case "application/octetstream": err = proto.Unmarshal(self.Body(), into) default: err = fmt.Errorf("Unknown content type: %s", self.delivery.ContentType) } return }
// ConfiguredHttpCaller with more explicit configuration options than simple HttpCaller func ConfiguredHttpCaller(opts Options) Caller { tp := &httpclient.Transport{ ConnectTimeout: durationOrDefault(opts.ConnectTimeout, 5*time.Second), RequestTimeout: durationOrDefault(opts.RequestTimeout, 5*time.Second), ResponseHeaderTimeout: durationOrDefault(opts.ResponseHeaderTimeout, 5*time.Second), } if opts.TlsSkipVerify { tp.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} } httpClient := &http.Client{Transport: tp} return func(req *client.Request, rsp proto.Message) errors.Error { u, err := url.Parse(opts.BaseUrl) q := u.Query() q.Set("session_id", req.SessionID()) q.Set("service", req.Service()) q.Set("endpoint", req.Endpoint()) u.Path = "/rpc" u.RawQuery = q.Encode() var httpReq *http.Request // send JSON req content-type to thin API as form-encoded data // send proto req content-type directly as bytes, with proto content type if req.ContentType() == jsonContentType { values := make(url.Values) values.Set("service", req.Service()) values.Set("endpoint", req.Endpoint()) values.Set("request", string(req.Payload())) httpReq, _ = http.NewRequest("POST", u.String(), bytes.NewReader([]byte(values.Encode()))) httpReq.Header.Set("Content-Type", formEncodedContentType) } else { httpReq, _ = http.NewRequest("POST", u.String(), bytes.NewReader(req.Payload())) httpReq.Header.Set("Content-Type", protoContentType) } log.Tracef("[Multiclient] HTTP caller - calling '%s' : content-type '%s'", u.String(), req.ContentType()) httpRsp, err := httpClient.Do(httpReq) if err != nil { log.Warnf("[Multiclient] HTTP caller error calling %s.%s via %s : %s", req.Service(), req.Endpoint(), u.String(), err) return errors.InternalServerError("multiclienthttp.postform", fmt.Sprintf("Error calling %s.%s via %s : %s", req.Service(), req.Endpoint(), u.String(), err)) } defer httpRsp.Body.Close() rspBody, err := ioutil.ReadAll(httpRsp.Body) if err != nil { return errors.BadResponse("multiclienthttp.readresponse", fmt.Sprintf("Error reading response bytes: %v", err)) } // what status code? if httpRsp.StatusCode != 200 { // deal with error e := &protoerror.PlatformError{} var err error if req.ContentType() == jsonContentType { jsonErr := &errorBody{} err = json.Unmarshal(rspBody, jsonErr) e.Code = proto.String(jsonErr.DottedCode) e.Context = jsonErr.Context e.Description = proto.String(jsonErr.Payload) e.HttpCode = proto.Uint32(uint32(httpRsp.StatusCode)) // this conversion is lossy, since the JSON response for errors, as crafted // by the "thin API", does not currently include the error type, so we have // to guess from HTTP status code, but there is no distinct code for "BAD_RESPONSE" switch httpRsp.StatusCode { case 400: e.Type = protoerror.PlatformError_BAD_REQUEST.Enum() case 403: e.Type = protoerror.PlatformError_FORBIDDEN.Enum() case 404: e.Type = protoerror.PlatformError_NOT_FOUND.Enum() case 500: e.Type = protoerror.PlatformError_INTERNAL_SERVER_ERROR.Enum() case 504: e.Type = protoerror.PlatformError_TIMEOUT.Enum() } } else { err = proto.Unmarshal(rspBody, e) } // some issue understanding error rsp if err != nil { return errors.BadResponse("multiclienthttp.unmarshalerr", fmt.Sprintf("Error unmarshaling error response '%s': %v", string(rspBody), err)) } return errors.FromProtobuf(e) } // unmarshal response if req.ContentType() == jsonContentType { err = json.Unmarshal(rspBody, rsp) } else { err = proto.Unmarshal(rspBody, rsp) } if err != nil { return errors.BadResponse("multiclienthttp.unmarshal", fmt.Sprintf("Error unmarshaling response: %v", err)) } return nil } }