func (c *context) Call(service, method string, in, out ProtoMessage, opts *CallOptions) error { if service == "__go__" { if method == "GetNamespace" { out.(*basepb.StringProto).Value = proto.String(c.req.Header.Get("X-AppEngine-Current-Namespace")) return nil } if method == "GetDefaultNamespace" { out.(*basepb.StringProto).Value = proto.String(c.req.Header.Get("X-AppEngine-Default-Namespace")) return nil } } if f, ok := apiOverrides[struct{ service, method string }{service, method}]; ok { return f(in, out, opts) } data, err := proto.Marshal(in) if err != nil { return err } requestID := c.req.Header.Get("X-Appengine-Internal-Request-Id") res, err := call(service, method, data, requestID) if err != nil { return err } return proto.Unmarshal(res, out) }
func readConfig(r io.Reader) *rpb.Config { raw, err := ioutil.ReadAll(r) if err != nil { log.Fatal("appengine: could not read from stdin: ", err) } if len(raw) == 0 { // If there were zero bytes, assume this code is not being run as part of // a complete app under devappserver2, and generate some reasonable defaults. log.Print("appengine: not running under devappserver2; using some default configuration") return &rpb.Config{ AppId: []byte("dev~my-app"), VersionId: []byte("1.2345"), ApiHost: proto.String("localhost"), ApiPort: proto.Int32(1), Datacenter: proto.String("us1"), InstanceId: proto.String("deadbeef"), } } b := make([]byte, base64.StdEncoding.DecodedLen(len(raw))) n, err := base64.StdEncoding.Decode(b, raw) if err != nil { log.Fatal("appengine: could not base64 decode stdin: ", err) } config := &rpb.Config{} err = proto.Unmarshal(b[:n], config) if err != nil { log.Fatal("appengine: could not decode runtime_config: ", err) } return config }
// Call is an implementation of appengine.Context's Call that delegates // to a child api_server.py instance. func (c *context) Call(service, method string, in, out appengine_internal.ProtoMessage, opts *appengine_internal.CallOptions) error { if service == "__go__" && (method == "GetNamespace" || method == "GetDefaultNamespace") { out.(*basepb.StringProto).Value = proto.String("") return nil } data, err := proto.Marshal(in) if err != nil { return err } req, err := proto.Marshal(&remoteapipb.Request{ ServiceName: proto.String(service), Method: proto.String(method), Request: data, RequestId: proto.String(c.session), }) if err != nil { return err } res, err := http.Post(c.apiURL, "application/octet-stream", bytes.NewReader(req)) if err != nil { return err } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if res.StatusCode != 200 { return fmt.Errorf("got status %d; body: %q", res.StatusCode, body) } if err != nil { return err } resp := &remoteapipb.Response{} err = proto.Unmarshal(body, resp) if err != nil { return err } if e := resp.GetApplicationError(); e != nil { return fmt.Errorf("remote_api error (%v): %v", *e.Code, *e.Detail) } return proto.Unmarshal(resp.Response, out) }
// Run starts a query for log records, which contain request and application // level log information. func (params *Query) Run(c appengine.Context) *Result { req := &pb.LogReadRequest{} appId := c.FullyQualifiedAppID() req.AppId = &appId if !params.StartTime.IsZero() { req.StartTime = proto.Int64(params.StartTime.UnixNano() / 1e3) } if !params.EndTime.IsZero() { req.EndTime = proto.Int64(params.EndTime.UnixNano() / 1e3) } if params.Offset != nil { var offset pb.LogOffset if err := proto.Unmarshal(params.Offset, &offset); err != nil { return &Result{context: c, err: fmt.Errorf("bad Offset: %v", err)} } req.Offset = &offset } if params.Incomplete { req.IncludeIncomplete = ¶ms.Incomplete } if params.AppLogs { req.IncludeAppLogs = ¶ms.AppLogs } if params.ApplyMinLevel { req.MinimumLogLevel = proto.Int32(int32(params.MinLevel)) } if params.Versions == nil { // If no versions were specified, default to the major version // used by this app. versionID := appengine.VersionID(c) if i := strings.Index(versionID, "."); i >= 0 { versionID = versionID[:i] } req.VersionId = []string{versionID} } else { req.VersionId = params.Versions } if params.RequestIDs != nil { ids := make([][]byte, len(params.RequestIDs)) for i, v := range params.RequestIDs { ids[i] = []byte(v) } req.RequestId = ids } return &Result{context: c, request: req} }
// Decode decodes a cursor from its base-64 string representation. func DecodeCursor(s string) (Cursor, error) { if s == "" { return Cursor{&zeroCC}, nil } if n := len(s) % 4; n != 0 { s += strings.Repeat("=", 4-n) } b, err := base64.URLEncoding.DecodeString(s) if err != nil { return Cursor{}, err } cc := &pb.CompiledCursor{} if err := proto.Unmarshal(b, cc); err != nil { return Cursor{}, err } return Cursor{cc}, nil }
// DecodeKey decodes a key from the opaque representation returned by Encode. func DecodeKey(encoded string) (*Key, error) { // Re-add padding. if m := len(encoded) % 4; m != 0 { encoded += strings.Repeat("=", 4-m) } b, err := base64.URLEncoding.DecodeString(encoded) if err != nil { return nil, err } ref := new(pb.Reference) if err := proto.Unmarshal(b, ref); err != nil { return nil, err } return protoToKey(ref) }
func call(service, method string, data []byte, requestID string) ([]byte, error) { req := &remote_api.Request{ ServiceName: &service, Method: &method, Request: data, RequestId: &requestID, } buf, err := proto.Marshal(req) if err != nil { return nil, err } resp, err := apiHTTPClient.Post(apiAddress, "application/octet-stream", bytes.NewReader(buf)) if err != nil { return nil, err } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } res := &remote_api.Response{} err = proto.Unmarshal(body, res) if err != nil { return nil, err } if ae := res.ApplicationError; ae != nil { // All Remote API application errors are API-level failures. return nil, &APIError{Service: service, Detail: *ae.Detail, Code: *ae.Code} } return res.Response, nil }
func handle(w http.ResponseWriter, req *http.Request) { c := appengine.NewContext(req) if !user.IsAdmin(c) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusUnauthorized) io.WriteString(w, "You must be logged in as an administrator to access this.\n") return } if req.Header.Get("X-Appcfg-Api-Version") == "" { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusForbidden) io.WriteString(w, "This request did not contain a necessary header.\n") return } if req.Method != "POST" { // Response must be YAML. rtok := req.FormValue("rtok") if rtok == "" { rtok = "0" } w.Header().Set("Content-Type", "text/yaml; charset=utf-8") fmt.Fprintf(w, `{app_id: %q, rtok: %q}`, c.FullyQualifiedAppID(), rtok) return } defer req.Body.Close() body, err := ioutil.ReadAll(req.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) c.Errorf("Failed reading body: %v", err) return } remReq := &pb.Request{} if err := proto.Unmarshal(body, remReq); err != nil { w.WriteHeader(http.StatusBadRequest) c.Errorf("Bad body: %v", err) return } // Only allow datastore_v3 for now, or AllocateIds for datastore_v4. service, method := *remReq.ServiceName, *remReq.Method ok := service == "datastore_v3" || (service == "datastore_v4" && method == "AllocateIds") if !ok { w.WriteHeader(http.StatusBadRequest) c.Errorf("Unsupported RPC /%s.%s", service, method) return } rawReq := &rawMessage{remReq.Request} rawRes := &rawMessage{} err = c.Call(service, method, rawReq, rawRes, nil) remRes := &pb.Response{} if err == nil { remRes.Response = rawRes.buf } else if ae, ok := err.(*appengine_internal.APIError); ok { remRes.ApplicationError = &pb.ApplicationError{ Code: &ae.Code, Detail: &ae.Detail, } } else { // This shouldn't normally happen. c.Errorf("appengine/remote_api: Unexpected error of type %T: %v", err, err) remRes.ApplicationError = &pb.ApplicationError{ Code: proto.Int32(0), Detail: proto.String(err.Error()), } } out, err := proto.Marshal(remRes) if err != nil { // This should not be possible. w.WriteHeader(500) c.Errorf("proto.Marshal: %v", err) return } c.Infof("Spooling %d bytes of response to /%s.%s", len(out), service, method) w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Length", strconv.Itoa(len(out))) w.Write(out) }