-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhttpterm.c
5197 lines (4506 loc) · 144 KB
/
httpterm.c
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
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
852
853
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
938
939
940
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
/*
* HTTPTerm : HTTP termination for benchmarks.
* Initial code extracted from HAProxy, copyright below.
*/
#ifdef ENABLE_ACCEPT4
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#if defined(__dietlibc__)
#include <strings.h>
#endif
#if defined(ENABLE_POLL)
#include <sys/poll.h>
#endif
#if defined(ENABLE_EPOLL)
#if !defined(USE_MY_EPOLL)
#include <sys/epoll.h>
#else
#include "include/epoll.h"
#endif
#endif
#include "include/mini-clist.h"
#ifndef HTTPTERM_VERSION
#define HTTPTERM_VERSION "1.7.10"
#endif
#ifndef HTTPTERM_DATE
#define HTTPTERM_DATE "2024/10/10"
#endif
#ifndef SHUT_RD
#define SHUT_RD 0
#endif
#ifndef SHUT_WR
#define SHUT_WR 1
#endif
#ifndef MSG_MORE
#define MSG_MORE 0
#endif
#ifndef SOCK_NONBLOCK
#define SOCK_NONBLOCK 0x800
#endif
/* it seems optimal to come back quickly into the epoll loop */
#define MAX_EVENTS 64
/* We'll try to enable SO_REUSEPORT on Linux 2.4 and 2.6 if not defined.
* There are two families of values depending on the architecture. Those
* are at least valid on Linux 2.4 and 2.6, reason why we'll rely on the
* NETFILTER define.
*/
#if !defined(SO_REUSEPORT) && defined(__linux__)
#if (SO_REUSEADDR == 2)
#define SO_REUSEPORT 15
#elif (SO_REUSEADDR == 0x0004)
#define SO_REUSEPORT 0x0200
#endif /* SO_REUSEADDR */
#endif /* SO_REUSEPORT */
#ifdef ENABLE_SPLICE
#ifndef F_SETPIPE_SZ
#define F_SETPIPE_SZ (1024 + 7)
#endif
#ifndef __NR_splice
#if defined(__x86_64__)
#define __NR_splice 275
#define __NR_tee 276
#define __NR_vmsplice 278
#elif defined (__i386__)
#define __NR_splice 313
#define __NR_tee 315
#define __NR_vmsplice 316
#elif defined (__arm__)
#define __NR_splice 340
#define __NR_tee 342
#define __NR_vmsplice 343
#elif defined (__mips__)
#define __NR_splice 304
#define __NR_tee 306
#define __NR_vmsplice 307
#endif /* $arch */
#endif /* __NR_splice */
#ifndef SPLICE_F_NONBLOCK
#define SPLICE_F_MOVE 1
#define SPLICE_F_NONBLOCK 2
#define SPLICE_F_MORE 4
#ifndef _syscall4
#define _syscall4(tr, nr, t1, n1, t2, n2, t3, n3, t4, n4) \
inline tr nr(t1 n1, t2 n2, t3 n3, t4 n4) { \
return syscall(__NR_##nr, n1, n2, n3, n4); \
}
#endif
#ifndef _syscall6
#define _syscall6(tr, nr, t1, n1, t2, n2, t3, n3, t4, n4, t5, n5, t6, n6) \
inline tr nr(t1 n1, t2 n2, t3 n3, t4 n4, t5 n5, t6 n6) { \
return syscall(__NR_##nr, n1, n2, n3, n4, n5, n6); \
}
#endif
static _syscall6(int, splice, int, fdin, loff_t *, off_in, int, fdout, loff_t *, off_out, size_t, len, unsigned long, flags);
static _syscall4(long, vmsplice, int, fd, const struct iovec *, iov, unsigned long, nr_segs, unsigned int, flags);
static _syscall4(long, tee, int, fd_in, int, fd_out, size_t, len, unsigned int, flags);
#endif
#endif
/*
* BUFSIZE defines the size of a read and write buffer. It is the maximum
* amount of bytes which can be stored by the proxy for each session. However,
* when reading HTTP headers, the proxy needs some spare space to add or rewrite
* headers if needed. The size of this spare is defined with MAXREWRITE. So it
* is not possible to process headers longer than BUFSIZE-MAXREWRITE bytes. By
* default, BUFSIZE=16384 bytes and MAXREWRITE=BUFSIZE/2, so the maximum length
* of headers accepted is 8192 bytes, which is in line with Apache's limits.
*/
#ifndef BUFSIZE
#define BUFSIZE 4096
#endif
/* no splicing below one page */
#define MIN_SPLICE 4096
// reserved buffer space for header rewriting. Must not be zero
// otherwise some requests don't get parsed !
#ifndef MAXREWRITE
#define MAXREWRITE 1
#endif
#ifndef RESPSIZE
#define RESPSIZE 65536
#endif
// max # args on a configuration line
#define MAX_LINE_ARGS 40
/* Default connections limit.
*
* A system limit can be enforced at build time in order to avoid using httpterm
* beyond reasonable system limits. For this, just define SYSTEM_MAXCONN to the
* absolute limit accepted by the system. If the configuration specifies a
* higher value, it will be capped to SYSTEM_MAXCONN and a warning will be
* emitted. The only way to override this limit will be to set it via the
* command-line '-n' argument.
*/
#ifndef SYSTEM_MAXCONN
#define DEFAULT_MAXCONN 2000
#else
#define DEFAULT_MAXCONN SYSTEM_MAXCONN
#endif
/* per-proxy maxconn. Doesn't fix any allocation size, only an accept limit */
#ifndef DEFAULT_MAXPCONN
#define DEFAULT_MAXPCONN (1<<30)
#endif
/* how many bits are needed to code the size of an int (eg: 32bits -> 5) */
#define INTBITS 5
/* this reduces the number of calls to select() by choosing appropriate
* sheduler precision in milliseconds. It should be near the minimum
* time that is needed by select() to collect all events. All timeouts
* are rounded up by adding this value prior to pass it to select().
*/
#define SCHEDULER_RESOLUTION 9
#define TIME_ETERNITY -1
/* returns the lowest delay amongst <old> and <new>, and respects TIME_ETERNITY */
#define MINTIME(old, new) (((new)<0)?(old):(((old)<0||(new)<(old))?(new):(old)))
#define SETNOW(a) (*a=now)
/****** string-specific macros and functions ******/
/* if a > max, then bound <a> to <max>. The macro returns the new <a> */
#define UBOUND(a, max) ({ typeof(a) b = (max); if ((a) > b) (a) = b; (a); })
/* if a < min, then bound <a> to <min>. The macro returns the new <a> */
#define LBOUND(a, min) ({ typeof(a) b = (min); if ((a) < b) (a) = b; (a); })
/* returns 1 only if only zero or one bit is set in X, which means that X is a
* power of 2, and 0 otherwise */
#define POWEROF2(x) (((x) & ((x)-1)) == 0)
/* Redefine fd_isset, fd_set, and fd_clr to work around bogus glibc
* version which purposely break it past 1023 while older versions work
* pretty fine just like any other operating system.
*/
static inline int my_fd_isset(int fd, const fd_set *set)
{
return (((const unsigned int *)set)[fd >> 5] >> (fd & 31)) & 1;
}
static inline void my_fd_set(int fd, fd_set *set)
{
((unsigned int *)set)[fd >> 5] |= 1 << (fd & 31);
}
static inline void my_fd_clr(int fd, fd_set *set)
{
((unsigned int *)set)[fd >> 5] &= ~(1 << (fd & 31));
}
static inline unsigned int read_u32(const void *p)
{
const union { unsigned int u32; } __attribute__((packed))*u = p;
return u->u32;
}
/*
* unsigned long long ASCII representation
* return the number of chars written, not counting the trailing '\0' which
* is added. Returns zero if not enough space.
*/
int ulltoa(unsigned long long n, char *dst, size_t size)
{
int i = 0;
char *res;
switch(n) {
case 1ULL ... 9ULL:
i = 0;
break;
case 10ULL ... 99ULL:
i = 1;
break;
case 100ULL ... 999ULL:
i = 2;
break;
case 1000ULL ... 9999ULL:
i = 3;
break;
case 10000ULL ... 99999ULL:
i = 4;
break;
case 100000ULL ... 999999ULL:
i = 5;
break;
case 1000000ULL ... 9999999ULL:
i = 6;
break;
case 10000000ULL ... 99999999ULL:
i = 7;
break;
case 100000000ULL ... 999999999ULL:
i = 8;
break;
case 1000000000ULL ... 9999999999ULL:
i = 9;
break;
case 10000000000ULL ... 99999999999ULL:
i = 10;
break;
case 100000000000ULL ... 999999999999ULL:
i = 11;
break;
case 1000000000000ULL ... 9999999999999ULL:
i = 12;
break;
case 10000000000000ULL ... 99999999999999ULL:
i = 13;
break;
case 100000000000000ULL ... 999999999999999ULL:
i = 14;
break;
case 1000000000000000ULL ... 9999999999999999ULL:
i = 15;
break;
case 10000000000000000ULL ... 99999999999999999ULL:
i = 16;
break;
case 100000000000000000ULL ... 999999999999999999ULL:
i = 17;
break;
case 1000000000000000000ULL ... 9999999999999999999ULL:
i = 18;
break;
case 10000000000000000000ULL ... ULLONG_MAX:
i = 19;
break;
}
if (i + 2 > size) // (i + 1) + '\0'
return 0; // too long
res = dst + i + 1;
*res = '\0';
for (; i >= 0; i--) {
dst[i] = n % 10ULL + '0';
n /= 10ULL;
}
return res - dst;
}
/* same with hex */
int ulltox(unsigned long long n, char *dst, size_t size)
{
int i = 0;
char *res;
switch(n) {
case 0x1ULL ... 0xFULL:
i = 0;
break;
case 0x10ULL ... 0xFFULL:
i = 1;
break;
case 0x100ULL ... 0xFFFULL:
i = 2;
break;
case 0x1000ULL ... 0xFFFFULL:
i = 3;
break;
case 0x10000ULL ... 0xFFFFFULL:
i = 4;
break;
case 0x100000ULL ... 0xFFFFFFULL:
i = 5;
break;
case 0x1000000ULL ... 0xFFFFFFFULL:
i = 6;
break;
case 0x10000000ULL ... 0xFFFFFFFFULL:
i = 7;
break;
case 0x100000000ULL ... 0xFFFFFFFFFULL:
i = 8;
break;
case 0x1000000000ULL ... 0xFFFFFFFFFFULL:
i = 9;
break;
case 0x10000000000ULL ... 0xFFFFFFFFFFFULL:
i = 10;
break;
case 0x100000000000ULL ... 0xFFFFFFFFFFFFULL:
i = 11;
break;
case 0x1000000000000ULL ... 0xFFFFFFFFFFFFFULL:
i = 12;
break;
case 0x10000000000000ULL ... 0xFFFFFFFFFFFFFFULL:
i = 13;
break;
case 0x100000000000000ULL ... 0xFFFFFFFFFFFFFFFULL:
i = 14;
break;
case 0x1000000000000000ULL ... 0xFFFFFFFFFFFFFFFFULL:
i = 15;
break;
}
if (i + 2 > size) // (i + 1) + '\0'
return 0; // too long
res = dst + i + 1;
*res = '\0';
for (; i >= 0; i--) {
dst[i] = "0123456789ABCDEF"[n & 15];
n >>= 4;
}
return res - dst;
}
/*
* copies at most <size-1> chars from <src> to <dst>. Last char is always
* set to 0, unless <size> is 0. The number of chars copied is returned
* (excluding the terminating zero).
* This code has been optimized for size and speed : on x86, it's 45 bytes
* long, uses only registers, and consumes only 4 cycles per char.
*/
int strlcpy2(char *dst, const char *src, int size) {
char *orig = dst;
if (size) {
while (--size && (*dst = *src)) {
src++; dst++;
}
*dst = 0;
}
return dst - orig;
}
/*
* Returns a pointer to an area of <__len> bytes taken from the pool <pool> or
* dynamically allocated. In the first case, <__pool> is updated to point to
* the next element in the list.
*/
#define pool_alloc_from(__pool, __len) ({ \
void *__p; \
if ((__p = (__pool)) == NULL) \
__p = malloc(((__len) >= sizeof (void *)) ? (__len) : sizeof(void *)); \
else { \
__pool = *(void **)(__pool); \
} \
__p; \
})
/*
* Puts a memory area back to the corresponding pool.
* Items are chained directly through a pointer that
* is written in the beginning of the memory area, so
* there's no need for any carrier cell. This implies
* that each memory area is at least as big as one
* pointer.
*/
#define pool_free_to(__pool, __ptr) ({ \
*(void **)(__ptr) = (void *)(__pool); \
__pool = (void *)(__ptr); \
})
#define MEM_OPTIM
#ifdef MEM_OPTIM
/*
* Returns a pointer to type <type> taken from the
* pool <pool_type> or dynamically allocated. In the
* first case, <pool_type> is updated to point to the
* next element in the list.
*/
#define pool_alloc(type) ({ \
void *__p; \
if ((__p = pool_##type) == NULL) \
__p = malloc(sizeof_##type); \
else { \
pool_##type = *(void **)pool_##type; \
} \
__p; \
})
/*
* Puts a memory area back to the corresponding pool.
* Items are chained directly through a pointer that
* is written in the beginning of the memory area, so
* there's no need for any carrier cell. This implies
* that each memory area is at least as big as one
* pointer.
*/
#define pool_free(type, ptr) ({ \
*(void **)ptr = (void *)pool_##type; \
pool_##type = (void *)ptr; \
})
#else
#define pool_alloc(type) (calloc(1,sizeof_##type));
#define pool_free(type, ptr) (free(ptr));
#endif /* MEM_OPTIM */
#define sizeof_task sizeof(struct task)
#define sizeof_session sizeof(struct session)
#define sizeof_buffer sizeof(struct buffer)
#define sizeof_fdtab sizeof(struct fdtab)
/* different possible states for the sockets */
#define FD_STCLOSE 0
#define FD_STLISTEN 1
#define FD_STCONN 2
#define FD_STREADY 3
#define FD_STERROR 4
/* values for task->state */
#define TASK_IDLE 0
#define TASK_RUNNING 1
/* values for proxy->state */
#define PR_STNEW 0
#define PR_STIDLE 1
#define PR_STRUN 2
#define PR_STSTOPPED 3
#define PR_STPAUSED 4
/* possible actions for the *poll() loops */
#define POLL_LOOP_ACTION_INIT 0
#define POLL_LOOP_ACTION_RUN 1
#define POLL_LOOP_ACTION_CLEAN 2
/* poll mechanisms available */
#define POLL_USE_SELECT (1<<0)
#define POLL_USE_POLL (1<<1)
#define POLL_USE_EPOLL (1<<2)
/* bits for proxy->options */
#define PR_O_HTTP_CLOSE 0x00010000 /* force 'connection: close' in both directions */
#define PR_O_CHK_CACHE 0x00020000 /* require examination of cacheability of the 'set-cookie' field */
#define PR_O_TCP_CLI_KA 0x00040000 /* enable TCP keep-alive on client-side sessions */
#define PR_O_FORCE_CLO 0x00200000 /* enforce the connection close immediately after server response */
/* different possible states for the client side */
#define CL_STHEADERS 0
#define CL_STBODY 1
#define CL_STWAIT 2
#define CL_STDATA 3
#define CL_STPAUSE 4
#define CL_STCLOSE 5
/* possible socket states */
#define SKST_SCR 1 /* client socket shut on the read direction */
#define SKST_SCW 2 /* client socket shut on the write direction */
#define SKST_SSR 4 /* server socket shut on the read direction */
#define SKST_SSW 8 /* server socket shut on the write direction */
/* result of an I/O event */
#define RES_SILENT 0 /* didn't happen */
#define RES_DATA 1 /* data were sent or received */
#define RES_NULL 2 /* result is 0 (read == 0), or connect without need for writing */
#define RES_ERROR 3 /* result -1 or error on the socket (eg: connect()) */
/* modes of operation (global.mode) */
#define MODE_DEBUG 1
#define MODE_DAEMON 8
#define MODE_QUIET 16
#define MODE_CHECK 32
#define MODE_VERBOSE 64
#define MODE_STARTING 128
#define MODE_FOREGROUND 256
/* configuration sections */
#define CFG_NONE 0
#define CFG_GLOBAL 1
#define CFG_LISTEN 2
#define ERR_NONE 0 /* no error */
#define ERR_RETRYABLE 1 /* retryable error, may be cumulated */
#define ERR_FATAL 2 /* fatal error, may be cumulated */
#define METH_HEAD 0
#define METH_GET 1
#define METH_POST 2
static char chunk_pattern[] = "1\r\n";
#define CHUNK_LEN (sizeof(chunk_pattern)-1)
/*********************************************************************/
#define LIST_HEAD(a) ((void *)(&(a)))
/*********************************************************************/
struct buffer {
unsigned int l; /* data length */
char *r, *w, *h, *lr; /* read ptr, write ptr, last header ptr, last read */
unsigned long long total; /* total data read */
char *data;
char data_buf[BUFSIZE];
};
struct server {
struct server *next;
char *id; /* just for identification */
unsigned char uweight, eweight; /* user-specified weight-1, and effective weight-1 */
unsigned int wscore; /* weight score, used during srv map computation */
int cur_sess; /* number of currently active sessions (including syn_sent) */
unsigned int cum_sess; /* cumulated number of sessions really sent to this server */
struct proxy *proxy; /* the proxy this server belongs to */
int resp_time; /* expected response time in milliseconds */
int resp_code; /* expected response code */
int resp_size; /* expected response size in bytes */
int resp_cache; /* expected cacheability (0=no, 1=yes) */
char *resp_data; /* response data if coming from another file */
};
/* The base for all tasks */
struct task {
struct task *next, *prev; /* chaining ... */
struct task *rqnext; /* chaining in run queue ... */
struct task *wq; /* the wait queue this task is in */
int state; /* task state : IDLE or RUNNING */
struct timeval expire; /* next expiration time for this task, use only for fast sorting */
int (*process)(struct task *t); /* the function which processes the task */
void *context; /* the task's context */
};
/* WARNING: if new fields are added, they must be initialized in event_accept() */
struct session {
struct task *task; /* the task associated with this session */
/* application specific below */
struct timeval crexpire; /* expiration date for a client read */
struct timeval cwexpire; /* expiration date for a client write */
struct timeval cnexpire; /* expiration date for a connect */
char res_cr, res_cw; /* results of some events */
struct proxy *proxy; /* the proxy this socket belongs to */
int cli_fd; /* the client side fd */
int cli_state; /* state of the client side */
int sock_st; /* socket states : SKST_S[CS][RW] */
int ka; /* .0: keep-alive .1: forced .2: http/1.1, .3: was_reused */
struct buffer *req; /* request buffer */
struct buffer *rep; /* response buffer */
unsigned long long to_write; /* #of response data bytes to write after headers */
struct sockaddr_storage cli_addr; /* the client address */
struct server *srv; /* the server being used */
struct {
struct timeval tv_accept; /* date of the accept() (beginning of the session) */
long t_request; /* delay before the end of the request arrives, -1 if never occurs */
long t_queue; /* delay before the session gets out of the connect queue, -1 if never occurs */
} logs;
unsigned int uniq_id; /* unique ID used for the traces */
unsigned int etag; /* e-tag to produce is not zero */
char *uri; /* the requested URI within the buffer */
signed long long req_size; /* values passed in the URI to override the server's */
signed long long req_body; /* remaining body to be consumed from the request */
signed long long req_maxbody; /* max body to be read before starting to respond, -1=all */
signed long long req_bodylen; /* <0:advertise CL; 0:don't; >0: advertise this bodylen */
int req_code;
int req_cache, req_time, ka_time, pause_time;
int req_chunked;
int req_nosplice;
int req_random;
int req_pieces;
int req_meth;
int req_verbose;
};
struct listener {
int fd; /* the listen socket */
struct sockaddr_storage addr; /* the address we listen to */
struct listener *next; /* next address or NULL */
};
struct proxy {
struct listener *listen; /* the listen addresses and sockets */
int state; /* proxy state */
struct server *srv; /* known servers */
int srv_act; /* # of running servers */
int tot_wact; /* total weights of active servers */
struct server **srv_map; /* the server map used to apply weights */
int srv_map_sz; /* the size of the effective server map */
int srv_rr_idx; /* next server to be elected in round robin mode */
int clitimeout; /* client I/O timeout (in milliseconds) */
char *id; /* proxy id */
int nbconn; /* # of active sessions */
unsigned int cum_conn; /* cumulated number of processed sessions */
int maxconn; /* max # of active sessions */
int options; /* PR_O_* ... */
struct proxy *next;
struct timeval stop_time; /* date to stop listening, when stopping != 0 */
int grace; /* grace time after stop request */
struct {
char *msg400; /* message for error 400 */
int len400; /* message length for error 400 */
char *msg403; /* message for error 403 */
int len403; /* message length for error 403 */
char *msg408; /* message for error 408 */
int len408; /* message length for error 408 */
char *msg500; /* message for error 500 */
int len500; /* message length for error 500 */
char *msg502; /* message for error 502 */
int len502; /* message length for error 502 */
char *msg503; /* message for error 503 */
int len503; /* message length for error 503 */
char *msg504; /* message for error 504 */
int len504; /* message length for error 504 */
} errmsg;
};
/* info about one given fd */
struct fdtab {
int (*read)(int fd); /* read function */
int (*write)(int fd); /* write function */
struct task *owner; /* the session (or proxy) associated with this fd */
int state; /* the state of this fd */
};
struct pipe {
int pipe[2];
int start_alignment;
int stop_alignment;
int usage;
};
/*********************************************************************/
int cfg_maxconn = 0; /* # of simultaneous connections, (-n) */
int cfg_sndbuf = 0; /* socket send buffer size in bytes (-B) */
char *cfg_cfgfile = NULL; /* configuration file */
char *progname = NULL; /* program name */
int pid; /* current process id */
char *cmdline_listen = NULL; /* command-line listen address (ip:port) */
int master_pipe[2], chunked_pipe[CHUNK_LEN][2], slave_pipe[2]; /* pipes used by splice() */
int slave_pipe_usage = 0;
struct pipe chunk_slave_pipe[CHUNK_LEN];
int pipesize = RESPSIZE;
int ignore_err;
/* send zeroes instead of aligned data */
#define GFLAGS_SEND_ZERO 0x1
/* don't use splice */
#define GFLAGS_NO_SPLICE 0x2
/* global options */
static struct {
int uid;
int gid;
int nbproc;
int maxconn;
int maxsock; /* max # of sockets */
int rlimit_nofile; /* default ulimit-n value : 0=unset */
int rlimit_memmax; /* default ulimit-d in megs value : 0=unset */
int mode;
char *chroot;
char *pidfile;
unsigned int flags; /* GFLAGS_* */
} global;
/*********************************************************************/
fd_set *StaticReadEvent,
*StaticWriteEvent;
int cfg_polling_mechanism = 0; /* POLL_USE_{SELECT|POLL|EPOLL} */
void **pool_session = NULL,
**pool_buffer = NULL,
**pool_fdtab = NULL,
**pool_task = NULL;
struct proxy *proxy = NULL; /* list of all existing proxies */
struct fdtab *fdtab = NULL; /* array of all the file descriptors */
struct task *rq = NULL; /* global run queue */
struct task wait_queue[2] = { /* global wait queue */
{
.prev = LIST_HEAD(wait_queue[0]), /* expirable tasks */
.next = LIST_HEAD(wait_queue[0]),
},
{
.prev = LIST_HEAD(wait_queue[1]), /* non-expirable tasks */
.next = LIST_HEAD(wait_queue[1]),
},
};
static int totalconn = 0; /* total # of terminated sessions */
static int actconn = 0; /* # of active sessions */
static int maxfd = 0; /* # of the highest fd + 1 */
static int listeners = 0; /* # of listeners */
static struct timeval now = {0,0}; /* the current date at any moment */
static struct proxy defproxy; /* fake proxy used to assign default values on all instances */
#if defined(ENABLE_EPOLL)
/* FIXME: this is dirty, but at the moment, there's no other solution to remove
* the old FDs from outside the loop. Perhaps we should export a global 'poll'
* structure with pointers to functions such as init_fd() and close_fd(), plus
* a private structure with several pointers to places such as below.
*/
static fd_set *PrevReadEvent = NULL, *PrevWriteEvent = NULL;
#endif
/* this is used to drain data, and as a temporary buffer for sprintf()... */
static char trash[BUFSIZE];
static char common_response[RESPSIZE];
static char common_chunk_resp[RESPSIZE];
static char *random_resp;
static int random_resp_len = RESPSIZE;
const int zero = 0;
const int one = 1;
#define MAX_HOSTNAME_LEN 32
static char hostname[MAX_HOSTNAME_LEN] = "";
const char *HTTP_302 =
"HTTP/1.0 302 Found\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Location: "; /* not terminated since it will be concatenated with the URL */
/* same as 302 except that the browser MUST retry with the GET method */
const char *HTTP_303 =
"HTTP/1.0 303 See Other\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Location: "; /* not terminated since it will be concatenated with the URL */
const char *HTTP_400 =
"HTTP/1.0 400 Bad request\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>400 Bad request</h1>\nYour browser sent an invalid request.\n</body></html>\n";
const char *HTTP_403 =
"HTTP/1.0 403 Forbidden\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>403 Forbidden</h1>\nRequest forbidden by administrative rules.\n</body></html>\n";
const char *HTTP_408 =
"HTTP/1.0 408 Request Time-out\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>408 Request Time-out</h1>\nYour browser didn't send a complete request in time.\n</body></html>\n";
const char *HTTP_500 =
"HTTP/1.0 500 Server Error\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>500 Server Error</h1>\nAn internal server error occured.\n</body></html>\n";
const char *HTTP_502 =
"HTTP/1.0 502 Bad Gateway\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>502 Bad Gateway</h1>\nThe server returned an invalid or incomplete response.\n</body></html>\n";
const char *HTTP_503 =
"HTTP/1.0 503 Service Unavailable\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>503 Service Unavailable</h1>\nNo server is available to handle this request.\n</body></html>\n";
const char *HTTP_504 =
"HTTP/1.0 504 Gateway Time-out\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"\r\n"
"<html><body><h1>504 Gateway Time-out</h1>\nThe server didn't respond in time.\n</body></html>\n";
const char *HTTP_HELP =
"HTTP/1.0 200\r\n"
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-type: text/plain\r\n"
"\r\n"
"HTTPTerm-" HTTPTERM_VERSION " - " HTTPTERM_DATE "\n"
"All integer argument values are in the form [digits]*[kmgr] (r=random(0..1)).\n"
"The following arguments are supported to override the default objects :\n"
" - /? or /?h show this help.\n"
" - /?s=<size> return <size> bytes.\n"
" E.g. /?s=20k\n"
" - /?r=<retcode> present <retcode> as the HTTP return code.\n"
" E.g. /?r=404\n"
" - /?c=<cache> set the return as not cacheable if <1.\n"
" E.g. /?c=0\n"
" - /?C=<close> force the response to use close if >0.\n"
" E.g. /?C=1\n"
" - /?K=<keep-alive> force the response to use keep-alive if >0.\n"
" E.g. /?K=1\n"
" - /?b=<bodylen> <0: send content-length; 0: don't; >0: send this value.\n"
" E.g. /?b=0 /?b=100k\n"
" - /?B=<maxbody> read no more than this amount of body before responding.\n"
" E.g. /?B=10000\n"
" - /?t=<time> wait <time> milliseconds before responding.\n"
" E.g. /?t=500 , /?t=10k for 10.24s , /?t=5000r for 0..5s\n"
" - /?v=<verbose> emit X-req and X-rsp headers with extra info if non-zero.\n"
" E.g. /?V=1\n"
" - /?w=<time> use keep-alive time <time> milliseconds.\n"
" E.g. /?w=1000\n"
" - /?P=<time> pause <time> milliseconds after responding (may delay close).\n"
" - /?e=<enable> Enable sending of the Etag header if >0 (for use with caches).\n"
" - /?k=<enable> Enable transfer encoding chunked with 1 byte chunks if >0.\n"
" - /?S=<enable> Disable use of splice() to send data if <1.\n"
" - /?R=<enable> Enable sending random data if >0 (disables splicing).\n"
" - /?p=<size> Make pieces no larger than this if >0 (disables splicing).\n"
"\n"
"Note that those arguments may be cumulated on one line separated by a set of\n"
"delimitors among [&?,;/] :\n"
" - GET /?s=20k&c=1&t=700&K=30r HTTP/1.0\n"
" - GET /?r=500?s=0?c=0?t=1000 HTTP/1.0\n"
"\n";
/*********************************************************************/
/* function prototypes *********************************************/
/*********************************************************************/
int event_accept(int fd);
int event_cli_read(int fd);
int event_cli_write(int fd);
int process_session(struct task *t);
/*********************************************************************/
/* general purpose functions ***************************************/
/*********************************************************************/
void display_version() {
printf("HTTPTerm version " HTTPTERM_VERSION " " HTTPTERM_DATE"\n");
printf("Copyright 2000-2024 Willy Tarreau <[email protected]>\n\n");
}
/*
* This function prints the command line usage and exits
*/
void usage(char *name) {
display_version();
fprintf(stderr,
"Usage : %s [-f <cfgfile>] [ -vdVD ] [ -n <maxconn> ] [ -B <size> ]\n"
" [ -p <pidfile> ] [ -m <max megs> ] [ -P <pipesize in kB> ]\n"
" -v displays version\n"
" -d enters debug mode ; -db only disables background mode.\n"
" -V enters verbose mode (disables quiet mode)\n"
" -D goes daemon ; implies -q\n"
" -q quiet mode : don't display messages\n"
" -c check mode : only check config file and exit\n"
" -n sets the maximum total # of connections (def: ulimit -Hn)\n"
" -m limits the usable amount of memory (in MB)\n"
" -p writes pids of all children to this file\n"
" -B limits the kernel-side socket sndbuf size (in bytes)\n"
#if defined(ENABLE_EPOLL)
" -de disables epoll() usage even when available\n"
#endif
#if defined(ENABLE_POLL)
" -dp disables poll() usage even when available\n"
#endif
#if defined(ENABLE_SPLICE)
" -dS disables splice() usage even when available\n"
" -P sets splice pipe size in kB\n"
#endif
" -L [<ip>]:<port> adds a listener with one server\n"
" -sf/-st [pid ]* finishes/terminates old pids. Must be last arguments.\n"
" At least one of -f or -L is required.\n"
"\n",
name);
exit(1);
}
/*
* Displays the message on stderr with the date and pid. Overrides the quiet
* mode during startup.
*/
void Alert(char *fmt, ...) {
va_list argp;
struct timeval tv;
struct tm *tm;
if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
va_start(argp, fmt);
gettimeofday(&tv, NULL);
tm=localtime(&tv.tv_sec);
fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
vfprintf(stderr, fmt, argp);
fflush(stderr);
va_end(argp);
}
}
/*