github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/Unknwon/com/http.go (about) 1 // Copyright 2013 com authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 package com 16 17 import ( 18 "bytes" 19 "encoding/json" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "net/http" 24 "os" 25 "path" 26 ) 27 28 type NotFoundError struct { 29 Message string 30 } 31 32 func (e NotFoundError) Error() string { 33 return e.Message 34 } 35 36 type RemoteError struct { 37 Host string 38 Err error 39 } 40 41 func (e *RemoteError) Error() string { 42 return e.Err.Error() 43 } 44 45 var UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1541.0 Safari/537.36" 46 47 // HttpCall makes HTTP method call. 48 func HttpCall(client *http.Client, method, url string, header http.Header, body io.Reader) (io.ReadCloser, error) { 49 req, err := http.NewRequest(method, url, body) 50 if err != nil { 51 return nil, err 52 } 53 req.Header.Set("User-Agent", UserAgent) 54 for k, vs := range header { 55 req.Header[k] = vs 56 } 57 resp, err := client.Do(req) 58 if err != nil { 59 return nil, err 60 } 61 if resp.StatusCode == 200 { 62 return resp.Body, nil 63 } 64 resp.Body.Close() 65 if resp.StatusCode == 404 { // 403 can be rate limit error. || resp.StatusCode == 403 { 66 err = fmt.Errorf("resource not found: %s", url) 67 } else { 68 err = fmt.Errorf("%s %s -> %d", method, url, resp.StatusCode) 69 } 70 return nil, err 71 } 72 73 // HttpGet gets the specified resource. 74 // ErrNotFound is returned if the server responds with status 404. 75 func HttpGet(client *http.Client, url string, header http.Header) (io.ReadCloser, error) { 76 return HttpCall(client, "GET", url, header, nil) 77 } 78 79 // HttpPost posts the specified resource. 80 // ErrNotFound is returned if the server responds with status 404. 81 func HttpPost(client *http.Client, url string, header http.Header, body []byte) (io.ReadCloser, error) { 82 return HttpCall(client, "POST", url, header, bytes.NewBuffer(body)) 83 } 84 85 // HttpGetToFile gets the specified resource and writes to file. 86 // ErrNotFound is returned if the server responds with status 404. 87 func HttpGetToFile(client *http.Client, url string, header http.Header, fileName string) error { 88 rc, err := HttpGet(client, url, header) 89 if err != nil { 90 return err 91 } 92 defer rc.Close() 93 94 os.MkdirAll(path.Dir(fileName), os.ModePerm) 95 f, err := os.Create(fileName) 96 if err != nil { 97 return err 98 } 99 defer f.Close() 100 _, err = io.Copy(f, rc) 101 return err 102 } 103 104 // HttpGetBytes gets the specified resource. ErrNotFound is returned if the server 105 // responds with status 404. 106 func HttpGetBytes(client *http.Client, url string, header http.Header) ([]byte, error) { 107 rc, err := HttpGet(client, url, header) 108 if err != nil { 109 return nil, err 110 } 111 defer rc.Close() 112 return ioutil.ReadAll(rc) 113 } 114 115 // HttpGetJSON gets the specified resource and mapping to struct. 116 // ErrNotFound is returned if the server responds with status 404. 117 func HttpGetJSON(client *http.Client, url string, v interface{}) error { 118 rc, err := HttpGet(client, url, nil) 119 if err != nil { 120 return err 121 } 122 defer rc.Close() 123 err = json.NewDecoder(rc).Decode(v) 124 if _, ok := err.(*json.SyntaxError); ok { 125 return fmt.Errorf("JSON syntax error at %s", url) 126 } 127 return nil 128 } 129 130 // HttpPostJSON posts the specified resource with struct values, 131 // and maps results to struct. 132 // ErrNotFound is returned if the server responds with status 404. 133 func HttpPostJSON(client *http.Client, url string, body, v interface{}) error { 134 data, err := json.Marshal(body) 135 if err != nil { 136 return err 137 } 138 rc, err := HttpPost(client, url, http.Header{"content-type": []string{"application/json"}}, data) 139 if err != nil { 140 return err 141 } 142 defer rc.Close() 143 err = json.NewDecoder(rc).Decode(v) 144 if _, ok := err.(*json.SyntaxError); ok { 145 return fmt.Errorf("JSON syntax error at %s", url) 146 } 147 return nil 148 } 149 150 // A RawFile describes a file that can be downloaded. 151 type RawFile interface { 152 Name() string 153 RawUrl() string 154 Data() []byte 155 SetData([]byte) 156 } 157 158 // FetchFiles fetches files specified by the rawURL field in parallel. 159 func FetchFiles(client *http.Client, files []RawFile, header http.Header) error { 160 ch := make(chan error, len(files)) 161 for i := range files { 162 go func(i int) { 163 p, err := HttpGetBytes(client, files[i].RawUrl(), nil) 164 if err != nil { 165 ch <- err 166 return 167 } 168 files[i].SetData(p) 169 ch <- nil 170 }(i) 171 } 172 for _ = range files { 173 if err := <-ch; err != nil { 174 return err 175 } 176 } 177 return nil 178 } 179 180 // FetchFiles uses command `curl` to fetch files specified by the rawURL field in parallel. 181 func FetchFilesCurl(files []RawFile, curlOptions ...string) error { 182 ch := make(chan error, len(files)) 183 for i := range files { 184 go func(i int) { 185 stdout, _, err := ExecCmd("curl", append(curlOptions, files[i].RawUrl())...) 186 if err != nil { 187 ch <- err 188 return 189 } 190 191 files[i].SetData([]byte(stdout)) 192 ch <- nil 193 }(i) 194 } 195 for _ = range files { 196 if err := <-ch; err != nil { 197 return err 198 } 199 } 200 return nil 201 }