Files
Atay-Makhzan/modules/httplib/request.go
T

166 lines
4.2 KiB
Go
Raw Normal View History

2014-05-06 11:50:31 -04:00
// Copyright 2013 The Beego Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2014-05-06 11:50:31 -04:00
package httplib
import (
"bytes"
"context"
"fmt"
2014-05-06 11:50:31 -04:00
"io"
"net"
"net/http"
"net/url"
"strings"
"sync"
2014-05-06 11:50:31 -04:00
"time"
)
var defaultTransport = sync.OnceValue(func() http.RoundTripper {
return &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: DialContextWithTimeout(10 * time.Second), // it is good enough in modern days
2014-08-23 21:13:55 +08:00
}
})
2014-05-06 11:50:31 -04:00
func DialContextWithTimeout(timeout time.Duration) func(ctx context.Context, network, address string) (net.Conn, error) {
return func(ctx context.Context, network, address string) (net.Conn, error) {
return (&net.Dialer{Timeout: timeout}).DialContext(ctx, network, address)
}
}
func NewRequest(url, method string) *Request {
return &Request{
url: url,
req: &http.Request{
Method: method,
Header: make(http.Header),
Proto: "HTTP/1.1", // FIXME: from legacy httplib, it shouldn't be hardcoded
ProtoMajor: 1,
ProtoMinor: 1,
},
params: map[string]string{},
// ATTENTION: from legacy httplib, callers must pay more attention to it, it will cause annoying bugs when the response takes a long time
readWriteTimeout: 60 * time.Second,
}
2014-05-06 11:50:31 -04:00
}
2015-08-27 23:06:14 +08:00
type Request struct {
url string
req *http.Request
params map[string]string
readWriteTimeout time.Duration
transport http.RoundTripper
2014-08-23 21:13:55 +08:00
}
// SetContext sets the request's Context
func (r *Request) SetContext(ctx context.Context) *Request {
r.req = r.req.WithContext(ctx)
return r
}
// SetTransport sets the request transport, if not set, will use httplib's default transport with environment proxy support
// ATTENTION: the http.Transport has a connection pool, so it should be reused as much as possible, do not create a lot of transports
func (r *Request) SetTransport(transport http.RoundTripper) *Request {
r.transport = transport
2015-08-27 23:06:14 +08:00
return r
2014-05-06 11:50:31 -04:00
}
func (r *Request) SetReadWriteTimeout(readWriteTimeout time.Duration) *Request {
r.readWriteTimeout = readWriteTimeout
return r
}
// Header set header item string in request.
2015-08-27 23:06:14 +08:00
func (r *Request) Header(key, value string) *Request {
r.req.Header.Set(key, value)
return r
}
2014-05-06 11:50:31 -04:00
// Param adds query param in to request.
// params build query string as ?key1=value1&key2=value2...
2015-08-27 23:06:14 +08:00
func (r *Request) Param(key, value string) *Request {
r.params[key] = value
return r
2014-05-06 11:50:31 -04:00
}
2025-01-31 19:05:48 +08:00
// Body adds request raw body. It supports string, []byte and io.Reader as body.
2023-07-04 20:36:08 +02:00
func (r *Request) Body(data any) *Request {
2025-03-10 17:36:02 +08:00
if r == nil {
return nil
}
2014-05-06 11:50:31 -04:00
switch t := data.(type) {
2025-01-31 19:05:48 +08:00
case nil: // do nothing
2014-05-06 11:50:31 -04:00
case string:
2025-04-01 12:14:01 +02:00
bf := strings.NewReader(t)
r.req.Body = io.NopCloser(bf)
2015-08-27 23:06:14 +08:00
r.req.ContentLength = int64(len(t))
2014-05-06 11:50:31 -04:00
case []byte:
bf := bytes.NewBuffer(t)
r.req.Body = io.NopCloser(bf)
2015-08-27 23:06:14 +08:00
r.req.ContentLength = int64(len(t))
2025-01-31 19:05:48 +08:00
case io.ReadCloser:
r.req.Body = t
case io.Reader:
r.req.Body = io.NopCloser(t)
default:
panic(fmt.Sprintf("unsupported request body type %T", t))
2014-05-06 11:50:31 -04:00
}
2015-08-27 23:06:14 +08:00
return r
2014-05-06 11:50:31 -04:00
}
// Response executes request client and returns the response.
// Caller MUST close the response body if no error occurs.
func (r *Request) Response() (*http.Response, error) {
2014-05-06 11:50:31 -04:00
var paramBody string
2015-08-27 23:06:14 +08:00
if len(r.params) > 0 {
2014-05-06 11:50:31 -04:00
var buf bytes.Buffer
2015-08-27 23:06:14 +08:00
for k, v := range r.params {
2014-05-06 11:50:31 -04:00
buf.WriteString(url.QueryEscape(k))
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(v))
buf.WriteByte('&')
}
paramBody = buf.String()
paramBody = paramBody[0 : len(paramBody)-1]
}
2025-04-01 12:14:01 +02:00
if r.req.Method == http.MethodGet && len(paramBody) > 0 {
2019-06-12 21:41:28 +02:00
if strings.Contains(r.url, "?") {
2015-08-27 23:06:14 +08:00
r.url += "&" + paramBody
2014-05-06 11:50:31 -04:00
} else {
2015-08-27 23:06:14 +08:00
r.url = r.url + "?" + paramBody
2014-05-06 11:50:31 -04:00
}
2025-04-01 12:14:01 +02:00
} else if r.req.Method == http.MethodPost && r.req.Body == nil && len(paramBody) > 0 {
2022-01-20 01:31:39 +01:00
r.Header("Content-Type", "application/x-www-form-urlencoded")
2025-01-31 19:05:48 +08:00
r.Body(paramBody) // string
2014-05-06 11:50:31 -04:00
}
var err error
r.req.URL, err = url.Parse(r.url)
2014-05-06 11:50:31 -04:00
if err != nil {
return nil, err
}
client := &http.Client{
Transport: r.transport,
Timeout: r.readWriteTimeout,
2014-08-23 21:13:55 +08:00
}
if client.Transport == nil {
client.Transport = defaultTransport()
2014-05-06 11:50:31 -04:00
}
if r.req.Header.Get("User-Agent") == "" {
r.req.Header.Set("User-Agent", "GiteaHttpLib")
2025-03-10 17:36:02 +08:00
}
2014-05-06 11:50:31 -04:00
return client.Do(r.req)
2014-05-06 11:50:31 -04:00
}
func (r *Request) GoString() string {
return fmt.Sprintf("%s %s", r.req.Method, r.url)
}