podman

Форк
0
481 строка · 12.9 Кб
1
// Copyright 2015 go-swagger maintainers
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//    http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package client
16

17
import (
18
	"bytes"
19
	"fmt"
20
	"io"
21
	"log"
22
	"mime/multipart"
23
	"net/http"
24
	"net/textproto"
25
	"net/url"
26
	"os"
27
	"path"
28
	"path/filepath"
29
	"strings"
30
	"time"
31

32
	"github.com/go-openapi/strfmt"
33

34
	"github.com/go-openapi/runtime"
35
)
36

37
// NewRequest creates a new swagger http client request
38
func newRequest(method, pathPattern string, writer runtime.ClientRequestWriter) (*request, error) {
39
	return &request{
40
		pathPattern: pathPattern,
41
		method:      method,
42
		writer:      writer,
43
		header:      make(http.Header),
44
		query:       make(url.Values),
45
		timeout:     DefaultTimeout,
46
		getBody:     getRequestBuffer,
47
	}, nil
48
}
49

50
// Request represents a swagger client request.
51
//
52
// This Request struct converts to a HTTP request.
53
// There might be others that convert to other transports.
54
// There is no error checking here, it is assumed to be used after a spec has been validated.
55
// so impossible combinations should not arise (hopefully).
56
//
57
// The main purpose of this struct is to hide the machinery of adding params to a transport request.
58
// The generated code only implements what is necessary to turn a param into a valid value for these methods.
59
type request struct {
60
	pathPattern string
61
	method      string
62
	writer      runtime.ClientRequestWriter
63

64
	pathParams map[string]string
65
	header     http.Header
66
	query      url.Values
67
	formFields url.Values
68
	fileFields map[string][]runtime.NamedReadCloser
69
	payload    interface{}
70
	timeout    time.Duration
71
	buf        *bytes.Buffer
72

73
	getBody func(r *request) []byte
74
}
75

76
var (
77
	// ensure interface compliance
78
	_ runtime.ClientRequest = new(request)
79
)
80

81
func (r *request) isMultipart(mediaType string) bool {
82
	if len(r.fileFields) > 0 {
83
		return true
84
	}
85

86
	return runtime.MultipartFormMime == mediaType
87
}
88

89
// BuildHTTP creates a new http request based on the data from the params
90
func (r *request) BuildHTTP(mediaType, basePath string, producers map[string]runtime.Producer, registry strfmt.Registry) (*http.Request, error) {
91
	return r.buildHTTP(mediaType, basePath, producers, registry, nil)
92
}
93
func escapeQuotes(s string) string {
94
	return strings.NewReplacer("\\", "\\\\", `"`, "\\\"").Replace(s)
95
}
96

97
func logClose(err error, pw *io.PipeWriter) {
98
	log.Println(err)
99
	closeErr := pw.CloseWithError(err)
100
	if closeErr != nil {
101
		log.Println(closeErr)
102
	}
103
}
104

105
func (r *request) buildHTTP(mediaType, basePath string, producers map[string]runtime.Producer, registry strfmt.Registry, auth runtime.ClientAuthInfoWriter) (*http.Request, error) {
106
	// build the data
107
	if err := r.writer.WriteToRequest(r, registry); err != nil {
108
		return nil, err
109
	}
110

111
	// Our body must be an io.Reader.
112
	// When we create the http.Request, if we pass it a
113
	// bytes.Buffer then it will wrap it in an io.ReadCloser
114
	// and set the content length automatically.
115
	var body io.Reader
116
	var pr *io.PipeReader
117
	var pw *io.PipeWriter
118

119
	r.buf = bytes.NewBuffer(nil)
120
	if r.payload != nil || len(r.formFields) > 0 || len(r.fileFields) > 0 {
121
		body = r.buf
122
		if r.isMultipart(mediaType) {
123
			pr, pw = io.Pipe()
124
			body = pr
125
		}
126
	}
127

128
	// check if this is a form type request
129
	if len(r.formFields) > 0 || len(r.fileFields) > 0 {
130
		if !r.isMultipart(mediaType) {
131
			r.header.Set(runtime.HeaderContentType, mediaType)
132
			formString := r.formFields.Encode()
133
			r.buf.WriteString(formString)
134
			goto DoneChoosingBodySource
135
		}
136

137
		mp := multipart.NewWriter(pw)
138
		r.header.Set(runtime.HeaderContentType, mangleContentType(mediaType, mp.Boundary()))
139

140
		go func() {
141
			defer func() {
142
				mp.Close()
143
				pw.Close()
144
			}()
145

146
			for fn, v := range r.formFields {
147
				for _, vi := range v {
148
					if err := mp.WriteField(fn, vi); err != nil {
149
						logClose(err, pw)
150
						return
151
					}
152
				}
153
			}
154

155
			defer func() {
156
				for _, ff := range r.fileFields {
157
					for _, ffi := range ff {
158
						ffi.Close()
159
					}
160
				}
161
			}()
162
			for fn, f := range r.fileFields {
163
				for _, fi := range f {
164
					var fileContentType string
165
					if p, ok := fi.(interface {
166
						ContentType() string
167
					}); ok {
168
						fileContentType = p.ContentType()
169
					} else {
170
						// Need to read the data so that we can detect the content type
171
						buf := make([]byte, 512)
172
						size, err := fi.Read(buf)
173
						if err != nil {
174
							logClose(err, pw)
175
							return
176
						}
177
						fileContentType = http.DetectContentType(buf)
178
						fi = runtime.NamedReader(fi.Name(), io.MultiReader(bytes.NewReader(buf[:size]), fi))
179
					}
180

181
					// Create the MIME headers for the new part
182
					h := make(textproto.MIMEHeader)
183
					h.Set("Content-Disposition",
184
						fmt.Sprintf(`form-data; name="%s"; filename="%s"`,
185
							escapeQuotes(fn), escapeQuotes(filepath.Base(fi.Name()))))
186
					h.Set("Content-Type", fileContentType)
187

188
					wrtr, err := mp.CreatePart(h)
189
					if err != nil {
190
						logClose(err, pw)
191
						return
192
					}
193
					if _, err := io.Copy(wrtr, fi); err != nil {
194
						logClose(err, pw)
195
					}
196
				}
197
			}
198
		}()
199

200
		goto DoneChoosingBodySource
201
	}
202

203
	// if there is payload, use the producer to write the payload, and then
204
	// set the header to the content-type appropriate for the payload produced
205
	if r.payload != nil {
206
		// TODO: infer most appropriate content type based on the producer used,
207
		// and the `consumers` section of the spec/operation
208
		r.header.Set(runtime.HeaderContentType, mediaType)
209
		if rdr, ok := r.payload.(io.ReadCloser); ok {
210
			body = rdr
211
			goto DoneChoosingBodySource
212
		}
213

214
		if rdr, ok := r.payload.(io.Reader); ok {
215
			body = rdr
216
			goto DoneChoosingBodySource
217
		}
218

219
		producer := producers[mediaType]
220
		if err := producer.Produce(r.buf, r.payload); err != nil {
221
			return nil, err
222
		}
223
	}
224

225
DoneChoosingBodySource:
226

227
	if runtime.CanHaveBody(r.method) && body != nil && r.header.Get(runtime.HeaderContentType) == "" {
228
		r.header.Set(runtime.HeaderContentType, mediaType)
229
	}
230

231
	if auth != nil {
232
		// If we're not using r.buf as our http.Request's body,
233
		// either the payload is an io.Reader or io.ReadCloser,
234
		// or we're doing a multipart form/file.
235
		//
236
		// In those cases, if the AuthenticateRequest call asks for the body,
237
		// we must read it into a buffer and provide that, then use that buffer
238
		// as the body of our http.Request.
239
		//
240
		// This is done in-line with the GetBody() request rather than ahead
241
		// of time, because there's no way to know if the AuthenticateRequest
242
		// will even ask for the body of the request.
243
		//
244
		// If for some reason the copy fails, there's no way to return that
245
		// error to the GetBody() call, so return it afterwards.
246
		//
247
		// An error from the copy action is prioritized over any error
248
		// from the AuthenticateRequest call, because the mis-read
249
		// body may have interfered with the auth.
250
		//
251
		var copyErr error
252
		if buf, ok := body.(*bytes.Buffer); body != nil && (!ok || buf != r.buf) {
253
			var copied bool
254
			r.getBody = func(r *request) []byte {
255
				if copied {
256
					return getRequestBuffer(r)
257
				}
258

259
				defer func() {
260
					copied = true
261
				}()
262

263
				if _, copyErr = io.Copy(r.buf, body); copyErr != nil {
264
					return nil
265
				}
266

267
				if closer, ok := body.(io.ReadCloser); ok {
268
					if copyErr = closer.Close(); copyErr != nil {
269
						return nil
270
					}
271
				}
272

273
				body = r.buf
274
				return getRequestBuffer(r)
275
			}
276
		}
277

278
		authErr := auth.AuthenticateRequest(r, registry)
279

280
		if copyErr != nil {
281
			return nil, fmt.Errorf("error retrieving the response body: %v", copyErr)
282
		}
283

284
		if authErr != nil {
285
			return nil, authErr
286
		}
287
	}
288

289
	// In case the basePath or the request pathPattern include static query parameters,
290
	// parse those out before constructing the final path. The parameters themselves
291
	// will be merged with the ones set by the client, with the priority given first to
292
	// the ones set by the client, then the path pattern, and lastly the base path.
293
	basePathURL, err := url.Parse(basePath)
294
	if err != nil {
295
		return nil, err
296
	}
297
	staticQueryParams := basePathURL.Query()
298

299
	pathPatternURL, err := url.Parse(r.pathPattern)
300
	if err != nil {
301
		return nil, err
302
	}
303
	for name, values := range pathPatternURL.Query() {
304
		if _, present := staticQueryParams[name]; present {
305
			staticQueryParams.Del(name)
306
		}
307
		for _, value := range values {
308
			staticQueryParams.Add(name, value)
309
		}
310
	}
311

312
	// create http request
313
	var reinstateSlash bool
314
	if pathPatternURL.Path != "" && pathPatternURL.Path != "/" && pathPatternURL.Path[len(pathPatternURL.Path)-1] == '/' {
315
		reinstateSlash = true
316
	}
317

318
	urlPath := path.Join(basePathURL.Path, pathPatternURL.Path)
319
	for k, v := range r.pathParams {
320
		urlPath = strings.Replace(urlPath, "{"+k+"}", url.PathEscape(v), -1)
321
	}
322
	if reinstateSlash {
323
		urlPath = urlPath + "/"
324
	}
325

326
	req, err := http.NewRequest(r.method, urlPath, body)
327
	if err != nil {
328
		return nil, err
329
	}
330

331
	originalParams := r.GetQueryParams()
332

333
	// Merge the query parameters extracted from the basePath with the ones set by
334
	// the client in this struct. In case of conflict, the client wins.
335
	for k, v := range staticQueryParams {
336
		_, present := originalParams[k]
337
		if !present {
338
			if err = r.SetQueryParam(k, v...); err != nil {
339
				return nil, err
340
			}
341
		}
342
	}
343

344
	req.URL.RawQuery = r.query.Encode()
345
	req.Header = r.header
346

347
	return req, nil
348
}
349

350
func mangleContentType(mediaType, boundary string) string {
351
	if strings.ToLower(mediaType) == runtime.URLencodedFormMime {
352
		return fmt.Sprintf("%s; boundary=%s", mediaType, boundary)
353
	}
354
	return "multipart/form-data; boundary=" + boundary
355
}
356

357
func (r *request) GetMethod() string {
358
	return r.method
359
}
360

361
func (r *request) GetPath() string {
362
	path := r.pathPattern
363
	for k, v := range r.pathParams {
364
		path = strings.Replace(path, "{"+k+"}", v, -1)
365
	}
366
	return path
367
}
368

369
func (r *request) GetBody() []byte {
370
	return r.getBody(r)
371
}
372

373
func getRequestBuffer(r *request) []byte {
374
	if r.buf == nil {
375
		return nil
376
	}
377
	return r.buf.Bytes()
378
}
379

380
// SetHeaderParam adds a header param to the request
381
// when there is only 1 value provided for the varargs, it will set it.
382
// when there are several values provided for the varargs it will add it (no overriding)
383
func (r *request) SetHeaderParam(name string, values ...string) error {
384
	if r.header == nil {
385
		r.header = make(http.Header)
386
	}
387
	r.header[http.CanonicalHeaderKey(name)] = values
388
	return nil
389
}
390

391
// GetHeaderParams returns the all headers currently set for the request
392
func (r *request) GetHeaderParams() http.Header {
393
	return r.header
394
}
395

396
// SetQueryParam adds a query param to the request
397
// when there is only 1 value provided for the varargs, it will set it.
398
// when there are several values provided for the varargs it will add it (no overriding)
399
func (r *request) SetQueryParam(name string, values ...string) error {
400
	if r.query == nil {
401
		r.query = make(url.Values)
402
	}
403
	r.query[name] = values
404
	return nil
405
}
406

407
// GetQueryParams returns a copy of all query params currently set for the request
408
func (r *request) GetQueryParams() url.Values {
409
	var result = make(url.Values)
410
	for key, value := range r.query {
411
		result[key] = append([]string{}, value...)
412
	}
413
	return result
414
}
415

416
// SetFormParam adds a forn param to the request
417
// when there is only 1 value provided for the varargs, it will set it.
418
// when there are several values provided for the varargs it will add it (no overriding)
419
func (r *request) SetFormParam(name string, values ...string) error {
420
	if r.formFields == nil {
421
		r.formFields = make(url.Values)
422
	}
423
	r.formFields[name] = values
424
	return nil
425
}
426

427
// SetPathParam adds a path param to the request
428
func (r *request) SetPathParam(name string, value string) error {
429
	if r.pathParams == nil {
430
		r.pathParams = make(map[string]string)
431
	}
432

433
	r.pathParams[name] = value
434
	return nil
435
}
436

437
// SetFileParam adds a file param to the request
438
func (r *request) SetFileParam(name string, files ...runtime.NamedReadCloser) error {
439
	for _, file := range files {
440
		if actualFile, ok := file.(*os.File); ok {
441
			fi, err := os.Stat(actualFile.Name())
442
			if err != nil {
443
				return err
444
			}
445
			if fi.IsDir() {
446
				return fmt.Errorf("%q is a directory, only files are supported", file.Name())
447
			}
448
		}
449
	}
450

451
	if r.fileFields == nil {
452
		r.fileFields = make(map[string][]runtime.NamedReadCloser)
453
	}
454
	if r.formFields == nil {
455
		r.formFields = make(url.Values)
456
	}
457

458
	r.fileFields[name] = files
459
	return nil
460
}
461

462
func (r *request) GetFileParam() map[string][]runtime.NamedReadCloser {
463
	return r.fileFields
464
}
465

466
// SetBodyParam sets a body parameter on the request.
467
// This does not yet serialze the object, this happens as late as possible.
468
func (r *request) SetBodyParam(payload interface{}) error {
469
	r.payload = payload
470
	return nil
471
}
472

473
func (r *request) GetBodyParam() interface{} {
474
	return r.payload
475
}
476

477
// SetTimeout sets the timeout for a request
478
func (r *request) SetTimeout(timeout time.Duration) error {
479
	r.timeout = timeout
480
	return nil
481
}
482

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.