github.com/sohaha/zlsgo@v1.7.13-0.20240501141223-10dd1a906f76/zhttp/response.go (about)

     1  package zhttp
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"encoding/json"
     7  	"encoding/xml"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"os"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/sohaha/zlsgo/zfile"
    16  	"github.com/sohaha/zlsgo/zjson"
    17  	"golang.org/x/net/html/charset"
    18  )
    19  
    20  type Res struct {
    21  	err    error
    22  	r      *Engine
    23  	req    *http.Request
    24  	resp   *http.Response
    25  	client *http.Client
    26  	*multipartHelper
    27  	downloadProgress DownloadProgress
    28  	tmpFile          string
    29  	requesterBody    []byte
    30  	responseBody     []byte
    31  	cost             time.Duration
    32  }
    33  
    34  func (r *Res) Request() *http.Request {
    35  	return r.req
    36  }
    37  
    38  func (r *Res) Response() *http.Response {
    39  	return r.resp
    40  }
    41  
    42  func (r *Res) StatusCode() int {
    43  	if r == nil || r.resp == nil {
    44  		return 0
    45  	}
    46  	_, _ = r.ToBytes()
    47  	return r.resp.StatusCode
    48  }
    49  
    50  func (r *Res) GetCookie() map[string]*http.Cookie {
    51  	cookiesRaw := r.Response().Cookies()
    52  	cookies := make(map[string]*http.Cookie, len(cookiesRaw))
    53  	var cookie *http.Cookie
    54  	for i := range cookiesRaw {
    55  		if cookie = cookiesRaw[i]; cookie != nil {
    56  			cookies[cookie.Name] = cookie
    57  		}
    58  	}
    59  	return cookies
    60  }
    61  
    62  func (r *Res) Bytes() []byte {
    63  	data, _ := r.ToBytes()
    64  	return data
    65  }
    66  
    67  func (r *Res) Stream(fn func(line []byte, eof bool) error) error {
    68  	if r.err != nil || r.resp == nil {
    69  		return r.err
    70  	}
    71  	r.responseBody = nil
    72  	defer r.resp.Body.Close()
    73  	br := bufio.NewReader(r.resp.Body)
    74  	for {
    75  		bs, err := br.ReadBytes('\n')
    76  
    77  		if err != nil && err != io.EOF {
    78  			return err
    79  		}
    80  
    81  		if err := fn(bs, err == io.EOF); err != nil {
    82  			return err
    83  		}
    84  
    85  		if err == io.EOF {
    86  			break
    87  		}
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func (r *Res) ToBytes() ([]byte, error) {
    94  	if r.err != nil || r.resp == nil {
    95  		return nil, r.err
    96  	}
    97  	if r.responseBody != nil {
    98  		return r.responseBody, nil
    99  	}
   100  	defer r.resp.Body.Close()
   101  	respBody, err := ioutil.ReadAll(r.resp.Body)
   102  	_, _ = io.Copy(ioutil.Discard, r.resp.Body)
   103  	if err != nil {
   104  		r.err = err
   105  		return nil, err
   106  	}
   107  
   108  	r.responseBody = forceUTF8(r, respBody)
   109  	return r.responseBody, nil
   110  }
   111  
   112  func forceUTF8(r *Res, respBody []byte) []byte {
   113  	ctype := r.resp.Header.Get("Content-Type")
   114  	c, n, _ := charset.DetermineEncoding(respBody, ctype)
   115  	if n != "utf-8" && n != "windows-1252" {
   116  		b, err := c.NewDecoder().Bytes(respBody)
   117  		if err == nil {
   118  			return b
   119  		}
   120  	}
   121  	return respBody
   122  }
   123  
   124  func (r *Res) Body() (body io.ReadCloser) {
   125  	if r.err != nil {
   126  		return nil
   127  	}
   128  	if r.responseBody != nil {
   129  		return ioutil.NopCloser(bytes.NewReader(r.responseBody))
   130  	}
   131  	defer r.resp.Body.Close()
   132  	respBody, err := ioutil.ReadAll(r.resp.Body)
   133  	_, _ = io.Copy(ioutil.Discard, r.resp.Body)
   134  	if err != nil {
   135  		r.err = err
   136  		return nil
   137  	}
   138  
   139  	r.responseBody = forceUTF8(r, respBody)
   140  	return ioutil.NopCloser(bytes.NewReader(r.responseBody))
   141  }
   142  
   143  func (r *Res) HTML() (doc QueryHTML) {
   144  	data, err := r.ToBytes()
   145  	if err != nil {
   146  		return QueryHTML{}
   147  	}
   148  	doc, _ = HTMLParse(data)
   149  	return
   150  }
   151  
   152  func (r *Res) String() string {
   153  	data, _ := r.ToBytes()
   154  	return string(data)
   155  }
   156  
   157  func (r *Res) JSONs() *zjson.Res {
   158  	data, _ := r.ToBytes()
   159  	return zjson.ParseBytes(data)
   160  }
   161  
   162  func (r *Res) JSON(key string) *zjson.Res {
   163  	j := r.JSONs()
   164  	return j.Get(key)
   165  }
   166  
   167  func (r *Res) ToString() (string, error) {
   168  	data, err := r.ToBytes()
   169  	return string(data), err
   170  }
   171  
   172  func (r *Res) ToJSON(v interface{}) error {
   173  	data, err := r.ToBytes()
   174  	if err != nil {
   175  		return err
   176  	}
   177  	return json.Unmarshal(data, v)
   178  }
   179  
   180  func (r *Res) ToXML(v interface{}) error {
   181  	data, err := r.ToBytes()
   182  	if err != nil {
   183  		return err
   184  	}
   185  	return xml.Unmarshal(data, v)
   186  }
   187  
   188  func (r *Res) ToFile(name string) error {
   189  	nameSplit := strings.Split(zfile.RealPath(name), "/")
   190  	nameSplitLen := len(nameSplit)
   191  	if nameSplitLen > 1 {
   192  		dir := strings.Join(nameSplit[0:nameSplitLen-1], "/")
   193  		name = zfile.RealPathMkdir(dir) + "/" + nameSplit[nameSplitLen-1]
   194  	}
   195  
   196  	if r.tmpFile != "" {
   197  		return zfile.CopyFile(r.tmpFile, name)
   198  	}
   199  
   200  	file, err := os.Create(name)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	//noinspection GoUnhandledErrorResult
   205  	defer file.Close()
   206  
   207  	if r.responseBody != nil {
   208  		_, err = file.Write(r.responseBody)
   209  		return err
   210  	}
   211  
   212  	if r.downloadProgress != nil && r.resp.ContentLength > 0 {
   213  		err = r.download(file)
   214  	} else {
   215  		//noinspection GoUnhandledErrorResult
   216  		defer r.resp.Body.Close()
   217  		_, err = io.Copy(file, r.resp.Body)
   218  	}
   219  	if err == nil {
   220  		r.tmpFile = name
   221  	}
   222  	return err
   223  }
   224  
   225  func (r *Res) download(file *os.File) error {
   226  	var (
   227  		current  int64
   228  		lastTime time.Time
   229  	)
   230  	p, b := make([]byte, 1024), r.resp.Body
   231  	duration, total := 200*time.Millisecond, r.resp.ContentLength
   232  	//noinspection GoUnhandledErrorResult
   233  	defer b.Close()
   234  	for {
   235  		l, err := b.Read(p)
   236  		if l > 0 {
   237  			_, _err := file.Write(p[:l])
   238  			if _err != nil {
   239  				return _err
   240  			}
   241  			current += int64(l)
   242  			if now := time.Now(); now.Sub(lastTime) > duration {
   243  				lastTime = now
   244  				r.downloadProgress(current, total)
   245  			}
   246  		}
   247  		if err != nil {
   248  			if err == io.EOF {
   249  				r.downloadProgress(total, total)
   250  				return nil
   251  			}
   252  			return err
   253  		}
   254  	}
   255  }