forked from secsy/goftp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathround_tripper.go
78 lines (70 loc) · 1.96 KB
/
round_tripper.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
package goftp
import (
"bufio"
"crypto/tls"
"io"
"net/http"
"strings"
)
// RoundTrip implements the http.RoundTripper interface to allow an http.Client
// to handle ftp:// or ftps:// URLs. If req.URL.User is nil, the user and password
// from config will be used instead.
// Typical usage would be to register a Config to handle
// ftp:// and/or ftps:// URLs with http.Transport.RegisterProtocol.
// The User and Password fields in Config will be used when connecting
// to the remote FTP server unless the http.Request’s URL.User is non-nil.
func (config Config) RoundTrip(req *http.Request) (*http.Response, error) {
switch req.URL.Scheme {
default:
return nil, http.ErrSkipAltProtocol
case "ftp":
case "ftps":
if config.TLSConfig == nil {
config.TLSConfig = &tls.Config{}
}
}
// If req.URL.User is non-nil, username and password
// will override config even if empty.
if req.URL.User != nil {
config.User = req.URL.User.Username()
config.Password, _ = req.URL.User.Password()
}
path := strings.TrimPrefix(req.URL.Path, "/")
client, err := DialConfig(config, req.URL.Host)
if err != nil {
return nil, err
}
res := &http.Response{}
switch req.Method {
default:
return nil, http.ErrNotSupported
case http.MethodGet:
// Pipe Client.Retrieve to res.Body so enable unbuffered reads
// of large files.
// Errors returned by Client.Retrieve (like the size check)
// will be returned by res.Body.Read().
r, w := io.Pipe()
brc := &bufferedReadCloser{bufio.NewReader(r), r}
res.Body = brc
go func() {
w.CloseWithError(client.Retrieve(path, w))
}()
_, err = brc.Peek(1) // Get error immediately on bad read
if err != nil {
res.Body.Close()
res = nil
} else {
// Simulate HTTP response semantics
res.StatusCode = 200
res.Status = http.StatusText(res.StatusCode)
}
}
return res, err
}
type bufferedReadCloser struct {
*bufio.Reader
rc io.ReadCloser
}
func (rc *bufferedReadCloser) Close() error {
return rc.rc.Close()
}