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  }