github.com/profzone/eden-framework@v1.0.10/pkg/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/profzone/eden-framework/pkg/courier"
    17  	"github.com/profzone/eden-framework/pkg/courier/httpx"
    18  	"github.com/profzone/eden-framework/pkg/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  			for _, v := range values {
   136  				req.Header.Add(key, v)
   137  			}
   138  		}
   139  	}
   140  
   141  	if m == nil {
   142  		return
   143  	}
   144  
   145  	for _, parameterMeta := range m.Parameters {
   146  		name := parameterMeta.Name
   147  		dataList, errForMarshal := parameterMeta.Marshal()
   148  		if errForMarshal != nil {
   149  			err = errForMarshal
   150  			return
   151  		}
   152  
   153  		if len(dataList) == 0 {
   154  			continue
   155  		}
   156  
   157  		switch parameterMeta.In {
   158  		case "query":
   159  			// todo use explode when service updated
   160  			query := url.Values{}
   161  			query.Add(parameterMeta.Name, string(bytes.Join(dataList, []byte(","))))
   162  
   163  			if req.URL.RawQuery == "" {
   164  				req.URL.RawQuery = query.Encode()
   165  			} else {
   166  				req.URL.RawQuery = req.URL.RawQuery + "&" + query.Encode()
   167  			}
   168  		case "cookie":
   169  			for _, v := range dataList {
   170  				req.AddCookie(&http.Cookie{
   171  					Name:  name,
   172  					Value: string(v),
   173  				})
   174  			}
   175  		case "path":
   176  			paramKey := ":" + name
   177  			if !strings.Contains(req.URL.Path, paramKey) {
   178  				err = fmt.Errorf("uri %s need path parameter %s, but uri has no %s", uri, name, paramKey)
   179  				return
   180  			}
   181  			value := string(bytes.Join(dataList, []byte(",")))
   182  			req.URL.Path = strings.Replace(req.URL.Path, paramKey, value, -1)
   183  		case "header":
   184  			value := string(bytes.Join(dataList, []byte(",")))
   185  			req.Header.Add(name, value)
   186  		}
   187  	}
   188  
   189  	if m.FormData.Len() > 0 {
   190  		if multipartWriter != nil {
   191  			req.Header.Add(httpx.HeaderContentType, multipartWriter.FormDataContentType())
   192  		} else {
   193  			req.Header.Add(httpx.HeaderContentType, httpx.MIME_POST_URLENCODED+"; param=value")
   194  		}
   195  	}
   196  
   197  	return
   198  }
   199  
   200  func appendFile(multipartWriter *multipart.Writer, field string, fileHeader *multipart.FileHeader, content []byte) (err error) {
   201  	if fileHeader == nil {
   202  		return
   203  	}
   204  	filePart, errForCreate := multipartWriter.CreateFormFile(field, fileHeader.Filename)
   205  	if errForCreate != nil {
   206  		err = errForCreate
   207  		return
   208  	}
   209  
   210  	if len(content) == 0 {
   211  		if file, errForOpen := fileHeader.Open(); errForOpen != nil {
   212  			err = errForOpen
   213  			return
   214  		} else if _, errForCopy := io.Copy(filePart, file); errForCopy != nil {
   215  			err = errForCopy
   216  			return
   217  		}
   218  		return nil
   219  	}
   220  
   221  	if _, errForWrite := filePart.Write(content); errForWrite != nil {
   222  		err = errForWrite
   223  		return
   224  	}
   225  	return nil
   226  }
   227  
   228  func NewFileHeader(fieldName string, filename string, content []byte) (fileHeader *multipart.FileHeader, err error) {
   229  	buffer := &bytes.Buffer{}
   230  	multipartWriter := multipart.NewWriter(buffer)
   231  	appendFile(multipartWriter, fieldName, &multipart.FileHeader{
   232  		Filename: filename,
   233  	}, content)
   234  	multipartWriter.Close()
   235  	reader := multipart.NewReader(buffer, multipartWriter.Boundary())
   236  	form, errForReader := reader.ReadForm(int64(buffer.Len()))
   237  	if errForReader != nil {
   238  		err = errForReader
   239  		return
   240  	}
   241  	fileHeader = form.File[fieldName][0]
   242  	return
   243  }
   244  
   245  type IParameterValuesGetter interface {
   246  	Initial() error
   247  	Param(name string) string
   248  	Query(name string) []string
   249  	Header(name string) []string
   250  	Cookie(name string) []string
   251  	FormValue(name string) []string
   252  	FormFile(name string) ([]*multipart.FileHeader, error)
   253  	Body() io.Reader
   254  }
   255  
   256  func NewParameterValuesGetter(r *http.Request) *ParameterValuesGetter {
   257  	return &ParameterValuesGetter{Request: r}
   258  }
   259  
   260  type ParameterValuesGetter struct {
   261  	Request *http.Request
   262  	query   url.Values
   263  	now     time.Time
   264  	cookies []*http.Cookie
   265  }
   266  
   267  func (getter *ParameterValuesGetter) Initial() error {
   268  	getter.now = time.Now()
   269  	getter.query = getter.Request.URL.Query()
   270  	return nil
   271  }
   272  
   273  func (getter *ParameterValuesGetter) Param(name string) string {
   274  	return ""
   275  }
   276  
   277  func (getter *ParameterValuesGetter) Query(name string) []string {
   278  	return getter.query[name]
   279  }
   280  
   281  func (getter *ParameterValuesGetter) Header(name string) []string {
   282  	return getter.Request.Header[textproto.CanonicalMIMEHeaderKey(name)]
   283  }
   284  
   285  func (getter *ParameterValuesGetter) Cookie(name string) []string {
   286  	values := make([]string, 0)
   287  	if len(getter.cookies) == 0 {
   288  		getter.cookies = getter.Request.Cookies()
   289  	}
   290  	for _, c := range getter.cookies {
   291  		if c.Name == name {
   292  			if c.Expires.IsZero() {
   293  				values = append(values, c.Value)
   294  			} else if c.Expires.After(getter.now) {
   295  				values = append(values, c.Value)
   296  			}
   297  		}
   298  	}
   299  	return values
   300  }
   301  
   302  func (getter *ParameterValuesGetter) FormValue(name string) []string {
   303  	if getter.Request.Form == nil {
   304  		// just parse form
   305  		getter.Request.FormValue("")
   306  	}
   307  	return getter.Request.Form[name]
   308  }
   309  
   310  func (getter *ParameterValuesGetter) FormFile(name string) ([]*multipart.FileHeader, error) {
   311  	if getter.Request.MultipartForm == nil {
   312  		// just parse form
   313  		_, fileHeader, err := getter.Request.FormFile(name)
   314  		if err != nil || fileHeader == nil {
   315  			return nil, err
   316  		}
   317  	}
   318  	return getter.Request.MultipartForm.File[name], nil
   319  }
   320  
   321  func (getter *ParameterValuesGetter) Body() io.Reader {
   322  	return getter.Request.Body
   323  }
   324  
   325  func MarshalParameters(group *ParameterGroup, getter IParameterValuesGetter) error {
   326  	parameterErrors := ParameterErrors{}
   327  
   328  	if err := getter.Initial(); err != nil {
   329  		parameterErrors.Merge(err)
   330  		return parameterErrors.Err()
   331  	}
   332  
   333  	for _, parameterMeta := range group.Parameters.List() {
   334  		switch parameterMeta.In {
   335  		case "path":
   336  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(strings.Split(getter.Param(parameterMeta.Name), ",")...))
   337  		case "header":
   338  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Header(parameterMeta.Name)...))
   339  		case "query":
   340  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Query(parameterMeta.Name)...))
   341  		case "cookie":
   342  			parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.Cookie(parameterMeta.Name)...))
   343  		}
   344  	}
   345  
   346  	for _, parameterMeta := range group.FormData {
   347  		if _, ok := parameterMeta.Value.Interface().([]*multipart.FileHeader); ok {
   348  			fileHeaders, err := getter.FormFile(parameterMeta.Name)
   349  			if err != nil || len(fileHeaders) == 0 {
   350  				return status_error.ReadFormFileFailed
   351  			}
   352  			parameterMeta.Value.Set(reflect.ValueOf(fileHeaders))
   353  			continue
   354  		}
   355  		if _, ok := parameterMeta.Value.Interface().(*multipart.FileHeader); ok {
   356  			fileHeaders, err := getter.FormFile(parameterMeta.Name)
   357  			if err != nil || len(fileHeaders) == 0 {
   358  				return status_error.ReadFormFileFailed
   359  			}
   360  			if parameterMeta.Value.CanSet() {
   361  				parameterMeta.Value.Set(reflect.ValueOf(fileHeaders[0]))
   362  			}
   363  			continue
   364  		}
   365  		parameterErrors.Merge(parameterMeta.UnmarshalStringAndValidate(getter.FormValue(parameterMeta.Name)...))
   366  	}
   367  
   368  	if parameterErrors.StatusError == nil {
   369  		isValid, errFields := group.ValidateNoBodyByHook()
   370  		if !isValid {
   371  			parameterErrors.Merge(status_error.InvalidField.StatusError().WithErrorFields(errFields...))
   372  		}
   373  	}
   374  
   375  	if group.Body != nil {
   376  		parameterErrors.Merge(group.Body.UnmarshalFromReader(getter.Body()))
   377  	}
   378  
   379  	return parameterErrors.Err()
   380  }