http_server.c revision c2faf032
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#if HAVE_OPEN_MEMSTREAM
1070static void
1071hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1072{
1073    char tbuf[0x100], *buf;
1074    ssize_t nread;
1075    char *path, *end, *filename;
1076
1077    if (!st_h->req_fh)
1078        st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz);
1079
1080    nread = lsquic_stream_read(stream, tbuf, sizeof(tbuf));
1081    if (nread > 0)
1082    {
1083        fwrite(tbuf, 1, nread, st_h->req_fh);
1084        return;
1085    }
1086
1087    if (nread < 0)
1088    {
1089        LSQ_WARN("error reading request from stream: %s", strerror(errno));
1090        lsquic_stream_close(stream);
1091        return;
1092    }
1093
1094    fwrite("", 1, 1, st_h->req_fh);
1095    fclose(st_h->req_fh);
1096    LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf);
1097
1098    buf = st_h->req_buf;
1099    path = strchr(buf, ' ');
1100    if (!path)
1101    {
1102        LSQ_WARN("invalid request (no space character): `%s'", buf);
1103        lsquic_stream_close(stream);
1104        return;
1105    }
1106    if (!(path - buf == 3 && 0 == strncasecmp(buf, "GET", 3)))
1107    {
1108        LSQ_NOTICE("unsupported method `%.*s'", (int) (path - buf), buf);
1109        lsquic_stream_close(stream);
1110        return;
1111    }
1112    ++path;
1113    for (end = buf + st_h->req_sz - 1; end > path
1114                && (*end == '\0' || *end == '\r' || *end == '\n'); --end)
1115        *end = '\0';
1116    LSQ_NOTICE("parsed out request path: %s", path);
1117
1118    filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + strlen(path) + 1);
1119    strcpy(filename, st_h->server_ctx->document_root);
1120    strcat(filename, "/");
1121    strcat(filename, path);
1122    LSQ_NOTICE("file to fetch: %s", filename);
1123    /* XXX This copy pasta is getting a bit annoying now: two mallocs of the
1124     * same thing?
1125     */
1126    st_h->req_filename = filename;
1127    st_h->req_path = strdup(filename);
1128    st_h->reader.lsqr_read = test_reader_read;
1129    st_h->reader.lsqr_size = test_reader_size;
1130    st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path);
1131    if (!st_h->reader.lsqr_ctx)
1132    {
1133        lsquic_stream_close(stream);
1134        return;
1135    }
1136    lsquic_stream_shutdown(stream, 0);
1137    lsquic_stream_wantwrite(stream, 1);
1138}
1139
1140
1141static void
1142hq_server_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1143{
1144    ssize_t nw;
1145
1146    nw = lsquic_stream_writef(stream, &st_h->reader);
1147    if (nw < 0)
1148    {
1149        struct lsquic_conn *conn = lsquic_stream_conn(stream);
1150        lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
1151        if (conn_h->flags & RECEIVED_GOAWAY)
1152        {
1153            LSQ_NOTICE("cannot write: goaway received");
1154            lsquic_stream_close(stream);
1155        }
1156        else
1157        {
1158            LSQ_ERROR("write error: %s", strerror(errno));
1159            lsquic_stream_close(stream);
1160        }
1161    }
1162    else if (bytes_left(st_h) > 0)
1163    {
1164        st_h->written += (size_t) nw;
1165        lsquic_stream_wantwrite(stream, 1);
1166    }
1167    else
1168    {
1169        lsquic_stream_shutdown(stream, 1);
1170        lsquic_stream_wantread(stream, 1);
1171    }
1172}
1173
1174
1175const struct lsquic_stream_if hq_server_if = {
1176    .on_new_conn            = http_server_on_new_conn,
1177    .on_conn_closed         = http_server_on_conn_closed,
1178    .on_new_stream          = http_server_on_new_stream,
1179    .on_read                = hq_server_on_read,
1180    .on_write               = hq_server_on_write,
1181    .on_close               = http_server_on_close,
1182};
1183#endif
1184
1185
1186#if HAVE_REGEX
1187struct req_map
1188{
1189    enum method             method;
1190    const char             *path;
1191    enum interop_handler    handler;
1192    const char             *status;
1193    enum {
1194        RM_WANTBODY     = 1 << 0,
1195        RM_REGEX        = 1 << 1,
1196        RM_COMPILED     = 1 << 2,
1197    }                       flags;
1198    regex_t                 re;
1199};
1200
1201
1202static struct req_map req_maps[] =
1203{
1204    { .method = GET, .path = "/", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1205    { .method = GET, .path = "/index.html", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1206    { .method = POST, .path = "/cgi-bin/md5sum.cgi", .handler = IOH_MD5SUM, .status = "200", .flags = RM_WANTBODY, },
1207    { .method = POST, .path = "/cgi-bin/verify-headers.cgi", .handler = IOH_VER_HEAD, .status = "200", .flags = RM_WANTBODY, },
1208    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1209    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1210    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1211    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1212    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1213    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1214    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1215    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1216};
1217
1218
1219#define MAX_MATCHES 5
1220
1221
1222static void
1223init_map_regexes (void)
1224{
1225    struct req_map *map;
1226
1227    for (map = req_maps; map < req_maps + sizeof(req_maps)
1228                                            / sizeof(req_maps[0]); ++map)
1229        if (map->flags & RM_REGEX)
1230        {
1231#ifndef NDEBUG
1232            int s;
1233            s =
1234#endif
1235            regcomp(&map->re, map->path, REG_EXTENDED|REG_ICASE);
1236            assert(0 == s);
1237            map->flags |= RM_COMPILED;
1238        }
1239}
1240
1241
1242static void
1243free_map_regexes (void)
1244{
1245    struct req_map *map;
1246
1247    for (map = req_maps; map < req_maps + sizeof(req_maps)
1248                                            / sizeof(req_maps[0]); ++map)
1249        if (map->flags & RM_COMPILED)
1250        {
1251            regfree(&map->re);
1252            map->flags &= ~RM_COMPILED;
1253        }
1254}
1255
1256
1257static const struct req_map *
1258find_handler (enum method method, const char *path, regmatch_t *matches)
1259{
1260    const struct req_map *map;
1261
1262    for (map = req_maps; map < req_maps + sizeof(req_maps)
1263                                            / sizeof(req_maps[0]); ++map)
1264        if (map->flags & RM_COMPILED)
1265        {
1266            if (0 == regexec(&map->re, path, MAX_MATCHES + 1, matches, 0))
1267                return map;
1268        }
1269        else if (0 == strcasecmp(path, map->path))
1270            return map;
1271
1272    return NULL;
1273}
1274
1275
1276static const char INDEX_HTML[] =
1277"<html>\n"
1278"   <head>\n"
1279"       <title>LiteSpeed IETF QUIC Server Index Page</title>\n"
1280"   </head>\n"
1281"   <body>\n"
1282"       <h1>LiteSpeed IETF QUIC Server Index Page</h1>\n"
1283"       <p>Hello!  Welcome to the interop.  Available services:\n"
1284"       <ul>\n"
1285"           <li><b>POST to /cgi-bin/md5sum.cgi</b>.  This will return\n"
1286"                   MD5 checksum of the request body.\n"
1287"           <li><b>GET /123K</b> or <b>GET /file-123K</b>.  This will return\n"
1288"                   requested number of payload in the form of repeating text\n"
1289"                   by Jerome K. Jerome.  The size specification must match\n"
1290"                   (\\d+)[KMG]? and the total size request must not exceed\n"
1291"                   2 gigabytes.  Then, you will get back that many bytes\n"
1292"                   of the <a\n"
1293"                       href=http://www.gutenberg.org/cache/epub/849/pg849.txt\n"
1294"                                                       >beloved classic</a>.\n"
1295"       </ul>\n"
1296"   </body>\n"
1297"</html>\n"
1298;
1299
1300
1301static size_t
1302read_md5 (void *ctx, const unsigned char *buf, size_t sz, int fin)
1303{
1304    struct lsquic_stream_ctx *st_h = ctx;
1305
1306    if (sz)
1307        MD5_Update(&st_h->interop_u.md5c.md5ctx, buf, sz);
1308
1309    if (fin)
1310        st_h->interop_u.md5c.done = 1;
1311
1312    return sz;
1313}
1314
1315
1316static void
1317http_server_interop_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1318{
1319#define ERROR_RESP(code, ...) do {  \
1320    LSQ_WARN(__VA_ARGS__);                                     \
1321    st_h->interop_handler = IOH_ERROR; \
1322    st_h->resp_status = #code;  \
1323    st_h->interop_u.err.resp.sz = snprintf(st_h->interop_u.err.buf, \
1324                            sizeof(st_h->interop_u.err.buf), __VA_ARGS__);  \
1325    if (st_h->interop_u.err.resp.sz >= sizeof(st_h->interop_u.err.buf))     \
1326        st_h->interop_u.err.resp.sz = sizeof(st_h->interop_u.err.buf) - 1;  \
1327    st_h->interop_u.err.resp.buf = st_h->interop_u.err.buf;                 \
1328    st_h->interop_u.err.resp.off = 0;                                       \
1329    goto err;                                                               \
1330} while (0)
1331
1332    const struct req_map *map;
1333    ssize_t nw;
1334    size_t need;
1335    unsigned len, i;
1336    struct interop_push_path *push_path;
1337    regmatch_t matches[MAX_MATCHES + 1];
1338    unsigned char md5sum[MD5_DIGEST_LENGTH];
1339    char md5str[ sizeof(md5sum) * 2 + 1 ];
1340    char byte[1];
1341
1342    if (!(st_h->flags & SH_HEADERS_READ))
1343    {
1344        st_h->flags |= SH_HEADERS_READ;
1345        st_h->req = lsquic_stream_get_hset(stream);
1346        if (!st_h->req)
1347            ERROR_RESP(500, "Internal error: cannot fetch header set from stream");
1348        else if (st_h->req->method == UNSET)
1349            ERROR_RESP(400, "Method is not specified");
1350        else if (!st_h->req->path)
1351            ERROR_RESP(400, "Path is not specified");
1352        else if (st_h->req->method == UNSUPPORTED)
1353            ERROR_RESP(501, "Method %s is not supported", st_h->req->method_str);
1354        else if (!(map = find_handler(st_h->req->method, st_h->req->path, matches)))
1355            ERROR_RESP(404, "No handler found for method: %s; path: %s",
1356                st_h->req->method_str, st_h->req->path);
1357        else
1358        {
1359            LSQ_INFO("found handler for %s %s", st_h->req->method_str, st_h->req->path);
1360            st_h->resp_status = map->status;
1361            st_h->interop_handler = map->handler;
1362            switch (map->handler)
1363            {
1364            case IOH_INDEX_HTML:
1365                st_h->interop_u.ihc.resp = (struct resp) { INDEX_HTML, sizeof(INDEX_HTML) - 1, 0, };
1366                break;
1367            case IOH_VER_HEAD:
1368                st_h->interop_u.vhc.resp = (struct resp) {
1369                        st_h->req->qif_str, st_h->req->qif_sz, 0, };
1370                break;
1371            case IOH_MD5SUM:
1372                MD5_Init(&st_h->interop_u.md5c.md5ctx);
1373                st_h->interop_u.md5c.done = 0;
1374                break;
1375            case IOH_GEN_FILE:
1376                STAILQ_INIT(&st_h->interop_u.gfc.push_paths);
1377                st_h->interop_u.gfc.remain = strtol(st_h->req->path + matches[1].rm_so, NULL, 10);
1378                if (matches[2].rm_so >= 0
1379                        && matches[2].rm_so < matches[2].rm_eo)
1380                {
1381                    switch (st_h->req->path[ matches[2].rm_so ])
1382                    {
1383                    case 'G':
1384                    case 'g':
1385                        st_h->interop_u.gfc.remain <<= 30;
1386                        break;
1387                    case 'M':
1388                    case 'm':
1389                        st_h->interop_u.gfc.remain <<= 20;
1390                        break;
1391                    case 'K':
1392                    case 'k':
1393                        st_h->interop_u.gfc.remain <<= 10;
1394                        break;
1395                    }
1396                }
1397                if (st_h->interop_u.gfc.remain > 2 * (1u << 30))
1398                    ERROR_RESP(406, "Response of %zd bytes is too long to generate",
1399                        st_h->interop_u.gfc.remain);
1400                st_h->interop_u.gfc.idle_off = 0;
1401                for (i = 3; i <= MAX_MATCHES; ++i)
1402                    if (matches[i].rm_so >= 0)
1403                    {
1404                        len = matches[i].rm_eo - matches[i].rm_so;
1405                        push_path = malloc(sizeof(*push_path) + len + 1);
1406                        memcpy(push_path->path, st_h->req->path
1407                            + matches[i].rm_so, len);
1408                        push_path->path[len] ='\0';
1409                        STAILQ_INSERT_TAIL(&st_h->interop_u.gfc.push_paths,
1410                                                                push_path, next);
1411                    }
1412                    else
1413                        break;
1414                break;
1415            default:
1416                /* TODO: implement this */
1417                assert(0);
1418                break;
1419            }
1420        }
1421
1422        if (!(map->flags & RM_WANTBODY))
1423        {
1424  err:
1425            lsquic_stream_shutdown(stream, 0);
1426            lsquic_stream_wantwrite(stream, 1);
1427        }
1428    }
1429    else
1430    {
1431        switch (st_h->interop_handler)
1432        {
1433        case IOH_MD5SUM:
1434            assert(!st_h->interop_u.md5c.done);
1435            nw = lsquic_stream_readf(stream, read_md5, st_h);
1436            if (nw < 0)
1437            {
1438                LSQ_ERROR("could not read from stream for MD5: %s", strerror(errno));
1439                exit(1);
1440            }
1441            if (nw == 0)
1442                st_h->interop_u.md5c.done = 1;
1443            if (st_h->interop_u.md5c.done)
1444            {
1445                MD5_Final(md5sum, &st_h->interop_u.md5c.md5ctx);
1446                lsquic_hexstr(md5sum, sizeof(md5sum), md5str, sizeof(md5str));
1447                snprintf(st_h->interop_u.md5c.resp_buf, sizeof(st_h->interop_u.md5c.resp_buf),
1448                    "<html><head><title>MD5 Checksum Result</title></head>\n"
1449                    "<body><h1>MD5 Checksum Result</h1>\n<p>"
1450                    "MD5 Checksum: <tt>%s</tt>\n</body></html>\n",
1451                    md5str);
1452                st_h->interop_u.md5c.resp.buf = st_h->interop_u.md5c.resp_buf;
1453                st_h->interop_u.md5c.resp.sz = strlen(st_h->interop_u.md5c.resp_buf);
1454                st_h->interop_u.md5c.resp.off = 0;
1455                lsquic_stream_shutdown(stream, 0);
1456                lsquic_stream_wantwrite(stream, 1);
1457            }
1458            break;
1459        case IOH_VER_HEAD:
1460            if (!st_h->interop_u.vhc.req_body)
1461            {
1462                st_h->interop_u.vhc.req_body = malloc(st_h->req->qif_sz);
1463                if (!st_h->interop_u.vhc.req_body)
1464                {
1465                    perror("malloc");
1466                    exit(1);
1467                }
1468            }
1469            need = st_h->req->qif_sz - st_h->interop_u.vhc.req_sz;
1470            if (need > 0)
1471            {
1472                nw = lsquic_stream_read(stream,
1473                        st_h->interop_u.vhc.req_body
1474                                        + st_h->interop_u.vhc.req_sz, need);
1475                if (nw > 0)
1476                    st_h->interop_u.vhc.req_sz += need;
1477                else if (nw == 0)
1478                {
1479                    LSQ_WARN("request body too short (does not match headers)");
1480                    lsquic_stream_shutdown(stream, 0);
1481                    lsquic_stream_wantwrite(stream, 1);
1482                }
1483                else
1484                {
1485                    LSQ_ERROR("error reading from stream");
1486                    exit(1);
1487                }
1488            }
1489            else
1490            {
1491                nw = lsquic_stream_read(stream, byte, sizeof(byte));
1492                if (nw == 0)
1493                {
1494                    if (0 == memcmp(st_h->req->qif_str,
1495                            st_h->interop_u.vhc.req_body, st_h->req->qif_sz))
1496                        LSQ_INFO("request headers and payload check out");
1497                    else
1498                        LSQ_WARN("request headers and payload are different");
1499                }
1500                else
1501                    LSQ_WARN("request body too long (does not match headers)");
1502                lsquic_stream_shutdown(stream, 0);
1503                lsquic_stream_wantwrite(stream, 1);
1504            }
1505            break;
1506        default:
1507            assert(0);
1508        }
1509    }
1510}
1511
1512
1513static int
1514send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h,
1515                    size_t content_len)
1516{
1517    char clbuf[0x20];
1518    struct header_buf hbuf;
1519
1520    snprintf(clbuf, sizeof(clbuf), "%zd", content_len);
1521
1522    hbuf.off = 0;
1523    struct lsxpack_header  headers_arr[4];
1524    header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V(st_h->resp_status));
1525    header_set_ptr(&headers_arr[1], &hbuf, V("server"), V(LITESPEED_ID));
1526    header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html"));
1527    header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf));
1528    lsquic_http_headers_t headers = {
1529        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
1530        .headers = headers_arr,
1531    };
1532
1533    return lsquic_stream_send_headers(st_h->stream, &headers, 0);
1534}
1535
1536#define MIN(a, b) ((a) < (b) ? (a) : (b))
1537
1538static size_t
1539idle_read (void *lsqr_ctx, void *buf, size_t count)
1540{
1541    struct gen_file_ctx *const gfc = lsqr_ctx;
1542    unsigned char *p = buf;
1543    unsigned char *const end = p + count;
1544    size_t towrite;
1545
1546    while (p < end && gfc->remain > 0)
1547    {
1548        towrite = MIN((unsigned) (end - p), IDLE_SIZE - gfc->idle_off);
1549        if (towrite > gfc->remain)
1550            towrite = gfc->remain;
1551        memcpy(p, on_being_idle + gfc->idle_off, towrite);
1552        gfc->idle_off += towrite;
1553        if (gfc->idle_off == IDLE_SIZE)
1554            gfc->idle_off = 0;
1555        p += towrite;
1556        gfc->remain -= towrite;
1557    }
1558
1559    return p - (unsigned char *) buf;
1560}
1561
1562
1563static size_t
1564idle_size (void *lsqr_ctx)
1565{
1566    struct gen_file_ctx *const gfc = lsqr_ctx;
1567
1568    return gfc->remain;
1569}
1570
1571
1572static struct req *
1573new_req (enum method method, const char *path, const char *authority)
1574{
1575    struct req *req;
1576
1577    req = malloc(sizeof(*req));
1578    if (!req)
1579        return NULL;
1580
1581    memset(req, 0, offsetof(struct req, decode_buf));
1582    req->method = method;
1583    req->path = strdup(path);
1584    req->authority_str = strdup(authority);
1585    if (!(req->path && req->authority_str))
1586    {
1587        free(req->path);
1588        free(req->authority_str);
1589        free(req);
1590        return NULL;
1591    }
1592
1593    return req;
1594}
1595
1596
1597static ssize_t
1598my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt)
1599{
1600    struct gen_file_ctx *const gfc = user_data;
1601    size_t nread, nr;
1602    int i;
1603
1604    nread = 0;
1605    for (i = 0; i < iovcnt; ++i)
1606    {
1607        nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len);
1608        nread += nr;
1609    }
1610
1611    return (ssize_t) nread;
1612}
1613
1614
1615static void
1616idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1617{
1618    struct gen_file_ctx *const gfc = &st_h->interop_u.gfc;
1619    struct interop_push_path *push_path;
1620    struct lsxpack_header header_arr[4];
1621    struct lsquic_http_headers headers;
1622    struct req *req;
1623    ssize_t nw;
1624    struct header_buf hbuf;
1625    struct lsquic_reader reader;
1626
1627    if (st_h->flags & SH_HEADERS_SENT)
1628    {
1629        if (s_pwritev)
1630        {
1631            nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc,
1632                                                            gfc->remain);
1633            if (nw == 0)
1634                goto with_reader;
1635        }
1636        else
1637        {
1638  with_reader:
1639            reader.lsqr_read = idle_read,
1640            reader.lsqr_size = idle_size,
1641            reader.lsqr_ctx = gfc,
1642            nw = lsquic_stream_writef(stream, &reader);
1643        }
1644        if (nw < 0)
1645        {
1646            LSQ_ERROR("error writing idle thoughts: %s", strerror(errno));
1647            exit(1);
1648        }
1649        if (gfc->remain == 0)
1650            lsquic_stream_shutdown(stream, 1);
1651    }
1652    else
1653    {
1654        if (st_h->req->authority_str)
1655            while ((push_path = STAILQ_FIRST(&gfc->push_paths)))
1656            {
1657                STAILQ_REMOVE_HEAD(&gfc->push_paths, next);
1658                LSQ_DEBUG("pushing promise for %s", push_path->path);
1659                hbuf.off = 0;
1660                header_set_ptr(&header_arr[0], &hbuf, V(":method"), V("GET"));
1661                header_set_ptr(&header_arr[1], &hbuf, V(":path"), V(push_path->path));
1662                header_set_ptr(&header_arr[2], &hbuf, V(":authority"), V(st_h->req->authority_str));
1663                header_set_ptr(&header_arr[3], &hbuf, V(":scheme"), V("https"));
1664                headers.headers = header_arr;
1665                headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
1666                req = new_req(GET, push_path->path, st_h->req->authority_str);
1667                if (req)
1668                {
1669                    if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream),
1670                                                            req, stream, &headers))
1671                    {
1672                        LSQ_WARN("stream push failed");
1673                        interop_server_hset_destroy(req);
1674                    }
1675                }
1676                else
1677                    LSQ_WARN("cannot allocate req for push");
1678                free(push_path);
1679            }
1680        if (0 == send_headers2(stream, st_h, gfc->remain))
1681            st_h->flags |= SH_HEADERS_SENT;
1682        else
1683        {
1684            LSQ_ERROR("cannot send headers: %s", strerror(errno));
1685            lsquic_stream_close(stream);
1686        }
1687    }
1688}
1689
1690
1691static void
1692http_server_interop_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1693{
1694    struct resp *resp;
1695    ssize_t nw;
1696
1697    switch (st_h->interop_handler)
1698    {
1699    case IOH_ERROR:
1700        resp = &st_h->interop_u.err.resp;
1701        goto reply;
1702    case IOH_INDEX_HTML:
1703        resp = &st_h->interop_u.ihc.resp;
1704        goto reply;
1705    case IOH_VER_HEAD:
1706        resp = &st_h->interop_u.vhc.resp;
1707        goto reply;
1708    case IOH_MD5SUM:
1709        resp = &st_h->interop_u.md5c.resp;
1710        goto reply;
1711    case IOH_GEN_FILE:
1712        idle_on_write(stream, st_h);
1713        return;
1714    default:
1715        assert(0);
1716        return;
1717    }
1718
1719  reply:
1720    assert(resp->sz);   /* We always need to send body */
1721    if (!(st_h->flags & SH_HEADERS_SENT))
1722    {
1723        send_headers2(stream, st_h, resp->sz);
1724        st_h->flags |= SH_HEADERS_SENT;
1725        return;
1726    }
1727
1728    nw = lsquic_stream_write(stream, resp->buf + resp->off, resp->sz - resp->off);
1729    if (nw < 0)
1730    {
1731        LSQ_ERROR("error writing to stream: %s", strerror(errno));
1732        lsquic_conn_abort(lsquic_stream_conn(stream));
1733        return;
1734    }
1735
1736    resp->off += nw;
1737    lsquic_stream_flush(stream);
1738    if (resp->off == resp->sz)
1739        lsquic_stream_shutdown(stream, 1);
1740}
1741
1742
1743const struct lsquic_stream_if interop_http_server_if = {
1744    .on_new_conn            = http_server_on_new_conn,
1745    .on_conn_closed         = http_server_on_conn_closed,
1746    .on_new_stream          = http_server_on_new_stream,
1747    .on_read                = http_server_interop_on_read,
1748    .on_write               = http_server_interop_on_write,
1749    .on_close               = http_server_on_close,
1750};
1751#endif /* HAVE_REGEX */
1752
1753
1754static void
1755usage (const char *prog)
1756{
1757    const char *const slash = strrchr(prog, '/');
1758    if (slash)
1759        prog = slash + 1;
1760    printf(
1761"Usage: %s [opts]\n"
1762"\n"
1763"Options:\n"
1764"   -r ROOT     Document root\n"
1765"   -p FILE     Push request with this path\n"
1766"   -w SIZE     Write immediately (LSWS mode).  Argument specifies maximum\n"
1767"                 size of the immediate write.\n"
1768#if HAVE_PREADV
1769"   -P SIZE     Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n"
1770"                 write to stream.  Positive SIZE indicate maximum value per\n"
1771"                 write; negative means always use remaining file size.\n"
1772"                 Incompatible with -w.\n"
1773#endif
1774"   -y DELAY    Delay response for this many seconds -- use for debugging\n"
1775"   -Q ALPN     Use hq mode; ALPN could be \"hq-29\", for example.\n"
1776            , prog);
1777}
1778
1779
1780static void *
1781interop_server_hset_create (void *hsi_ctx, lsquic_stream_t *stream,
1782                            int is_push_promise)
1783{
1784    struct req *req;
1785
1786    req = malloc(sizeof(struct req));
1787    memset(req, 0, offsetof(struct req, decode_buf));
1788
1789    return req;
1790}
1791
1792
1793static struct lsxpack_header *
1794interop_server_hset_prepare_decode (void *hset_p, struct lsxpack_header *xhdr,
1795                                                                size_t req_space)
1796{
1797    struct req *req = hset_p;
1798
1799    if (xhdr)
1800    {
1801        LSQ_WARN("we don't reallocate headers: can't give more");
1802        return NULL;
1803    }
1804
1805    if (req->flags & HAVE_XHDR)
1806    {
1807        if (req->decode_off + lsxpack_header_get_dec_size(&req->xhdr)
1808                                                    >= sizeof(req->decode_buf))
1809        {
1810            LSQ_WARN("Not enough room in header");
1811            return NULL;
1812        }
1813        req->decode_off += lsxpack_header_get_dec_size(&req->xhdr);
1814    }
1815    else
1816        req->flags |= HAVE_XHDR;
1817
1818    lsxpack_header_prepare_decode(&req->xhdr, req->decode_buf,
1819                req->decode_off, sizeof(req->decode_buf) - req->decode_off);
1820    return &req->xhdr;
1821}
1822
1823
1824static int
1825interop_server_hset_add_header (void *hset_p, struct lsxpack_header *xhdr)
1826{
1827    struct req *req = hset_p;
1828    const char *name, *value;
1829    unsigned name_len, value_len;
1830
1831    if (!xhdr)
1832        return 0;
1833
1834    name = lsxpack_header_get_name(xhdr);
1835    value = lsxpack_header_get_value(xhdr);
1836    name_len = xhdr->name_len;
1837    value_len = xhdr->val_len;
1838
1839    req->qif_str = realloc(req->qif_str,
1840                        req->qif_sz + name_len + value_len + 2);
1841    if (!req->qif_str)
1842    {
1843        LSQ_ERROR("malloc failed");
1844        return -1;
1845    }
1846    memcpy(req->qif_str + req->qif_sz, name, name_len);
1847    req->qif_str[req->qif_sz + name_len] = '\t';
1848    memcpy(req->qif_str + req->qif_sz + name_len + 1, value, value_len);
1849    req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n';
1850    req->qif_sz += name_len + value_len + 2;
1851
1852    if (5 == name_len && 0 == strncmp(name, ":path", 5))
1853    {
1854        if (req->path)
1855            return 1;
1856        req->path = strndup(value, value_len);
1857        if (!req->path)
1858            return -1;
1859        return 0;
1860    }
1861
1862    if (7 == name_len && 0 == strncmp(name, ":method", 7))
1863    {
1864        if (req->method != UNSET)
1865            return 1;
1866        req->method_str = strndup(value, value_len);
1867        if (!req->method_str)
1868            return -1;
1869        if (0 == strcmp(req->method_str, "GET"))
1870            req->method = GET;
1871        else if (0 == strcmp(req->method_str, "POST"))
1872            req->method = POST;
1873        else
1874            req->method = UNSUPPORTED;
1875        return 0;
1876    }
1877
1878    if (10 == name_len && 0 == strncmp(name, ":authority", 10))
1879    {
1880        req->authority_str = strndup(value, value_len);
1881        if (!req->authority_str)
1882            return -1;
1883        return 0;
1884    }
1885
1886    return 0;
1887}
1888
1889
1890static void
1891interop_server_hset_destroy (void *hset_p)
1892{
1893    struct req *req = hset_p;
1894    free(req->qif_str);
1895    free(req->path);
1896    free(req->method_str);
1897    free(req->authority_str);
1898    free(req);
1899}
1900
1901
1902static const struct lsquic_hset_if header_bypass_api =
1903{
1904    .hsi_create_header_set  = interop_server_hset_create,
1905    .hsi_prepare_decode     = interop_server_hset_prepare_decode,
1906    .hsi_process_header     = interop_server_hset_add_header,
1907    .hsi_discard_header_set = interop_server_hset_destroy,
1908};
1909
1910
1911int
1912main (int argc, char **argv)
1913{
1914    int opt, s;
1915    struct stat st;
1916    struct server_ctx server_ctx;
1917    struct prog prog;
1918    const char *const *alpn;
1919
1920#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX)
1921    fprintf(stderr, "cannot run server without regex or open_memstream\n");
1922    return 1;
1923#endif
1924
1925    memset(&server_ctx, 0, sizeof(server_ctx));
1926    TAILQ_INIT(&server_ctx.sports);
1927    server_ctx.prog = &prog;
1928
1929    prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
1930                                            &http_server_if, &server_ctx);
1931
1932    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h"
1933#if HAVE_OPEN_MEMSTREAM
1934                                                    "Q:"
1935#endif
1936                                                                        )))
1937    {
1938        switch (opt) {
1939        case 'n':
1940            server_ctx.max_conn = atoi(optarg);
1941            break;
1942        case 'p':
1943            server_ctx.push_path = optarg;
1944            break;
1945        case 'r':
1946            if (-1 == stat(optarg, &st))
1947            {
1948                perror("stat");
1949                exit(2);
1950            }
1951#ifndef WIN32
1952            if (!S_ISDIR(st.st_mode))
1953            {
1954                fprintf(stderr, "`%s' is not a directory\n", optarg);
1955                exit(2);
1956            }
1957#endif
1958            server_ctx.document_root = optarg;
1959            break;
1960        case 'w':
1961            s_immediate_write = atoi(optarg);
1962            break;
1963        case 'P':
1964#if HAVE_PREADV
1965            s_pwritev = strtoull(optarg, NULL, 10);
1966            break;
1967#else
1968            fprintf(stderr, "preadv is not supported on this platform, "
1969                                                        "cannot use -P\n");
1970            exit(EXIT_FAILURE);
1971#endif
1972        case 'y':
1973            server_ctx.delay_resp_sec = atoi(optarg);
1974            break;
1975        case 'h':
1976            usage(argv[0]);
1977            prog_print_common_options(&prog, stdout);
1978            exit(0);
1979#if HAVE_OPEN_MEMSTREAM
1980        case 'Q':
1981            /* XXX A bit hacky, as `prog' has already been initialized... */
1982            prog.prog_engine_flags &= ~LSENG_HTTP;
1983            prog.prog_api.ea_stream_if = &hq_server_if;
1984            add_alpn(optarg);
1985            break;
1986#endif
1987        default:
1988            if (0 != prog_set_opt(&prog, opt, optarg))
1989                exit(1);
1990        }
1991    }
1992
1993    if (!server_ctx.document_root)
1994    {
1995#if HAVE_REGEX
1996        LSQ_NOTICE("Document root is not set: start in Interop Mode");
1997        init_map_regexes();
1998        prog.prog_api.ea_stream_if = &interop_http_server_if;
1999        prog.prog_api.ea_hsi_if = &header_bypass_api;
2000        prog.prog_api.ea_hsi_ctx = NULL;
2001#else
2002        LSQ_ERROR("Document root is not set: use -r option");
2003        exit(EXIT_FAILURE);
2004#endif
2005    }
2006
2007    if (s_immediate_write && s_pwritev)
2008    {
2009        LSQ_ERROR("-w and -P are incompatible options");
2010        exit(EXIT_FAILURE);
2011    }
2012
2013    alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions);
2014    while (*alpn)
2015    {
2016        if (0 == add_alpn(*alpn))
2017            ++alpn;
2018        else
2019        {
2020            LSQ_ERROR("cannot add ALPN %s", *alpn);
2021            exit(EXIT_FAILURE);
2022        }
2023    }
2024
2025    if (0 != prog_prep(&prog))
2026    {
2027        LSQ_ERROR("could not prep");
2028        exit(EXIT_FAILURE);
2029    }
2030
2031    LSQ_DEBUG("entering event loop");
2032
2033    s = prog_run(&prog);
2034    prog_cleanup(&prog);
2035
2036#if HAVE_REGEX
2037    if (!server_ctx.document_root)
2038        free_map_regexes();
2039#endif
2040
2041    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
2042}
2043