package saashttp import ( "bytes" "compress/gzip" "crypto/md5" "encoding/hex" "encoding/json" "fmt" "io" "net/http" "net/url" "os" "strconv" "strings" "time" "git.algo.com.cn/public/saasapi" "google.golang.org/protobuf/proto" ) type ResponseEncoder int const ( RESPONSE_ENCODER_PROTOBUF ResponseEncoder = iota RESPONSE_ENCODER_JSON = 1 ) type SaasClient struct { Client *http.Client ApiUrls *ApiUrls Auth *Auth ResponseEncoder ResponseEncoder } func (c *SaasClient) Info(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.InfoPath) return c.post(postUrl, saasReq) } func (c *SaasClient) Write(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.WritePath) return c.post(postUrl, saasReq) } func (c *SaasClient) Read(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ReadPath) return c.post(postUrl, saasReq) } func (c *SaasClient) ColumnWrite(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ColumnWritePath) return c.post(postUrl, saasReq) } func (c *SaasClient) TaskCreate(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskCreatePath) return c.post(postUrl, saasReq) } func (c *SaasClient) TaskList(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskListPath) return c.post(postUrl, saasReq) } func (c *SaasClient) TaskDelete(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskDeletePath) return c.post(postUrl, saasReq) } func (c *SaasClient) TaskInfo(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskInfoPath) return c.post(postUrl, saasReq) } func (c *SaasClient) TaskRun(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskRunPath) return c.post(postUrl, saasReq) } func (c *SaasClient) TaskUpload(sha256 string, file *os.File, offset int64, size int) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskUploadPath, "sha256", sha256) return c.upload(postUrl, file, offset, size) } func (c *SaasClient) TaskDownload(sha256 string, file *os.File, offset int64, size int) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TaskDownloadPath, "sha256", sha256) return c.download(postUrl, file, offset, size) } func (c *SaasClient) TargetList(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TargetListPath) return c.post(postUrl, saasReq) } func (c *SaasClient) TargetCreate(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TargetCreatePath) return c.post(postUrl, saasReq) } func (c *SaasClient) TargetDelete(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.TargetDeletePath) return c.post(postUrl, saasReq) } func (c *SaasClient) BindSet(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.BindSetPath) return c.post(postUrl, saasReq) } func (c *SaasClient) BindDelete(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.BindDeletePath) return c.post(postUrl, saasReq) } func (c *SaasClient) ScriptRun(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ScriptRunPath) return c.post(postUrl, saasReq) } func (c *SaasClient) ExpList(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ExpListPath) return c.post(postUrl, saasReq) } func (c *SaasClient) ExpGet(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ExpGetPath) return c.post(postUrl, saasReq) } func (c *SaasClient) makeUrl(baseUrl, path string, params ...string) string { url, err := url.Parse(baseUrl) if err != nil { panic(err) } url = url.JoinPath(path) queryValues := url.Query() switch c.ResponseEncoder { case RESPONSE_ENCODER_PROTOBUF: queryValues.Add("resmode", "protobuf") case RESPONSE_ENCODER_JSON: queryValues.Add("resmode", "json") default: queryValues.Add("resmode", "protobuf") } for i := 0; i < len(params); i += 2 { queryValues.Add(params[i], params[i+1]) } url.RawQuery = queryValues.Encode() return url.String() } func (c *SaasClient) post(url string, saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postBuf, err := proto.Marshal(saasReq) if err != nil { fmt.Println("marshal saas req error", err) return nil, err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(postBuf)) if err != nil { fmt.Println("http new request error", err) return nil, err } timeStamp := strconv.FormatInt(time.Now().Unix(), 10) md5byte := md5.Sum([]byte(c.Auth.Account + c.Auth.Token + timeStamp)) authorization := hex.EncodeToString(md5byte[:]) req.Header.Add("Account", c.Auth.Account) req.Header.Add("Time", timeStamp) req.Header.Add("Authorization", authorization) req.Header.Add("Content-Type", "application/x-protobuf") res, err := c.Client.Do(req) if err != nil { fmt.Println("http send error", err) return nil, err } defer res.Body.Close() resBody, err := io.ReadAll(res.Body) if err != nil { fmt.Println("http read body error", err) return nil, err } saasRes = &saasapi.SaasRes{} if c.ResponseEncoder == RESPONSE_ENCODER_PROTOBUF { err = proto.Unmarshal(resBody, saasRes) if err != nil { fmt.Println("unmarshal response body to protobuf error", err) return nil, err } } else { err = json.Unmarshal(resBody, saasRes) if err != nil { fmt.Println("unmarshal response body to json error", err) return nil, err } } return saasRes, nil } func (c *SaasClient) upload(url string, file *os.File, offset int64, size int) (saasRes *saasapi.SaasRes, err error) { if file == nil { return nil, fmt.Errorf("file is nil") } if size <= 0 { return nil, fmt.Errorf("size is invalid") } buf := make([]byte, size) n, err := file.ReadAt(buf, offset) if err != nil { fmt.Println("read file error", err) return nil, err } if n != size { return nil, fmt.Errorf("read file error") } var gzBuf bytes.Buffer gz := gzip.NewWriter(&gzBuf) if _, err := gz.Write(buf); err != nil { fmt.Println("gzip write error", err) return nil, err } if err = gz.Close(); err != nil { return nil, err } req, err := http.NewRequest("POST", url, bytes.NewBuffer(gzBuf.Bytes())) if err != nil { fmt.Println("http new request error", err) return nil, err } timeStamp := strconv.FormatInt(time.Now().Unix(), 10) md5byte := md5.Sum([]byte(c.Auth.Account + c.Auth.Token + timeStamp)) authorization := hex.EncodeToString(md5byte[:]) req.Header.Add("Account", c.Auth.Account) req.Header.Add("Time", timeStamp) req.Header.Add("Authorization", authorization) req.Header.Add("Content-Type", "application/octet-stream") req.Header.Add("Content-Encoding", "gzip") res, err := c.Client.Do(req) if err != nil { fmt.Println("http send error", err) return nil, err } defer res.Body.Close() resBody, err := io.ReadAll(res.Body) if err != nil { fmt.Println("http read body error", err) return nil, err } saasRes = &saasapi.SaasRes{} if c.ResponseEncoder == RESPONSE_ENCODER_PROTOBUF { err = proto.Unmarshal(resBody, saasRes) if err != nil { fmt.Println("unmarshal response body to protobuf error", err) return nil, err } } else { err = json.Unmarshal(resBody, saasRes) if err != nil { fmt.Println("unmarshal response body to json error", err) return nil, err } } return saasRes, nil } func (c *SaasClient) download(url string, file *os.File, offset int64, size int) (saasRes *saasapi.SaasRes, err error) { if file == nil { return nil, fmt.Errorf("file is nil") } if size <= 0 { return nil, fmt.Errorf("size is invalid") } req, err := http.NewRequest("GET", url, bytes.NewBuffer([]byte{})) if err != nil { fmt.Println("http new request error", err) return nil, err } timeStamp := strconv.FormatInt(time.Now().Unix(), 10) md5byte := md5.Sum([]byte(c.Auth.Account + c.Auth.Token + timeStamp)) authorization := hex.EncodeToString(md5byte[:]) req.Header.Add("Account", c.Auth.Account) req.Header.Add("Time", timeStamp) req.Header.Add("Authorization", authorization) req.Header.Add("Accept-Encoding", "gzip") res, err := c.Client.Do(req) if err != nil { fmt.Println("http send error", err) return nil, err } defer res.Body.Close() if res.StatusCode != 200 { err = fmt.Errorf("NOT 200. %v", res.StatusCode) fmt.Println("http state error", err) return nil, err } bodyReader := res.Body if strings.Contains(res.Header.Get("Content-Encoding"), "gzip") { gz, err := gzip.NewReader(res.Body) if err != nil { fmt.Println("gzip newreader error", err) return nil, err } defer gz.Close() bodyReader = gz } resBody, err := io.ReadAll(bodyReader) if err != nil { fmt.Println("read body error", err) return nil, err } saasRes = &saasapi.SaasRes{} if strings.Contains(res.Header.Get("Content-Type"), "application/octet-stream") { if len(resBody) == size { file.WriteAt(resBody, offset) } else { err = fmt.Errorf("body size error. body:%v, want:%v", len(resBody), size) fmt.Println("http read body error", err) return nil, err } } else { if c.ResponseEncoder == RESPONSE_ENCODER_PROTOBUF { err = proto.Unmarshal(resBody, saasRes) if err != nil { fmt.Println("unmarshal response body to protobuf error", err) return nil, err } } else { err = json.Unmarshal(resBody, saasRes) if err != nil { fmt.Println("unmarshal response body to json error", err) return nil, err } } } return saasRes, nil } func (c *SaasClient) ScriptCreate(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ScriptCreatePath) return c.post(postUrl, saasReq) } func (c *SaasClient) ScriptList(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ScriptListPath) return c.post(postUrl, saasReq) } func (c *SaasClient) ScriptDelete(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ScriptDeletePath) return c.post(postUrl, saasReq) } func (c *SaasClient) ScriptGet(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ScriptGetPath) return c.post(postUrl, saasReq) } func (c *SaasClient) ScriptUse(saasReq *saasapi.SaasReq) (saasRes *saasapi.SaasRes, err error) { postUrl := c.makeUrl(c.ApiUrls.BaseUrl, c.ApiUrls.ScriptUsePath) return c.post(postUrl, saasReq) }