func getQuery(req cmds.Request) (string, error) { query := url.Values{} for k, v := range req.Options() { if OptionSkipMap[k] { continue } str := fmt.Sprintf("%v", v) query.Set(k, str) } args := req.Arguments() argDefs := req.Command().Arguments argDefIndex := 0 for _, arg := range args { argDef := argDefs[argDefIndex] // skip ArgFiles for argDef.Type == cmds.ArgFile { argDefIndex++ argDef = argDefs[argDefIndex] } query.Add("arg", arg) if len(argDefs) > argDefIndex+1 { argDefIndex++ } } return query.Encode(), 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 }
// read json objects off of the given stream, and write the objects out to // the 'out' channel func readStreamedJson(req cmds.Request, rr io.Reader, out chan<- interface{}) { defer close(out) dec := json.NewDecoder(rr) outputType := reflect.TypeOf(req.Command().Type) ctx := req.Context() for { v, err := decodeTypedVal(outputType, dec) if err != nil { if err != io.EOF { log.Error(err) } return } select { case <-ctx.Done(): return case out <- v: } } }
// read json objects off of the given stream, and write the objects out to // the 'out' channel func readStreamedJson(req cmds.Request, rr io.Reader, out chan<- interface{}) { defer close(out) dec := json.NewDecoder(rr) outputType := reflect.TypeOf(req.Command().Type) ctx := req.Context() for { v, err := decodeTypedVal(outputType, dec) if err != nil { // reading on a closed response body is as good as an io.EOF here if !(strings.Contains(err.Error(), "read on closed response body") || err == io.EOF) { log.Error(err) } return } select { case <-ctx.Done(): return case out <- v: } } }
// getResponse decodes a http.Response to create a cmds.Response func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error) { var err error res := cmds.NewResponse(req) contentType := httpRes.Header.Get(contentTypeHeader) contentType = strings.Split(contentType, ";")[0] lengthHeader := httpRes.Header.Get(extraContentLengthHeader) if len(lengthHeader) > 0 { length, err := strconv.ParseUint(lengthHeader, 10, 64) if err != nil { return nil, err } res.SetLength(length) } rr := &httpResponseReader{httpRes} res.SetCloser(rr) if contentType != applicationJson { // for all non json output types, just stream back the output res.SetOutput(rr) return res, nil } else if len(httpRes.Header.Get(channelHeader)) > 0 { // if output is coming from a channel, decode each chunk outChan := make(chan interface{}) go readStreamedJson(req, rr, outChan) res.SetOutput((<-chan interface{})(outChan)) return res, nil } dec := json.NewDecoder(rr) // If we ran into an error if httpRes.StatusCode >= http.StatusBadRequest { e := cmds.Error{} switch { case httpRes.StatusCode == http.StatusNotFound: // handle 404s e.Message = "Command not found." e.Code = cmds.ErrClient case contentType == plainText: // handle non-marshalled errors mes, err := ioutil.ReadAll(rr) if err != nil { return nil, err } e.Message = string(mes) e.Code = cmds.ErrNormal default: // handle marshalled errors err = dec.Decode(&e) if err != nil { return nil, err } } res.SetError(e, e.Code) return res, nil } outputType := reflect.TypeOf(req.Command().Type) v, err := decodeTypedVal(outputType, dec) if err != nil && err != io.EOF { return nil, err } res.SetOutput(v) return res, nil }
// getResponse decodes a http.Response to create a cmds.Response func getResponse(httpRes *http.Response, req cmds.Request) (cmds.Response, error) { var err error res := cmds.NewResponse(req) contentType := httpRes.Header.Get(contentTypeHeader) contentType = strings.Split(contentType, ";")[0] lengthHeader := httpRes.Header.Get(contentLengthHeader) if len(lengthHeader) > 0 { length, err := strconv.ParseUint(lengthHeader, 10, 64) if err != nil { return nil, err } res.SetLength(length) } if len(httpRes.Header.Get(streamHeader)) > 0 { // if output is a stream, we can just use the body reader res.SetOutput(httpRes.Body) return res, nil } else if len(httpRes.Header.Get(channelHeader)) > 0 { // if output is coming from a channel, decode each chunk outChan := make(chan interface{}) go func() { dec := json.NewDecoder(httpRes.Body) outputType := reflect.TypeOf(req.Command().Type) ctx := req.Context().Context for { var v interface{} var err error if outputType != nil { v = reflect.New(outputType).Interface() err = dec.Decode(v) } else { err = dec.Decode(&v) } if err != nil && err != io.EOF { fmt.Println(err.Error()) return } select { case <-ctx.Done(): close(outChan) return default: } if err == io.EOF { close(outChan) return } outChan <- v } }() res.SetOutput((<-chan interface{})(outChan)) return res, nil } dec := json.NewDecoder(httpRes.Body) if httpRes.StatusCode >= http.StatusBadRequest { e := cmds.Error{} if httpRes.StatusCode == http.StatusNotFound { // handle 404s e.Message = "Command not found." e.Code = cmds.ErrClient } else if contentType == "text/plain" { // handle non-marshalled errors buf := bytes.NewBuffer(nil) io.Copy(buf, httpRes.Body) e.Message = string(buf.Bytes()) e.Code = cmds.ErrNormal } else { // handle marshalled errors err = dec.Decode(&e) if err != nil { return nil, err } } res.SetError(e, e.Code) } else { outputType := reflect.TypeOf(req.Command().Type) var v interface{} if outputType != nil { v = reflect.New(outputType).Interface() err = dec.Decode(v) } else { err = dec.Decode(&v) } if err != nil && err != io.EOF { return nil, err } if v != nil { res.SetOutput(v) } } return res, nil }