appier 1.34.6__py2.py3-none-any.whl → 1.34.8__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- appier/__init__.py +1 -1
- appier/api.py +10 -0
- appier/asgi.py +10 -0
- appier/async_neo.py +2 -2
- appier/async_old.py +1 -1
- appier/base.py +15 -2
- appier/bus.py +10 -0
- appier/config.py +409 -401
- appier/data.py +2 -0
- appier/exceptions.py +450 -442
- appier/http.py +1292 -1283
- appier/model.py +7 -2
- appier/mongo.py +24 -0
- appier/scheduler.py +342 -334
- appier/test/data.py +10 -0
- appier/test/error_handler.py +142 -0
- appier/test/exception_handler.py +146 -0
- appier/test/http.py +24 -0
- appier/test/tags.py +109 -0
- appier/util.py +2517 -2503
- {appier-1.34.6.dist-info → appier-1.34.8.dist-info}/METADATA +1 -1
- {appier-1.34.6.dist-info → appier-1.34.8.dist-info}/RECORD +25 -22
- {appier-1.34.6.dist-info → appier-1.34.8.dist-info}/LICENSE +0 -0
- {appier-1.34.6.dist-info → appier-1.34.8.dist-info}/WHEEL +0 -0
- {appier-1.34.6.dist-info → appier-1.34.8.dist-info}/top_level.txt +0 -0
appier/http.py
CHANGED
|
@@ -1,1283 +1,1292 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
# Hive Appier Framework
|
|
5
|
-
# Copyright (c) 2008-2024 Hive Solutions Lda.
|
|
6
|
-
#
|
|
7
|
-
# This file is part of Hive Appier Framework.
|
|
8
|
-
#
|
|
9
|
-
# Hive Appier Framework is free software: you can redistribute it and/or modify
|
|
10
|
-
# it under the terms of the Apache License as published by the Apache
|
|
11
|
-
# Foundation, either version 2.0 of the License, or (at your option) any
|
|
12
|
-
# later version.
|
|
13
|
-
#
|
|
14
|
-
# Hive Appier Framework is distributed in the hope that it will be useful,
|
|
15
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
-
# Apache License for more details.
|
|
18
|
-
#
|
|
19
|
-
# You should have received a copy of the Apache License along with
|
|
20
|
-
# Hive Appier Framework. If not, see <http://www.apache.org/licenses/>.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
""" The
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"""
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
if
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
elif
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
#
|
|
666
|
-
#
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
#
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
# the
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
# verifies if the
|
|
722
|
-
#
|
|
723
|
-
#
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
#
|
|
733
|
-
|
|
734
|
-
if reuse:
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
#
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
#
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
#
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
#
|
|
770
|
-
#
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
#
|
|
780
|
-
#
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
#
|
|
785
|
-
#
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
# the
|
|
852
|
-
|
|
853
|
-
if
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
#
|
|
858
|
-
#
|
|
859
|
-
#
|
|
860
|
-
#
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
#
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
#
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
#
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
#
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
def
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
def
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
#
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
#
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
#
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
#
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
#
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
#
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
#
|
|
1087
|
-
#
|
|
1088
|
-
#
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
return
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
def
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
def
|
|
1280
|
-
return self.
|
|
1281
|
-
|
|
1282
|
-
def
|
|
1283
|
-
return self.
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# Hive Appier Framework
|
|
5
|
+
# Copyright (c) 2008-2024 Hive Solutions Lda.
|
|
6
|
+
#
|
|
7
|
+
# This file is part of Hive Appier Framework.
|
|
8
|
+
#
|
|
9
|
+
# Hive Appier Framework is free software: you can redistribute it and/or modify
|
|
10
|
+
# it under the terms of the Apache License as published by the Apache
|
|
11
|
+
# Foundation, either version 2.0 of the License, or (at your option) any
|
|
12
|
+
# later version.
|
|
13
|
+
#
|
|
14
|
+
# Hive Appier Framework is distributed in the hope that it will be useful,
|
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
# Apache License for more details.
|
|
18
|
+
#
|
|
19
|
+
# You should have received a copy of the Apache License along with
|
|
20
|
+
# Hive Appier Framework. If not, see <http://www.apache.org/licenses/>.
|
|
21
|
+
|
|
22
|
+
"""appier.http
|
|
23
|
+
|
|
24
|
+
Unified HTTP helper wrapping requests for synchronous calls.
|
|
25
|
+
Provides thin convenience functions (`get`, `post`, etc.) that
|
|
26
|
+
automatically propagate headers, query strings and timeouts as
|
|
27
|
+
expected by Appier internals. Used by API clients and tests to
|
|
28
|
+
execute outbound requests in a consistent way.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
__author__ = "João Magalhães <joamag@hive.pt>"
|
|
32
|
+
""" The author(s) of the module """
|
|
33
|
+
|
|
34
|
+
__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
|
|
35
|
+
""" The copyright for the module """
|
|
36
|
+
|
|
37
|
+
__license__ = "Apache License, Version 2.0"
|
|
38
|
+
""" The license for the module """
|
|
39
|
+
|
|
40
|
+
import os
|
|
41
|
+
import json
|
|
42
|
+
import base64
|
|
43
|
+
import string
|
|
44
|
+
import random
|
|
45
|
+
import logging
|
|
46
|
+
import threading
|
|
47
|
+
|
|
48
|
+
from . import util
|
|
49
|
+
from . import common
|
|
50
|
+
from . import legacy
|
|
51
|
+
from . import typesf
|
|
52
|
+
from . import config
|
|
53
|
+
from . import exceptions
|
|
54
|
+
from . import structures
|
|
55
|
+
|
|
56
|
+
TIMEOUT = 60
|
|
57
|
+
""" The timeout in seconds to be used for the blocking
|
|
58
|
+
operations in the HTTP connection, this value avoid unwanted
|
|
59
|
+
blocking operations to remain open for an infinite time """
|
|
60
|
+
|
|
61
|
+
RANGE = string.ascii_letters + string.digits
|
|
62
|
+
""" The range of characters that are going to be used in
|
|
63
|
+
the generation of the boundary value for the mime """
|
|
64
|
+
|
|
65
|
+
SEQUENCE_TYPES = (list, tuple)
|
|
66
|
+
""" The sequence defining the various types that are
|
|
67
|
+
considered to be sequence based for python """
|
|
68
|
+
|
|
69
|
+
AUTH_ERRORS = (401, 403, 440, 499)
|
|
70
|
+
""" The sequence that defines the various HTTP errors
|
|
71
|
+
considered to be authentication related and for which a
|
|
72
|
+
new authentication try will be performed """
|
|
73
|
+
|
|
74
|
+
ACCESS_LOCK = threading.RLock()
|
|
75
|
+
""" Global access lock used for locking global operations
|
|
76
|
+
that require thread safety under the HTTP infra-structure """
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def file_g(path, chunk=40960):
|
|
80
|
+
yield os.path.getsize(path)
|
|
81
|
+
file = open(path, "rb")
|
|
82
|
+
try:
|
|
83
|
+
while True:
|
|
84
|
+
data = file.read(chunk)
|
|
85
|
+
if not data:
|
|
86
|
+
break
|
|
87
|
+
yield data
|
|
88
|
+
finally:
|
|
89
|
+
file.close()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def get_f(*args, **kwargs):
|
|
93
|
+
name = kwargs.pop("name", "default")
|
|
94
|
+
kwargs["handle"] = kwargs.get("handle", True)
|
|
95
|
+
kwargs["redirect"] = kwargs.get("redirect", True)
|
|
96
|
+
data, response = get(*args, **kwargs)
|
|
97
|
+
info = response.info()
|
|
98
|
+
mime = info.get("Content-Type", None)
|
|
99
|
+
file_tuple = util.FileTuple((name, mime, data))
|
|
100
|
+
return typesf.File(file_tuple)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get(
|
|
104
|
+
url,
|
|
105
|
+
params=None,
|
|
106
|
+
headers=None,
|
|
107
|
+
handle=None,
|
|
108
|
+
silent=None,
|
|
109
|
+
redirect=None,
|
|
110
|
+
timeout=None,
|
|
111
|
+
auth_callback=None,
|
|
112
|
+
**kwargs
|
|
113
|
+
):
|
|
114
|
+
return _method(
|
|
115
|
+
_get,
|
|
116
|
+
url,
|
|
117
|
+
params=params,
|
|
118
|
+
headers=headers,
|
|
119
|
+
handle=handle,
|
|
120
|
+
silent=silent,
|
|
121
|
+
redirect=redirect,
|
|
122
|
+
timeout=timeout,
|
|
123
|
+
auth_callback=auth_callback,
|
|
124
|
+
**kwargs
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def post(
|
|
129
|
+
url,
|
|
130
|
+
params=None,
|
|
131
|
+
data=None,
|
|
132
|
+
data_j=None,
|
|
133
|
+
data_m=None,
|
|
134
|
+
headers=None,
|
|
135
|
+
mime=None,
|
|
136
|
+
handle=None,
|
|
137
|
+
silent=None,
|
|
138
|
+
redirect=None,
|
|
139
|
+
timeout=None,
|
|
140
|
+
auth_callback=None,
|
|
141
|
+
**kwargs
|
|
142
|
+
):
|
|
143
|
+
return _method(
|
|
144
|
+
_post,
|
|
145
|
+
url,
|
|
146
|
+
params=params,
|
|
147
|
+
data=data,
|
|
148
|
+
data_j=data_j,
|
|
149
|
+
data_m=data_m,
|
|
150
|
+
headers=headers,
|
|
151
|
+
mime=mime,
|
|
152
|
+
handle=handle,
|
|
153
|
+
silent=silent,
|
|
154
|
+
redirect=redirect,
|
|
155
|
+
timeout=timeout,
|
|
156
|
+
auth_callback=auth_callback,
|
|
157
|
+
**kwargs
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def put(
|
|
162
|
+
url,
|
|
163
|
+
params=None,
|
|
164
|
+
data=None,
|
|
165
|
+
data_j=None,
|
|
166
|
+
data_m=None,
|
|
167
|
+
headers=None,
|
|
168
|
+
mime=None,
|
|
169
|
+
handle=None,
|
|
170
|
+
silent=None,
|
|
171
|
+
redirect=None,
|
|
172
|
+
timeout=None,
|
|
173
|
+
auth_callback=None,
|
|
174
|
+
**kwargs
|
|
175
|
+
):
|
|
176
|
+
return _method(
|
|
177
|
+
_put,
|
|
178
|
+
url,
|
|
179
|
+
params=params,
|
|
180
|
+
data=data,
|
|
181
|
+
data_j=data_j,
|
|
182
|
+
data_m=data_m,
|
|
183
|
+
headers=headers,
|
|
184
|
+
mime=mime,
|
|
185
|
+
handle=handle,
|
|
186
|
+
silent=silent,
|
|
187
|
+
redirect=redirect,
|
|
188
|
+
timeout=timeout,
|
|
189
|
+
auth_callback=auth_callback,
|
|
190
|
+
**kwargs
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def delete(
|
|
195
|
+
url,
|
|
196
|
+
params=None,
|
|
197
|
+
headers=None,
|
|
198
|
+
handle=None,
|
|
199
|
+
silent=None,
|
|
200
|
+
redirect=None,
|
|
201
|
+
timeout=None,
|
|
202
|
+
auth_callback=None,
|
|
203
|
+
**kwargs
|
|
204
|
+
):
|
|
205
|
+
return _method(
|
|
206
|
+
_delete,
|
|
207
|
+
url,
|
|
208
|
+
params=params,
|
|
209
|
+
headers=headers,
|
|
210
|
+
handle=handle,
|
|
211
|
+
silent=silent,
|
|
212
|
+
redirect=redirect,
|
|
213
|
+
timeout=timeout,
|
|
214
|
+
auth_callback=auth_callback,
|
|
215
|
+
**kwargs
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def patch(
|
|
220
|
+
url,
|
|
221
|
+
params=None,
|
|
222
|
+
data=None,
|
|
223
|
+
data_j=None,
|
|
224
|
+
data_m=None,
|
|
225
|
+
headers=None,
|
|
226
|
+
mime=None,
|
|
227
|
+
handle=None,
|
|
228
|
+
silent=None,
|
|
229
|
+
redirect=None,
|
|
230
|
+
timeout=None,
|
|
231
|
+
auth_callback=None,
|
|
232
|
+
**kwargs
|
|
233
|
+
):
|
|
234
|
+
return _method(
|
|
235
|
+
_patch,
|
|
236
|
+
url,
|
|
237
|
+
params=params,
|
|
238
|
+
data=data,
|
|
239
|
+
data_j=data_j,
|
|
240
|
+
data_m=data_m,
|
|
241
|
+
headers=headers,
|
|
242
|
+
mime=mime,
|
|
243
|
+
handle=handle,
|
|
244
|
+
silent=silent,
|
|
245
|
+
redirect=redirect,
|
|
246
|
+
timeout=timeout,
|
|
247
|
+
auth_callback=auth_callback,
|
|
248
|
+
**kwargs
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def basic_auth(username, password=None):
|
|
253
|
+
if not password:
|
|
254
|
+
password = username
|
|
255
|
+
authorization = _authorization(username, password)
|
|
256
|
+
return "Basic %s" % authorization
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _try_auth(auth_callback, params, headers=None):
|
|
260
|
+
if not auth_callback:
|
|
261
|
+
raise
|
|
262
|
+
if headers == None:
|
|
263
|
+
headers = dict()
|
|
264
|
+
auth_callback(params, headers)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _method(method, *args, **kwargs):
|
|
268
|
+
try:
|
|
269
|
+
auth_callback = kwargs.pop("auth_callback", None)
|
|
270
|
+
result = method(*args, **kwargs)
|
|
271
|
+
except legacy.HTTPError as error:
|
|
272
|
+
try:
|
|
273
|
+
params = kwargs.get("params", None)
|
|
274
|
+
headers = kwargs.get("headers", None)
|
|
275
|
+
if not error.code in AUTH_ERRORS:
|
|
276
|
+
raise
|
|
277
|
+
_try_auth(auth_callback, params, headers)
|
|
278
|
+
result = method(*args, **kwargs)
|
|
279
|
+
except legacy.HTTPError as error:
|
|
280
|
+
code = error.getcode()
|
|
281
|
+
raise exceptions.HTTPError(error, code)
|
|
282
|
+
|
|
283
|
+
return result
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _get(
|
|
287
|
+
url,
|
|
288
|
+
params=None,
|
|
289
|
+
headers=None,
|
|
290
|
+
handle=None,
|
|
291
|
+
silent=None,
|
|
292
|
+
redirect=None,
|
|
293
|
+
timeout=None,
|
|
294
|
+
**kwargs
|
|
295
|
+
):
|
|
296
|
+
return _method_empty(
|
|
297
|
+
"GET",
|
|
298
|
+
url,
|
|
299
|
+
params=params,
|
|
300
|
+
headers=headers,
|
|
301
|
+
handle=handle,
|
|
302
|
+
silent=silent,
|
|
303
|
+
redirect=redirect,
|
|
304
|
+
timeout=timeout,
|
|
305
|
+
**kwargs
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _post(
|
|
310
|
+
url,
|
|
311
|
+
params=None,
|
|
312
|
+
data=None,
|
|
313
|
+
data_j=None,
|
|
314
|
+
data_m=None,
|
|
315
|
+
headers=None,
|
|
316
|
+
mime=None,
|
|
317
|
+
handle=None,
|
|
318
|
+
silent=None,
|
|
319
|
+
redirect=None,
|
|
320
|
+
timeout=None,
|
|
321
|
+
**kwargs
|
|
322
|
+
):
|
|
323
|
+
return _method_payload(
|
|
324
|
+
"POST",
|
|
325
|
+
url,
|
|
326
|
+
params=params,
|
|
327
|
+
data=data,
|
|
328
|
+
data_j=data_j,
|
|
329
|
+
data_m=data_m,
|
|
330
|
+
headers=headers,
|
|
331
|
+
mime=mime,
|
|
332
|
+
handle=handle,
|
|
333
|
+
silent=silent,
|
|
334
|
+
redirect=redirect,
|
|
335
|
+
timeout=timeout,
|
|
336
|
+
**kwargs
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _put(
|
|
341
|
+
url,
|
|
342
|
+
params=None,
|
|
343
|
+
data=None,
|
|
344
|
+
data_j=None,
|
|
345
|
+
data_m=None,
|
|
346
|
+
headers=None,
|
|
347
|
+
mime=None,
|
|
348
|
+
handle=None,
|
|
349
|
+
silent=None,
|
|
350
|
+
redirect=None,
|
|
351
|
+
timeout=None,
|
|
352
|
+
**kwargs
|
|
353
|
+
):
|
|
354
|
+
return _method_payload(
|
|
355
|
+
"PUT",
|
|
356
|
+
url,
|
|
357
|
+
params=params,
|
|
358
|
+
data=data,
|
|
359
|
+
data_j=data_j,
|
|
360
|
+
data_m=data_m,
|
|
361
|
+
headers=headers,
|
|
362
|
+
mime=mime,
|
|
363
|
+
handle=handle,
|
|
364
|
+
silent=silent,
|
|
365
|
+
redirect=redirect,
|
|
366
|
+
timeout=timeout,
|
|
367
|
+
**kwargs
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _delete(
|
|
372
|
+
url,
|
|
373
|
+
params=None,
|
|
374
|
+
headers=None,
|
|
375
|
+
handle=None,
|
|
376
|
+
silent=None,
|
|
377
|
+
redirect=None,
|
|
378
|
+
timeout=None,
|
|
379
|
+
**kwargs
|
|
380
|
+
):
|
|
381
|
+
return _method_empty(
|
|
382
|
+
"DELETE",
|
|
383
|
+
url,
|
|
384
|
+
params=params,
|
|
385
|
+
headers=headers,
|
|
386
|
+
handle=handle,
|
|
387
|
+
silent=silent,
|
|
388
|
+
redirect=redirect,
|
|
389
|
+
timeout=timeout,
|
|
390
|
+
**kwargs
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _patch(
|
|
395
|
+
url,
|
|
396
|
+
params=None,
|
|
397
|
+
data=None,
|
|
398
|
+
data_j=None,
|
|
399
|
+
data_m=None,
|
|
400
|
+
headers=None,
|
|
401
|
+
mime=None,
|
|
402
|
+
handle=None,
|
|
403
|
+
silent=None,
|
|
404
|
+
redirect=None,
|
|
405
|
+
timeout=None,
|
|
406
|
+
**kwargs
|
|
407
|
+
):
|
|
408
|
+
return _method_payload(
|
|
409
|
+
"PATCH",
|
|
410
|
+
url,
|
|
411
|
+
params=params,
|
|
412
|
+
data=data,
|
|
413
|
+
data_j=data_j,
|
|
414
|
+
data_m=data_m,
|
|
415
|
+
headers=headers,
|
|
416
|
+
mime=mime,
|
|
417
|
+
handle=handle,
|
|
418
|
+
silent=silent,
|
|
419
|
+
redirect=redirect,
|
|
420
|
+
timeout=timeout,
|
|
421
|
+
**kwargs
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _method_empty(
|
|
426
|
+
name,
|
|
427
|
+
url,
|
|
428
|
+
params=None,
|
|
429
|
+
headers=None,
|
|
430
|
+
handle=None,
|
|
431
|
+
silent=None,
|
|
432
|
+
redirect=None,
|
|
433
|
+
timeout=None,
|
|
434
|
+
**kwargs
|
|
435
|
+
):
|
|
436
|
+
if handle == None:
|
|
437
|
+
handle = False
|
|
438
|
+
if silent == None:
|
|
439
|
+
silent = config.conf("HTTP_SILENT", False, cast=bool)
|
|
440
|
+
if redirect == None:
|
|
441
|
+
redirect = config.conf("HTTP_REDIRECT", False, cast=bool)
|
|
442
|
+
if timeout == None:
|
|
443
|
+
timeout = config.conf("HTTP_TIMEOUT", TIMEOUT, cast=int)
|
|
444
|
+
values = params or dict()
|
|
445
|
+
|
|
446
|
+
values_s = " with '%s'" % str(values) if values else ""
|
|
447
|
+
if not silent:
|
|
448
|
+
logging.debug("%s %s%s" % (name, url, values_s))
|
|
449
|
+
|
|
450
|
+
url, scheme, host, authorization, extra = _parse_url(url)
|
|
451
|
+
if extra:
|
|
452
|
+
values.update(extra)
|
|
453
|
+
data = _urlencode(values)
|
|
454
|
+
|
|
455
|
+
headers = dict(headers) if headers else dict()
|
|
456
|
+
if host:
|
|
457
|
+
headers["Host"] = host
|
|
458
|
+
if authorization:
|
|
459
|
+
headers["Authorization"] = "Basic %s" % authorization
|
|
460
|
+
url = url + "?" + data if data else url
|
|
461
|
+
url = str(url)
|
|
462
|
+
|
|
463
|
+
_method_callback(handle, kwargs)
|
|
464
|
+
|
|
465
|
+
# runs the concrete resolution method (taking into account the adapter)
|
|
466
|
+
# providing it with the required parameters for request execution
|
|
467
|
+
file = _resolve(url, name, headers, None, silent, timeout, **kwargs)
|
|
468
|
+
|
|
469
|
+
# verifies if the resulting "file" from the resolution process is either
|
|
470
|
+
# an invalid or tuple value and if that's the case as the request has
|
|
471
|
+
# probably been deferred for asynchronous execution
|
|
472
|
+
if file == None:
|
|
473
|
+
return file
|
|
474
|
+
if isinstance(file, tuple):
|
|
475
|
+
return file
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
result = file.read()
|
|
479
|
+
finally:
|
|
480
|
+
file.close()
|
|
481
|
+
|
|
482
|
+
code = file.getcode()
|
|
483
|
+
info = file.info()
|
|
484
|
+
|
|
485
|
+
location = info.get("Location", None) if redirect else None
|
|
486
|
+
if location:
|
|
487
|
+
return _redirect(
|
|
488
|
+
location,
|
|
489
|
+
scheme,
|
|
490
|
+
host,
|
|
491
|
+
handle=handle,
|
|
492
|
+
silent=silent,
|
|
493
|
+
redirect=redirect,
|
|
494
|
+
timeout=timeout,
|
|
495
|
+
**kwargs
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
if not silent:
|
|
499
|
+
logging.debug("%s %s returned '%d'" % (name, url, code))
|
|
500
|
+
|
|
501
|
+
result = _result(result, info)
|
|
502
|
+
return (result, file) if handle else result
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
def _method_payload(
|
|
506
|
+
name,
|
|
507
|
+
url,
|
|
508
|
+
params=None,
|
|
509
|
+
data=None,
|
|
510
|
+
data_j=None,
|
|
511
|
+
data_m=None,
|
|
512
|
+
headers=None,
|
|
513
|
+
mime=None,
|
|
514
|
+
handle=None,
|
|
515
|
+
silent=None,
|
|
516
|
+
redirect=None,
|
|
517
|
+
timeout=None,
|
|
518
|
+
**kwargs
|
|
519
|
+
):
|
|
520
|
+
if handle == None:
|
|
521
|
+
handle = False
|
|
522
|
+
if silent == None:
|
|
523
|
+
silent = config.conf("HTTP_SILENT", False, cast=bool)
|
|
524
|
+
if redirect == None:
|
|
525
|
+
redirect = config.conf("HTTP_REDIRECT", False, cast=bool)
|
|
526
|
+
if timeout == None:
|
|
527
|
+
timeout = config.conf("HTTP_TIMEOUT", TIMEOUT, cast=int)
|
|
528
|
+
values = params or dict()
|
|
529
|
+
|
|
530
|
+
values_s = " with '%s'" % str(values) if values else ""
|
|
531
|
+
if not silent:
|
|
532
|
+
logging.debug("%s %s%s" % (name, url, values_s))
|
|
533
|
+
|
|
534
|
+
url, scheme, host, authorization, extra = _parse_url(url)
|
|
535
|
+
if extra:
|
|
536
|
+
values.update(extra)
|
|
537
|
+
data_e = _urlencode(values)
|
|
538
|
+
|
|
539
|
+
if not data == None:
|
|
540
|
+
url = url + "?" + data_e if data_e else url
|
|
541
|
+
elif not data_j == None:
|
|
542
|
+
data = json.dumps(data_j)
|
|
543
|
+
url = url + "?" + data_e if data_e else url
|
|
544
|
+
mime = mime or "application/json"
|
|
545
|
+
elif not data_m == None:
|
|
546
|
+
url = url + "?" + data_e if data_e else url
|
|
547
|
+
content_type, data = _encode_multipart(data_m, mime=mime, doseq=True)
|
|
548
|
+
mime = content_type
|
|
549
|
+
elif data_e:
|
|
550
|
+
data = data_e
|
|
551
|
+
mime = mime or "application/x-www-form-urlencoded"
|
|
552
|
+
|
|
553
|
+
if legacy.is_unicode(data):
|
|
554
|
+
data = legacy.bytes(data, force=True)
|
|
555
|
+
|
|
556
|
+
if not data:
|
|
557
|
+
length = 0
|
|
558
|
+
elif legacy.is_bytes(data):
|
|
559
|
+
length = len(data)
|
|
560
|
+
else:
|
|
561
|
+
length = -1
|
|
562
|
+
|
|
563
|
+
headers = dict(headers) if headers else dict()
|
|
564
|
+
if not length == -1:
|
|
565
|
+
headers["Content-Length"] = str(length)
|
|
566
|
+
if mime:
|
|
567
|
+
headers["Content-Type"] = mime
|
|
568
|
+
if host:
|
|
569
|
+
headers["Host"] = host
|
|
570
|
+
if authorization:
|
|
571
|
+
headers["Authorization"] = "Basic %s" % authorization
|
|
572
|
+
url = str(url)
|
|
573
|
+
|
|
574
|
+
_method_callback(handle, kwargs)
|
|
575
|
+
file = _resolve(url, name, headers, data, silent, timeout, **kwargs)
|
|
576
|
+
if file == None:
|
|
577
|
+
return file
|
|
578
|
+
|
|
579
|
+
try:
|
|
580
|
+
result = file.read()
|
|
581
|
+
finally:
|
|
582
|
+
file.close()
|
|
583
|
+
|
|
584
|
+
code = file.getcode()
|
|
585
|
+
info = file.info()
|
|
586
|
+
|
|
587
|
+
location = info.get("Location", None) if redirect else None
|
|
588
|
+
if location:
|
|
589
|
+
return _redirect(
|
|
590
|
+
location,
|
|
591
|
+
scheme,
|
|
592
|
+
host,
|
|
593
|
+
handle=handle,
|
|
594
|
+
silent=silent,
|
|
595
|
+
redirect=redirect,
|
|
596
|
+
timeout=timeout,
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
if not silent:
|
|
600
|
+
logging.debug("%s %s returned '%d'" % (name, url, code))
|
|
601
|
+
|
|
602
|
+
result = _result(result, info)
|
|
603
|
+
return (result, file) if handle else result
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
def _method_callback(handle, kwargs):
|
|
607
|
+
# tries to determine if a callback value has been registered
|
|
608
|
+
# in the set of keyword argument and if that's not the case
|
|
609
|
+
# returns immediately (nothing to be done)
|
|
610
|
+
callback = kwargs.get("callback", None)
|
|
611
|
+
if not callback:
|
|
612
|
+
return
|
|
613
|
+
|
|
614
|
+
def callback_wrap(file):
|
|
615
|
+
# determines if the received file is valid (no error)
|
|
616
|
+
# or if instead there's an error with the connection
|
|
617
|
+
# and an invalid/unset value has been provided
|
|
618
|
+
if file:
|
|
619
|
+
info = file.info()
|
|
620
|
+
try:
|
|
621
|
+
result = file.read()
|
|
622
|
+
finally:
|
|
623
|
+
file.close()
|
|
624
|
+
result = _result(result, info)
|
|
625
|
+
else:
|
|
626
|
+
result = None
|
|
627
|
+
|
|
628
|
+
# taking into account the handle flag determines the
|
|
629
|
+
# kind of result structure that should be "returned"
|
|
630
|
+
result = (result, file) if handle else (result,)
|
|
631
|
+
callback(*result)
|
|
632
|
+
|
|
633
|
+
# sets the "new" callback clojure in the set of keyword
|
|
634
|
+
# based arguments for the calling of the HTTP handler method
|
|
635
|
+
# so that this new callback is called instead of the original
|
|
636
|
+
kwargs["callback"] = callback_wrap
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
def _redirect(
|
|
640
|
+
location,
|
|
641
|
+
scheme,
|
|
642
|
+
host,
|
|
643
|
+
handle=None,
|
|
644
|
+
silent=None,
|
|
645
|
+
redirect=None,
|
|
646
|
+
timeout=None,
|
|
647
|
+
**kwargs
|
|
648
|
+
):
|
|
649
|
+
is_relative = location.startswith("/")
|
|
650
|
+
if is_relative:
|
|
651
|
+
location = scheme + "://" + host + location
|
|
652
|
+
logging.debug("Redirecting to %s" % location)
|
|
653
|
+
return get(
|
|
654
|
+
location,
|
|
655
|
+
handle=handle,
|
|
656
|
+
silent=silent,
|
|
657
|
+
redirect=redirect,
|
|
658
|
+
timeout=timeout,
|
|
659
|
+
**kwargs
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def _resolve(*args, **kwargs):
|
|
664
|
+
# obtains the reference to the global set of variables, so
|
|
665
|
+
# that it's possible to obtain the proper resolver method
|
|
666
|
+
# according to the requested client
|
|
667
|
+
_global = globals()
|
|
668
|
+
|
|
669
|
+
# tries to retrieve the global configuration values that
|
|
670
|
+
# will condition the way the request is going to be performed
|
|
671
|
+
client = config.conf("HTTP_CLIENT", "netius")
|
|
672
|
+
reuse = config.conf("HTTP_REUSE", True, cast=bool)
|
|
673
|
+
|
|
674
|
+
# tries to determine the set of configurations requested on
|
|
675
|
+
# a request basis (not global) these have priority when
|
|
676
|
+
# compared with the global configuration ones
|
|
677
|
+
client = kwargs.pop("client", client)
|
|
678
|
+
reuse = kwargs.pop("reuse", reuse)
|
|
679
|
+
|
|
680
|
+
# sets the value for connection re-usage so that a connection
|
|
681
|
+
# pool is used if request, otherwise one connection per request
|
|
682
|
+
# is going to be used (at the expense of resources)
|
|
683
|
+
kwargs["reuse"] = reuse
|
|
684
|
+
|
|
685
|
+
# tries to retrieve the reference to the resolve method for the
|
|
686
|
+
# current client and then runs it, retrieve then the final result,
|
|
687
|
+
# note that the result structure may be engine dependent
|
|
688
|
+
resolver = _global.get("_resolve_" + client, _resolve_legacy)
|
|
689
|
+
try:
|
|
690
|
+
result = resolver(*args, **kwargs)
|
|
691
|
+
except ImportError:
|
|
692
|
+
result = _resolve_legacy(*args, **kwargs)
|
|
693
|
+
return result
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def _resolve_legacy(url, method, headers, data, silent, timeout, **kwargs):
|
|
697
|
+
is_generator = not data == None and legacy.is_generator(data)
|
|
698
|
+
if is_generator:
|
|
699
|
+
next(data)
|
|
700
|
+
data = b"".join(data)
|
|
701
|
+
is_file = hasattr(data, "tell")
|
|
702
|
+
if is_file:
|
|
703
|
+
data = data.read()
|
|
704
|
+
opener = legacy.build_opener(legacy.HTTPHandler)
|
|
705
|
+
request = legacy.Request(url, data=data, headers=headers)
|
|
706
|
+
request.get_method = lambda: method
|
|
707
|
+
return opener.open(request, timeout=timeout)
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def _resolve_requests(url, method, headers, data, silent, timeout, **kwargs):
|
|
711
|
+
util.ensure_pip("requests")
|
|
712
|
+
import requests
|
|
713
|
+
|
|
714
|
+
global _requests_session
|
|
715
|
+
|
|
716
|
+
# retrieves the various dynamic parameters for the HTTP client
|
|
717
|
+
# usage under the requests infra-structure
|
|
718
|
+
reuse = kwargs.get("reuse", True)
|
|
719
|
+
connections = kwargs.get("connections", 256)
|
|
720
|
+
|
|
721
|
+
# verifies if the provided data is a generator, assumes that if the
|
|
722
|
+
# data is not invalid and is of type generator then it's a generator
|
|
723
|
+
# and then if that's the case encapsulates this size based generator
|
|
724
|
+
# into a generator based file-like object so that it can be used inside
|
|
725
|
+
# the request infra-structure (as it accepts only file objects)
|
|
726
|
+
is_generator = not data == None and legacy.is_generator(data)
|
|
727
|
+
if is_generator:
|
|
728
|
+
data = structures.GeneratorFile(data)
|
|
729
|
+
|
|
730
|
+
# verifies if the session for the requests infra-structure is
|
|
731
|
+
# already created and if that's not the case and the re-use
|
|
732
|
+
# flag is sets creates a new session for the requested settings
|
|
733
|
+
registered = "_requests_session" in globals()
|
|
734
|
+
if not registered and reuse:
|
|
735
|
+
_requests_session = requests.Session()
|
|
736
|
+
adapter = requests.adapters.HTTPAdapter(
|
|
737
|
+
pool_connections=connections, pool_maxsize=connections
|
|
738
|
+
)
|
|
739
|
+
_requests_session.mount("", adapter)
|
|
740
|
+
|
|
741
|
+
# determines the based object from which the concrete methods
|
|
742
|
+
# are going to be loaded by inspecting the re-use flag
|
|
743
|
+
if reuse:
|
|
744
|
+
base = _requests_session
|
|
745
|
+
else:
|
|
746
|
+
base = requests
|
|
747
|
+
|
|
748
|
+
# converts the string based method value into a lower cased value
|
|
749
|
+
# and then uses it to retrieve the method object method (callable)
|
|
750
|
+
# that is going to be called to perform the request
|
|
751
|
+
method = method.lower()
|
|
752
|
+
caller = getattr(base, method)
|
|
753
|
+
|
|
754
|
+
# runs the caller method (according to selected method) and waits for
|
|
755
|
+
# the result object converting it then to the target response object
|
|
756
|
+
result = caller(url, headers=headers, data=data, timeout=timeout)
|
|
757
|
+
response = HTTPResponse(
|
|
758
|
+
data=result.content, code=result.status_code, headers=result.headers
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# retrieves the response code of the created response and verifies if
|
|
762
|
+
# it represent an error, if that's the case raised an error exception
|
|
763
|
+
# to the upper layers to break the current execution logic properly
|
|
764
|
+
code = response.getcode()
|
|
765
|
+
is_error = _is_error(code)
|
|
766
|
+
if is_error:
|
|
767
|
+
raise legacy.HTTPError(url, code, "HTTP retrieval problem", None, response)
|
|
768
|
+
|
|
769
|
+
# returns the final response object to the caller method, this object
|
|
770
|
+
# should comply with the proper upper layers structure
|
|
771
|
+
return response
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
def _resolve_netius(url, method, headers, data, silent, timeout, **kwargs):
|
|
775
|
+
util.ensure_pip("netius")
|
|
776
|
+
import netius.clients
|
|
777
|
+
|
|
778
|
+
# determines the final value of the silent flag taking into
|
|
779
|
+
# account if the current infra-structure is not running under
|
|
780
|
+
# a development environment
|
|
781
|
+
silent = silent or False
|
|
782
|
+
silent |= not common.is_devel()
|
|
783
|
+
|
|
784
|
+
# converts the provided dictionary of headers into a new map to
|
|
785
|
+
# allow any re-writing of values, valuable for a re-connect
|
|
786
|
+
headers = dict(headers)
|
|
787
|
+
|
|
788
|
+
# tries to determine the proper level of verbosity to be used by
|
|
789
|
+
# the client, for that the system tries to determine if the current
|
|
790
|
+
# execution environment is a development one (verbose)
|
|
791
|
+
level = logging.CRITICAL if silent else logging.DEBUG
|
|
792
|
+
|
|
793
|
+
# retrieves the various dynamic parameters for the HTTP client
|
|
794
|
+
# usage under the netius infra-structure
|
|
795
|
+
retry = kwargs.get("retry", 1)
|
|
796
|
+
reuse = kwargs.get("reuse", True)
|
|
797
|
+
level = kwargs.get("level", level)
|
|
798
|
+
asynchronous = kwargs.get("async", False)
|
|
799
|
+
asynchronous = kwargs.get("asynchronous", asynchronous)
|
|
800
|
+
use_file = kwargs.get("use_file", False)
|
|
801
|
+
callback = kwargs.get("callback", None)
|
|
802
|
+
callback_init = kwargs.get("callback_init", None)
|
|
803
|
+
callback_open = kwargs.get("callback_open", None)
|
|
804
|
+
callback_headers = kwargs.get("callback_headers", None)
|
|
805
|
+
callback_data = kwargs.get("callback_data", None)
|
|
806
|
+
callback_result = kwargs.get("callback_result", None)
|
|
807
|
+
|
|
808
|
+
# re-calculates the retry and re-use flags taking into account
|
|
809
|
+
# the async flag, if the execution mode is async we don't want
|
|
810
|
+
# to re-use the HTTP client as it would create issues
|
|
811
|
+
retry, reuse = (0, False) if asynchronous else (retry, reuse)
|
|
812
|
+
|
|
813
|
+
# creates the proper set of extra parameters to be sent to the
|
|
814
|
+
# HTTP client taking into account a possible async method request
|
|
815
|
+
extra = (
|
|
816
|
+
_async_netius(
|
|
817
|
+
callback=callback,
|
|
818
|
+
callback_init=callback_init,
|
|
819
|
+
callback_open=callback_open,
|
|
820
|
+
callback_headers=callback_headers,
|
|
821
|
+
callback_data=callback_data,
|
|
822
|
+
callback_result=callback_result,
|
|
823
|
+
)
|
|
824
|
+
if asynchronous
|
|
825
|
+
else dict(
|
|
826
|
+
on_init=lambda c: callback_init and callback_init(c),
|
|
827
|
+
on_open=lambda c: callback_open and callback_open(c),
|
|
828
|
+
on_headers=lambda c, p: callback_headers and callback_headers(p.headers),
|
|
829
|
+
on_data=lambda c, p, d: callback_data and callback_data(d),
|
|
830
|
+
on_result=lambda c, p, r: callback_result and callback_result(r),
|
|
831
|
+
)
|
|
832
|
+
)
|
|
833
|
+
|
|
834
|
+
# verifies if client re-usage must be enforced and if that's the
|
|
835
|
+
# case the global client object is requested (singleton) otherwise
|
|
836
|
+
# the client should be created inside the HTTP client static method
|
|
837
|
+
http_client = _client_netius(level=level) if reuse else None
|
|
838
|
+
result = netius.clients.HTTPClient.method_s(
|
|
839
|
+
method,
|
|
840
|
+
url,
|
|
841
|
+
headers=headers,
|
|
842
|
+
data=data,
|
|
843
|
+
asynchronous=asynchronous,
|
|
844
|
+
timeout=timeout,
|
|
845
|
+
use_file=use_file,
|
|
846
|
+
http_client=http_client,
|
|
847
|
+
level=level,
|
|
848
|
+
**extra
|
|
849
|
+
)
|
|
850
|
+
|
|
851
|
+
# if the async mode is defined the result (tuple) is returned immediately
|
|
852
|
+
# as the processing will be taking place latter (on callback)
|
|
853
|
+
if asynchronous:
|
|
854
|
+
return result
|
|
855
|
+
|
|
856
|
+
# tries to retrieve any possible error coming from the result object
|
|
857
|
+
# if this happens it means an exception has been raised internally and
|
|
858
|
+
# the error should be handled in a proper manner, if the error is related
|
|
859
|
+
# to a closed connection a retry may be performed to try to re-establish
|
|
860
|
+
# the connection (allows for reconnection in connection pool)
|
|
861
|
+
error = result.get("error", None)
|
|
862
|
+
if error == "closed" and retry > 0:
|
|
863
|
+
kwargs["retry"] = retry - 1
|
|
864
|
+
return _resolve_netius(url, method, headers, data, silent, timeout, **kwargs)
|
|
865
|
+
|
|
866
|
+
# converts the netius specific result map into a response compatible
|
|
867
|
+
# object (equivalent to the urllib one) to be used by the upper layers
|
|
868
|
+
# under an equivalent and compatible approach note that this conversion
|
|
869
|
+
# may raise an exception in case the result represent an error
|
|
870
|
+
response = netius.clients.HTTPClient.to_response(result)
|
|
871
|
+
|
|
872
|
+
# retrieves the response code of the created response and verifies if
|
|
873
|
+
# it represent an error, if that's the case raised an error exception
|
|
874
|
+
# to the upper layers to break the current execution logic properly
|
|
875
|
+
code = response.getcode()
|
|
876
|
+
is_error = _is_error(code)
|
|
877
|
+
if is_error:
|
|
878
|
+
raise legacy.HTTPError(url, code, "HTTP retrieval problem", None, response)
|
|
879
|
+
|
|
880
|
+
# returns the final response object to the upper layers, this object
|
|
881
|
+
# may be used freely under the compatibility interface it provides
|
|
882
|
+
return response
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
def _client_netius(level=logging.CRITICAL):
|
|
886
|
+
import netius.clients
|
|
887
|
+
|
|
888
|
+
global _netius_clients
|
|
889
|
+
|
|
890
|
+
# retrieves the reference to the current thread and uses the value
|
|
891
|
+
# to retrieve the thread identifier (TID) for it, to be used in the
|
|
892
|
+
# identification of the client resource associated with it
|
|
893
|
+
tid = threading.current_thread().ident
|
|
894
|
+
|
|
895
|
+
# acquires the global HTTP lock and executes a series of validation
|
|
896
|
+
# and initialization of the netius client infra-structure, this
|
|
897
|
+
# operations required thread safety
|
|
898
|
+
ACCESS_LOCK.acquire()
|
|
899
|
+
try:
|
|
900
|
+
registered = "_netius_clients" in globals()
|
|
901
|
+
_netius_clients = _netius_clients if registered else dict()
|
|
902
|
+
netius_client = _netius_clients.get(tid, None)
|
|
903
|
+
finally:
|
|
904
|
+
ACCESS_LOCK.release()
|
|
905
|
+
|
|
906
|
+
# in case a previously created netius client has been retrieved
|
|
907
|
+
# returns it to the caller method for proper re-usage
|
|
908
|
+
if netius_client:
|
|
909
|
+
return netius_client
|
|
910
|
+
|
|
911
|
+
# creates the "new" HTTP client for the current thread and registers
|
|
912
|
+
# it under the netius client structure so that it may be re-used
|
|
913
|
+
netius_client = netius.clients.HTTPClient(auto_release=False)
|
|
914
|
+
_netius_clients[tid] = netius_client
|
|
915
|
+
|
|
916
|
+
# in case this is the first registration of the dictionary a new on
|
|
917
|
+
# exit callback is registered to cleanup the netius infra-structure
|
|
918
|
+
# then the final client is returned to the caller of the method
|
|
919
|
+
if not registered:
|
|
920
|
+
common.base().on_exit(_cleanup_netius)
|
|
921
|
+
return netius_client
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def _async_netius(
|
|
925
|
+
callback=None,
|
|
926
|
+
callback_init=None,
|
|
927
|
+
callback_open=None,
|
|
928
|
+
callback_headers=None,
|
|
929
|
+
callback_data=None,
|
|
930
|
+
callback_result=None,
|
|
931
|
+
):
|
|
932
|
+
import netius.clients
|
|
933
|
+
|
|
934
|
+
buffer = []
|
|
935
|
+
extra = dict()
|
|
936
|
+
|
|
937
|
+
def _on_init(protocol):
|
|
938
|
+
callback_init and callback_init(protocol)
|
|
939
|
+
|
|
940
|
+
def _on_open(protocol):
|
|
941
|
+
callback_open and callback_open(protocol)
|
|
942
|
+
|
|
943
|
+
def _on_close(protocol):
|
|
944
|
+
callback and callback(None)
|
|
945
|
+
|
|
946
|
+
def _on_headers(protocol, parser):
|
|
947
|
+
callback_headers and callback_headers(parser.headers)
|
|
948
|
+
|
|
949
|
+
def _on_data(protocol, parser, data):
|
|
950
|
+
data = data
|
|
951
|
+
data and buffer.append(data)
|
|
952
|
+
callback_data and callback_data(data)
|
|
953
|
+
|
|
954
|
+
def _on_result(protocol, parser, result):
|
|
955
|
+
callback_result and callback_result(result)
|
|
956
|
+
|
|
957
|
+
def _callback(protocol, parser, message):
|
|
958
|
+
result = netius.clients.HTTPProtocol.set_request(parser, buffer)
|
|
959
|
+
response = netius.clients.HTTPClient.to_response(result)
|
|
960
|
+
callback and callback(response)
|
|
961
|
+
|
|
962
|
+
extra["callback"] = _callback
|
|
963
|
+
extra["on_data"] = _on_data
|
|
964
|
+
if callback_init:
|
|
965
|
+
extra["_on_init"] = _on_init
|
|
966
|
+
if callback_open:
|
|
967
|
+
extra["on_open"] = _on_open
|
|
968
|
+
if callback:
|
|
969
|
+
extra["on_close"] = _on_close
|
|
970
|
+
if callback_headers:
|
|
971
|
+
extra["on_headers"] = _on_headers
|
|
972
|
+
if callback_result:
|
|
973
|
+
extra["on_result"] = _on_result
|
|
974
|
+
|
|
975
|
+
return extra
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def _cleanup_netius():
|
|
979
|
+
global _netius_clients
|
|
980
|
+
for netius_client in _netius_clients.values():
|
|
981
|
+
netius_client.cleanup()
|
|
982
|
+
del _netius_clients
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
def _parse_url(url):
|
|
986
|
+
parse = legacy.urlparse(url)
|
|
987
|
+
scheme = parse.scheme
|
|
988
|
+
secure = scheme == "https"
|
|
989
|
+
default = 443 if secure else 80
|
|
990
|
+
port = parse.port or default
|
|
991
|
+
url = parse.scheme + "://" + parse.hostname + ":" + str(port) + parse.path
|
|
992
|
+
if port in (80, 443):
|
|
993
|
+
host = parse.hostname
|
|
994
|
+
else:
|
|
995
|
+
host = parse.hostname + ":" + str(port)
|
|
996
|
+
authorization = _authorization(parse.username, parse.password)
|
|
997
|
+
params = _params(parse.query)
|
|
998
|
+
return (url, scheme, host, authorization, params)
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
def _result(data, info={}, force=False, strict=False):
|
|
1002
|
+
# tries to retrieve the content type value from the headers
|
|
1003
|
+
# info and verifies if the current data is JSON encoded, so
|
|
1004
|
+
# that it gets automatically decoded for such cases
|
|
1005
|
+
content_type = info.get("Content-Type", None) or ""
|
|
1006
|
+
is_json = (
|
|
1007
|
+
util.is_content_type(
|
|
1008
|
+
content_type, ("application/json", "text/json", "text/javascript")
|
|
1009
|
+
)
|
|
1010
|
+
or force
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
# verifies if the current result set is JSON encoded and in
|
|
1014
|
+
# case it's decodes it and loads it as JSON otherwise returns
|
|
1015
|
+
# the "raw" data to the caller method as expected, note that
|
|
1016
|
+
# the strict flag is used to determine if the exception should
|
|
1017
|
+
# be re-raised to the upper level in case of value error
|
|
1018
|
+
if is_json and legacy.is_bytes(data):
|
|
1019
|
+
data = data.decode("utf-8")
|
|
1020
|
+
try:
|
|
1021
|
+
data = json.loads(data) if is_json else data
|
|
1022
|
+
except ValueError:
|
|
1023
|
+
if strict:
|
|
1024
|
+
raise
|
|
1025
|
+
return data
|
|
1026
|
+
|
|
1027
|
+
|
|
1028
|
+
def _params(query):
|
|
1029
|
+
# creates the dictionary that is going to be used to store the
|
|
1030
|
+
# complete information regarding the parameters in query
|
|
1031
|
+
params = dict()
|
|
1032
|
+
|
|
1033
|
+
# validates that the provided query value is valid and if
|
|
1034
|
+
# that's not the case returns the created parameters immediately
|
|
1035
|
+
# (empty parameters are returned)
|
|
1036
|
+
if not query:
|
|
1037
|
+
return params
|
|
1038
|
+
|
|
1039
|
+
# splits the query value around the initial parameter separator
|
|
1040
|
+
# symbol and iterates over each of them to parse them and create
|
|
1041
|
+
# the proper parameters dictionary (of lists)
|
|
1042
|
+
query_s = query.split("&")
|
|
1043
|
+
for part in query_s:
|
|
1044
|
+
parts = part.split("=", 1)
|
|
1045
|
+
if len(parts) == 1:
|
|
1046
|
+
value = ""
|
|
1047
|
+
else:
|
|
1048
|
+
value = parts[1]
|
|
1049
|
+
key = parts[0]
|
|
1050
|
+
key = legacy.unquote_plus(key)
|
|
1051
|
+
value = legacy.unquote_plus(value)
|
|
1052
|
+
param = params.get(key, [])
|
|
1053
|
+
param.append(value)
|
|
1054
|
+
params[key] = param
|
|
1055
|
+
|
|
1056
|
+
# returns the final parameters dictionary to the caller method
|
|
1057
|
+
# so that it may be used as a proper structure representation
|
|
1058
|
+
return params
|
|
1059
|
+
|
|
1060
|
+
|
|
1061
|
+
def _urlencode(values, as_string=True):
|
|
1062
|
+
# creates the list that will hold the final tuple of values
|
|
1063
|
+
# (without the unset and invalid values)
|
|
1064
|
+
final = []
|
|
1065
|
+
|
|
1066
|
+
# verifies if the provided value is a sequence and in case it's
|
|
1067
|
+
# not converts it into a sequence (assuming a map)
|
|
1068
|
+
is_sequence = isinstance(values, (list, tuple))
|
|
1069
|
+
if not is_sequence:
|
|
1070
|
+
values = values.items()
|
|
1071
|
+
|
|
1072
|
+
# iterates over all the items in the values sequence to
|
|
1073
|
+
# try to filter the values that are not valid
|
|
1074
|
+
for key, value in values:
|
|
1075
|
+
# creates the list that will hold the valid values
|
|
1076
|
+
# of the current key in iteration (sanitized values)
|
|
1077
|
+
_values = []
|
|
1078
|
+
|
|
1079
|
+
# in case the current data type of the key is unicode
|
|
1080
|
+
# the value must be converted into a string using the
|
|
1081
|
+
# default utf encoding strategy (as defined)
|
|
1082
|
+
if type(key) == legacy.UNICODE:
|
|
1083
|
+
key = key.encode("utf-8")
|
|
1084
|
+
|
|
1085
|
+
# verifies the type of the current value and in case
|
|
1086
|
+
# it's sequence based converts it into a list using
|
|
1087
|
+
# the conversion method otherwise creates a new list
|
|
1088
|
+
# and includes the value in it
|
|
1089
|
+
value_t = type(value)
|
|
1090
|
+
if value_t in SEQUENCE_TYPES:
|
|
1091
|
+
value = list(value)
|
|
1092
|
+
else:
|
|
1093
|
+
value = [value]
|
|
1094
|
+
|
|
1095
|
+
# iterates over all the values in the current sequence
|
|
1096
|
+
# and adds the valid values to the sanitized sequence,
|
|
1097
|
+
# this includes the conversion from unicode string into
|
|
1098
|
+
# a simple string using the default utf encoder
|
|
1099
|
+
for _value in value:
|
|
1100
|
+
if _value == None:
|
|
1101
|
+
continue
|
|
1102
|
+
is_string = type(_value) in legacy.STRINGS
|
|
1103
|
+
if not is_string:
|
|
1104
|
+
_value = str(_value)
|
|
1105
|
+
is_unicode = type(_value) == legacy.UNICODE
|
|
1106
|
+
if is_unicode:
|
|
1107
|
+
_value = _value.encode("utf-8")
|
|
1108
|
+
_values.append(_value)
|
|
1109
|
+
|
|
1110
|
+
# sets the sanitized list of values as the new value for
|
|
1111
|
+
# the key in the final dictionary of values
|
|
1112
|
+
final.append((key, _values))
|
|
1113
|
+
|
|
1114
|
+
# in case the "as string" flag is not set the ended key to value
|
|
1115
|
+
# dictionary should be returned to the called method and not the
|
|
1116
|
+
# "default" linear and string based value
|
|
1117
|
+
if not as_string:
|
|
1118
|
+
return final
|
|
1119
|
+
|
|
1120
|
+
# runs the encoding with sequence support on the final map
|
|
1121
|
+
# of sanitized values and returns the encoded result to the
|
|
1122
|
+
# caller method as the encoded value
|
|
1123
|
+
return legacy.urlencode(final, doseq=True)
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
def _quote(values, plus=False, safe="/"):
|
|
1127
|
+
method = legacy.quote_plus if plus else legacy.quote
|
|
1128
|
+
values = _urlencode(values, as_string=False)
|
|
1129
|
+
|
|
1130
|
+
final = dict()
|
|
1131
|
+
|
|
1132
|
+
for key, value in values:
|
|
1133
|
+
key = method(key, safe=safe)
|
|
1134
|
+
value = method(value[0], safe=safe)
|
|
1135
|
+
final[key] = value
|
|
1136
|
+
|
|
1137
|
+
return final
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
def _authorization(username, password):
|
|
1141
|
+
if not username:
|
|
1142
|
+
return None
|
|
1143
|
+
if not password:
|
|
1144
|
+
return None
|
|
1145
|
+
payload = "%s:%s" % (username, password)
|
|
1146
|
+
payload = legacy.bytes(payload)
|
|
1147
|
+
authorization = base64.b64encode(payload)
|
|
1148
|
+
authorization = legacy.str(authorization)
|
|
1149
|
+
return authorization
|
|
1150
|
+
|
|
1151
|
+
|
|
1152
|
+
def _encode_multipart(fields, mime=None, doseq=False):
|
|
1153
|
+
mime = mime or "multipart/form-data"
|
|
1154
|
+
boundary = _create_boundary(fields, doseq=doseq)
|
|
1155
|
+
boundary_b = legacy.bytes(boundary)
|
|
1156
|
+
|
|
1157
|
+
buffer = []
|
|
1158
|
+
|
|
1159
|
+
for key, values in fields.items():
|
|
1160
|
+
is_list = doseq and type(values) == list
|
|
1161
|
+
values = values if is_list else [values]
|
|
1162
|
+
|
|
1163
|
+
for value in values:
|
|
1164
|
+
if value == None:
|
|
1165
|
+
continue
|
|
1166
|
+
|
|
1167
|
+
if isinstance(value, dict):
|
|
1168
|
+
header_l = []
|
|
1169
|
+
data = None
|
|
1170
|
+
for key, item in value.items():
|
|
1171
|
+
if key == "data":
|
|
1172
|
+
data = item
|
|
1173
|
+
else:
|
|
1174
|
+
header_l.append("%s: %s" % (key, item))
|
|
1175
|
+
value = data
|
|
1176
|
+
header = "\r\n".join(header_l)
|
|
1177
|
+
elif isinstance(value, tuple):
|
|
1178
|
+
content_type = None
|
|
1179
|
+
if len(value) == 2:
|
|
1180
|
+
name, contents = value
|
|
1181
|
+
else:
|
|
1182
|
+
name, content_type, contents = value
|
|
1183
|
+
header = 'Content-Disposition: form-data; name="%s"; filename="%s"' % (
|
|
1184
|
+
key,
|
|
1185
|
+
name,
|
|
1186
|
+
)
|
|
1187
|
+
if content_type:
|
|
1188
|
+
header += "\r\nContent-Type: %s" % content_type
|
|
1189
|
+
value = contents
|
|
1190
|
+
else:
|
|
1191
|
+
header = 'Content-Disposition: form-data; name="%s"' % key
|
|
1192
|
+
value = _encode(value)
|
|
1193
|
+
|
|
1194
|
+
header = _encode(header)
|
|
1195
|
+
value = _encode(value)
|
|
1196
|
+
|
|
1197
|
+
buffer.append(b"--" + boundary_b)
|
|
1198
|
+
buffer.append(header)
|
|
1199
|
+
buffer.append(b"")
|
|
1200
|
+
buffer.append(value)
|
|
1201
|
+
|
|
1202
|
+
buffer.append(b"--" + boundary_b + b"--")
|
|
1203
|
+
buffer.append(b"")
|
|
1204
|
+
body = b"\r\n".join(buffer)
|
|
1205
|
+
content_type = "%s; boundary=%s" % (mime, boundary)
|
|
1206
|
+
|
|
1207
|
+
return content_type, body
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
def _create_boundary(fields, size=32, doseq=False):
|
|
1211
|
+
while True:
|
|
1212
|
+
base = "".join(random.choice(RANGE) for _value in range(size))
|
|
1213
|
+
boundary = "----------" + base
|
|
1214
|
+
result = _try_boundary(fields, boundary, doseq=doseq)
|
|
1215
|
+
if result:
|
|
1216
|
+
break
|
|
1217
|
+
|
|
1218
|
+
return boundary
|
|
1219
|
+
|
|
1220
|
+
|
|
1221
|
+
def _try_boundary(fields, boundary, doseq=False):
|
|
1222
|
+
boundary_b = legacy.bytes(boundary)
|
|
1223
|
+
|
|
1224
|
+
for key, values in fields.items():
|
|
1225
|
+
is_list = doseq and type(values) == list
|
|
1226
|
+
values = values if is_list else [values]
|
|
1227
|
+
|
|
1228
|
+
for value in values:
|
|
1229
|
+
if isinstance(value, dict):
|
|
1230
|
+
name = ""
|
|
1231
|
+
value = value.get("data", b"")
|
|
1232
|
+
elif isinstance(value, tuple):
|
|
1233
|
+
if len(value) == 2:
|
|
1234
|
+
name = value[0] or ""
|
|
1235
|
+
value = value[1] or b""
|
|
1236
|
+
else:
|
|
1237
|
+
name = value[0] or ""
|
|
1238
|
+
value = value[2] or b""
|
|
1239
|
+
else:
|
|
1240
|
+
name = ""
|
|
1241
|
+
value = _encode(value)
|
|
1242
|
+
|
|
1243
|
+
if not key.find(boundary) == -1:
|
|
1244
|
+
return False
|
|
1245
|
+
if not name.find(boundary) == -1:
|
|
1246
|
+
return False
|
|
1247
|
+
if not value.find(boundary_b) == -1:
|
|
1248
|
+
return False
|
|
1249
|
+
|
|
1250
|
+
return True
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
def _is_error(code):
|
|
1254
|
+
return code // 100 in (4, 5) if code else True
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
def _encode(value, encoding="utf-8"):
|
|
1258
|
+
value_t = type(value)
|
|
1259
|
+
if value_t == legacy.BYTES:
|
|
1260
|
+
return value
|
|
1261
|
+
elif value_t == legacy.UNICODE:
|
|
1262
|
+
return value.encode(encoding)
|
|
1263
|
+
return legacy.bytes(str(value))
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
class HTTPResponse(object):
|
|
1267
|
+
"""
|
|
1268
|
+
Compatibility object to be used by HTTP libraries that do
|
|
1269
|
+
not support the legacy HTTP response object as a return
|
|
1270
|
+
for any of their structures.
|
|
1271
|
+
"""
|
|
1272
|
+
|
|
1273
|
+
def __init__(self, data=None, code=200, status=None, headers=None):
|
|
1274
|
+
self.data = data
|
|
1275
|
+
self.code = code
|
|
1276
|
+
self.status = status
|
|
1277
|
+
self.headers = headers
|
|
1278
|
+
|
|
1279
|
+
def read(self):
|
|
1280
|
+
return self.data
|
|
1281
|
+
|
|
1282
|
+
def readline(self):
|
|
1283
|
+
return self.read()
|
|
1284
|
+
|
|
1285
|
+
def close(self):
|
|
1286
|
+
pass
|
|
1287
|
+
|
|
1288
|
+
def getcode(self):
|
|
1289
|
+
return self.code
|
|
1290
|
+
|
|
1291
|
+
def info(self):
|
|
1292
|
+
return self.headers
|