summaryrefslogtreecommitdiffstats
path: root/src/http.S
blob: 9ff6a536618ca2b62383291fe0fad5d28480e146 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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
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
439
440
441
442
443
444
445
446
447
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
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
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
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
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
#include "abi.inc"
#include "defs.inc"

#define CURLOPT_URL                 10002
#define CURLOPT_WRITEFUNCTION       20011
#define CURLOPT_WRITEDATA           10001
#define CURLOPT_HEADERFUNCTION      20079
#define CURLOPT_HEADERDATA          10029
#define CURLOPT_USERAGENT           10018
#define CURLOPT_HTTPHEADER          10023
#define CURLOPT_TIMEOUT_MS          155
#define CURLOPT_CONNECTTIMEOUT_MS   156
#define CURLOPT_ACCEPT_ENCODING     10102
#define CURLOPT_NOSIGNAL            99
#define CURLOPT_ERRORBUFFER         10010
#define CURLOPT_FOLLOWLOCATION      52
#define CURLINFO_RESPONSE_CODE      2097154
#define CURL_GLOBAL_DEFAULT         3
#define CURL_ERRSZ                  256

#define HC_VALUE    0
#define HC_HAVE     128
#define HC_SIZE     136

#define HG_HDR      -184
#define HG_ERRBUF   -440
#define HG_HANDLE   -448
#define HG_SLIST    -456
#define HG_DEADLINE -464
#define HG_HASDL    -472
#define HG_RA       -480
#define HG_HASRA    -488
#define HG_LASTST   -496
#define HG_CODE     -504
#define HG_ATTEMPT  -512
#define HG_ACC503   -520
#define HG_OUTST    -528
#define HG_CURLRC   -536
#define HG_WAIT     -544
#define HG_TMPL     -552

#define RA_TM       -104
#define RA_END      -112
#define RA_DAY      -120
#define RA_MON      -128
#define RA_YEAR     -136
#define RA_HH       -144
#define RA_MI       -152
#define RA_SS       -160

.macro COPT_LONG opt, val
        mov     HG_HANDLE(%rbp), %rdi
        mov     $\opt, %esi
        mov     $\val, %rdx
        xor     %eax, %eax
        call    curl_easy_setopt@PLT
.endm

.macro COPT_REG opt, reg
        mov     HG_HANDLE(%rbp), %rdi
        mov     $\opt, %esi
        mov     \reg, %rdx
        xor     %eax, %eax
        call    curl_easy_setopt@PLT
.endm

        .text

PUBFUNC cepis_global_init
        PROLOGUE 8
        xor     %eax, %eax
        mov     $1, %ecx
        lock cmpxchgl %ecx, cepis__curl_state(%rip)
        jne     .Lonce_out
        mov     $CURL_GLOBAL_DEFAULT, %edi
        call    curl_global_init@PLT
        movl    $2, cepis__curl_state(%rip)
.Lonce_out:
        EPILOGUE
        ret
ENDFUNC cepis_global_init

PUBFUNC cepis_global_cleanup
        PROLOGUE 8
        call    cepis__default_client_destroy
        cmpl    $0, cepis__curl_state(%rip)
        je      .Lgc_out
        movl    $0, cepis__curl_state(%rip)
        call    curl_global_cleanup@PLT
.Lgc_out:
        EPILOGUE
        ret
ENDFUNC cepis_global_cleanup

FUNC cepis__mono
        PROLOGUE 24
        mov     $1, %edi
        lea     LOCAL1(%rbp), %rsi
        call    clock_gettime@PLT
        cvtsi2sdq LOCAL1(%rbp), %xmm0
        cvtsi2sdq LOCAL0(%rbp), %xmm1
        mulsd   .Lnano(%rip), %xmm1
        addsd   %xmm1, %xmm0
        EPILOGUE
        ret
ENDFUNC cepis__mono

FUNC cepis__sleep
        PROLOGUE 40
        xorpd   %xmm1, %xmm1
        comisd  %xmm1, %xmm0
        jbe     .Lsleep_out
        cvttsd2siq %xmm0, %rax
        mov     %rax, LOCAL1(%rbp)
        cvtsi2sdq %rax, %xmm1
        subsd   %xmm1, %xmm0
        mulsd   .Lgiga(%rip), %xmm0
        cvttsd2siq %xmm0, %rax
        cmp     $0, %rax
        jge     .Lsleep_store
        xor     %eax, %eax
.Lsleep_store:
        mov     %rax, LOCAL0(%rbp)
.Lsleep_call:
        lea     LOCAL1(%rbp), %rdi
        lea     LOCAL3(%rbp), %rsi
        call    nanosleep@PLT
        test    %eax, %eax
        jz      .Lsleep_out
        call    __errno_location@PLT
        cmpl    $4, (%rax)
        jne     .Lsleep_out
        mov     LOCAL3(%rbp), %rax
        mov     %rax, LOCAL1(%rbp)
        mov     LOCAL2(%rbp), %rax
        mov     %rax, LOCAL0(%rbp)
        jmp     .Lsleep_call
.Lsleep_out:
        EPILOGUE
        ret
ENDFUNC cepis__sleep

FUNC cepis__rand01
        mov     CL_RNG(%rdi), %rax
.Lrand_loop:
        mov     %rax, %r8
        mov     %r8, %rdx
        shl     $13, %rdx
        xor     %rdx, %r8
        mov     %r8, %rdx
        shr     $7, %rdx
        xor     %rdx, %r8
        mov     %r8, %rdx
        shl     $17, %rdx
        xor     %rdx, %r8
        lock cmpxchgq %r8, CL_RNG(%rdi)
        jne     .Lrand_loop
        mov     %r8, %rax
        shr     $11, %rax
        cvtsi2sdq %rax, %xmm0
        mulsd   .Ltwo53(%rip), %xmm0
        ret
ENDFUNC cepis__rand01

FUNC cepis__backoff
        PROLOGUE 24
        mov     %rdi, %rbx
        test    %edx, %edx
        jz      .Lbo_calc
        movsd   CL_BACKOFF_MAX(%rbx), %xmm1
        minsd   %xmm1, %xmm0
        xorpd   %xmm1, %xmm1
        maxsd   %xmm1, %xmm0
        EPILOGUE
        ret
.Lbo_calc:
        movsd   CL_BACKOFF_BASE(%rbx), %xmm0
        movsd   CL_BACKOFF_MAX(%rbx), %xmm1
        mov     %esi, %ecx
        dec     %ecx
.Lbo_loop:
        test    %ecx, %ecx
        jle     .Lbo_cap
        comisd  %xmm1, %xmm0
        jae     .Lbo_cap
        addsd   %xmm0, %xmm0
        dec     %ecx
        jmp     .Lbo_loop
.Lbo_cap:
        minsd   %xmm1, %xmm0
        movsd   %xmm0, LOCAL0(%rbp)
        mov     %rbx, %rdi
        call    cepis__rand01
        mulsd   LOCAL0(%rbp), %xmm0
        EPILOGUE
        ret
ENDFUNC cepis__backoff

FUNC cepis__read_uint
        xor     %ecx, %ecx
        xor     %r8d, %r8d
        movl    $0, (%rsi)
.Lru_loop:
        cmp     %edx, %r8d
        jae     .Lru_end
        movzbl  (%rdi), %eax
        cmp     $'0', %al
        jb      .Lru_end
        cmp     $'9', %al
        ja      .Lru_end
        sub     $'0', %eax
        imul    $10, %ecx, %ecx
        add     %eax, %ecx
        inc     %rdi
        inc     %r8d
        jmp     .Lru_loop
.Lru_end:
        test    %r8d, %r8d
        jz      .Lru_bad
        mov     %ecx, (%rsi)
        mov     %rdi, %rax
        ret
.Lru_bad:
        xor     %eax, %eax
        ret
ENDFUNC cepis__read_uint

FUNC cepis__skip_blanks
.Lsb_loop:
        movzbl  (%rdi), %eax
        cmp     $' ', %al
        je      .Lsb_adv
        cmp     $9, %al
        je      .Lsb_adv
        mov     %rdi, %rax
        ret
.Lsb_adv:
        inc     %rdi
        jmp     .Lsb_loop
ENDFUNC cepis__skip_blanks

FUNC cepis__retry_after
        PROLOGUE 120
        mov     %rsi, %r14
        call    cepis__skip_blanks
        mov     %rax, %rbx
        cmpb    $0, (%rbx)
        je      .Lra_bad
        mov     %rbx, %rdi
        lea     RA_END(%rbp), %rsi
        call    strtod@PLT
        mov     RA_END(%rbp), %rax
        cmp     %rbx, %rax
        je      .Lra_date
        movsd   %xmm0, LOCAL0(%rbp)
        mov     %rax, %rdi
        call    cepis__skip_blanks
        movzbl  (%rax), %ecx
        test    %cl, %cl
        jz      .Lra_numeric
        cmp     $13, %cl
        je      .Lra_numeric
        cmp     $10, %cl
        je      .Lra_numeric
        jmp     .Lra_date
.Lra_numeric:
        movsd   LOCAL0(%rbp), %xmm0
        xorpd   %xmm1, %xmm1
        maxsd   %xmm1, %xmm0
        movsd   %xmm0, (%r14)
        mov     $1, %eax
        EPILOGUE
        ret
.Lra_date:
        mov     %rbx, %r12
.Lra_findnum:
        movzbl  (%r12), %eax
        test    %al, %al
        jz      .Lra_bad
        cmp     $'0', %al
        jb      .Lra_findnum_adv
        cmp     $'9', %al
        jbe     .Lra_day
.Lra_findnum_adv:
        inc     %r12
        jmp     .Lra_findnum
.Lra_day:
        mov     %r12, %rdi
        lea     RA_DAY(%rbp), %rsi
        mov     $2, %edx
        call    cepis__read_uint
        test    %rax, %rax
        jz      .Lra_bad
        mov     %rax, %rdi
        call    cepis__skip_blanks
        mov     %rax, %r12
        xor     %r13d, %r13d
.Lra_mon_loop:
        cmp     $12, %r13d
        jae     .Lra_bad
        lea     cepis__months(%rip), %rax
        mov     (%rax, %r13, 8), %rsi
        mov     %r12, %rdi
        mov     $3, %rdx
        call    strncasecmp@PLT
        test    %eax, %eax
        jz      .Lra_mon_hit
        inc     %r13d
        jmp     .Lra_mon_loop
.Lra_mon_hit:
        mov     %r13d, RA_MON(%rbp)
        lea     3(%r12), %rdi
        call    cepis__skip_blanks
        mov     %rax, %rdi
        lea     RA_YEAR(%rbp), %rsi
        mov     $4, %edx
        call    cepis__read_uint
        test    %rax, %rax
        jz      .Lra_bad
        mov     %rax, %rdi
        call    cepis__skip_blanks
        mov     %rax, %rdi
        lea     RA_HH(%rbp), %rsi
        mov     $2, %edx
        call    cepis__read_uint
        test    %rax, %rax
        jz      .Lra_bad
        cmpb    $':', (%rax)
        jne     .Lra_bad
        lea     1(%rax), %rdi
        lea     RA_MI(%rbp), %rsi
        mov     $2, %edx
        call    cepis__read_uint
        test    %rax, %rax
        jz      .Lra_bad
        cmpb    $':', (%rax)
        jne     .Lra_bad
        lea     1(%rax), %rdi
        lea     RA_SS(%rbp), %rsi
        mov     $2, %edx
        call    cepis__read_uint
        test    %rax, %rax
        jz      .Lra_bad
        lea     RA_TM(%rbp), %rdi
        xor     %esi, %esi
        mov     $56, %rdx
        call    memset@PLT
        mov     RA_SS(%rbp), %eax
        mov     %eax, RA_TM+0(%rbp)
        mov     RA_MI(%rbp), %eax
        mov     %eax, RA_TM+4(%rbp)
        mov     RA_HH(%rbp), %eax
        mov     %eax, RA_TM+8(%rbp)
        mov     RA_DAY(%rbp), %eax
        mov     %eax, RA_TM+12(%rbp)
        mov     RA_MON(%rbp), %eax
        mov     %eax, RA_TM+16(%rbp)
        mov     RA_YEAR(%rbp), %eax
        sub     $1900, %eax
        mov     %eax, RA_TM+20(%rbp)
        movl    $-1, RA_TM+32(%rbp)
        lea     RA_TM(%rbp), %rdi
        call    timegm@PLT
        cmp     $-1, %rax
        je      .Lra_bad
        mov     %rax, %rbx
        xor     %edi, %edi
        call    time@PLT
        sub     %rax, %rbx
        cmp     $0, %rbx
        jge     .Lra_diff
        xor     %ebx, %ebx
.Lra_diff:
        cvtsi2sdq %rbx, %xmm0
        movsd   %xmm0, (%r14)
        mov     $1, %eax
        EPILOGUE
        ret
.Lra_bad:
        xor     %eax, %eax
        EPILOGUE
        ret
ENDFUNC cepis__retry_after

FUNC cepis__write_cb
        PROLOGUE 24
        mov     %rdi, %r12
        mov     %rsi, %rax
        imul    %rdx, %rax
        mov     %rax, %r13
        mov     %rcx, %rdi
        mov     %r12, %rsi
        mov     %r13, %rdx
        call    cepis__buf_append
        test    %eax, %eax
        jnz     .Lwcb_fail
        mov     %r13, %rax
        EPILOGUE
        ret
.Lwcb_fail:
        xor     %eax, %eax
        EPILOGUE
        ret
ENDFUNC cepis__write_cb

FUNC cepis__header_cb
        PROLOGUE 24
        mov     %rdi, %r12
        mov     %rsi, %rax
        imul    %rdx, %rax
        mov     %rax, %r13
        mov     %rcx, %rbx
        cmp     $12, %r13
        jb      .Lhcb_out
        mov     %r12, %rdi
        lea     .Lhdr_retry_after(%rip), %rsi
        mov     $12, %rdx
        call    strncasecmp@PLT
        test    %eax, %eax
        jnz     .Lhcb_out
        lea     12(%r12), %r14
        mov     %r12, %r15
        add     %r13, %r15
.Lhcb_lead:
        cmp     %r15, %r14
        jae     .Lhcb_copy
        movzbl  (%r14), %eax
        cmp     $' ', %al
        je      .Lhcb_lead_adv
        cmp     $9, %al
        jne     .Lhcb_trail
.Lhcb_lead_adv:
        inc     %r14
        jmp     .Lhcb_lead
.Lhcb_trail:
        cmp     %r15, %r14
        jae     .Lhcb_copy
        movzbl  -1(%r15), %eax
        cmp     $' ', %al
        je      .Lhcb_trail_adv
        cmp     $9, %al
        je      .Lhcb_trail_adv
        cmp     $13, %al
        je      .Lhcb_trail_adv
        cmp     $10, %al
        je      .Lhcb_trail_adv
        jmp     .Lhcb_copy
.Lhcb_trail_adv:
        dec     %r15
        jmp     .Lhcb_trail
.Lhcb_copy:
        mov     %r15, %rdx
        sub     %r14, %rdx
        cmp     $127, %rdx
        jbe     .Lhcb_do
        mov     $127, %rdx
.Lhcb_do:
        mov     %rdx, LOCAL0(%rbp)
        mov     %rbx, %rdi
        mov     %r14, %rsi
        call    memcpy@PLT
        mov     LOCAL0(%rbp), %rdx
        movb    $0, (%rbx, %rdx, 1)
        movl    $1, HC_HAVE(%rbx)
.Lhcb_out:
        mov     %r13, %rax
        EPILOGUE
        ret
ENDFUNC cepis__header_cb

FUNC cepis__http_get
        PROLOGUE 520
        mov     %rdi, %rbx
        mov     %rsi, %r12
        mov     %edx, %eax
        mov     %eax, HG_ACC503(%rbp)
        mov     %rcx, HG_OUTST(%rbp)
        mov     %r8, %r13
        mov     %r9, %r14
        movl    $0, (%rcx)
        movl    $0, HG_LASTST(%rbp)
        movl    $0, HG_HASRA(%rbp)
        movl    $0, HG_CURLRC(%rbp)
        movq    $0, HG_RA(%rbp)
        movb    $0, HG_ERRBUF(%rbp)
        call    cepis_global_init
        movsd   CL_TOTAL_TIMEOUT(%rbx), %xmm0
        xorpd   %xmm1, %xmm1
        comisd  %xmm1, %xmm0
        jbe     .Lhg_nodl
        call    cepis__mono
        addsd   CL_TOTAL_TIMEOUT(%rbx), %xmm0
        movsd   %xmm0, HG_DEADLINE(%rbp)
        movl    $1, HG_HASDL(%rbp)
        jmp     .Lhg_start
.Lhg_nodl:
        movl    $0, HG_HASDL(%rbp)
.Lhg_start:
        movl    $1, HG_ATTEMPT(%rbp)
.Lhg_attempt:
        cmpl    $0, HG_HASDL(%rbp)
        je      .Lhg_go
        call    cepis__mono
        comisd  HG_DEADLINE(%rbp), %xmm0
        jae     .Lhg_exhausted
.Lhg_go:
        mov     %r13, %rdi
        call    cepis__buf_reset
        lea     HG_HDR(%rbp), %rdi
        movl    $0, HC_HAVE(%rdi)
        movb    $0, HC_VALUE(%rdi)
        movb    $0, HG_ERRBUF(%rbp)
        movq    $0, HG_SLIST(%rbp)
        call    curl_easy_init@PLT
        test    %rax, %rax
        jz      .Lhg_nomem
        mov     %rax, HG_HANDLE(%rbp)
        xor     %edi, %edi
        lea     .Lhdr_accept(%rip), %rsi
        call    curl_slist_append@PLT
        mov     %rax, HG_SLIST(%rbp)
        COPT_REG CURLOPT_URL, %r12
        lea     cepis__write_cb(%rip), %rax
        COPT_REG CURLOPT_WRITEFUNCTION, %rax
        COPT_REG CURLOPT_WRITEDATA, %r13
        lea     cepis__header_cb(%rip), %rax
        COPT_REG CURLOPT_HEADERFUNCTION, %rax
        lea     HG_HDR(%rbp), %rax
        COPT_REG CURLOPT_HEADERDATA, %rax
        mov     CL_USER_AGENT(%rbx), %rax
        COPT_REG CURLOPT_USERAGENT, %rax
        mov     HG_SLIST(%rbp), %rax
        COPT_REG CURLOPT_HTTPHEADER, %rax
        lea     .Lidentity(%rip), %rax
        COPT_REG CURLOPT_ACCEPT_ENCODING, %rax
        lea     HG_ERRBUF(%rbp), %rax
        COPT_REG CURLOPT_ERRORBUFFER, %rax
        COPT_LONG CURLOPT_NOSIGNAL, 1
        COPT_LONG CURLOPT_FOLLOWLOCATION, 0
        movsd   CL_TIMEOUT(%rbx), %xmm0
        mulsd   .Lthousand(%rip), %xmm0
        cvttsd2siq %xmm0, %rax
        cmp     $1, %rax
        jge     .Lhg_tmo
        mov     $1, %rax
.Lhg_tmo:
        mov     %rax, HG_TMPL(%rbp)
        COPT_REG CURLOPT_TIMEOUT_MS, %rax
        mov     HG_TMPL(%rbp), %rax
        COPT_REG CURLOPT_CONNECTTIMEOUT_MS, %rax
        mov     HG_HANDLE(%rbp), %rdi
        call    curl_easy_perform@PLT
        mov     %eax, HG_CURLRC(%rbp)
        test    %eax, %eax
        jnz     .Lhg_network
        mov     HG_HANDLE(%rbp), %rdi
        mov     $CURLINFO_RESPONSE_CODE, %esi
        lea     HG_CODE(%rbp), %rdx
        xor     %eax, %eax
        call    curl_easy_getinfo@PLT
        mov     HG_CODE(%rbp), %rax
        mov     %eax, %r15d
        cmp     $200, %r15d
        je      .Lhg_accept
        cmpl    $0, HG_ACC503(%rbp)
        je      .Lhg_notaccept
        cmp     $503, %r15d
        je      .Lhg_accept
.Lhg_notaccept:
        cmp     $429, %r15d
        je      .Lhg_retriable
        cmp     $500, %r15d
        je      .Lhg_retriable
        cmp     $502, %r15d
        je      .Lhg_retriable
        cmp     $503, %r15d
        je      .Lhg_retriable
        cmp     $504, %r15d
        je      .Lhg_retriable
.Lhg_accept:
        mov     HG_OUTST(%rbp), %rax
        mov     %r15d, (%rax)
        lea     HG_HANDLE(%rbp), %rdi
        lea     HG_SLIST(%rbp), %rsi
        call    cepis__curl_release
        xor     %eax, %eax
        EPILOGUE
        ret
.Lhg_retriable:
        mov     %r15d, HG_LASTST(%rbp)
        movl    $0, HG_HASRA(%rbp)
        lea     HG_HDR(%rbp), %rax
        cmpl    $0, HC_HAVE(%rax)
        je      .Lhg_after
        lea     HG_HDR(%rbp), %rdi
        lea     HG_RA(%rbp), %rsi
        call    cepis__retry_after
        mov     %eax, HG_HASRA(%rbp)
        jmp     .Lhg_after
.Lhg_network:
        movl    $0, HG_LASTST(%rbp)
        movl    $0, HG_HASRA(%rbp)
.Lhg_after:
        lea     HG_HANDLE(%rbp), %rdi
        lea     HG_SLIST(%rbp), %rsi
        call    cepis__curl_release
        mov     HG_ATTEMPT(%rbp), %eax
        cmp     CL_MAX_ATTEMPTS(%rbx), %eax
        jge     .Lhg_exhausted
        mov     %rbx, %rdi
        mov     HG_ATTEMPT(%rbp), %esi
        movsd   HG_RA(%rbp), %xmm0
        mov     HG_HASRA(%rbp), %edx
        call    cepis__backoff
        movsd   %xmm0, HG_WAIT(%rbp)
        cmpl    $0, HG_HASDL(%rbp)
        je      .Lhg_sleep
        call    cepis__mono
        movsd   HG_DEADLINE(%rbp), %xmm1
        subsd   %xmm0, %xmm1
        xorpd   %xmm2, %xmm2
        comisd  %xmm2, %xmm1
        jbe     .Lhg_exhausted
        movsd   HG_WAIT(%rbp), %xmm0
        minsd   %xmm1, %xmm0
        movsd   %xmm0, HG_WAIT(%rbp)
.Lhg_sleep:
        movsd   HG_WAIT(%rbp), %xmm0
        call    cepis__sleep
        incl    HG_ATTEMPT(%rbp)
        jmp     .Lhg_attempt
.Lhg_exhausted:
        mov     CL_MAX_ATTEMPTS(%rbx), %r15d
        cmpl    $429, HG_LASTST(%rbp)
        je      .Lhg_rate
        cmpl    $0, HG_LASTST(%rbp)
        jne     .Lhg_server
        test    %r14, %r14
        jz      .Lhg_ret_network
        mov     HG_CURLRC(%rbp), %edi
        call    curl_easy_strerror@PLT
        mov     %rax, %r9
        cmpb    $0, HG_ERRBUF(%rbp)
        je      .Lhg_net_msg
        lea     HG_ERRBUF(%rbp), %r9
.Lhg_net_msg:
        lea     E_MESSAGE(%r14), %rdi
        mov     $E_MSGLEN, %esi
        lea     .Lfmt_network(%rip), %rdx
        mov     %r12, %rcx
        mov     %r15d, %r8d
        xor     %eax, %eax
        call    snprintf@PLT
        movl    $CEPIS_ERR_NETWORK, E_STATUS(%r14)
.Lhg_ret_network:
        mov     $CEPIS_ERR_NETWORK, %eax
        EPILOGUE
        ret
.Lhg_server:
        test    %r14, %r14
        jz      .Lhg_ret_server
        mov     HG_LASTST(%rbp), %eax
        mov     %eax, E_HTTP_STATUS(%r14)
        movl    $CEPIS_ERR_SERVER, E_STATUS(%r14)
        lea     E_MESSAGE(%r14), %rdi
        mov     $E_MSGLEN, %esi
        lea     .Lfmt_server(%rip), %rdx
        mov     HG_LASTST(%rbp), %ecx
        mov     %r12, %r8
        mov     %r15d, %r9d
        xor     %eax, %eax
        call    snprintf@PLT
.Lhg_ret_server:
        mov     $CEPIS_ERR_SERVER, %eax
        EPILOGUE
        ret
.Lhg_rate:
        test    %r14, %r14
        jz      .Lhg_ret_rate
        movl    $429, E_HTTP_STATUS(%r14)
        movl    $CEPIS_ERR_RATE_LIMITED, E_STATUS(%r14)
        cmpl    $0, HG_HASRA(%rbp)
        je      .Lhg_rate_msg
        movsd   HG_RA(%rbp), %xmm0
        movsd   %xmm0, E_RETRY_AFTER(%r14)
.Lhg_rate_msg:
        lea     E_MESSAGE(%r14), %rdi
        mov     $E_MSGLEN, %esi
        lea     .Lfmt_rate(%rip), %rdx
        mov     %r12, %rcx
        mov     %r15d, %r8d
        xor     %eax, %eax
        call    snprintf@PLT
.Lhg_ret_rate:
        mov     $CEPIS_ERR_RATE_LIMITED, %eax
        EPILOGUE
        ret
.Lhg_nomem:
        mov     $CEPIS_ERR_NOMEM, %eax
        EPILOGUE
        ret
ENDFUNC cepis__http_get

FUNC cepis__curl_release
        PROLOGUE 8
        mov     %rdi, %rbx
        mov     %rsi, %r12
        mov     (%rbx), %rdi
        test    %rdi, %rdi
        jz      .Lrel_slist
        call    curl_easy_cleanup@PLT
        movq    $0, (%rbx)
.Lrel_slist:
        mov     (%r12), %rdi
        test    %rdi, %rdi
        jz      .Lrel_out
        call    curl_slist_free_all@PLT
        movq    $0, (%r12)
.Lrel_out:
        EPILOGUE
        ret
ENDFUNC cepis__curl_release

        .section .rodata
        .p2align 3
.Lnano:         .double 1e-9
.Lgiga:         .double 1e9
.Ltwo53:        .double 1.1102230246251565e-16
.Lthousand:     .double 1000.0
.Lhdr_retry_after:  .asciz "retry-after:"
.Lhdr_accept:   .asciz "Accept: application/json, text/csv;q=0.9, */*;q=0.1"
.Lidentity:     .asciz "identity"
.Lfmt_network:  .asciz "could not reach %s after %d attempt(s): %s"
.Lfmt_server:   .asciz "the API answered %d at %s after %d attempt(s)"
.Lfmt_rate:     .asciz "rate limit reached at %s after %d attempt(s)"
.Ls_jan:        .asciz "Jan"
.Ls_feb:        .asciz "Feb"
.Ls_mar:        .asciz "Mar"
.Ls_apr:        .asciz "Apr"
.Ls_may:        .asciz "May"
.Ls_jun:        .asciz "Jun"
.Ls_jul:        .asciz "Jul"
.Ls_aug:        .asciz "Aug"
.Ls_sep:        .asciz "Sep"
.Ls_oct:        .asciz "Oct"
.Ls_nov:        .asciz "Nov"
.Ls_dec:        .asciz "Dec"
        .section .data.rel.ro,"aw",@progbits
        .p2align 3
cepis__months:
        .quad .Ls_jan, .Ls_feb, .Ls_mar, .Ls_apr, .Ls_may, .Ls_jun
        .quad .Ls_jul, .Ls_aug, .Ls_sep, .Ls_oct, .Ls_nov, .Ls_dec

        .data
        .p2align 2
cepis__curl_state:
        .long   0

        .section .note.GNU-stack,"",@progbits