2014-05-06 19:50:31 +04:00
// Copyright 2013 The Beego Authors. All rights reserved.
// Copyright 2014 The Gogs Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2014-05-06 19:50:31 +04:00
package httplib
import (
"bytes"
2021-07-14 17:43:13 +03:00
"context"
2014-05-06 19:50:31 +04:00
"crypto/tls"
"io"
"net"
"net/http"
"net/url"
"strings"
"time"
)
2022-01-20 03:31:39 +03:00
var defaultSetting = Settings { "GiteaServer" , 60 * time . Second , 60 * time . Second , nil , nil }
2014-08-23 17:13:55 +04:00
2016-11-25 09:32:09 +03:00
// newRequest returns *Request with specific method
2015-10-26 16:16:24 +03:00
func newRequest ( url , method string ) * Request {
2014-08-23 17:13:55 +04:00
var resp http . Response
req := http . Request {
Method : method ,
Header : make ( http . Header ) ,
Proto : "HTTP/1.1" ,
ProtoMajor : 1 ,
ProtoMinor : 1 ,
}
2022-01-20 03:31:39 +03:00
return & Request { url , & req , map [ string ] string { } , defaultSetting , & resp , nil }
2014-08-23 17:13:55 +04:00
}
2014-05-06 19:50:31 +04:00
2017-04-19 06:45:01 +03:00
// NewRequest returns *Request with specific method
func NewRequest ( url , method string ) * Request {
return newRequest ( url , method )
}
2016-11-25 09:32:09 +03:00
// Settings is the default settings for http client
2015-08-27 18:06:14 +03:00
type Settings struct {
2014-08-23 17:13:55 +04:00
UserAgent string
ConnectTimeout time . Duration
ReadWriteTimeout time . Duration
2016-11-25 09:32:09 +03:00
TLSClientConfig * tls . Config
2014-08-23 17:13:55 +04:00
Transport http . RoundTripper
2014-05-06 19:50:31 +04:00
}
2016-11-25 09:32:09 +03:00
// Request provides more useful methods for requesting one url than http.Request.
2015-08-27 18:06:14 +03:00
type Request struct {
2014-08-23 17:13:55 +04:00
url string
req * http . Request
params map [ string ] string
2015-08-27 18:06:14 +03:00
setting Settings
2014-08-23 17:13:55 +04:00
resp * http . Response
body [ ] byte
}
2021-07-14 17:43:13 +03:00
// SetContext sets the request's Context
func ( r * Request ) SetContext ( ctx context . Context ) * Request {
r . req = r . req . WithContext ( ctx )
return r
}
2014-05-06 19:50:31 +04:00
// SetTimeout sets connect time out and read-write time out for BeegoRequest.
2015-08-27 18:06:14 +03:00
func ( r * Request ) SetTimeout ( connectTimeout , readWriteTimeout time . Duration ) * Request {
r . setting . ConnectTimeout = connectTimeout
r . setting . ReadWriteTimeout = readWriteTimeout
return r
2014-05-06 19:50:31 +04:00
}
// SetTLSClientConfig sets tls connection configurations if visiting https url.
2015-08-27 18:06:14 +03:00
func ( r * Request ) SetTLSClientConfig ( config * tls . Config ) * Request {
2016-11-25 09:32:09 +03:00
r . setting . TLSClientConfig = config
2015-08-27 18:06:14 +03:00
return r
2014-05-06 19:50:31 +04:00
}
// Header add header item string in request.
2015-08-27 18:06:14 +03:00
func ( r * Request ) Header ( key , value string ) * Request {
r . req . Header . Set ( key , value )
return r
}
2016-11-25 09:32:09 +03:00
// SetTransport sets transport to
2015-08-27 18:06:14 +03:00
func ( r * Request ) SetTransport ( transport http . RoundTripper ) * Request {
r . setting . Transport = transport
return r
2014-05-06 19:50:31 +04:00
}
// Param adds query param in to request.
// params build query string as ?key1=value1&key2=value2...
2015-08-27 18:06:14 +03:00
func ( r * Request ) Param ( key , value string ) * Request {
r . params [ key ] = value
return r
2014-05-06 19:50:31 +04:00
}
// Body adds request raw body.
// it supports string and []byte.
2015-08-27 18:06:14 +03:00
func ( r * Request ) Body ( data interface { } ) * Request {
2014-05-06 19:50:31 +04:00
switch t := data . ( type ) {
case string :
bf := bytes . NewBufferString ( t )
2021-09-22 08:38:34 +03:00
r . req . Body = io . NopCloser ( bf )
2015-08-27 18:06:14 +03:00
r . req . ContentLength = int64 ( len ( t ) )
2014-05-06 19:50:31 +04:00
case [ ] byte :
bf := bytes . NewBuffer ( t )
2021-09-22 08:38:34 +03:00
r . req . Body = io . NopCloser ( bf )
2015-08-27 18:06:14 +03:00
r . req . ContentLength = int64 ( len ( t ) )
2014-05-06 19:50:31 +04:00
}
2015-08-27 18:06:14 +03:00
return r
2014-05-06 19:50:31 +04:00
}
2015-08-27 18:06:14 +03:00
func ( r * Request ) getResponse ( ) ( * http . Response , error ) {
if r . resp . StatusCode != 0 {
return r . resp , nil
2014-08-23 17:13:55 +04:00
}
2022-01-20 03:31:39 +03:00
2014-05-06 19:50:31 +04:00
var paramBody string
2015-08-27 18:06:14 +03:00
if len ( r . params ) > 0 {
2014-05-06 19:50:31 +04:00
var buf bytes . Buffer
2015-08-27 18:06:14 +03:00
for k , v := range r . params {
2014-05-06 19: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 ]
}
2015-08-27 18:06:14 +03:00
if r . req . Method == "GET" && len ( paramBody ) > 0 {
2019-06-12 22:41:28 +03:00
if strings . Contains ( r . url , "?" ) {
2015-08-27 18:06:14 +03:00
r . url += "&" + paramBody
2014-05-06 19:50:31 +04:00
} else {
2015-08-27 18:06:14 +03:00
r . url = r . url + "?" + paramBody
2014-05-06 19:50:31 +04:00
}
2022-01-20 03:31:39 +03:00
} else if r . req . Method == "POST" && r . req . Body == nil && len ( paramBody ) > 0 {
r . Header ( "Content-Type" , "application/x-www-form-urlencoded" )
r . Body ( paramBody )
2014-05-06 19:50:31 +04:00
}
2015-08-27 18:06:14 +03:00
url , err := url . Parse ( r . url )
2014-05-06 19:50:31 +04:00
if err != nil {
return nil , err
}
2015-08-27 18:06:14 +03:00
r . req . URL = url
2014-05-06 19:50:31 +04:00
2015-08-27 18:06:14 +03:00
trans := r . setting . Transport
2014-05-06 19:50:31 +04:00
if trans == nil {
// create default transport
trans = & http . Transport {
2016-11-25 09:32:09 +03:00
TLSClientConfig : r . setting . TLSClientConfig ,
2022-01-20 03:31:39 +03:00
Proxy : http . ProxyFromEnvironment ,
2021-07-14 17:43:13 +03:00
DialContext : TimeoutDialer ( r . setting . ConnectTimeout ) ,
2014-05-06 19:50:31 +04:00
}
2019-06-12 22:41:28 +03:00
} else if t , ok := trans . ( * http . Transport ) ; ok {
if t . TLSClientConfig == nil {
t . TLSClientConfig = r . setting . TLSClientConfig
}
2021-07-14 17:43:13 +03:00
if t . DialContext == nil {
t . DialContext = TimeoutDialer ( r . setting . ConnectTimeout )
2014-05-06 19:50:31 +04:00
}
}
client := & http . Client {
Transport : trans ,
2021-04-25 21:48:12 +03:00
Timeout : r . setting . ReadWriteTimeout ,
2014-08-23 17:13:55 +04:00
}
2015-08-27 18:06:14 +03:00
if len ( r . setting . UserAgent ) > 0 && len ( r . req . Header . Get ( "User-Agent" ) ) == 0 {
r . req . Header . Set ( "User-Agent" , r . setting . UserAgent )
2014-08-23 17:13:55 +04:00
}
2015-08-27 18:06:14 +03:00
resp , err := client . Do ( r . req )
2014-05-06 19:50:31 +04:00
if err != nil {
return nil , err
}
2015-08-27 18:06:14 +03:00
r . resp = resp
2014-05-06 19:50:31 +04:00
return resp , nil
}
2017-03-15 03:52:01 +03:00
// Response executes request client gets response manually.
2015-08-27 18:06:14 +03:00
func ( r * Request ) Response ( ) ( * http . Response , error ) {
return r . getResponse ( )
2014-05-06 19:50:31 +04:00
}
// TimeoutDialer returns functions of connection dialer with timeout settings for http.Transport Dial field.
2021-07-14 17:43:13 +03:00
func TimeoutDialer ( cTimeout time . Duration ) func ( ctx context . Context , net , addr string ) ( c net . Conn , err error ) {
return func ( ctx context . Context , netw , addr string ) ( net . Conn , error ) {
d := net . Dialer { Timeout : cTimeout }
conn , err := d . DialContext ( ctx , netw , addr )
2014-05-06 19:50:31 +04:00
if err != nil {
return nil , err
}
2021-04-25 21:48:12 +03:00
return conn , nil
2014-05-06 19:50:31 +04:00
}
}