func callCommand(ctx context.Context, req cmds.Request, root *cmds.Command, cmd *cmds.Command) (cmds.Response, error) { log.Info(config.EnvDir, " ", req.InvocContext().ConfigRoot) var res cmds.Response err := req.SetRootContext(ctx) if err != nil { return nil, err } details, err := commandDetails(req.Path(), root) if err != nil { return nil, err } client, err := commandShouldRunOnDaemon(*details, req, root) if err != nil { return nil, err } err = callPreCommandHooks(ctx, *details, req, root) if err != nil { return nil, err } if cmd.PreRun != nil { err = cmd.PreRun(req) if err != nil { return nil, err } } if client != nil && !cmd.External { log.Debug("Executing command via API") res, err = client.Send(req) if err != nil { if isConnRefused(err) { err = repo.ErrApiNotRunning } return nil, err } } else { log.Debug("Executing command locally") err := req.SetRootContext(ctx) if err != nil { return nil, err } // Okay!!!!! NOW we can call the command. res = root.Call(req) } if cmd.PostRun != nil { cmd.PostRun(req, res) } return res, nil }
// commandShouldRunOnDaemon determines, from commmand details, whether a // command ought to be executed on an IPFS daemon. // // It returns a client if the command should be executed on a daemon and nil if // it should be executed on a client. It returns an error if the command must // NOT be executed on either. func commandShouldRunOnDaemon(details cmdDetails, req cmds.Request, root *cmds.Command) (cmdsHttp.Client, error) { path := req.Path() // root command. if len(path) < 1 { return nil, nil } if details.cannotRunOnClient && details.cannotRunOnDaemon { return nil, fmt.Errorf("command disabled: %s", path[0]) } if details.doesNotUseRepo && details.canRunOnClient() { return nil, nil } // at this point need to know whether api is running. we defer // to this point so that we dont check unnecessarily // did user specify an api to use for this command? apiAddrStr, _, err := req.Option(coreCmds.ApiOption).String() if err != nil { return nil, err } client, err := getApiClient(req.InvocContext().ConfigRoot, apiAddrStr) if err == repo.ErrApiNotRunning { if apiAddrStr != "" && req.Command() != daemonCmd { // if user SPECIFIED an api, and this cmd is not daemon // we MUST use it. so error out. return nil, err } // ok for api not to be running } else if err != nil { // some other api error return nil, err } if client != nil { // daemon is running if details.cannotRunOnDaemon { e := "cannot use API with this command." // check if daemon locked. legacy error text, for now. daemonLocked, _ := fsrepo.LockedByOtherProcess(req.InvocContext().ConfigRoot) if daemonLocked { e = "ipfs daemon is running. please stop it to run this command" } return nil, cmds.ClientError(e) } return client, nil } if details.cannotRunOnClient { return nil, cmds.ClientError("must run on the ipfs daemon") } return nil, nil }
// commandShouldRunOnDaemon determines, from commmand details, whether a // command ought to be executed on an IPFS daemon. // // It returns true if the command should be executed on a daemon and false if // it should be executed on a client. It returns an error if the command must // NOT be executed on either. func commandShouldRunOnDaemon(details cmdDetails, req cmds.Request, root *cmds.Command) (bool, error) { path := req.Path() // root command. if len(path) < 1 { return false, nil } if details.cannotRunOnClient && details.cannotRunOnDaemon { return false, fmt.Errorf("command disabled: %s", path[0]) } if details.doesNotUseRepo && details.canRunOnClient() { return false, nil } // at this point need to know whether daemon is running. we defer // to this point so that some commands dont open files unnecessarily. daemonLocked, err := fsrepo.LockedByOtherProcess(req.InvocContext().ConfigRoot) if err != nil { return false, err } if daemonLocked { log.Info("a daemon is running...") if details.cannotRunOnDaemon { e := "ipfs daemon is running. please stop it to run this command" return false, cmds.ClientError(e) } return true, nil } if details.cannotRunOnClient { return false, cmds.ClientError("must run on the ipfs daemon") } return false, nil }
func (c *client) Send(req cmds.Request) (cmds.Response, error) { if req.Context() == nil { log.Warningf("no context set in request") if err := req.SetRootContext(context.TODO()); err != nil { return nil, err } } // save user-provided encoding previousUserProvidedEncoding, found, err := req.Option(cmds.EncShort).String() if err != nil { return nil, err } // override with json to send to server req.SetOption(cmds.EncShort, cmds.JSON) // stream channel output req.SetOption(cmds.ChanOpt, "true") query, err := getQuery(req) if err != nil { return nil, err } var fileReader *MultiFileReader var reader io.Reader if req.Files() != nil { fileReader = NewMultiFileReader(req.Files(), true) reader = fileReader } path := strings.Join(req.Path(), "/") url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, ApiPath, path, query) httpReq, err := http.NewRequest("POST", url, reader) if err != nil { return nil, err } // TODO extract string consts? if fileReader != nil { httpReq.Header.Set(contentTypeHeader, "multipart/form-data; boundary="+fileReader.Boundary()) } else { httpReq.Header.Set(contentTypeHeader, applicationOctetStream) } httpReq.Header.Set(uaHeader, config.ApiVersion) httpReq.Cancel = req.Context().Done() httpReq.Close = true httpRes, err := c.httpClient.Do(httpReq) if err != nil { return nil, err } // using the overridden JSON encoding in request res, err := getResponse(httpRes, req) if err != nil { return nil, err } if found && len(previousUserProvidedEncoding) > 0 { // reset to user provided encoding after sending request // NB: if user has provided an encoding but it is the empty string, // still leave it as JSON. req.SetOption(cmds.EncShort, previousUserProvidedEncoding) } return res, nil }
func callCommand(ctx context.Context, req cmds.Request, root *cmds.Command, cmd *cmds.Command) (cmds.Response, error) { log.Info(config.EnvDir, " ", req.InvocContext().ConfigRoot) var res cmds.Response err := req.SetRootContext(ctx) if err != nil { return nil, err } details, err := commandDetails(req.Path(), root) if err != nil { return nil, err } log.Debug("looking for running daemon...") useDaemon, err := commandShouldRunOnDaemon(*details, req, root) if err != nil { return nil, err } err = callPreCommandHooks(ctx, *details, req, root) if err != nil { return nil, err } if cmd.PreRun != nil { err = cmd.PreRun(req) if err != nil { return nil, err } } if useDaemon { cfg, err := req.InvocContext().GetConfig() if err != nil { return nil, err } addr, err := ma.NewMultiaddr(cfg.Addresses.API) if err != nil { return nil, err } log.Infof("Executing command on daemon running at %s", addr) _, host, err := manet.DialArgs(addr) if err != nil { return nil, err } client := cmdsHttp.NewClient(host) res, err = client.Send(req) if err != nil { return nil, err } } else { log.Debug("Executing command locally") err := req.SetRootContext(ctx) if err != nil { return nil, err } // Okay!!!!! NOW we can call the command. res = root.Call(req) } if cmd.PostRun != nil { cmd.PostRun(req, res) } return res, nil }
func (c *client) Send(req cmds.Request) (cmds.Response, error) { if req.Context() == nil { log.Warningf("no context set in request") if err := req.SetRootContext(context.TODO()); err != nil { return nil, err } } // save user-provided encoding previousUserProvidedEncoding, found, err := req.Option(cmds.EncShort).String() if err != nil { return nil, err } // override with json to send to server req.SetOption(cmds.EncShort, cmds.JSON) // stream channel output req.SetOption(cmds.ChanOpt, "true") query, err := getQuery(req) if err != nil { return nil, err } var fileReader *MultiFileReader var reader io.Reader if req.Files() != nil { fileReader = NewMultiFileReader(req.Files(), true) reader = fileReader } path := strings.Join(req.Path(), "/") url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, ApiPath, path, query) httpReq, err := http.NewRequest("POST", url, reader) if err != nil { return nil, err } // TODO extract string consts? if fileReader != nil { httpReq.Header.Set(contentTypeHeader, "multipart/form-data; boundary="+fileReader.Boundary()) httpReq.Header.Set(contentDispHeader, "form-data: name=\"files\"") } else { httpReq.Header.Set(contentTypeHeader, applicationOctetStream) } httpReq.Header.Set(uaHeader, config.ApiVersion) ec := make(chan error, 1) rc := make(chan cmds.Response, 1) dc := req.Context().Done() go func() { httpRes, err := c.httpClient.Do(httpReq) if err != nil { ec <- err return } // using the overridden JSON encoding in request res, err := getResponse(httpRes, req) if err != nil { ec <- err return } rc <- res }() for { select { case <-dc: log.Debug("Context cancelled, cancelling HTTP request...") tr := http.DefaultTransport.(*http.Transport) tr.CancelRequest(httpReq) dc = nil // Wait for ec or rc case err := <-ec: return nil, err case res := <-rc: if found && len(previousUserProvidedEncoding) > 0 { // reset to user provided encoding after sending request // NB: if user has provided an encoding but it is the empty string, // still leave it as JSON. req.SetOption(cmds.EncShort, previousUserProvidedEncoding) } return res, nil } } }
func (c *client) Send(req cmds.Request) (cmds.Response, error) { // save user-provided encoding previousUserProvidedEncoding, found, err := req.Option(cmds.EncShort).String() if err != nil { return nil, err } // override with json to send to server req.SetOption(cmds.EncShort, cmds.JSON) // stream channel output req.SetOption(cmds.ChanOpt, "true") query, err := getQuery(req) if err != nil { return nil, err } var fileReader *MultiFileReader var reader io.Reader if req.Files() != nil { fileReader = NewMultiFileReader(req.Files(), true) reader = fileReader } else { // if we have no file data, use an empty Reader // (http.NewRequest panics when a nil Reader is used) reader = strings.NewReader("") } path := strings.Join(req.Path(), "/") url := fmt.Sprintf(ApiUrlFormat, c.serverAddress, ApiPath, path, query) httpReq, err := http.NewRequest("POST", url, reader) if err != nil { return nil, err } // TODO extract string consts? if fileReader != nil { httpReq.Header.Set("Content-Type", "multipart/form-data; boundary="+fileReader.Boundary()) httpReq.Header.Set("Content-Disposition", "form-data: name=\"files\"") } else { httpReq.Header.Set("Content-Type", "application/octet-stream") } version := config.CurrentVersionNumber httpReq.Header.Set("User-Agent", fmt.Sprintf("/go-ipfs/%s/", version)) ec := make(chan error, 1) rc := make(chan cmds.Response, 1) dc := req.Context().Context.Done() go func() { httpRes, err := http.DefaultClient.Do(httpReq) if err != nil { ec <- err return } // using the overridden JSON encoding in request res, err := getResponse(httpRes, req) if err != nil { ec <- err return } rc <- res }() for { select { case <-dc: log.Debug("Context cancelled, cancelling HTTP request...") tr := http.DefaultTransport.(*http.Transport) tr.CancelRequest(httpReq) dc = nil // Wait for ec or rc case err := <-ec: return nil, err case res := <-rc: if found && len(previousUserProvidedEncoding) > 0 { // reset to user provided encoding after sending request // NB: if user has provided an encoding but it is the empty string, // still leave it as JSON. req.SetOption(cmds.EncShort, previousUserProvidedEncoding) } return res, nil } } }