github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/courier/transport_http/transform/request.go (about)

     1  package transform
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	"mime/multipart"
     9  	"net/http"
    10  	"net/textproto"
    11  	"net/url"
    12  	"reflect"
    13  	"strings"
    14  	"time"
    15  
    16  	"github.com/johnnyeven/libtools/courier"
    17  	"github.com/johnnyeven/libtools/courier/httpx"
    18  	"github.com/johnnyeven/libtools/courier/status_error"
    19  )
    20  
    21  func NewRequest(method string, uri string, v interface{}, metadatas ...courier.Metadata) (*http.Request, error) {
    22  	if v == nil {
    23  		return NewHttpRequestFromParameterGroup(method, uri, nil, metadatas...)
    24  	}
    25  	return NewHttpRequestFromParameterGroup(method, uri, ParameterGroupFromValue(v), metadatas...)
    26  }
    27  
    28  func CloneRequestBody(req *http.Request) ([]byte, error) {
    29  	bodyBytes, err := ioutil.ReadAll(req.Body)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	req.Body.Close()
    34  	req.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
    35  	return bodyBytes, nil
    36  }
    37  
    38  func NewHttpRequestFromParameterGroup(method string, uri string, m *ParameterGroup, metadatas ...courier.Metadata) (req *http.Request, err error) {
    39  	body := &bytes.Buffer{}
    40  	multipartWriter := (*multipart.Writer)(nil)
    41  
    42  	if m != nil {
    43  		if m.FormData.Len() > 0 {
    44  			postForm := (*url.Values)(nil)
    45  
    46  			for _, parameterMeta := range m.FormData {
    47  				name := parameterMeta.Name
    48  				dataList, errForMarshal := parameterMeta.Marshal()
    49  
    50  				if errForMarshal != nil {
    51  					err = errForMarshal
    52  					return
    53  				}
    54  
    55  				if parameterMeta.IsMultipart() {
    56  					if multipartWriter == nil {
    57  						multipartWriter = multipart.NewWriter(body)
    58  					}
    59  
    60  					if fileHeaders, ok := parameterMeta.Value.Interface().([]*multipart.FileHeader); ok {
    61  						for _, fileHeader := range fileHeaders {
    62  							if errForAppend := appendFile(multipartWriter, name, fileHeader, nil); errForAppend != nil {
    63  								err = errForAppend
    64  								return
    65  							}
    66  						}
    67  						continue
    68  					}
    69  
    70  					if fileHeader, ok := parameterMeta.Value.Interface().(*multipart.FileHeader); ok {
    71  						if errForAppend := appendFile(multipartWriter, name, fileHeader, nil); errForAppend != nil {
    72  							err = errForAppend
    73  							return
    74  						}
    75  						continue
    76  					}
    77  
    78  					for _, v := range dataList {
    79  						err = multipartWriter.WriteField(name, string(v))
    80  						if err != nil {
    81  							return
    82  						}
    83  					}
    84  				} else {
    85  					if postForm == nil {
    86  						postForm = &url.Values{}
    87  					}
    88  					for _, v := range dataList {
    89  						postForm.Add(name, string(v))
    90  					}
    91  				}
    92  			}
    93  			if postForm != nil {
    94  				body.WriteString(postForm.Encode())
    95  			}
    96  		} else if m.Body != nil {
    97  			dataList, errForMarshal := m.Body.Marshal()
    98  			if errForMarshal != nil {
    99  				err = errForMarshal
   100  				return
   101  			}
   102  			if len(dataList) > 0 {
   103  				body.Write(dataList[0])
   104  			}
   105  		}
   106  	}
   107  	if multipartWriter != nil {
   108  		multipartWriter.Close()
   109  	}
   110  
   111  	u, errForParseUrl := url.Parse(uri)
   112  	if errForParseUrl != nil {
   113  		err = errForParseUrl
   114  		return
   115  	}
   116  
   117  	if u.Path == "" {
   118  		u.Path = "/"
   119  	}
   120  
   121  	req, err = http.NewRequest(
   122  		method,
   123  		u.String(),
   124  		body,
   125  	)
   126  
   127  	if err != nil {
   128  		return
   129  	}
   130  
   131  	req.Close = true
   132  
   133  	if metadatas != nil {
   134  		for key, values := range courier.MetadataMerge(metadatas...) {
   135  			if key == httpx.HeaderAuthorization {
   136  				req.SetBasicAuth(values[0], values[1])
   137  				continue
   138  			}
   139  
   140  			for _, v := range values {
   141  				req.Header.Add(key, v)
   142  			}
   143  		}
   144  	}
   145  
   146  	if m == nil {
   147  		return
   148  	}
   149  
   150  	for _, parameterMeta := range m.Parameters {
   151  		name := parameterMeta.Name
   152  		dataList, errForMarshal := parameterMeta.Marshal()
   153  		if errForMarshal != nil {
   154  			err = errForMarshal
   155  			return
   156  		}
   157  
   158  		if len(dataList) == 0 {
   159  			continue
   160  		}
   161  
   162  		switch parameterMeta.In {
   163  		case "query":
   164  			// todo use explode when service updated
   165  			query := url.Values{}
   166  			query.Add(parameterMeta.Name, string(bytes.Join(dataList, []byte(","))))
   167  
   168  			if req.URL.RawQuery == "" {
   169  				req.URL.RawQuery = query.Encode()
   170  			} else {
   171  				req.URL.RawQuery = req.URL.RawQuery + "&" + query.Encode()
   172  			}
   173  		case "cookie":
   174  			for _, v := range dataList {
   175  				req.AddCookie(&http.Cookie{
   176  					Name:  name,
   177  					Value: string(v),
   178  				})
   179  			}
   180  		case "path":
   181  			paramKey := ":" + name
   182  			if !strings.Contains(req.URL.Path, paramKey) {
   183  				err = fmt.Errorf("uri %s need path parameter %s, but uri has no %s", uri, name, paramKey)
   184  				return
   185  			}
   186  			value := string(bytes.Join(dataList, []byte(",")))
   187  			req.URL.Path = strings.Replace(req.URL.Path, paramKey, value, -1)
   188  		case "header":
   189  			value := string(bytes.Join(dataList, []byte(",")))
   190  			req.Header.Add(name, value)
   191  		}
   192  	}
   193  
   194  	if m.FormData.Len() > 0 {
   195  		if multipartWriter != nil {
   196  			req.Header.Add(httpx.HeaderContentType, multipartWriter.FormDataContentType())
   197  		} else {
   198  			req.Header.Add(httpx.HeaderContentType, httpx.MIMEPOSTForm+"; param=value")
   199  		}
   200  	}
   201  
   202  	return
   203  }
   204  
   205  func appendFile(multipartWriter *multipart.Writer, field string, fileHeader *multipart.FileHeader, content []byte) (err error) {
   206  	if fileHeader == nil {
   207  		return
   208  	}
   209  	filePart, errForCreate := multipartWriter.CreateFormFile(field, fileHeader.Filename)
   210  	if errForCreate != nil {
   211  		err = errForCreate
   212  		return
   213  	}
   214  
   215  	if len(content) == 0 {
   216  		if file, errForOpen := fileHeader.Open(); errForOpen != nil {
   217  			err = errForOpen
   218  			return
   219  		} else if _, errForCopy := io.Copy(filePart, file); errForCopy != nil {
   220  			err = errForCopy
   221  			return
   222  		}
   223  		return nil
   224  	}
   225  
   226  	if _, errForWrite := filePart.Write(content); errForWrite != nil {
   227  		err = errForWrite
   228  		return
   229  	}
   230  	return nil
   231  }
   232  
   233  func NewFileHeader(fieldName string, filename string, content []byte) (fileHeader *multipart.FileHeader, err error) {
   234  	buffer := &bytes.Buffer{}
   235  	multipartWriter := multipart.NewWriter(buffer)
   236  	appendFile(multipartWriter, fieldName, &multipart.FileHeader{
   237  		Filename: filename,
   238  	}, content)
   239  	multipartWriter.Close()
   240  	reader := multipart.NewReader(buffer, multipartWriter.Boundary())
   241  	form, errForReader := reader.ReadForm(int64(buffer.Len()))
   242  	if errForReader != nil {
   243  		err = errForReader
   244  		return
   245  	}
   246  	fileHeader = form.File[fieldName][0]
   247  	return
   248  }
   249  
   250  type IParameterValuesGetter interface {
   251  	Initial() error
   252  	Param(name string) string
   253  	Query(name string) []string
   254  	Header(name string) []string
   255  	Cookie(name string) []string
   256  	FormValue(name string) []string
   257  	FormFile(name string) ([]*multipart.FileHeader, error)
   258  	Body() io.Reader
   259  }
   260  
   261  func NewParameterValuesGetter(r *http.Request) *ParameterValuesGetter {
   262  	return &ParameterValuesGetter{Request: r}
   263  }
   264  
   265  type ParameterValuesGetter struct {
   266  	Request *http.Request
   267  	query   url.Values
   268  	now     time.Time
   269  	cookies []*http.Cookie
   270  }
   271  
   272  func (getter *ParameterValuesGetter) Initial() error {
   273  	getter.now = time.Now()
   274  	getter.query = getter.Request.URL.Query()
   275  	return nil
   276  }
   277  
   278  func (getter *ParameterValuesGetter) Param(name string) string {
   279  	return ""
   280  }
   281  
   282  func (getter *ParameterValuesGetter) Query(name string) []string {
   283  	return getter.query[name]
   284  }
   285  
   286  func (getter *ParameterValuesGetter) Header(name string) []string {
   287  	return getter.Request.Header[textproto.CanonicalMIMEHeaderKey(name)]
   288  }
   289  
   290  func (getter *ParameterValuesGetter) Cookie(name string) []string {
   291  	values := make([]string, 0)
   292  	if len(getter.cookies) == 0 {
   293  		getter.cookies = getter.Request.Cookies()
   294  	}
   295  	for _, c := range getter.cookies {
   296  		if c.Name == name {
   297  			if c.Expires.IsZero() {
   298  				values = append(values, c.Value)
   299  			} else if c.Expires.After(getter.now) {
   300  				values = append(values, c.Value)
   301  			}
   302  		}
   303  	}
   304  	return values
   305  }
   306  
   307  func (getter *ParameterValuesGetter) FormValue(name string) []string {
   308  	if getter.Request.Form == nil {
   309  		// just parse form
   310  		getter.Request.FormValue("")
   311  	}
   312  	return getter.Request.Form[name]
   313  }
   314  
   315  func (getter *ParameterValuesGetter) FormFile(name string) ([]*multipart.FileHeader, error) {
   316  	if getter.Request.MultipartForm == nil {
   317  		// just parse form
   318  		_, fileHeader, err := getter.Request.FormFile(name)
   319  		if err != nil || fileHeader == nil {
   320  			return nil, err
   321  		}
   322  	}
   323  	return getter.Request.MultipartForm.File[name], nil
   324  }
   325  
   326  func (getter *ParameterValuesGetter) Body() io.Reader {
   327  	return getter.Request.Body
   328  }
   329  
   330  func MarshalParameters(group *ParameterGroup, getter IParameterValuesGetter) error {
   331  	parameterErrors := ParameterErrors{}
   332  
   333  	if err := getter.Initial(); err != nil {
   334  		parameterErrors.Merge(err)
   335  		return parameterErrors.Err()
   336  	}
   337  
   338  	for _, parameterMeta := range group.Parameters.List() {
   339  		switch parameterMeta.In {
   340  		case "path":
   341  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(strings.Split(getter.Param(parameterMeta.Name), ",")...))
   342  		case "header":
   343  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Header(parameterMeta.Name)...))
   344  		case "query":
   345  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Query(parameterMeta.Name)...))
   346  		case "cookie":
   347  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Cookie(parameterMeta.Name)...))
   348  		}
   349  	}
   350  
   351  	for _, parameterMeta := range group.FormData {
   352  		if _, ok := parameterMeta.Value.Interface().([]*multipart.FileHeader); ok {
   353  			fileHeaders, err := getter.FormFile(parameterMeta.Name)
   354  			if err != nil || len(fileHeaders) == 0 {
   355  				return status_error.ReadFormFileFailed
   356  			}
   357  			parameterMeta.Value.Set(reflect.ValueOf(fileHeaders))
   358  			continue
   359  		}
   360  		if _, ok := parameterMeta.Value.Interface().(*multipart.FileHeader); ok {
   361  			fileHeaders, err := getter.FormFile(parameterMeta.Name)
   362  			if err != nil || len(fileHeaders) == 0 {
   363  				return status_error.ReadFormFileFailed
   364  			}
   365  			if parameterMeta.Value.CanSet() {
   366  				parameterMeta.Value.Set(reflect.ValueOf(fileHeaders[0]))
   367  			}
   368  			continue
   369  		}
   370  		parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.FormValue(parameterMeta.Name)...))
   371  	}
   372  
   373  	if parameterErrors.StatusError == nil {
   374  		isValid, errFields := group.ValidateNoBodyByHook()
   375  		if !isValid {
   376  			parameterErrors.Merge(status_error.InvalidField.StatusError().WithErrorFields(errFields...))
   377  		}
   378  	}
   379  
   380  	if group.Body != nil {
   381  		parameterErrors.Merge(group.Body.UnmarshalFromReader(getter.Body()))
   382  	}
   383  
   384  	return parameterErrors.Err()
   385  }