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 }