You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

http.go 9.4 kB

1 year ago
2 years ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. package http
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "mime"
  7. "mime/multipart"
  8. "net/http"
  9. "net/textproto"
  10. ul "net/url"
  11. "strings"
  12. "gitlink.org.cn/cloudream/common/pkgs/iterator"
  13. "gitlink.org.cn/cloudream/common/utils/math2"
  14. "gitlink.org.cn/cloudream/common/utils/serder"
  15. )
  16. const (
  17. ContentTypeJSON = "application/json"
  18. ContentTypeForm = "application/x-www-form-urlencoded"
  19. ContentTypeMultiPart = "multipart/form-data"
  20. ContentTypeOctetStream = "application/octet-stream"
  21. )
  22. type RequestParam struct {
  23. Header any
  24. Query any
  25. Body any
  26. }
  27. func GetJSON(url string, param RequestParam) (*http.Response, error) {
  28. req, err := http.NewRequest(http.MethodGet, url, nil)
  29. if err != nil {
  30. return nil, err
  31. }
  32. if err = prepareQuery(req, param.Query); err != nil {
  33. return nil, err
  34. }
  35. if err = prepareHeader(req, param.Header); err != nil {
  36. return nil, err
  37. }
  38. if err = prepareJSONBody(req, param.Body); err != nil {
  39. return nil, err
  40. }
  41. return http.DefaultClient.Do(req)
  42. }
  43. func GetForm(url string, param RequestParam) (*http.Response, error) {
  44. req, err := http.NewRequest(http.MethodGet, url, nil)
  45. if err != nil {
  46. return nil, err
  47. }
  48. if err = prepareQuery(req, param.Query); err != nil {
  49. return nil, err
  50. }
  51. if err = prepareHeader(req, param.Header); err != nil {
  52. return nil, err
  53. }
  54. if err = prepareFormBody(req, param.Body); err != nil {
  55. return nil, err
  56. }
  57. return http.DefaultClient.Do(req)
  58. }
  59. func PostJSON(url string, param RequestParam) (*http.Response, error) {
  60. req, err := http.NewRequest(http.MethodPost, url, nil)
  61. if err != nil {
  62. return nil, err
  63. }
  64. if err = prepareQuery(req, param.Query); err != nil {
  65. return nil, err
  66. }
  67. if err = prepareHeader(req, param.Header); err != nil {
  68. return nil, err
  69. }
  70. if err = prepareJSONBody(req, param.Body); err != nil {
  71. return nil, err
  72. }
  73. return http.DefaultClient.Do(req)
  74. }
  75. func PostForm(url string, param RequestParam) (*http.Response, error) {
  76. req, err := http.NewRequest(http.MethodPost, url, nil)
  77. if err != nil {
  78. return nil, err
  79. }
  80. if err = prepareQuery(req, param.Query); err != nil {
  81. return nil, err
  82. }
  83. if err = prepareHeader(req, param.Header); err != nil {
  84. return nil, err
  85. }
  86. if err = prepareFormBody(req, param.Body); err != nil {
  87. return nil, err
  88. }
  89. return http.DefaultClient.Do(req)
  90. }
  91. func ParseJSONResponse[TBody any](resp *http.Response) (TBody, error) {
  92. var ret TBody
  93. contType := resp.Header.Get("Content-Type")
  94. if strings.Contains(contType, ContentTypeJSON) {
  95. if err := serder.JSONToObjectStream(resp.Body, &ret); err != nil {
  96. return ret, fmt.Errorf("parsing response: %w", err)
  97. }
  98. return ret, nil
  99. }
  100. cont, err := io.ReadAll(resp.Body)
  101. if err != nil {
  102. return ret, fmt.Errorf("unknow response content type: %s, status: %d", contType, resp.StatusCode)
  103. }
  104. strCont := string(cont)
  105. return ret, fmt.Errorf("unknow response content type: %s, status: %d, body(prefix): %s", contType, resp.StatusCode, strCont[:math2.Min(len(strCont), 200)])
  106. }
  107. type MultiPartFile struct {
  108. FieldName string
  109. FileName string
  110. File io.ReadCloser
  111. Header textproto.MIMEHeader
  112. }
  113. type multiPartFileIterator struct {
  114. mr *multipart.Reader
  115. firstFile *multipart.Part
  116. }
  117. func (m *multiPartFileIterator) MoveNext() (*MultiPartFile, error) {
  118. if m.firstFile != nil {
  119. f := m.firstFile
  120. m.firstFile = nil
  121. fileName, err := ul.PathUnescape(f.FileName())
  122. if err != nil {
  123. return nil, fmt.Errorf("unescape file name: %w", err)
  124. }
  125. return &MultiPartFile{
  126. FieldName: f.FormName(),
  127. FileName: fileName,
  128. File: f,
  129. Header: f.Header,
  130. }, nil
  131. }
  132. for {
  133. part, err := m.mr.NextPart()
  134. if err == io.EOF {
  135. return nil, iterator.ErrNoMoreItem
  136. }
  137. if err != nil {
  138. return nil, err
  139. }
  140. fileName, err := ul.PathUnescape(part.FileName())
  141. if err != nil {
  142. return nil, fmt.Errorf("unescape file name: %w", err)
  143. }
  144. if part.FileName() != "" {
  145. return &MultiPartFile{
  146. FieldName: part.FormName(),
  147. FileName: fileName,
  148. File: part,
  149. Header: part.Header,
  150. }, nil
  151. }
  152. }
  153. }
  154. func (m *multiPartFileIterator) Close() {}
  155. // 解析multipart/form-data响应,只支持form参数在头部的情况
  156. func ParseMultiPartResponse(resp *http.Response) (map[string]string, iterator.Iterator[*MultiPartFile], error) {
  157. mtype, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type"))
  158. if err != nil {
  159. return nil, nil, fmt.Errorf("parse content type: %w", err)
  160. }
  161. if mtype != ContentTypeMultiPart {
  162. return nil, nil, fmt.Errorf("unknow content type: %s", mtype)
  163. }
  164. boundary := params["boundary"]
  165. if boundary == "" {
  166. return nil, nil, fmt.Errorf("no boundary in content type: %s", mtype)
  167. }
  168. formValues := make(map[string]string)
  169. rd := multipart.NewReader(resp.Body, boundary)
  170. var firstFile *multipart.Part
  171. for {
  172. part, err := rd.NextPart()
  173. if err == io.EOF {
  174. return formValues, iterator.Empty[*MultiPartFile](), nil
  175. }
  176. if err != nil {
  177. return nil, nil, err
  178. }
  179. formName := part.FormName()
  180. fileName := part.FileName()
  181. if formName == "" {
  182. continue
  183. }
  184. if fileName != "" {
  185. firstFile = part
  186. break
  187. }
  188. data, err := io.ReadAll(part)
  189. if err != nil {
  190. return nil, nil, err
  191. }
  192. formValues[formName] = string(data)
  193. }
  194. return formValues, &multiPartFileIterator{
  195. mr: rd,
  196. firstFile: firstFile,
  197. }, nil
  198. }
  199. type MultiPartRequestParam struct {
  200. Header any
  201. Query any
  202. Form any
  203. Files MultiPartFileIterator
  204. }
  205. type MultiPartFileIterator = iterator.Iterator[*IterMultiPartFile]
  206. type IterMultiPartFile struct {
  207. FieldName string // 这个文件所属的form字段
  208. FileName string // 文件名
  209. File io.ReadCloser
  210. }
  211. func PostMultiPart(url string, param MultiPartRequestParam) (*http.Response, error) {
  212. req, err := http.NewRequest(http.MethodPost, url, nil)
  213. if err != nil {
  214. return nil, err
  215. }
  216. if err = prepareQuery(req, param.Query); err != nil {
  217. return nil, err
  218. }
  219. if err = prepareHeader(req, param.Header); err != nil {
  220. return nil, err
  221. }
  222. pr, pw := io.Pipe()
  223. muWriter := multipart.NewWriter(pw)
  224. setHeader(req.Header, "Content-Type", fmt.Sprintf("%s;boundary=%s", ContentTypeMultiPart, muWriter.Boundary()))
  225. writeResult := make(chan error, 1)
  226. go func() {
  227. writeResult <- func() error {
  228. defer pw.Close()
  229. defer muWriter.Close()
  230. if param.Form != nil {
  231. mp, err := serder.ObjectToMap(param.Form)
  232. if err != nil {
  233. return fmt.Errorf("formValues object to map failed, err: %w", err)
  234. }
  235. for k, v := range mp {
  236. err := muWriter.WriteField(k, fmt.Sprintf("%v", v))
  237. if err != nil {
  238. return fmt.Errorf("write form field failed, err: %w", err)
  239. }
  240. }
  241. }
  242. for {
  243. file, err := param.Files.MoveNext()
  244. if err == iterator.ErrNoMoreItem {
  245. break
  246. }
  247. if err != nil {
  248. return fmt.Errorf("opening file: %w", err)
  249. }
  250. err = func() error {
  251. defer file.File.Close()
  252. w, err := muWriter.CreateFormFile(file.FieldName, ul.PathEscape(file.FileName))
  253. if err != nil {
  254. return fmt.Errorf("create form file failed, err: %w", err)
  255. }
  256. _, err = io.Copy(w, file.File)
  257. if err != nil {
  258. return err
  259. }
  260. return nil
  261. }()
  262. if err != nil {
  263. return err
  264. }
  265. }
  266. return nil
  267. }()
  268. }()
  269. req.Body = pr
  270. cli := http.Client{}
  271. resp, err := cli.Do(req)
  272. if err != nil {
  273. return nil, err
  274. }
  275. writeErr := <-writeResult
  276. if writeErr != nil {
  277. return nil, writeErr
  278. }
  279. return resp, nil
  280. }
  281. func prepareQuery(req *http.Request, query any) error {
  282. if query == nil {
  283. return nil
  284. }
  285. mp, ok := query.(map[string]any)
  286. if !ok {
  287. var err error
  288. if mp, err = serder.ObjectToMap(query); err != nil {
  289. return fmt.Errorf("query object to map: %w", err)
  290. }
  291. }
  292. values := make(ul.Values)
  293. for k, v := range mp {
  294. values.Add(k, fmt.Sprintf("%v", v))
  295. }
  296. req.URL.RawQuery = values.Encode()
  297. return nil
  298. }
  299. func prepareHeader(req *http.Request, header any) error {
  300. if header == nil {
  301. return nil
  302. }
  303. mp, ok := header.(map[string]any)
  304. if !ok {
  305. var err error
  306. if mp, err = serder.ObjectToMap(header); err != nil {
  307. return fmt.Errorf("header object to map: %w", err)
  308. }
  309. }
  310. req.Header = make(http.Header)
  311. for k, v := range mp {
  312. req.Header.Set(k, fmt.Sprintf("%v", v))
  313. }
  314. return nil
  315. }
  316. func prepareJSONBody(req *http.Request, body any) error {
  317. setHeader(req.Header, "Content-Type", ContentTypeJSON)
  318. if body == nil {
  319. return nil
  320. }
  321. data, err := serder.ObjectToJSON(body)
  322. if err != nil {
  323. return err
  324. }
  325. req.ContentLength = int64(len(data))
  326. req.Body = io.NopCloser(bytes.NewReader(data))
  327. return nil
  328. }
  329. func prepareFormBody(req *http.Request, body any) error {
  330. setHeader(req.Header, "Content-Type", ContentTypeForm)
  331. if body == nil {
  332. return nil
  333. }
  334. mp, ok := body.(map[string]any)
  335. if !ok {
  336. var err error
  337. if mp, err = serder.ObjectToMap(body); err != nil {
  338. return fmt.Errorf("body object to map: %w", err)
  339. }
  340. }
  341. values := make(ul.Values)
  342. for k, v := range mp {
  343. values.Add(k, fmt.Sprintf("%v", v))
  344. }
  345. data := values.Encode()
  346. req.Body = io.NopCloser(strings.NewReader(data))
  347. req.ContentLength = int64(len(data))
  348. return nil
  349. }
  350. func setHeader(mp http.Header, key, value string) http.Header {
  351. if mp == nil {
  352. mp = make(http.Header)
  353. }
  354. mp.Set(key, value)
  355. return mp
  356. }
  357. func setValue(values ul.Values, key, value string) ul.Values {
  358. if values == nil {
  359. values = make(ul.Values)
  360. }
  361. values.Add(key, value)
  362. return values
  363. }