// CopyObject copies the source object to the destination. // The copied object's attributes are overwritten by attrs if non-nil. func (c *Client) CopyObject(ctx context.Context, srcBucket, srcName string, destBucket, destName string, attrs *ObjectAttrs) (*ObjectAttrs, error) { if srcBucket == "" || destBucket == "" { return nil, errors.New("storage: srcBucket and destBucket must both be non-empty") } if srcName == "" || destName == "" { return nil, errors.New("storage: srcName and destName must be non-empty") } if !utf8.ValidString(srcName) { return nil, fmt.Errorf("storage: srcName %q is not valid UTF-8", srcName) } if !utf8.ValidString(destName) { return nil, fmt.Errorf("storage: destName %q is not valid UTF-8", destName) } var rawObject *raw.Object if attrs != nil { attrs.Name = destName if attrs.ContentType == "" { return nil, errors.New("storage: attrs.ContentType must be non-empty") } rawObject = attrs.toRawObject(destBucket) } o, err := c.raw.Objects.Copy( srcBucket, srcName, destBucket, destName, rawObject).Projection("full").Context(ctx).Do() if err != nil { return nil, err } return newObject(o), nil }
func main() { s := "Hello, \x90\xA2\x8A\x45" // CP932 encoded version of "Hello, 世界" , 这里的 s 是string类型,说明string没有字符集的概念 r, _ := charset.NewReader("CP932", strings.NewReader(s)) // convert from CP932 to UTF-8 s2_, _ := ioutil.ReadAll(r) s2 := string(s2_) fmt.Println(s2) // => Hello, 世界 fmt.Println(len(s2)) // => 13 fmt.Println(utf8.RuneCountInString(s2)) // => 9 fmt.Println(utf8.ValidString(s2)) // => true fmt.Println(utf8.ValidString(s)) // => false fmt.Printf("%T|%#v\n", s, s) // 注意 %v 与 %#v 的区别 ss := "This is not utf-8 string \xa1" fmt.Println(utf8.ValidString(ss)) // => false pice := []int32{20, 30, 40, 90} sss := string(pice) // string 似乎执行了内存拷贝,但是不会涉及到字符集的处理(转换或校验) fmt.Printf("%T:%p %T:%p:%d\n", pice, pice, sss, &sss, len(sss)) // 为什么打印字符串变量的地址还需要取地址符 tr, err := charset.TranslatorTo("windows-1252") //需要检查字符集列表 if err != nil { fmt.Println(err) os.Exit(1) } _, gbk, err2 := tr.Translate([]byte("utf-8汉字"), true) if err2 != nil { fmt.Println(err2) os.Exit(1) } fmt.Println(gbk) }
// CopyTo copies the object to the given dst. // The copied object's attributes are overwritten by attrs if non-nil. func (o *ObjectHandle) CopyTo(ctx context.Context, dst *ObjectHandle, attrs *ObjectAttrs) (*ObjectAttrs, error) { // TODO(djd): move bucket/object name validation to a single helper func. if o.bucket == "" || dst.bucket == "" { return nil, errors.New("storage: the source and destination bucket names must both be non-empty") } if o.object == "" || dst.object == "" { return nil, errors.New("storage: the source and destination object names must both be non-empty") } if !utf8.ValidString(o.object) { return nil, fmt.Errorf("storage: object name %q is not valid UTF-8", o.object) } if !utf8.ValidString(dst.object) { return nil, fmt.Errorf("storage: dst name %q is not valid UTF-8", dst.object) } var rawObject *raw.Object if attrs != nil { attrs.Name = dst.object if attrs.ContentType == "" { return nil, errors.New("storage: attrs.ContentType must be non-empty") } rawObject = attrs.toRawObject(dst.bucket) } call := o.c.raw.Objects.Copy(o.bucket, o.object, dst.bucket, dst.object, rawObject).Projection("full").Context(ctx) if err := applyConds("CopyTo destination", dst.conds, call); err != nil { return nil, err } if err := applyConds("CopyTo source", toSourceConds(o.conds), call); err != nil { return nil, err } obj, err := call.Do() if err != nil { return nil, err } return newObject(obj), nil }
// validateValues reads the values provided as args and validates that they are // valid UTF-8. func (c *configCommand) validateValues(ctx *cmd.Context) (map[string]string, error) { settings := map[string]string{} for k, v := range c.values { //empty string is also valid as a setting value if v == "" { settings[k] = v continue } if v[0] != '@' { if !utf8.ValidString(v) { return nil, errors.Errorf("value for option %q contains non-UTF-8 sequences", k) } settings[k] = v continue } nv, err := readValue(ctx, v[1:]) if err != nil { return nil, err } if !utf8.ValidString(nv) { return nil, errors.Errorf("value for option %q contains non-UTF-8 sequences", k) } settings[k] = nv } return settings, nil }
// Run updates the configuration of a service. func (c *SetCommand) Run(ctx *cmd.Context) error { api, err := c.getAPI() if err != nil { return err } defer api.Close() if c.SettingsYAML.Path != "" { b, err := c.SettingsYAML.Read(ctx) if err != nil { return err } return block.ProcessBlockedError(api.ServiceSetYAML(c.ServiceName, string(b)), block.BlockChange) } else if len(c.SettingsStrings) == 0 { return nil } settings := map[string]string{} for k, v := range c.SettingsStrings { //empty string is also valid as a setting value if v == "" { settings[k] = v continue } if v[0] != '@' { if !utf8.ValidString(v) { return fmt.Errorf("value for option %q contains non-UTF-8 sequences", k) } settings[k] = v continue } nv, err := readValue(ctx, v[1:]) if err != nil { return err } if !utf8.ValidString(nv) { return fmt.Errorf("value for option %q contains non-UTF-8 sequences", k) } settings[k] = nv } result, err := api.ServiceGet(c.ServiceName) if err != nil { return err } for k, v := range settings { configValue := result.Config[k] configValueMap, ok := configValue.(map[string]interface{}) if ok { // convert the value to string and compare if fmt.Sprintf("%v", configValueMap["value"]) == v { logger.Warningf("the configuration setting %q already has the value %q", k, v) } } } return block.ProcessBlockedError(api.ServiceSet(c.ServiceName, settings), block.BlockChange) }
// fix line endings func (step *ProblemStep) Normalize(n int64) error { step.Step = n step.Note = strings.TrimSpace(step.Note) if step.Note == "" { return fmt.Errorf("missing note for step %d", n+1) } instructions, err := step.BuildInstructions() if err != nil { return fmt.Errorf("error building instructions for step %d: %v", n+1, err) } step.Instructions = instructions if step.Weight <= 0.0 { // default to 1.0 step.Weight = 1.0 } clean := make(map[string]string) for name, contents := range step.Files { parts := strings.Split(name, "/") fixed := contents if (len(parts) < 2 || !ProblemStepDirectoryWhitelist[parts[0]]) && utf8.ValidString(contents) { fixed = fixLineEndings(contents) if fixed != contents { log.Printf("fixed line endings for %s", name) } } else if utf8.ValidString(contents) { fixed = fixNewLines(contents) if fixed != contents { log.Printf("fixed newlines for %s", name) } } clean[name] = fixed } step.Files = clean return nil }
// decodeHeader decodes header, detecting its charset. // It guarantees to produce utf-8 string or error. func decodeHeader(rawheader string) (string, error) { dec := &mime.WordDecoder{ CharsetReader: charset.NewReaderLabel, } header, err := dec.DecodeHeader(rawheader) if err != nil { return header, err } if !utf8.ValidString(header) { nheader, err := decodeCharset(header, "") if err != nil { return header, err } if !utf8.ValidString(nheader) { return header, fmt.Errorf("decode header: non-utf8 byte left after decode") } return nheader, nil } return header, nil }
func ExampleValidString() { valid := "Hello, 世界" invalid := string([]byte{0xff, 0xfe, 0xfd}) fmt.Println(utf8.ValidString(valid)) fmt.Println(utf8.ValidString(invalid)) // Output: // true // false }
func (s *DoSuite) SetUpTest(c *gc.C) { s.BaseActionSuite.SetUpTest(c) s.dir = c.MkDir() c.Assert(utf8.ValidString(validParamsYaml), jc.IsTrue) c.Assert(utf8.ValidString(invalidParamsYaml), jc.IsTrue) c.Assert(utf8.ValidString(invalidUTFYaml), jc.IsFalse) setupValueFile(c, s.dir, "validParams.yml", validParamsYaml) setupValueFile(c, s.dir, "invalidParams.yml", invalidParamsYaml) setupValueFile(c, s.dir, "invalidUTF.yml", invalidUTFYaml) }
func (s *SetSuite) SetUpTest(c *gc.C) { s.FakeJujuHomeSuite.SetUpTest(c) s.fakeServiceAPI = &fakeServiceAPI{serviceName: "dummy-service"} s.dir = c.MkDir() c.Assert(utf8.ValidString(validSetTestValue), jc.IsTrue) c.Assert(utf8.ValidString(invalidSetTestValue), jc.IsFalse) setupValueFile(c, s.dir, "valid.txt", validSetTestValue) setupValueFile(c, s.dir, "invalid.txt", invalidSetTestValue) setupBigFile(c, s.dir) setupConfigFile(c, s.dir) }
func (s *SetSuite) SetUpTest(c *gc.C) { s.JujuConnSuite.SetUpTest(c) ch := s.AddTestingCharm(c, "dummy") svc := s.AddTestingService(c, "dummy-service", ch) s.svc = svc s.dir = c.MkDir() c.Assert(utf8.ValidString(validSetTestValue), gc.Equals, true) c.Assert(utf8.ValidString(invalidSetTestValue), gc.Equals, false) setupValueFile(c, s.dir, "valid.txt", validSetTestValue) setupValueFile(c, s.dir, "invalid.txt", invalidSetTestValue) setupBigFile(c, s.dir) setupConfigFile(c, s.dir) }
func newEntry(path, prefix string, fileInfo os.FileInfo) (*entry, error) { if !utf8.ValidString(path) || !utf8.ValidString(prefix) || !utf8.ValidString(fileInfo.Name()) { return nil, errors.New("pathname is not valid UTF8") } entry := entry{ path: path, prefix: prefix, fileInfo: fileInfo, } return &entry, nil }
func logMorph(log irclogsme.LogMessage) Log { res := Log{ Id: log.Id.String(), Time: log.Time, Nick: log.Nick, Ident: log.Ident, Host: log.Host, } // now to specify switch log.Type { case irclogsme.LMT_PRIVMSG: res.Type = "privmsg" res.Data = log.Payload case irclogsme.LMT_NOTICE: res.Type = "notice" res.Data = log.Payload case irclogsme.LMT_JOIN: res.Type = "join" case irclogsme.LMT_PART: res.Type = "part" res.Data = log.Payload case irclogsme.LMT_TOPIC: res.Type = "topic" res.Data = log.Payload case irclogsme.LMT_QUIT: res.Type = "quit" res.Data = log.Payload case irclogsme.LMT_ACTION: res.Type = "action" res.Data = log.Payload case irclogsme.LMT_KICK: res.Type = "kick" lk := LogKick{Target: log.Target.(string)} if message, ok := log.Payload.(string); ok { lk.Message = message } res.Data = lk } if sdata, ok := res.Data.(string); ok { if !utf8.ValidString(sdata) { res.Data = "[invalid unicode]" } } else if sdata, ok := res.Data.(LogKick); ok { if !utf8.ValidString(sdata.Message) { lk := res.Data.(LogKick) lk.Message = "[invalid unicode]" res.Data = lk } } return res }
func fixDisplayNameForAppGroup(info ItemInfo) ItemInfo { if info.FileType == uint16(gio.FileTypeDirectory) && strings.HasPrefix(info.BaseName, AppGroupPrefix) { info.DisplayName = strings.TrimPrefix(info.DisplayName, AppGroupPrefix) } // fix bug: name contains invalid coding if !utf8.ValidString(info.DisplayName) { info.DisplayName = fmt.Sprintf("%q", info.DisplayName) } if !utf8.ValidString(info.BaseName) { info.BaseName = fmt.Sprintf("%q", info.BaseName) } return info }
func findUnmanagedFiles(dir string, rpmFiles map[string]string, rpmDirs map[string]bool, unmanagedFiles map[string]string, ignoreList map[string]bool) { files, _ := readDir(dir) for _, f := range files { fileName := dir + f.Name() if !utf8.ValidString(fileName) { fmt.Fprintln(os.Stderr, fileName, "contains invalid UTF-8 characters. Skipping.") } else { if _, ok := ignoreList[fileName]; !ok { if f.IsDir() { if _, ok := rpmDirs[fileName]; ok { findUnmanagedFiles(fileName+"/", rpmFiles, rpmDirs, unmanagedFiles, ignoreList) } else { if !hasManagedDirs(fileName, rpmDirs) { unmanagedFiles[fileName+"/"] = "dir" } } } else { if _, ok := rpmFiles[fileName]; !ok { if f.Mode()& (os.ModeSocket|os.ModeNamedPipe|os.ModeDevice|os.ModeCharDevice) != 0 { // Ignore sockets, named pipes and devices } else if f.Mode()&os.ModeSymlink == os.ModeSymlink { unmanagedFiles[fileName] = "link" } else { unmanagedFiles[fileName] = "file" } } } } } } }
func (job *CreateJob) getFilename(destFsType string) (string, bool) { filename := job.filename filenameIsUtf8 := false if filename != "" { filenameIsUtf8 = utf8.ValidString(filename) } else { if job.makeDir { filename = Tr("New folder") filenameIsUtf8 = true } else { if job.src != nil { basename := job.src.GetBasename() filename = basename } if filename == "" { filename = Tr("New document") filenameIsUtf8 = true } } } // TODO: destFsType is empty which makes makeFileNameValidForDestFs usefuless, // why call this function on nautilus??? filename, _ = makeFileNameValidForDestFs(filename, destFsType) return filename, filenameIsUtf8 }
func BuildFileList(filePath, dir string) []*fileindex.FileItem { watcher.Watch(filepath.Join(filePath, dir)) var fileList []*fileindex.FileItem newPath := filepath.Join(filePath, dir) infoList, err := ioutil.ReadDir(newPath) if err != nil { log.Fatal(err) } for _, fileInfo := range infoList { if fileInfo.IsDir() { fileList = append(fileList, BuildFileList(newPath, fileInfo.Name())...) } else { fileItem, err := GetFileItem(newPath, fileInfo) _, ok := err.(*os.PathError) if err != nil && ok { log.Println(err) continue } if utf8.ValidString(fileItem.FileName) { fileList = append(fileList, fileItem) } else { log.Printf("%s is not properly utf-8 encoded", fileItem.FileName) } } } return fileList }
func main() { s := "¶ Greetings!" r, l := utf8.DecodeRuneInString(s) l2 := utf8.RuneLen(r) ok := utf8.ValidString(s) fmt.Printf("rune %c length %d = %d ok %t\n", r, l, l2, ok) }
func ParseQuery(rawinput string) (q FrontendQuery, err error) { var token queryParserToken if !utf8.ValidString(rawinput) { return nil, errQueryParserInputNotUtf8 } if len(rawinput) >= maxQuerySize { return nil, errQueryTooLong } input := []rune(rawinput) input, err = nextToken(input, &token, flagAllowEOF) if err != nil { return nil, err } else if token.typ == tokEOF { return NewEmptyQuery(), nil } else if token.typ == tokSemicolon { return NewEmptyQuery(), semicolonOrEOF(input) } else if token.typ != tokIdentifier { return nil, fmt.Errorf("unexpected token type %q", token.typ) } switch token.payload { case "select": return parseSelect(input) case "listen": return parseListen(input) case "unlisten": return parseUnlisten(input) default: return nil, fmt.Errorf("parse error at or near %q", token.payload) } }
// Append (to satisfy the Appender interface) adds a log message to the internal // buffer, and translates the log message into a format that is used by the // remote endpoint. func (apiLgr *APILogger) Append(log *slogger.Log) error { message := strings.TrimRight(log.Message(), "\r\n \t") // MCI-972: ensure message is valid UTF-8 if !utf8.ValidString(message) { message = strconv.QuoteToASCII(message) } logMessage := &model.LogMessage{ Timestamp: log.Timestamp, Severity: levelToString(log.Level), Type: log.Prefix, Version: evergreen.LogmessageCurrentVersion, Message: message, } apiLgr.appendLock.Lock() defer apiLgr.appendLock.Unlock() apiLgr.messages = append(apiLgr.messages, *logMessage) if len(apiLgr.messages) < apiLgr.SendAfterLines || time.Since(apiLgr.lastFlush) < apiLgr.SendAfterDuration { return nil } apiLgr.flushInternal() return nil }
func (w *Writer) open() error { attrs := w.ObjectAttrs // Check the developer didn't change the object Name (this is unfortunate, but // we don't want to store an object under the wrong name). if attrs.Name != w.name { return fmt.Errorf("storage: Writer.Name %q does not match object name %q", attrs.Name, w.name) } if !utf8.ValidString(attrs.Name) { return fmt.Errorf("storage: object name %q is not valid UTF-8", attrs.Name) } pr, pw := io.Pipe() r := &contentTyper{pr, attrs.ContentType} w.pw = pw w.opened = true go func() { resp, err := w.client.raw.Objects.Insert( w.bucket, attrs.toRawObject(w.bucket)).Media(r).Projection("full").Context(w.ctx).Do() w.err = err if err == nil { w.obj = newObject(resp) } else { pr.CloseWithError(w.err) } close(w.donec) }() return nil }
//获取淘宝属性信息 func GetAttrbuites(p *goquery.Document) string { attribute := make([]string, 0, 20) p.Find("#J_AttrUL li").Each(func(index int, element *goquery.Selection) { as := strings.Split(element.Text(), ":") if len(as) < 2 { as = strings.Split(element.Text(), ":") } b := "" if len(as) >= 2 && !utf8.ValidString(as[1]) { as[1] = as[1] b = as[1] } attribute = append(attribute, as[0]+":"+b) }) if len(attribute) == 0 { p.Find("#attributes .attributes-list li").Each(func(index int, element *goquery.Selection) { attribute = append(attribute, element.Text()) }) } return strings.Join(attribute, "##") }
// readMessage reads the next bitcoin message from r for the provided protocol // version and message header. func readMessage(r io.Reader, pver uint32, hdr *messageHeader) (Message, []byte, error) { if hdr == nil { return nil, nil, fmt.Errorf("readMessage: nil header") } command := hdr.command if !utf8.ValidString(command) { discardInput(r, hdr.length) str := "readMessage: invalid command %v" return nil, nil, fmt.Errorf(str, []byte(command)) } // Create struct of appropriate message type based on the command. msg, err := makeEmptyMessage(command) if err != nil { discardInput(r, hdr.length) return nil, nil, fmt.Errorf("readMessage: %v", err) } // Check for maximum length based on the message type as a malicious client // could otherwise create a well-formed header and set the length to max // numbers in order to exhaust the machine's memory. mpl := msg.MaxPayloadLength(pver) if hdr.length > mpl { discardInput(r, hdr.length) str := "ReadMessage: payload exceeds max length - Header " + "indicates %v bytes, but max payload size for messages of type " + "[%v] is %v." return nil, nil, fmt.Errorf(str, hdr.length, command, mpl) } // Read payload. payload := make([]byte, hdr.length) n, err := io.ReadFull(r, payload) if err != nil { return nil, nil, err } if uint32(n) != hdr.length { str := "readMessage: failed to read payload - Read %v " + "bytes, but payload size is %v bytes." return nil, nil, fmt.Errorf(str, n, hdr.length) } // Test checksum. checksum := DoubleSha256(payload)[0:4] if !bytes.Equal(checksum[:], hdr.checksum[:]) { str := "readMessage: payload checksum failed - Header " + "indicates %v, but actual checksum is %v." return nil, nil, fmt.Errorf(str, hdr.checksum, checksum) } // Unmarshal message. pr := bytes.NewBuffer(payload) err = msg.BtcDecode(pr, pver) if err != nil { return nil, nil, err } return msg, payload, nil }
// ensureUtf8 produces a valid utf-8 encoded string. In case its input is // invalid, all bad characters are replaced with \ufffd (aka "error" // rune). func ensureUtf8(s string) string { if utf8.ValidString(s) { return s } buf := bytes.Buffer{} start := 0 for i := 0; i < len(s); { if s[i] < utf8.RuneSelf { i++ continue } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { if start < i { buf.WriteString(s[start:i]) } buf.WriteRune(utf8.RuneError) i++ start = i continue } i += size } if start < len(s) { buf.WriteString(s[start:]) } return buf.String() }
func normalizeLinkText(text string) string { unescaped, _ := url.QueryUnescape(text) if unescaped == "" || !utf8.ValidString(unescaped) { return text } return unescaped }
func facetsToProto(src []Facet) ([]*pb.Facet, error) { dst := make([]*pb.Facet, 0, len(src)) for _, f := range src { if !validFieldName(f.Name) { return nil, fmt.Errorf("search: invalid facet name %q", f.Name) } facetValue := &pb.FacetValue{} switch x := f.Value.(type) { case Atom: if !utf8.ValidString(string(x)) { return nil, fmt.Errorf("search: %q facet is invalid UTF-8: %q", f.Name, x) } facetValue.Type = pb.FacetValue_ATOM.Enum() facetValue.StringValue = proto.String(string(x)) case float64: if !validFloat(x) { return nil, fmt.Errorf("search: numeric facet %q with invalid value %f", f.Name, x) } facetValue.Type = pb.FacetValue_NUMBER.Enum() facetValue.StringValue = proto.String(strconv.FormatFloat(x, 'e', -1, 64)) default: return nil, fmt.Errorf("search: unsupported facet type: %v", reflect.TypeOf(f.Value)) } dst = append(dst, &pb.Facet{ Name: proto.String(f.Name), Value: facetValue, }) } return dst, nil }
func validateKey(key string) error { // Keys must be valid UTF-8 and no more than 1024 bytes long. if len(key) > 1024 { return fmt.Errorf("Keys may be no longer than 1024 bytes.") } if !utf8.ValidString(key) { return fmt.Errorf("Keys must be valid UTF-8.") } // Because of the semantics of the "LIST bucket" request, keys must be // non-empty. (Otherwise an empty marker would exclude the first key.) Amazon // will reject empty keys with an HTTP 400 response. if key == "" { return fmt.Errorf("Keys must be non-empty.") } // Because "LIST bucket" responses are expressed using XML 1.0, keys must // contain only characters valid in XML 1.0. for _, r := range key { if !isLegalXmlCharacter(r) { return fmt.Errorf("Key contains invalid codepoint: %U", r) } } return nil }
func CrawlPackage(httpClient *http.Client, pkg string) (p *Package, err error) { pdoc, err := doc.Get(httpClient, pkg, "") if err != nil { return nil, villa.NestErrorf(err, "CrawlPackage(%s)", pkg) } readmeFn, readmeData := "", "" for fn, data := range pdoc.ReadmeFiles { readmeFn, readmeData = fn, string(data) if utf8.ValidString(readmeData) { break } else { readmeFn, readmeData = "", "" } } if pdoc.Doc == "" && pdoc.Synopsis == "" { pdoc.Synopsis = godoc.Synopsis(readmeData) } return &Package{ Name: pdoc.Name, ImportPath: pdoc.ImportPath, Synopsis: pdoc.Synopsis, Doc: pdoc.Doc, ProjectURL: pdoc.ProjectURL, StarCount: pdoc.StarCount, ReadmeFn: readmeFn, ReadmeData: readmeData, Imports: pdoc.Imports, References: pdoc.References, }, nil }
func (c *Connection) OnBroadcast(data []byte) { m := &EventDataIn{} if err := Unmarshal(data, m); err != nil { c.SendError("protocolerror") return } if c.user == nil { c.SendError("needlogin") return } if !c.user.featureGet(ISADMIN) { c.SendError("nopermission") return } msg := strings.TrimSpace(m.Data) msglen := utf8.RuneCountInString(msg) if !utf8.ValidString(msg) || msglen == 0 || msglen > 512 || invalidmessage.MatchString(msg) { c.SendError("invalidmsg") return } out := c.getEventDataOut() out.Data = msg c.Broadcast("BROADCAST", out) }
// NewReader creates a new Reader to read the contents of the // object. // ErrObjectNotExist will be returned if the object is not found. func (o *ObjectHandle) NewReader(ctx context.Context) (*Reader, error) { if !utf8.ValidString(o.object) { return nil, fmt.Errorf("storage: object name %q is not valid UTF-8", o.object) } u := &url.URL{ Scheme: "https", Host: "storage.googleapis.com", Path: fmt.Sprintf("/%s/%s", o.bucket, o.object), } res, err := o.c.hc.Get(u.String()) if err != nil { return nil, err } if res.StatusCode == http.StatusNotFound { res.Body.Close() return nil, ErrObjectNotExist } if res.StatusCode < 200 || res.StatusCode > 299 { res.Body.Close() return nil, fmt.Errorf("storage: can't read object %v/%v, status code: %v", o.bucket, o.object, res.Status) } return &Reader{ body: res.Body, size: res.ContentLength, contentType: res.Header.Get("Content-Type"), }, nil }