github.com/jkawamoto/roadie-azure@v0.3.5/roadie/url.go (about) 1 // 2 // roadie/url.go 3 // 4 // Copyright (c) 2017 Junpei Kawamoto 5 // 6 // This file is part of Roadie Azure. 7 // 8 // Roadie Azure is free software: you can redistribute it and/or modify 9 // it under the terms of the GNU General Public License as published by 10 // the Free Software Foundation, either version 3 of the License, or 11 // (at your option) any later version. 12 // 13 // Roadie Azure is distributed in the hope that it will be useful, 14 // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 // GNU General Public License for more details. 17 // 18 // You should have received a copy of the GNU General Public License 19 // along with Roadie Azure. If not, see <http://www.gnu.org/licenses/>. 20 // 21 22 package roadie 23 24 import ( 25 "compress/gzip" 26 "context" 27 "io" 28 "net/http" 29 "net/url" 30 "path" 31 "path/filepath" 32 "regexp" 33 "strings" 34 35 "golang.org/x/net/context/ctxhttp" 36 ) 37 38 var ( 39 // RegexpContentDisposition is a regular expression to obtain a file name 40 // from Content-Disposition header. 41 RegexpContentDisposition = regexp.MustCompile(`filename="?([^"]+)"?;?`) 42 ) 43 44 // Object representing a file in a web server. 45 type Object struct { 46 // Response is a raw response from a http server. 47 Response *http.Response 48 // Name of this object. 49 Name string 50 // Destination where this object should be stored. 51 Dest string 52 // Body is the stream of content body. 53 Body io.ReadCloser 54 } 55 56 // OpenURL opens a given url and returns an object associated with it. 57 func OpenURL(ctx context.Context, u string) (obj *Object, err error) { 58 59 loc, err := url.Parse(u) 60 if err != nil { 61 return 62 } 63 64 comps := filepath.SplitList(loc.Path) 65 var dest, name string 66 if len(comps) != 1 { 67 loc.Path = comps[0] 68 dest = path.Dir(comps[1]) 69 if !strings.HasSuffix(comps[1], "/") { 70 name = path.Base(comps[1]) 71 } 72 } 73 74 if loc.Scheme == "dropbox" { 75 loc = expandDropboxURL(loc) 76 } 77 78 req, err := http.NewRequest(http.MethodGet, loc.String(), nil) 79 if err != nil { 80 return 81 } 82 req.Header.Add("Accept-encoding", "gzip") 83 84 res, err := ctxhttp.Do(ctx, nil, req) 85 if err != nil { 86 return 87 } 88 89 body := res.Body 90 if res.Header.Get("Content-Encoding") == "gzip" { 91 body, err = gzip.NewReader(body) 92 if err != nil { 93 return 94 } 95 } 96 97 // Name is the base of the url but if Content-Disposition header is given, 98 // use that value instead. 99 if name == "" { 100 if disposition := res.Header.Get("Content-Disposition"); disposition != "" { 101 if match := RegexpContentDisposition.FindStringSubmatch(disposition); match != nil { 102 name = match[1] 103 } 104 } 105 if name == "" { 106 name = path.Base(loc.Path) 107 } 108 } 109 110 obj = &Object{ 111 Response: res, 112 Name: name, 113 Dest: dest, 114 Body: body, 115 } 116 return 117 118 } 119 120 // expandDropboxURL modifies a given URL which has dropbox schema. 121 func expandDropboxURL(loc *url.URL) *url.URL { 122 123 loc.Scheme = "https" 124 loc.Path = path.Join("/", loc.Host, loc.Path) 125 loc.Host = "www.dropbox.com" 126 loc.RawQuery = "dl=1" 127 return loc 128 129 }