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