http_server.c revision 5650ee6c
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * http_server.c -- A simple HTTP/QUIC server
4 *
5 * It serves up files from the filesystem.
6 */
7
8#include <assert.h>
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/queue.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <inttypes.h>
17
18#ifndef WIN32
19#include <netinet/in.h>
20#include <unistd.h>
21#include <fcntl.h>
22#else
23#include "vc_compat.h"
24#include "getopt.h"
25#endif
26
27#include <event2/event.h>
28
29#include <openssl/md5.h>
30
31#include "lsquic.h"
32#include "../src/liblsquic/lsquic_hash.h"
33#include "lsxpack_header.h"
34#include "test_config.h"
35#include "test_common.h"
36#include "test_cert.h"
37#include "prog.h"
38
39#if HAVE_REGEX
40#ifndef WIN32
41#include <regex.h>
42#else
43#include <pcreposix.h>
44#endif
45#endif
46
47#include "../src/liblsquic/lsquic_logger.h"
48#include "../src/liblsquic/lsquic_int_types.h"
49#include "../src/liblsquic/lsquic_util.h"
50
51#if HAVE_REGEX
52static const char on_being_idle[] =
53"ON BEING IDLE.\n"
54"\n"
55"Now, this is a subject on which I flatter myself I really am _au fait_.\n"
56"The gentleman who, when I was young, bathed me at wisdom's font for nine\n"
57"guineas a term--no extras--used to say he never knew a boy who could\n"
58"do less work in more time; and I remember my poor grandmother once\n"
59"incidentally observing, in the course of an instruction upon the use\n"
60"of the Prayer-book, that it was highly improbable that I should ever do\n"
61"much that I ought not to do, but that she felt convinced beyond a doubt\n"
62"that I should leave undone pretty well everything that I ought to do.\n"
63"\n"
64"I am afraid I have somewhat belied half the dear old lady's prophecy.\n"
65"Heaven help me! I have done a good many things that I ought not to have\n"
66"done, in spite of my laziness. But I have fully confirmed the accuracy\n"
67"of her judgment so far as neglecting much that I ought not to have\n"
68"neglected is concerned. Idling always has been my strong point. I take\n"
69"no credit to myself in the matter--it is a gift. Few possess it. There\n"
70"are plenty of lazy people and plenty of slow-coaches, but a genuine\n"
71"idler is a rarity. He is not a man who slouches about with his hands in\n"
72"his pockets. On the contrary, his most startling characteristic is that\n"
73"he is always intensely busy.\n"
74"\n"
75"It is impossible to enjoy idling thoroughly unless one has plenty of\n"
76"work to do. There is no fun in doing nothing when you have nothing to\n"
77"do. Wasting time is merely an occupation then, and a most exhausting\n"
78"one. Idleness, like kisses, to be sweet must be stolen.\n"
79"\n"
80"Many years ago, when I was a young man, I was taken very ill--I never\n"
81"could see myself that much was the matter with me, except that I had\n"
82"a beastly cold. But I suppose it was something very serious, for the\n"
83"doctor said that I ought to have come to him a month before, and that\n"
84"if it (whatever it was) had gone on for another week he would not have\n"
85"answered for the consequences. It is an extraordinary thing, but I\n"
86"never knew a doctor called into any case yet but what it transpired\n"
87"that another day's delay would have rendered cure hopeless. Our medical\n"
88"guide, philosopher, and friend is like the hero in a melodrama--he\n"
89"always comes upon the scene just, and only just, in the nick of time. It\n"
90"is Providence, that is what it is.\n"
91"\n"
92"Well, as I was saying, I was very ill and was ordered to Buxton for a\n"
93"month, with strict injunctions to do nothing whatever all the while\n"
94"that I was there. \"Rest is what you require,\" said the doctor, \"perfect\n"
95"rest.\"\n"
96"\n"
97"It seemed a delightful prospect. \"This man evidently understands my\n"
98"complaint,\" said I, and I pictured to myself a glorious time--a four\n"
99"weeks' _dolce far niente_ with a dash of illness in it. Not too much\n"
100"illness, but just illness enough--just sufficient to give it the flavor\n"
101"of suffering and make it poetical. I should get up late, sip chocolate,\n"
102"and have my breakfast in slippers and a dressing-gown. I should lie out\n"
103"in the garden in a hammock and read sentimental novels with a melancholy\n"
104"ending, until the books should fall from my listless hand, and I should\n"
105"recline there, dreamily gazing into the deep blue of the firmament,\n"
106"watching the fleecy clouds floating like white-sailed ships across\n"
107"its depths, and listening to the joyous song of the birds and the low\n"
108"rustling of the trees. Or, on becoming too weak to go out of doors,\n"
109"I should sit propped up with pillows at the open window of the\n"
110"ground-floor front, and look wasted and interesting, so that all the\n"
111"pretty girls would sigh as they passed by.\n"
112"\n"
113"And twice a day I should go down in a Bath chair to the Colonnade to\n"
114"drink the waters. Oh, those waters! I knew nothing about them then,\n"
115"and was rather taken with the idea. \"Drinking the waters\" sounded\n"
116"fashionable and Queen Anne-fied, and I thought I should like them. But,\n"
117"ugh! after the first three or four mornings! Sam Weller's description of\n"
118"them as \"having a taste of warm flat-irons\" conveys only a faint idea of\n"
119"their hideous nauseousness. If anything could make a sick man get well\n"
120"quickly, it would be the knowledge that he must drink a glassful of them\n"
121"every day until he was recovered. I drank them neat for six consecutive\n"
122"days, and they nearly killed me; but after then I adopted the plan of\n"
123"taking a stiff glass of brandy-and-water immediately on the top of them,\n"
124"and found much relief thereby. I have been informed since, by various\n"
125"eminent medical gentlemen, that the alcohol must have entirely\n"
126"counteracted the effects of the chalybeate properties contained in the\n"
127"water. I am glad I was lucky enough to hit upon the right thing.\n"
128"\n"
129"But \"drinking the waters\" was only a small portion of the torture I\n"
130"experienced during that memorable month--a month which was, without\n"
131"exception, the most miserable I have ever spent. During the best part of\n"
132"it I religiously followed the doctor's mandate and did nothing whatever,\n"
133"except moon about the house and garden and go out for two hours a day in\n"
134"a Bath chair. That did break the monotony to a certain extent. There is\n"
135"more excitement about Bath-chairing--especially if you are not used to\n"
136"the exhilarating exercise--than might appear to the casual observer. A\n"
137"sense of danger, such as a mere outsider might not understand, is ever\n"
138"present to the mind of the occupant. He feels convinced every minute\n"
139"that the whole concern is going over, a conviction which becomes\n"
140"especially lively whenever a ditch or a stretch of newly macadamized\n"
141"road comes in sight. Every vehicle that passes he expects is going to\n"
142"run into him; and he never finds himself ascending or descending a\n"
143"hill without immediately beginning to speculate upon his chances,\n"
144"supposing--as seems extremely probable--that the weak-kneed controller\n"
145"of his destiny should let go.\n"
146"\n"
147"But even this diversion failed to enliven after awhile, and the _ennui_\n"
148"became perfectly unbearable. I felt my mind giving way under it. It is\n"
149"not a strong mind, and I thought it would be unwise to tax it too far.\n"
150"So somewhere about the twentieth morning I got up early, had a good\n"
151"breakfast, and walked straight off to Hayfield, at the foot of the\n"
152"Kinder Scout--a pleasant, busy little town, reached through a lovely\n"
153"valley, and with two sweetly pretty women in it. At least they were\n"
154"sweetly pretty then; one passed me on the bridge and, I think, smiled;\n"
155"and the other was standing at an open door, making an unremunerative\n"
156"investment of kisses upon a red-faced baby. But it is years ago, and I\n"
157"dare say they have both grown stout and snappish since that time.\n"
158"Coming back, I saw an old man breaking stones, and it roused such strong\n"
159"longing in me to use my arms that I offered him a drink to let me take\n"
160"his place. He was a kindly old man and he humored me. I went for those\n"
161"stones with the accumulated energy of three weeks, and did more work in\n"
162"half an hour than he had done all day. But it did not make him jealous.\n"
163"\n"
164"Having taken the plunge, I went further and further into dissipation,\n"
165"going out for a long walk every morning and listening to the band in\n"
166"the pavilion every evening. But the days still passed slowly\n"
167"notwithstanding, and I was heartily glad when the last one came and I\n"
168"was being whirled away from gouty, consumptive Buxton to London with its\n"
169"stern work and life. I looked out of the carriage as we rushed through\n"
170"Hendon in the evening. The lurid glare overhanging the mighty city\n"
171"seemed to warm my heart, and when, later on, my cab rattled out of St.\n"
172"Pancras' station, the old familiar roar that came swelling up around me\n"
173"sounded the sweetest music I had heard for many a long day.\n"
174"\n"
175"I certainly did not enjoy that month's idling. I like idling when I\n"
176"ought not to be idling; not when it is the only thing I have to do. That\n"
177"is my pig-headed nature. The time when I like best to stand with my\n"
178"back to the fire, calculating how much I owe, is when my desk is heaped\n"
179"highest with letters that must be answered by the next post. When I like\n"
180"to dawdle longest over my dinner is when I have a heavy evening's work\n"
181"before me. And if, for some urgent reason, I ought to be up particularly\n"
182"early in the morning, it is then, more than at any other time, that I\n"
183"love to lie an extra half-hour in bed.\n"
184"\n"
185"Ah! how delicious it is to turn over and go to sleep again: \"just for\n"
186"five minutes.\" Is there any human being, I wonder, besides the hero of\n"
187"a Sunday-school \"tale for boys,\" who ever gets up willingly? There\n"
188"are some men to whom getting up at the proper time is an utter\n"
189"impossibility. If eight o'clock happens to be the time that they should\n"
190"turn out, then they lie till half-past. If circumstances change and\n"
191"half-past eight becomes early enough for them, then it is nine before\n"
192"they can rise. They are like the statesman of whom it was said that he\n"
193"was always punctually half an hour late. They try all manner of schemes.\n"
194"They buy alarm-clocks (artful contrivances that go off at the wrong time\n"
195"and alarm the wrong people). They tell Sarah Jane to knock at the door\n"
196"and call them, and Sarah Jane does knock at the door and does call them,\n"
197"and they grunt back \"awri\" and then go comfortably to sleep again. I\n"
198"knew one man who would actually get out and have a cold bath; and even\n"
199"that was of no use, for afterward he would jump into bed again to warm\n"
200"himself.\n"
201"\n"
202"I think myself that I could keep out of bed all right if I once got\n"
203"out. It is the wrenching away of the head from the pillow that I find so\n"
204"hard, and no amount of over-night determination makes it easier. I say\n"
205"to myself, after having wasted the whole evening, \"Well, I won't do\n"
206"any more work to-night; I'll get up early to-morrow morning;\" and I am\n"
207"thoroughly resolved to do so--then. In the morning, however, I feel less\n"
208"enthusiastic about the idea, and reflect that it would have been much\n"
209"better if I had stopped up last night. And then there is the trouble of\n"
210"dressing, and the more one thinks about that the more one wants to put\n"
211"it off.\n"
212"\n"
213"It is a strange thing this bed, this mimic grave, where we stretch our\n"
214"tired limbs and sink away so quietly into the silence and rest. \"O bed,\n"
215"O bed, delicious bed, that heaven on earth to the weary head,\" as sang\n"
216"poor Hood, you are a kind old nurse to us fretful boys and girls. Clever\n"
217"and foolish, naughty and good, you take us all in your motherly lap and\n"
218"hush our wayward crying. The strong man full of care--the sick man\n"
219"full of pain--the little maiden sobbing for her faithless lover--like\n"
220"children we lay our aching heads on your white bosom, and you gently\n"
221"soothe us off to by-by.\n"
222"\n"
223"Our trouble is sore indeed when you turn away and will not comfort us.\n"
224"How long the dawn seems coming when we cannot sleep! Oh! those hideous\n"
225"nights when we toss and turn in fever and pain, when we lie, like living\n"
226"men among the dead, staring out into the dark hours that drift so slowly\n"
227"between us and the light. And oh! those still more hideous nights when\n"
228"we sit by another in pain, when the low fire startles us every now and\n"
229"then with a falling cinder, and the tick of the clock seems a hammer\n"
230"beating out the life that we are watching.\n"
231"\n"
232"But enough of beds and bedrooms. I have kept to them too long, even for\n"
233"an idle fellow. Let us come out and have a smoke. That wastes time just\n"
234"as well and does not look so bad. Tobacco has been a blessing to us\n"
235"idlers. What the civil-service clerk before Sir Walter's time found\n"
236"to occupy their minds with it is hard to imagine. I attribute the\n"
237"quarrelsome nature of the Middle Ages young men entirely to the want of\n"
238"the soothing weed. They had no work to do and could not smoke, and\n"
239"the consequence was they were forever fighting and rowing. If, by any\n"
240"extraordinary chance, there was no war going, then they got up a deadly\n"
241"family feud with the next-door neighbor, and if, in spite of this, they\n"
242"still had a few spare moments on their hands, they occupied them with\n"
243"discussions as to whose sweetheart was the best looking, the arguments\n"
244"employed on both sides being battle-axes, clubs, etc. Questions of taste\n"
245"were soon decided in those days. When a twelfth-century youth fell in\n"
246"love he did not take three paces backward, gaze into her eyes, and tell\n"
247"her she was too beautiful to live. He said he would step outside and see\n"
248"about it. And if, when he got out, he met a man and broke his head--the\n"
249"other man's head, I mean--then that proved that his--the first\n"
250"fellow's--girl was a pretty girl. But if the other fellow broke _his_\n"
251"head--not his own, you know, but the other fellow's--the other fellow\n"
252"to the second fellow, that is, because of course the other fellow would\n"
253"only be the other fellow to him, not the first fellow who--well, if he\n"
254"broke his head, then _his_ girl--not the other fellow's, but the fellow\n"
255"who _was_ the--Look here, if A broke B's head, then A's girl was a\n"
256"pretty girl; but if B broke A's head, then A's girl wasn't a pretty\n"
257"girl, but B's girl was. That was their method of conducting art\n"
258"criticism.\n"
259"\n"
260"Nowadays we light a pipe and let the girls fight it out among\n"
261"themselves.\n"
262"\n"
263"They do it very well. They are getting to do all our work. They are\n"
264"doctors, and barristers, and artists. They manage theaters, and promote\n"
265"swindles, and edit newspapers. I am looking forward to the time when we\n"
266"men shall have nothing to do but lie in bed till twelve, read two novels\n"
267"a day, have nice little five-o'clock teas all to ourselves, and tax\n"
268"our brains with nothing more trying than discussions upon the latest\n"
269"patterns in trousers and arguments as to what Mr. Jones' coat was\n"
270"made of and whether it fitted him. It is a glorious prospect--for idle\n"
271"fellows.\n"
272"\n\n\n"
273;
274static const size_t IDLE_SIZE = sizeof(on_being_idle) - 1;
275#endif
276
277/* This is the "LSWS" mode: first write is performed immediately, outside
278 * of the on_write() callback.  This makes it possible to play with buffered
279 * packet queues.
280 */
281static int s_immediate_write;
282
283/* Use preadv(2) in conjuction with lsquic_stream_pwritev() to reduce
284 * number of system calls required to read from disk.  The actual value
285 * specifies maximum write size.  A negative value indicates always to use
286 * the remaining file size.
287 */
288static ssize_t s_pwritev;
289
290#define MIN(a, b) ((a) < (b) ? (a) : (b))
291#define V(v) (v), strlen(v)
292
293struct lsquic_conn_ctx;
294
295static void interop_server_hset_destroy (void *);
296
297
298struct server_ctx {
299    struct lsquic_conn_ctx  *conn_h;
300    lsquic_engine_t             *engine;
301    const char                  *document_root;
302    const char                  *push_path;
303    struct sport_head            sports;
304    struct prog                 *prog;
305    unsigned                     max_conn;
306    unsigned                     n_conn;
307    unsigned                     n_current_conns;
308    unsigned                     delay_resp_sec;
309};
310
311struct lsquic_conn_ctx {
312    lsquic_conn_t       *conn;
313    struct server_ctx   *server_ctx;
314    enum {
315        RECEIVED_GOAWAY = 1 << 0,
316    }                    flags;
317};
318
319
320static lsquic_conn_ctx_t *
321http_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
322{
323    struct server_ctx *server_ctx = stream_if_ctx;
324    const char *sni;
325
326    sni = lsquic_conn_get_sni(conn);
327    LSQ_DEBUG("new connection, SNI: %s", sni ? sni : "<not set>");
328
329    lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h));
330    conn_h->conn = conn;
331    conn_h->server_ctx = server_ctx;
332    server_ctx->conn_h = conn_h;
333    ++server_ctx->n_current_conns;
334    return conn_h;
335}
336
337
338static void
339http_server_on_goaway (lsquic_conn_t *conn)
340{
341    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
342    conn_h->flags |= RECEIVED_GOAWAY;
343    LSQ_INFO("received GOAWAY");
344}
345
346
347static void
348http_server_on_conn_closed (lsquic_conn_t *conn)
349{
350    static int stopped;
351    lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
352    LSQ_INFO("Connection closed");
353    --conn_h->server_ctx->n_current_conns;
354    if ((conn_h->server_ctx->prog->prog_flags & PROG_FLAG_COOLDOWN)
355                                && 0 == conn_h->server_ctx->n_current_conns)
356    {
357        if (!stopped)
358        {
359            stopped = 1;
360            prog_stop(conn_h->server_ctx->prog);
361        }
362    }
363    if (conn_h->server_ctx->max_conn > 0)
364    {
365        ++conn_h->server_ctx->n_conn;
366        LSQ_NOTICE("Connection closed, remaining: %d",
367                   conn_h->server_ctx->max_conn - conn_h->server_ctx->n_conn);
368        if (conn_h->server_ctx->n_conn >= conn_h->server_ctx->max_conn)
369        {
370            if (!stopped)
371            {
372                stopped = 1;
373                prog_stop(conn_h->server_ctx->prog);
374            }
375        }
376    }
377    /* No provision is made to stop HTTP server */
378    free(conn_h);
379}
380
381
382struct resp
383{
384    const char      *buf;
385    size_t           sz;
386    size_t           off;
387};
388
389
390struct index_html_ctx
391{
392    struct resp resp;
393};
394
395
396struct ver_head_ctx
397{
398    struct resp resp;
399    unsigned char   *req_body;
400    size_t           req_sz;    /* Expect it to be the same as qif_sz */
401};
402
403
404struct md5sum_ctx
405{
406    char        resp_buf[0x100];
407    MD5_CTX     md5ctx;
408    struct resp resp;
409    int         done;
410};
411
412
413struct req
414{
415    enum method {
416        UNSET, GET, POST, UNSUPPORTED,
417    }            method;
418    enum {
419        HAVE_XHDR   = 1 << 0,
420    }            flags;
421    char        *path;
422    char        *method_str;
423    char        *authority_str;
424    char        *qif_str;
425    size_t       qif_sz;
426    struct lsxpack_header
427                 xhdr;
428    size_t       decode_off;
429    char         decode_buf[MIN(LSXPACK_MAX_STRLEN + 1, 64 * 1024)];
430};
431
432
433struct interop_push_path
434{
435    STAILQ_ENTRY(interop_push_path)     next;
436    char                                path[0];
437};
438
439
440struct gen_file_ctx
441{
442    STAILQ_HEAD(, interop_push_path)    push_paths;
443    size_t      remain;
444    unsigned    idle_off;
445};
446
447
448struct lsquic_stream_ctx {
449    lsquic_stream_t     *stream;
450    struct server_ctx   *server_ctx;
451    FILE                *req_fh;
452    char                *req_buf;
453    char                *req_filename;
454    char                *req_path;
455    size_t               req_sz;
456    enum {
457        SH_HEADERS_SENT = (1 << 0),
458        SH_DELAYED      = (1 << 1),
459        SH_HEADERS_READ = (1 << 2),
460    }                    flags;
461    struct lsquic_reader reader;
462    int                  file_fd;   /* Used by pwritev */
463
464    /* Fields below are used by interop callbacks: */
465    enum interop_handler {
466        IOH_ERROR,
467        IOH_INDEX_HTML,
468        IOH_MD5SUM,
469        IOH_VER_HEAD,
470        IOH_GEN_FILE,
471        IOH_ECHO,
472    }                    interop_handler;
473    struct req          *req;
474    const char          *resp_status;
475    union {
476        struct index_html_ctx   ihc;
477        struct ver_head_ctx     vhc;
478        struct md5sum_ctx       md5c;
479        struct gen_file_ctx     gfc;
480        struct {
481            char buf[0x100];
482            struct resp resp;
483        }                       err;
484    }                    interop_u;
485    struct event        *resume_resp;
486    size_t               written;
487    size_t               file_size; /* Used by pwritev */
488};
489
490
491static lsquic_stream_ctx_t *
492http_server_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
493{
494    lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
495    st_h->stream = stream;
496    st_h->server_ctx = stream_if_ctx;
497    lsquic_stream_wantread(stream, 1);
498    return st_h;
499}
500
501
502static int
503ends_with (const char *filename, const char *ext)
504{
505    const char *where;
506
507    where = strstr(filename, ext);
508    return where
509        && strlen(where) == strlen(ext);
510}
511
512
513static const char *
514select_content_type (lsquic_stream_ctx_t *st_h)
515{
516    if (     ends_with(st_h->req_filename, ".html"))
517        return "text/html";
518    else if (ends_with(st_h->req_filename, ".png"))
519        return "image/png";
520    else if (ends_with(st_h->req_filename, ".css"))
521        return "text/css";
522    else if (ends_with(st_h->req_filename, ".gif"))
523        return "image/gif";
524    else if (ends_with(st_h->req_filename, ".txt"))
525        return "text/plain";
526    else
527        return "application/octet-stream";
528}
529
530
531static int
532send_headers (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
533{
534    const char *content_type;
535    struct header_buf hbuf;
536
537    content_type = select_content_type(st_h);
538    struct lsxpack_header headers_arr[2];
539
540    hbuf.off = 0;
541    header_set_ptr(&headers_arr[0], &hbuf, ":status", 7, "200", 3);
542    header_set_ptr(&headers_arr[1], &hbuf, "content-type", 12,
543                                        content_type, strlen(content_type));
544    lsquic_http_headers_t headers = {
545        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
546        .headers = headers_arr,
547    };
548    if (0 != lsquic_stream_send_headers(stream, &headers, 0))
549    {
550        LSQ_ERROR("cannot send headers: %s", strerror(errno));
551        return -1;
552    }
553
554    st_h->flags |= SH_HEADERS_SENT;
555    return 0;
556}
557
558
559static void
560resume_response (evutil_socket_t fd, short what, void *arg)
561{
562    struct lsquic_stream_ctx *const st_h = arg;
563
564    lsquic_stream_wantwrite(st_h->stream, 1);
565    event_del(st_h->resume_resp);
566    event_free(st_h->resume_resp);
567    st_h->resume_resp = NULL;
568
569    LSQ_NOTICE("resume response to stream %"PRIu64,
570                                                lsquic_stream_id(st_h->stream));
571    prog_process_conns(st_h->server_ctx->prog);
572}
573
574
575static size_t
576bytes_left (lsquic_stream_ctx_t *st_h)
577{
578    if (s_pwritev)
579        return st_h->file_size - st_h->written;
580    else
581        return test_reader_size(st_h->reader.lsqr_ctx);
582}
583
584
585static ssize_t
586my_preadv (void *user_data, const struct iovec *iov, int iovcnt)
587{
588#if HAVE_PREADV
589    lsquic_stream_ctx_t *const st_h = user_data;
590    ssize_t nread = preadv(st_h->file_fd, iov, iovcnt, st_h->written);
591    LSQ_DEBUG("%s: wrote %zd bytes", __func__, (size_t) nread);
592    return nread;
593#else
594    return -1;
595#endif
596}
597
598
599static size_t
600pwritev_fallback_read (void *lsqr_ctx, void *buf, size_t count)
601{
602    lsquic_stream_ctx_t *const st_h = lsqr_ctx;
603    struct iovec iov;
604    size_t ntoread;
605
606    ntoread = st_h->file_size - st_h->written;
607    if (ntoread > count)
608        count = ntoread;
609    iov.iov_base = buf;
610    iov.iov_len = count;
611    return my_preadv(lsqr_ctx, &iov, 1);
612}
613
614
615static size_t
616pwritev_fallback_size (void *lsqr_ctx)
617{
618    lsquic_stream_ctx_t *const st_h = lsqr_ctx;
619    return st_h->file_size - st_h->written;
620}
621
622
623static void
624http_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
625{
626    if (st_h->flags & SH_HEADERS_SENT)
627    {
628        ssize_t nw;
629        if (bytes_left(st_h) > 0)
630        {
631            if (st_h->server_ctx->delay_resp_sec
632                    && !(st_h->flags & SH_DELAYED)
633                        && st_h->written > 10000000)
634            {
635                struct timeval delay = {
636                                .tv_sec = st_h->server_ctx->delay_resp_sec, };
637                st_h->resume_resp = event_new(st_h->server_ctx->prog->prog_eb,
638                            -1, EV_TIMEOUT, resume_response, st_h);
639                if (st_h->resume_resp)
640                {
641                    event_add(st_h->resume_resp, &delay);
642                    lsquic_stream_wantwrite(stream, 0);
643                    st_h->flags |= SH_DELAYED;
644                    LSQ_NOTICE("delay response of stream %"PRIu64" for %u seconds",
645                        lsquic_stream_id(stream), st_h->server_ctx->delay_resp_sec);
646                    return;
647                }
648                else
649                    LSQ_ERROR("cannot allocate event");
650            }
651            if (s_pwritev)
652            {
653                size_t to_write = bytes_left(st_h);
654                if (s_pwritev > 0 && (size_t) s_pwritev < to_write)
655                    to_write = s_pwritev;
656                nw = lsquic_stream_pwritev(stream, my_preadv, st_h, to_write);
657                if (nw == 0)
658                {
659                    struct lsquic_reader reader = {
660                        .lsqr_read = pwritev_fallback_read,
661                        .lsqr_size = pwritev_fallback_size,
662                        .lsqr_ctx = st_h,
663                    };
664                    nw = lsquic_stream_writef(stream, &reader);
665                }
666            }
667            else
668            {
669                nw = lsquic_stream_writef(stream, &st_h->reader);
670            }
671            if (nw < 0)
672            {
673                struct lsquic_conn *conn = lsquic_stream_conn(stream);
674                lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
675                if (conn_h->flags & RECEIVED_GOAWAY)
676                {
677                    LSQ_NOTICE("cannot write: goaway received");
678                    lsquic_stream_close(stream);
679                }
680                else
681                {
682                    LSQ_ERROR("write error: %s", strerror(errno));
683                    exit(1);
684                }
685            }
686            if (bytes_left(st_h) > 0)
687            {
688                st_h->written += (size_t) nw;
689                lsquic_stream_wantwrite(stream, 1);
690            }
691            else
692            {
693                lsquic_stream_shutdown(stream, 1);
694                lsquic_stream_wantread(stream, 1);
695            }
696        }
697        else
698        {
699            lsquic_stream_shutdown(stream, 1);
700            lsquic_stream_wantread(stream, 1);
701        }
702    }
703    else
704    {
705        if (0 != send_headers(stream, st_h))
706            exit(1);
707    }
708}
709
710
711struct capped_reader_ctx
712{
713    struct lsquic_reader *inner_reader;
714    size_t                nread;
715};
716
717
718static size_t
719capped_reader_size (void *void_ctx)
720{
721    struct capped_reader_ctx *const capped_reader_ctx = void_ctx;
722    struct lsquic_reader *const inner_reader = capped_reader_ctx->inner_reader;
723    size_t size;
724
725    size = inner_reader->lsqr_size(inner_reader->lsqr_ctx);
726    return MIN((size_t) (s_immediate_write - capped_reader_ctx->nread), size);
727}
728
729
730static size_t
731capped_reader_read (void *void_ctx, void *buf, size_t count)
732{
733    struct capped_reader_ctx *const capped_reader_ctx = void_ctx;
734    struct lsquic_reader *const inner_reader = capped_reader_ctx->inner_reader;
735    size_t size;
736
737    count = MIN(count, (size_t) (s_immediate_write - capped_reader_ctx->nread));
738    size = inner_reader->lsqr_read(inner_reader->lsqr_ctx, buf, count);
739    capped_reader_ctx->nread += size;
740    return size;
741}
742
743
744#if HAVE_OPEN_MEMSTREAM
745static void
746parse_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
747{
748    char *filename;
749    int s;
750    regex_t re;
751    regmatch_t matches[2];
752
753    s = regcomp(&re, "GET (.*) HTTP/1.[01]\r\n", REG_EXTENDED);
754    if (0 != s)
755    {
756        perror("regcomp");
757        exit(3);
758    }
759
760    s = regexec(&re, st_h->req_buf, 2, matches, 0);
761    if (0 != s)
762    {
763        LSQ_WARN("GET request could not be parsed: `%s'", st_h->req_buf);
764        regfree(&re);
765        return;
766    }
767
768    regfree(&re);
769
770    filename = malloc(strlen(st_h->server_ctx->document_root) + 1 +
771                                matches[1].rm_eo - matches[1].rm_so + 1);
772    strcpy(filename, st_h->server_ctx->document_root);
773    strcat(filename, "/");
774    strncat(filename, st_h->req_buf + matches[1].rm_so,
775                                        matches[1].rm_eo - matches[1].rm_so);
776
777    LSQ_INFO("filename to fetch: %s", filename);
778
779    st_h->req_filename = filename;
780    st_h->req_path     = strdup(filename);
781}
782
783
784static void
785process_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
786{
787    struct stat st;
788
789    if (s_pwritev)
790    {
791        st_h->file_fd = open(st_h->req_path, O_RDONLY);
792        if (st_h->file_fd < 0)
793        {
794            LSQ_ERROR("cannot open %s for reading: %s", st_h->req_path,
795                                                            strerror(errno));
796            exit(1);
797        }
798        if (fstat(st_h->file_fd, &st) < 0)
799        {
800            LSQ_ERROR("fstat: %s", strerror(errno));
801            exit(1);
802        }
803        st_h->file_size = st.st_size;
804    }
805    else
806    {
807        st_h->reader.lsqr_read = test_reader_read;
808        st_h->reader.lsqr_size = test_reader_size;
809        st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path);
810        if (!st_h->reader.lsqr_ctx)
811            exit(1);
812    }
813
814    if (s_immediate_write)
815    {
816        if (0 != send_headers(stream, st_h))
817            exit(1);
818
819        if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
820        {
821            struct capped_reader_ctx capped_reader_ctx =
822            {
823                .inner_reader = &st_h->reader,
824            };
825            struct lsquic_reader capped_reader =
826            {
827                .lsqr_read  = capped_reader_read,
828                .lsqr_size  = capped_reader_size,
829                .lsqr_ctx   = &capped_reader_ctx,
830            };
831            ssize_t nw;
832            nw = lsquic_stream_writef(stream, &capped_reader);
833            if (nw < 0)
834            {
835                LSQ_ERROR("write error: %s", strerror(errno));
836                exit(1);
837            }
838        }
839
840        if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
841        {
842            lsquic_stream_flush(stream);
843            lsquic_stream_wantwrite(stream, 1);
844        }
845        else
846        {
847            lsquic_stream_shutdown(stream, 1);
848            lsquic_stream_wantread(stream, 1);
849        }
850    }
851    else
852        lsquic_stream_wantwrite(st_h->stream, 1);
853}
854
855
856static struct hset_fm      /* FM stands for Filesystem Mode */
857{
858    unsigned    id;
859    char       *path;
860} *
861new_hset_fm (const char *path)
862{
863    static unsigned hfm_id;
864    struct hset_fm *const hfm = malloc(sizeof(*hfm));
865    char *const str = strdup(path);
866    if (hfm && path)
867    {
868        hfm->id = hfm_id++;
869        hfm->path = str;
870        return hfm;
871    }
872    else
873    {
874        free(str);
875        free(hfm);
876        return NULL;
877    }
878}
879
880
881static void
882destroy_hset_fm (struct hset_fm *hfm)
883{
884    free(hfm->path);
885    free(hfm);
886}
887
888
889static int
890push_promise (lsquic_stream_ctx_t *st_h, lsquic_stream_t *stream)
891{
892    lsquic_conn_t *conn;
893    int s;
894    regex_t re;
895    regmatch_t matches[2];
896    struct hset_fm *hfm;
897    struct header_buf hbuf;
898
899    s = regcomp(&re, "\r\nHost: *([[:alnum:].][[:alnum:].]*)\r\n",
900                                                    REG_EXTENDED|REG_ICASE);
901    if (0 != s)
902    {
903        perror("regcomp");
904        exit(3);
905    }
906
907    s = regexec(&re, st_h->req_buf, 2, matches, 0);
908    if (0 != s)
909    {
910        LSQ_WARN("Could not find host header in request `%s'", st_h->req_buf);
911        regfree(&re);
912        return -1;
913    }
914    regfree(&re);
915
916    hfm = new_hset_fm(st_h->server_ctx->push_path);
917    if (!hfm)
918    {
919        LSQ_WARN("Could not allocate hfm");
920        return -1;
921    }
922
923#define V(v) (v), strlen(v)
924    hbuf.off = 0;
925    struct lsxpack_header headers_arr[6];
926    header_set_ptr(&headers_arr[0], &hbuf, V(":method"), V("GET"));
927    header_set_ptr(&headers_arr[1], &hbuf, V(":path"),
928                                            V(st_h->server_ctx->push_path));
929    header_set_ptr(&headers_arr[2], &hbuf, V(":authority"),
930        st_h->req_buf + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
931    header_set_ptr(&headers_arr[3], &hbuf, V(":scheme"), V("https"));
932    header_set_ptr(&headers_arr[4], &hbuf, V("x-some-header"),
933                                                        V("x-some-value"));
934    header_set_ptr(&headers_arr[5], &hbuf, V("x-kenny-status"),
935                        V("Oh my God!  They killed Kenny!!!  You bastards!"));
936    lsquic_http_headers_t headers = {
937        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
938        .headers = headers_arr,
939    };
940
941    conn = lsquic_stream_conn(stream);
942    s = lsquic_conn_push_stream(conn, hfm, stream, &headers);
943    if (0 == s)
944        LSQ_NOTICE("pushed stream successfully");
945    else
946    {
947        destroy_hset_fm(hfm);
948        LSQ_ERROR("could not push stream: %s", strerror(errno));
949    }
950
951    return 0;
952}
953
954
955static void
956http_server_on_read_pushed (struct lsquic_stream *stream,
957                                                    lsquic_stream_ctx_t *st_h)
958{
959    struct hset_fm *hfm;
960
961    hfm = lsquic_stream_get_hset(stream);
962    if (!hfm)
963    {
964        LSQ_ERROR("%s: error fetching hset: %s", __func__, strerror(errno));
965        lsquic_stream_close(stream);
966        return;
967    }
968
969    LSQ_INFO("got push request #%u for %s", hfm->id, hfm->path);
970    st_h->req_path = malloc(strlen(st_h->server_ctx->document_root) + 1 +
971                                strlen(hfm->path) + 1);
972    strcpy(st_h->req_path, st_h->server_ctx->document_root);
973    strcat(st_h->req_path, "/");
974    strcat(st_h->req_path, hfm->path);
975    st_h->req_filename = strdup(st_h->req_path);  /* XXX Only used for ends_with: drop it? */
976
977    process_request(stream, st_h);
978    free(st_h->req_buf);
979    lsquic_stream_shutdown(stream, 0);
980    destroy_hset_fm(hfm);
981}
982
983
984static void
985http_server_on_read_regular (struct lsquic_stream *stream,
986                                                    lsquic_stream_ctx_t *st_h)
987{
988    unsigned char buf[0x400];
989    ssize_t nread;
990    int s;
991
992    if (!st_h->req_fh)
993        st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz);
994
995    nread = lsquic_stream_read(stream, buf, sizeof(buf));
996    if (nread > 0)
997        fwrite(buf, 1, nread, st_h->req_fh);
998    else if (0 == nread)
999    {
1000        fwrite("", 1, 1, st_h->req_fh);  /* NUL-terminate so that we can regex the string */
1001        fclose(st_h->req_fh);
1002        LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf);
1003        parse_request(stream, st_h);
1004        if (st_h->server_ctx->push_path &&
1005                0 != strcmp(st_h->req_path, st_h->server_ctx->push_path))
1006        {
1007            s = push_promise(st_h, stream);
1008            if (s != 0)
1009                exit(1);
1010        }
1011        process_request(stream, st_h);
1012        free(st_h->req_buf);
1013        lsquic_stream_shutdown(stream, 0);
1014    }
1015    else
1016    {
1017        LSQ_ERROR("error reading: %s", strerror(errno));
1018        lsquic_stream_close(stream);
1019    }
1020}
1021#endif
1022
1023
1024static void
1025http_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1026{
1027#if HAVE_OPEN_MEMSTREAM
1028    if (lsquic_stream_is_pushed(stream))
1029        http_server_on_read_pushed(stream, st_h);
1030    else
1031        http_server_on_read_regular(stream, st_h);
1032#else
1033    LSQ_ERROR("%s: open_memstream not supported\n", __func__);
1034    exit(1);
1035#endif
1036}
1037
1038
1039static void
1040http_server_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1041{
1042    free(st_h->req_filename);
1043    free(st_h->req_path);
1044    if (st_h->reader.lsqr_ctx)
1045        destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
1046#if HAVE_PREADV
1047    if (s_pwritev)
1048        close(st_h->file_fd);
1049#endif
1050    if (st_h->req)
1051        interop_server_hset_destroy(st_h->req);
1052    free(st_h);
1053    LSQ_INFO("%s called, has unacked data: %d", __func__,
1054                                lsquic_stream_has_unacked_data(stream));
1055}
1056
1057
1058const struct lsquic_stream_if http_server_if = {
1059    .on_new_conn            = http_server_on_new_conn,
1060    .on_conn_closed         = http_server_on_conn_closed,
1061    .on_new_stream          = http_server_on_new_stream,
1062    .on_read                = http_server_on_read,
1063    .on_write               = http_server_on_write,
1064    .on_close               = http_server_on_close,
1065    .on_goaway_received     = http_server_on_goaway,
1066};
1067
1068
1069/* XXX Assume we can always read the request in one shot.  This is not a
1070 * good assumption to make in a real product.
1071 */
1072static void
1073hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1074{
1075    char buf[0x400];
1076    ssize_t nread;
1077    char *path, *end, *filename;
1078
1079    nread = lsquic_stream_read(stream, buf, sizeof(buf));
1080    if (nread >= (ssize_t) sizeof(buf))
1081    {
1082        LSQ_WARN("request too large, at least %zd bytes", sizeof(buf));
1083        lsquic_stream_close(stream);
1084        return;
1085    }
1086    else if (nread < 0)
1087    {
1088        LSQ_WARN("error reading request from stream: %s", strerror(errno));
1089        lsquic_stream_close(stream);
1090        return;
1091    }
1092    buf[nread] = '\0';
1093    path = strchr(buf, ' ');
1094    if (!path)
1095    {
1096        LSQ_WARN("invalid request (no space character): `%s'", buf);
1097        lsquic_stream_close(stream);
1098        return;
1099    }
1100    if (!(path - buf == 3 && 0 == strncasecmp(buf, "GET", 3)))
1101    {
1102        LSQ_NOTICE("unsupported method `%.*s'", (int) (path - buf), buf);
1103        lsquic_stream_close(stream);
1104        return;
1105    }
1106    ++path;
1107    for (end = path + nread - 5; end > path
1108                                    && (*end == '\r' || *end == '\n'); --end)
1109        *end = '\0';
1110    LSQ_NOTICE("parsed out request path: %s", path);
1111
1112    filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + strlen(path) + 1);
1113    strcpy(filename, st_h->server_ctx->document_root);
1114    strcat(filename, "/");
1115    strcat(filename, path);
1116    LSQ_NOTICE("file to fetch: %s", filename);
1117    /* XXX This copy pasta is getting a bit annoying now: two mallocs of the
1118     * same thing?
1119     */
1120    st_h->req_filename = filename;
1121    st_h->req_path = strdup(filename);
1122    st_h->reader.lsqr_read = test_reader_read;
1123    st_h->reader.lsqr_size = test_reader_size;
1124    st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path);
1125    if (!st_h->reader.lsqr_ctx)
1126    {
1127        lsquic_stream_close(stream);
1128        return;
1129    }
1130    lsquic_stream_shutdown(stream, 0);
1131    lsquic_stream_wantwrite(stream, 1);
1132}
1133
1134
1135static void
1136hq_server_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1137{
1138    ssize_t nw;
1139
1140    nw = lsquic_stream_writef(stream, &st_h->reader);
1141    if (nw < 0)
1142    {
1143        struct lsquic_conn *conn = lsquic_stream_conn(stream);
1144        lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
1145        if (conn_h->flags & RECEIVED_GOAWAY)
1146        {
1147            LSQ_NOTICE("cannot write: goaway received");
1148            lsquic_stream_close(stream);
1149        }
1150        else
1151        {
1152            LSQ_ERROR("write error: %s", strerror(errno));
1153            lsquic_stream_close(stream);
1154        }
1155    }
1156    else if (bytes_left(st_h) > 0)
1157    {
1158        st_h->written += (size_t) nw;
1159        lsquic_stream_wantwrite(stream, 1);
1160    }
1161    else
1162    {
1163        lsquic_stream_shutdown(stream, 1);
1164        lsquic_stream_wantread(stream, 1);
1165    }
1166}
1167
1168
1169const struct lsquic_stream_if hq_server_if = {
1170    .on_new_conn            = http_server_on_new_conn,
1171    .on_conn_closed         = http_server_on_conn_closed,
1172    .on_new_stream          = http_server_on_new_stream,
1173    .on_read                = hq_server_on_read,
1174    .on_write               = hq_server_on_write,
1175    .on_close               = http_server_on_close,
1176};
1177
1178
1179#if HAVE_REGEX
1180struct req_map
1181{
1182    enum method             method;
1183    const char             *path;
1184    enum interop_handler    handler;
1185    const char             *status;
1186    enum {
1187        RM_WANTBODY     = 1 << 0,
1188        RM_REGEX        = 1 << 1,
1189        RM_COMPILED     = 1 << 2,
1190    }                       flags;
1191    regex_t                 re;
1192};
1193
1194
1195static struct req_map req_maps[] =
1196{
1197    { .method = GET, .path = "/", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1198    { .method = GET, .path = "/index.html", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1199    { .method = POST, .path = "/cgi-bin/md5sum.cgi", .handler = IOH_MD5SUM, .status = "200", .flags = RM_WANTBODY, },
1200    { .method = POST, .path = "/cgi-bin/verify-headers.cgi", .handler = IOH_VER_HEAD, .status = "200", .flags = RM_WANTBODY, },
1201    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1202    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1203    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1204    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1205    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1206    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1207    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1208    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1209};
1210
1211
1212#define MAX_MATCHES 5
1213
1214
1215static void
1216init_map_regexes (void)
1217{
1218    struct req_map *map;
1219
1220    for (map = req_maps; map < req_maps + sizeof(req_maps)
1221                                            / sizeof(req_maps[0]); ++map)
1222        if (map->flags & RM_REGEX)
1223        {
1224#ifndef NDEBUG
1225            int s;
1226            s =
1227#endif
1228            regcomp(&map->re, map->path, REG_EXTENDED|REG_ICASE);
1229            assert(0 == s);
1230            map->flags |= RM_COMPILED;
1231        }
1232}
1233
1234
1235static void
1236free_map_regexes (void)
1237{
1238    struct req_map *map;
1239
1240    for (map = req_maps; map < req_maps + sizeof(req_maps)
1241                                            / sizeof(req_maps[0]); ++map)
1242        if (map->flags & RM_COMPILED)
1243        {
1244            regfree(&map->re);
1245            map->flags &= ~RM_COMPILED;
1246        }
1247}
1248
1249
1250static const struct req_map *
1251find_handler (enum method method, const char *path, regmatch_t *matches)
1252{
1253    const struct req_map *map;
1254
1255    for (map = req_maps; map < req_maps + sizeof(req_maps)
1256                                            / sizeof(req_maps[0]); ++map)
1257        if (map->flags & RM_COMPILED)
1258        {
1259            if (0 == regexec(&map->re, path, MAX_MATCHES + 1, matches, 0))
1260                return map;
1261        }
1262        else if (0 == strcasecmp(path, map->path))
1263            return map;
1264
1265    return NULL;
1266}
1267
1268
1269static const char INDEX_HTML[] =
1270"<html>\n"
1271"   <head>\n"
1272"       <title>LiteSpeed IETF QUIC Server Index Page</title>\n"
1273"   </head>\n"
1274"   <body>\n"
1275"       <h1>LiteSpeed IETF QUIC Server Index Page</h1>\n"
1276"       <p>Hello!  Welcome to the interop.  Available services:\n"
1277"       <ul>\n"
1278"           <li><b>POST to /cgi-bin/md5sum.cgi</b>.  This will return\n"
1279"                   MD5 checksum of the request body.\n"
1280"           <li><b>GET /123K</b> or <b>GET /file-123K</b>.  This will return\n"
1281"                   requested number of payload in the form of repeating text\n"
1282"                   by Jerome K. Jerome.  The size specification must match\n"
1283"                   (\\d+)[KMG]? and the total size request must not exceed\n"
1284"                   2 gigabytes.  Then, you will get back that many bytes\n"
1285"                   of the <a\n"
1286"                       href=http://www.gutenberg.org/cache/epub/849/pg849.txt\n"
1287"                                                       >beloved classic</a>.\n"
1288"       </ul>\n"
1289"   </body>\n"
1290"</html>\n"
1291;
1292
1293
1294static size_t
1295read_md5 (void *ctx, const unsigned char *buf, size_t sz, int fin)
1296{
1297    struct lsquic_stream_ctx *st_h = ctx;
1298
1299    if (sz)
1300        MD5_Update(&st_h->interop_u.md5c.md5ctx, buf, sz);
1301
1302    if (fin)
1303        st_h->interop_u.md5c.done = 1;
1304
1305    return sz;
1306}
1307
1308
1309static void
1310http_server_interop_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1311{
1312#define ERROR_RESP(code, ...) do {  \
1313    LSQ_WARN(__VA_ARGS__);                                     \
1314    st_h->interop_handler = IOH_ERROR; \
1315    st_h->resp_status = #code;  \
1316    st_h->interop_u.err.resp.sz = snprintf(st_h->interop_u.err.buf, \
1317                            sizeof(st_h->interop_u.err.buf), __VA_ARGS__);  \
1318    if (st_h->interop_u.err.resp.sz >= sizeof(st_h->interop_u.err.buf))     \
1319        st_h->interop_u.err.resp.sz = sizeof(st_h->interop_u.err.buf) - 1;  \
1320    st_h->interop_u.err.resp.buf = st_h->interop_u.err.buf;                 \
1321    st_h->interop_u.err.resp.off = 0;                                       \
1322    goto err;                                                               \
1323} while (0)
1324
1325    const struct req_map *map;
1326    ssize_t nw;
1327    size_t need;
1328    unsigned len, i;
1329    struct interop_push_path *push_path;
1330    regmatch_t matches[MAX_MATCHES + 1];
1331    unsigned char md5sum[MD5_DIGEST_LENGTH];
1332    char md5str[ sizeof(md5sum) * 2 + 1 ];
1333    char byte[1];
1334
1335    if (!(st_h->flags & SH_HEADERS_READ))
1336    {
1337        st_h->flags |= SH_HEADERS_READ;
1338        st_h->req = lsquic_stream_get_hset(stream);
1339        if (!st_h->req)
1340            ERROR_RESP(500, "Internal error: cannot fetch header set from stream");
1341        else if (st_h->req->method == UNSET)
1342            ERROR_RESP(400, "Method is not specified");
1343        else if (!st_h->req->path)
1344            ERROR_RESP(400, "Path is not specified");
1345        else if (st_h->req->method == UNSUPPORTED)
1346            ERROR_RESP(501, "Method %s is not supported", st_h->req->method_str);
1347        else if (!(map = find_handler(st_h->req->method, st_h->req->path, matches)))
1348            ERROR_RESP(404, "No handler found for method: %s; path: %s",
1349                st_h->req->method_str, st_h->req->path);
1350        else
1351        {
1352            LSQ_INFO("found handler for %s %s", st_h->req->method_str, st_h->req->path);
1353            st_h->resp_status = map->status;
1354            st_h->interop_handler = map->handler;
1355            switch (map->handler)
1356            {
1357            case IOH_INDEX_HTML:
1358                st_h->interop_u.ihc.resp = (struct resp) { INDEX_HTML, sizeof(INDEX_HTML) - 1, 0, };
1359                break;
1360            case IOH_VER_HEAD:
1361                st_h->interop_u.vhc.resp = (struct resp) {
1362                        st_h->req->qif_str, st_h->req->qif_sz, 0, };
1363                break;
1364            case IOH_MD5SUM:
1365                MD5_Init(&st_h->interop_u.md5c.md5ctx);
1366                st_h->interop_u.md5c.done = 0;
1367                break;
1368            case IOH_GEN_FILE:
1369                STAILQ_INIT(&st_h->interop_u.gfc.push_paths);
1370                st_h->interop_u.gfc.remain = strtol(st_h->req->path + matches[1].rm_so, NULL, 10);
1371                if (matches[2].rm_so >= 0
1372                        && matches[2].rm_so < matches[2].rm_eo)
1373                {
1374                    switch (st_h->req->path[ matches[2].rm_so ])
1375                    {
1376                    case 'G':
1377                    case 'g':
1378                        st_h->interop_u.gfc.remain <<= 30;
1379                        break;
1380                    case 'M':
1381                    case 'm':
1382                        st_h->interop_u.gfc.remain <<= 20;
1383                        break;
1384                    case 'K':
1385                    case 'k':
1386                        st_h->interop_u.gfc.remain <<= 10;
1387                        break;
1388                    }
1389                }
1390                if (st_h->interop_u.gfc.remain > 2 * (1u << 30))
1391                    ERROR_RESP(406, "Response of %zd bytes is too long to generate",
1392                        st_h->interop_u.gfc.remain);
1393                st_h->interop_u.gfc.idle_off = 0;
1394                for (i = 3; i <= MAX_MATCHES; ++i)
1395                    if (matches[i].rm_so >= 0)
1396                    {
1397                        len = matches[i].rm_eo - matches[i].rm_so;
1398                        push_path = malloc(sizeof(*push_path) + len + 1);
1399                        memcpy(push_path->path, st_h->req->path
1400                            + matches[i].rm_so, len);
1401                        push_path->path[len] ='\0';
1402                        STAILQ_INSERT_TAIL(&st_h->interop_u.gfc.push_paths,
1403                                                                push_path, next);
1404                    }
1405                    else
1406                        break;
1407                break;
1408            default:
1409                /* TODO: implement this */
1410                assert(0);
1411                break;
1412            }
1413        }
1414
1415        if (!(map->flags & RM_WANTBODY))
1416        {
1417  err:
1418            lsquic_stream_shutdown(stream, 0);
1419            lsquic_stream_wantwrite(stream, 1);
1420        }
1421    }
1422    else
1423    {
1424        switch (st_h->interop_handler)
1425        {
1426        case IOH_MD5SUM:
1427            assert(!st_h->interop_u.md5c.done);
1428            nw = lsquic_stream_readf(stream, read_md5, st_h);
1429            if (nw < 0)
1430            {
1431                LSQ_ERROR("could not read from stream for MD5: %s", strerror(errno));
1432                exit(1);
1433            }
1434            if (nw == 0)
1435                st_h->interop_u.md5c.done = 1;
1436            if (st_h->interop_u.md5c.done)
1437            {
1438                MD5_Final(md5sum, &st_h->interop_u.md5c.md5ctx);
1439                lsquic_hexstr(md5sum, sizeof(md5sum), md5str, sizeof(md5str));
1440                snprintf(st_h->interop_u.md5c.resp_buf, sizeof(st_h->interop_u.md5c.resp_buf),
1441                    "<html><head><title>MD5 Checksum Result</title></head>\n"
1442                    "<body><h1>MD5 Checksum Result</h1>\n<p>"
1443                    "MD5 Checksum: <tt>%s</tt>\n</body></html>\n",
1444                    md5str);
1445                st_h->interop_u.md5c.resp.buf = st_h->interop_u.md5c.resp_buf;
1446                st_h->interop_u.md5c.resp.sz = strlen(st_h->interop_u.md5c.resp_buf);
1447                st_h->interop_u.md5c.resp.off = 0;
1448                lsquic_stream_shutdown(stream, 0);
1449                lsquic_stream_wantwrite(stream, 1);
1450            }
1451            break;
1452        case IOH_VER_HEAD:
1453            if (!st_h->interop_u.vhc.req_body)
1454            {
1455                st_h->interop_u.vhc.req_body = malloc(st_h->req->qif_sz);
1456                if (!st_h->interop_u.vhc.req_body)
1457                {
1458                    perror("malloc");
1459                    exit(1);
1460                }
1461            }
1462            need = st_h->req->qif_sz - st_h->interop_u.vhc.req_sz;
1463            if (need > 0)
1464            {
1465                nw = lsquic_stream_read(stream,
1466                        st_h->interop_u.vhc.req_body
1467                                        + st_h->interop_u.vhc.req_sz, need);
1468                if (nw > 0)
1469                    st_h->interop_u.vhc.req_sz += need;
1470                else if (nw == 0)
1471                {
1472                    LSQ_WARN("request body too short (does not match headers)");
1473                    lsquic_stream_shutdown(stream, 0);
1474                    lsquic_stream_wantwrite(stream, 1);
1475                }
1476                else
1477                {
1478                    LSQ_ERROR("error reading from stream");
1479                    exit(1);
1480                }
1481            }
1482            else
1483            {
1484                nw = lsquic_stream_read(stream, byte, sizeof(byte));
1485                if (nw == 0)
1486                {
1487                    if (0 == memcmp(st_h->req->qif_str,
1488                            st_h->interop_u.vhc.req_body, st_h->req->qif_sz))
1489                        LSQ_INFO("request headers and payload check out");
1490                    else
1491                        LSQ_WARN("request headers and payload are different");
1492                }
1493                else
1494                    LSQ_WARN("request body too long (does not match headers)");
1495                lsquic_stream_shutdown(stream, 0);
1496                lsquic_stream_wantwrite(stream, 1);
1497            }
1498            break;
1499        default:
1500            assert(0);
1501        }
1502    }
1503}
1504
1505
1506static int
1507send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h,
1508                    size_t content_len)
1509{
1510    char clbuf[0x20];
1511    struct header_buf hbuf;
1512
1513    snprintf(clbuf, sizeof(clbuf), "%zd", content_len);
1514
1515    hbuf.off = 0;
1516    struct lsxpack_header  headers_arr[4];
1517    header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V(st_h->resp_status));
1518    header_set_ptr(&headers_arr[1], &hbuf, V("server"), V(LITESPEED_ID));
1519    header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html"));
1520    header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf));
1521    lsquic_http_headers_t headers = {
1522        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
1523        .headers = headers_arr,
1524    };
1525
1526    return lsquic_stream_send_headers(st_h->stream, &headers, 0);
1527}
1528
1529#define MIN(a, b) ((a) < (b) ? (a) : (b))
1530
1531static size_t
1532idle_read (void *lsqr_ctx, void *buf, size_t count)
1533{
1534    struct gen_file_ctx *const gfc = lsqr_ctx;
1535    unsigned char *p = buf;
1536    unsigned char *const end = p + count;
1537    size_t towrite;
1538
1539    while (p < end && gfc->remain > 0)
1540    {
1541        towrite = MIN((unsigned) (end - p), IDLE_SIZE - gfc->idle_off);
1542        if (towrite > gfc->remain)
1543            towrite = gfc->remain;
1544        memcpy(p, on_being_idle + gfc->idle_off, towrite);
1545        gfc->idle_off += towrite;
1546        if (gfc->idle_off == IDLE_SIZE)
1547            gfc->idle_off = 0;
1548        p += towrite;
1549        gfc->remain -= towrite;
1550    }
1551
1552    return p - (unsigned char *) buf;
1553}
1554
1555
1556static size_t
1557idle_size (void *lsqr_ctx)
1558{
1559    struct gen_file_ctx *const gfc = lsqr_ctx;
1560
1561    return gfc->remain;
1562}
1563
1564
1565static struct req *
1566new_req (enum method method, const char *path, const char *authority)
1567{
1568    struct req *req;
1569
1570    req = malloc(sizeof(*req));
1571    if (!req)
1572        return NULL;
1573
1574    memset(req, 0, offsetof(struct req, decode_buf));
1575    req->method = method;
1576    req->path = strdup(path);
1577    req->authority_str = strdup(authority);
1578    if (!(req->path && req->authority_str))
1579    {
1580        free(req->path);
1581        free(req->authority_str);
1582        free(req);
1583        return NULL;
1584    }
1585
1586    return req;
1587}
1588
1589
1590static ssize_t
1591my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt)
1592{
1593    struct gen_file_ctx *const gfc = user_data;
1594    size_t nread, nr;
1595    int i;
1596
1597    nread = 0;
1598    for (i = 0; i < iovcnt; ++i)
1599    {
1600        nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len);
1601        nread += nr;
1602    }
1603
1604    return (ssize_t) nread;
1605}
1606
1607
1608static void
1609idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1610{
1611    struct gen_file_ctx *const gfc = &st_h->interop_u.gfc;
1612    struct interop_push_path *push_path;
1613    struct lsxpack_header header_arr[4];
1614    struct lsquic_http_headers headers;
1615    struct req *req;
1616    ssize_t nw;
1617    struct header_buf hbuf;
1618    struct lsquic_reader reader;
1619
1620    if (st_h->flags & SH_HEADERS_SENT)
1621    {
1622        if (s_pwritev)
1623        {
1624            nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc,
1625                                                            gfc->remain);
1626            if (nw == 0)
1627                goto with_reader;
1628        }
1629        else
1630        {
1631  with_reader:
1632            reader.lsqr_read = idle_read,
1633            reader.lsqr_size = idle_size,
1634            reader.lsqr_ctx = gfc,
1635            nw = lsquic_stream_writef(stream, &reader);
1636        }
1637        if (nw < 0)
1638        {
1639            LSQ_ERROR("error writing idle thoughts: %s", strerror(errno));
1640            exit(1);
1641        }
1642        if (gfc->remain == 0)
1643            lsquic_stream_shutdown(stream, 1);
1644    }
1645    else
1646    {
1647        if (st_h->req->authority_str)
1648            while ((push_path = STAILQ_FIRST(&gfc->push_paths)))
1649            {
1650                STAILQ_REMOVE_HEAD(&gfc->push_paths, next);
1651                LSQ_DEBUG("pushing promise for %s", push_path->path);
1652                hbuf.off = 0;
1653                header_set_ptr(&header_arr[0], &hbuf, V(":method"), V("GET"));
1654                header_set_ptr(&header_arr[1], &hbuf, V(":path"), V(push_path->path));
1655                header_set_ptr(&header_arr[2], &hbuf, V(":authority"), V(st_h->req->authority_str));
1656                header_set_ptr(&header_arr[3], &hbuf, V(":scheme"), V("https"));
1657                headers.headers = header_arr;
1658                headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
1659                req = new_req(GET, push_path->path, st_h->req->authority_str);
1660                if (req)
1661                {
1662                    if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream),
1663                                                            req, stream, &headers))
1664                    {
1665                        LSQ_WARN("stream push failed");
1666                        interop_server_hset_destroy(req);
1667                    }
1668                }
1669                else
1670                    LSQ_WARN("cannot allocate req for push");
1671                free(push_path);
1672            }
1673        if (0 == send_headers2(stream, st_h, gfc->remain))
1674            st_h->flags |= SH_HEADERS_SENT;
1675        else
1676        {
1677            LSQ_ERROR("cannot send headers: %s", strerror(errno));
1678            lsquic_stream_close(stream);
1679        }
1680    }
1681}
1682
1683
1684static void
1685http_server_interop_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1686{
1687    struct resp *resp;
1688    ssize_t nw;
1689
1690    switch (st_h->interop_handler)
1691    {
1692    case IOH_ERROR:
1693        resp = &st_h->interop_u.err.resp;
1694        goto reply;
1695    case IOH_INDEX_HTML:
1696        resp = &st_h->interop_u.ihc.resp;
1697        goto reply;
1698    case IOH_VER_HEAD:
1699        resp = &st_h->interop_u.vhc.resp;
1700        goto reply;
1701    case IOH_MD5SUM:
1702        resp = &st_h->interop_u.md5c.resp;
1703        goto reply;
1704    case IOH_GEN_FILE:
1705        idle_on_write(stream, st_h);
1706        return;
1707    default:
1708        assert(0);
1709        return;
1710    }
1711
1712  reply:
1713    assert(resp->sz);   /* We always need to send body */
1714    if (!(st_h->flags & SH_HEADERS_SENT))
1715    {
1716        send_headers2(stream, st_h, resp->sz);
1717        st_h->flags |= SH_HEADERS_SENT;
1718        return;
1719    }
1720
1721    nw = lsquic_stream_write(stream, resp->buf + resp->off, resp->sz - resp->off);
1722    if (nw < 0)
1723    {
1724        LSQ_ERROR("error writing to stream: %s", strerror(errno));
1725        lsquic_conn_abort(lsquic_stream_conn(stream));
1726        return;
1727    }
1728
1729    resp->off += nw;
1730    lsquic_stream_flush(stream);
1731    if (resp->off == resp->sz)
1732        lsquic_stream_shutdown(stream, 1);
1733}
1734
1735
1736const struct lsquic_stream_if interop_http_server_if = {
1737    .on_new_conn            = http_server_on_new_conn,
1738    .on_conn_closed         = http_server_on_conn_closed,
1739    .on_new_stream          = http_server_on_new_stream,
1740    .on_read                = http_server_interop_on_read,
1741    .on_write               = http_server_interop_on_write,
1742    .on_close               = http_server_on_close,
1743};
1744#endif /* HAVE_REGEX */
1745
1746
1747static void
1748usage (const char *prog)
1749{
1750    const char *const slash = strrchr(prog, '/');
1751    if (slash)
1752        prog = slash + 1;
1753    printf(
1754"Usage: %s [opts]\n"
1755"\n"
1756"Options:\n"
1757"   -r ROOT     Document root\n"
1758"   -p FILE     Push request with this path\n"
1759"   -w SIZE     Write immediately (LSWS mode).  Argument specifies maximum\n"
1760"                 size of the immediate write.\n"
1761#if HAVE_PREADV
1762"   -P SIZE     Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n"
1763"                 write to stream.  Positive SIZE indicate maximum value per\n"
1764"                 write; negative means always use remaining file size.\n"
1765"                 Incompatible with -w.\n"
1766#endif
1767"   -y DELAY    Delay response for this many seconds -- use for debugging\n"
1768"   -Q ALPN     Use hq mode; ALPN could be \"hq-29\", for example.\n"
1769            , prog);
1770}
1771
1772
1773static void *
1774interop_server_hset_create (void *hsi_ctx, lsquic_stream_t *stream,
1775                            int is_push_promise)
1776{
1777    struct req *req;
1778
1779    req = malloc(sizeof(struct req));
1780    memset(req, 0, offsetof(struct req, decode_buf));
1781
1782    return req;
1783}
1784
1785
1786static struct lsxpack_header *
1787interop_server_hset_prepare_decode (void *hset_p, struct lsxpack_header *xhdr,
1788                                                                size_t req_space)
1789{
1790    struct req *req = hset_p;
1791
1792    if (xhdr)
1793    {
1794        LSQ_WARN("we don't reallocate headers: can't give more");
1795        return NULL;
1796    }
1797
1798    if (req->flags & HAVE_XHDR)
1799    {
1800        if (req->decode_off + lsxpack_header_get_dec_size(&req->xhdr)
1801                                                    >= sizeof(req->decode_buf))
1802        {
1803            LSQ_WARN("Not enough room in header");
1804            return NULL;
1805        }
1806        req->decode_off += lsxpack_header_get_dec_size(&req->xhdr);
1807    }
1808    else
1809        req->flags |= HAVE_XHDR;
1810
1811    lsxpack_header_prepare_decode(&req->xhdr, req->decode_buf,
1812                req->decode_off, sizeof(req->decode_buf) - req->decode_off);
1813    return &req->xhdr;
1814}
1815
1816
1817static int
1818interop_server_hset_add_header (void *hset_p, struct lsxpack_header *xhdr)
1819{
1820    struct req *req = hset_p;
1821    const char *name, *value;
1822    unsigned name_len, value_len;
1823
1824    if (!xhdr)
1825        return 0;
1826
1827    name = lsxpack_header_get_name(xhdr);
1828    value = lsxpack_header_get_value(xhdr);
1829    name_len = xhdr->name_len;
1830    value_len = xhdr->val_len;
1831
1832    req->qif_str = realloc(req->qif_str,
1833                        req->qif_sz + name_len + value_len + 2);
1834    if (!req->qif_str)
1835    {
1836        LSQ_ERROR("malloc failed");
1837        return -1;
1838    }
1839    memcpy(req->qif_str + req->qif_sz, name, name_len);
1840    req->qif_str[req->qif_sz + name_len] = '\t';
1841    memcpy(req->qif_str + req->qif_sz + name_len + 1, value, value_len);
1842    req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n';
1843    req->qif_sz += name_len + value_len + 2;
1844
1845    if (5 == name_len && 0 == strncmp(name, ":path", 5))
1846    {
1847        if (req->path)
1848            return 1;
1849        req->path = strndup(value, value_len);
1850        if (!req->path)
1851            return -1;
1852        return 0;
1853    }
1854
1855    if (7 == name_len && 0 == strncmp(name, ":method", 7))
1856    {
1857        if (req->method != UNSET)
1858            return 1;
1859        req->method_str = strndup(value, value_len);
1860        if (!req->method_str)
1861            return -1;
1862        if (0 == strcmp(req->method_str, "GET"))
1863            req->method = GET;
1864        else if (0 == strcmp(req->method_str, "POST"))
1865            req->method = POST;
1866        else
1867            req->method = UNSUPPORTED;
1868        return 0;
1869    }
1870
1871    if (10 == name_len && 0 == strncmp(name, ":authority", 10))
1872    {
1873        req->authority_str = strndup(value, value_len);
1874        if (!req->authority_str)
1875            return -1;
1876        return 0;
1877    }
1878
1879    return 0;
1880}
1881
1882
1883static void
1884interop_server_hset_destroy (void *hset_p)
1885{
1886    struct req *req = hset_p;
1887    free(req->qif_str);
1888    free(req->path);
1889    free(req->method_str);
1890    free(req->authority_str);
1891    free(req);
1892}
1893
1894
1895static const struct lsquic_hset_if header_bypass_api =
1896{
1897    .hsi_create_header_set  = interop_server_hset_create,
1898    .hsi_prepare_decode     = interop_server_hset_prepare_decode,
1899    .hsi_process_header     = interop_server_hset_add_header,
1900    .hsi_discard_header_set = interop_server_hset_destroy,
1901};
1902
1903
1904int
1905main (int argc, char **argv)
1906{
1907    int opt, s;
1908    struct stat st;
1909    struct server_ctx server_ctx;
1910    struct prog prog;
1911    const char *const *alpn;
1912
1913#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX)
1914    fprintf(stderr, "cannot run server without regex or open_memstream\n");
1915    return 1;
1916#endif
1917
1918    memset(&server_ctx, 0, sizeof(server_ctx));
1919    TAILQ_INIT(&server_ctx.sports);
1920    server_ctx.prog = &prog;
1921
1922    prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
1923                                            &http_server_if, &server_ctx);
1924
1925    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:hQ:")))
1926    {
1927        switch (opt) {
1928        case 'n':
1929            server_ctx.max_conn = atoi(optarg);
1930            break;
1931        case 'p':
1932            server_ctx.push_path = optarg;
1933            break;
1934        case 'r':
1935            if (-1 == stat(optarg, &st))
1936            {
1937                perror("stat");
1938                exit(2);
1939            }
1940#ifndef WIN32
1941            if (!S_ISDIR(st.st_mode))
1942            {
1943                fprintf(stderr, "`%s' is not a directory\n", optarg);
1944                exit(2);
1945            }
1946#endif
1947            server_ctx.document_root = optarg;
1948            break;
1949        case 'w':
1950            s_immediate_write = atoi(optarg);
1951            break;
1952        case 'P':
1953#if HAVE_PREADV
1954            s_pwritev = strtoull(optarg, NULL, 10);
1955            break;
1956#else
1957            fprintf(stderr, "preadv is not supported on this platform, "
1958                                                        "cannot use -P\n");
1959            exit(EXIT_FAILURE);
1960#endif
1961        case 'y':
1962            server_ctx.delay_resp_sec = atoi(optarg);
1963            break;
1964        case 'h':
1965            usage(argv[0]);
1966            prog_print_common_options(&prog, stdout);
1967            exit(0);
1968        case 'Q':
1969            /* XXX A bit hacky, as `prog' has already been initialized... */
1970            prog.prog_engine_flags &= ~LSENG_HTTP;
1971            prog.prog_api.ea_stream_if = &hq_server_if;
1972            add_alpn(optarg);
1973            break;
1974        default:
1975            if (0 != prog_set_opt(&prog, opt, optarg))
1976                exit(1);
1977        }
1978    }
1979
1980    if (!server_ctx.document_root)
1981    {
1982#if HAVE_REGEX
1983        LSQ_NOTICE("Document root is not set: start in Interop Mode");
1984        init_map_regexes();
1985        prog.prog_api.ea_stream_if = &interop_http_server_if;
1986        prog.prog_api.ea_hsi_if = &header_bypass_api;
1987        prog.prog_api.ea_hsi_ctx = NULL;
1988#else
1989        LSQ_ERROR("Document root is not set: use -r option");
1990        exit(EXIT_FAILURE);
1991#endif
1992    }
1993
1994    if (s_immediate_write && s_pwritev)
1995    {
1996        LSQ_ERROR("-w and -P are incompatible options");
1997        exit(EXIT_FAILURE);
1998    }
1999
2000    alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions);
2001    while (*alpn)
2002    {
2003        if (0 == add_alpn(*alpn))
2004            ++alpn;
2005        else
2006        {
2007            LSQ_ERROR("cannot add ALPN %s", *alpn);
2008            exit(EXIT_FAILURE);
2009        }
2010    }
2011
2012    if (0 != prog_prep(&prog))
2013    {
2014        LSQ_ERROR("could not prep");
2015        exit(EXIT_FAILURE);
2016    }
2017
2018    LSQ_DEBUG("entering event loop");
2019
2020    s = prog_run(&prog);
2021    prog_cleanup(&prog);
2022
2023#if HAVE_REGEX
2024    if (!server_ctx.document_root)
2025        free_map_regexes();
2026#endif
2027
2028    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
2029}
2030