1/* Copyright (c) 2017 - 2022 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    enum {
422        PH_AUTHORITY    = 1 << 0,
423        PH_METHOD       = 1 << 1,
424        PH_PATH         = 1 << 2,
425    }            pseudo_headers;
426    char        *path;
427    char        *method_str;
428    char        *authority_str;
429    char        *qif_str;
430    size_t       qif_sz;
431    struct lsxpack_header
432                 xhdr;
433    size_t       decode_off;
434    char         decode_buf[MIN(LSXPACK_MAX_STRLEN + 1, 64 * 1024)];
435};
436
437
438struct interop_push_path
439{
440    STAILQ_ENTRY(interop_push_path)     next;
441    char                                path[0];
442};
443
444
445struct gen_file_ctx
446{
447    STAILQ_HEAD(, interop_push_path)    push_paths;
448    size_t      remain;
449    unsigned    idle_off;
450};
451
452
453struct lsquic_stream_ctx {
454    lsquic_stream_t     *stream;
455    struct server_ctx   *server_ctx;
456    FILE                *req_fh;
457    char                *req_buf;
458    char                *req_filename;
459    char                *req_path;
460    size_t               req_sz;
461    enum {
462        SH_HEADERS_SENT = (1 << 0),
463        SH_DELAYED      = (1 << 1),
464        SH_HEADERS_READ = (1 << 2),
465    }                    flags;
466    struct lsquic_reader reader;
467    int                  file_fd;   /* Used by pwritev */
468
469    /* Fields below are used by interop callbacks: */
470    enum interop_handler {
471        IOH_ERROR,
472        IOH_INDEX_HTML,
473        IOH_MD5SUM,
474        IOH_VER_HEAD,
475        IOH_GEN_FILE,
476        IOH_ECHO,
477    }                    interop_handler;
478    struct req          *req;
479    const char          *resp_status;
480    union {
481        struct index_html_ctx   ihc;
482        struct ver_head_ctx     vhc;
483        struct md5sum_ctx       md5c;
484        struct gen_file_ctx     gfc;
485        struct {
486            char buf[0x100];
487            struct resp resp;
488        }                       err;
489    }                    interop_u;
490    struct event        *resume_resp;
491    size_t               written;
492    size_t               file_size; /* Used by pwritev */
493};
494
495
496static lsquic_stream_ctx_t *
497http_server_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream)
498{
499    lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h));
500    st_h->stream = stream;
501    st_h->server_ctx = stream_if_ctx;
502    lsquic_stream_wantread(stream, 1);
503    return st_h;
504}
505
506
507static int
508ends_with (const char *filename, const char *ext)
509{
510    const char *where;
511
512    where = strstr(filename, ext);
513    return where
514        && strlen(where) == strlen(ext);
515}
516
517
518static const char *
519select_content_type (lsquic_stream_ctx_t *st_h)
520{
521    if (     ends_with(st_h->req_filename, ".html"))
522        return "text/html";
523    else if (ends_with(st_h->req_filename, ".png"))
524        return "image/png";
525    else if (ends_with(st_h->req_filename, ".css"))
526        return "text/css";
527    else if (ends_with(st_h->req_filename, ".gif"))
528        return "image/gif";
529    else if (ends_with(st_h->req_filename, ".txt"))
530        return "text/plain";
531    else
532        return "application/octet-stream";
533}
534
535
536static int
537send_headers (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
538{
539    const char *content_type;
540    struct header_buf hbuf;
541
542    content_type = select_content_type(st_h);
543    struct lsxpack_header headers_arr[2];
544
545    hbuf.off = 0;
546    header_set_ptr(&headers_arr[0], &hbuf, ":status", 7, "200", 3);
547    header_set_ptr(&headers_arr[1], &hbuf, "content-type", 12,
548                                        content_type, strlen(content_type));
549    lsquic_http_headers_t headers = {
550        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
551        .headers = headers_arr,
552    };
553    if (0 != lsquic_stream_send_headers(stream, &headers, 0))
554    {
555        LSQ_ERROR("cannot send headers: %s", strerror(errno));
556        return -1;
557    }
558
559    st_h->flags |= SH_HEADERS_SENT;
560    return 0;
561}
562
563
564static void
565resume_response (evutil_socket_t fd, short what, void *arg)
566{
567    struct lsquic_stream_ctx *const st_h = arg;
568
569    lsquic_stream_wantwrite(st_h->stream, 1);
570    event_del(st_h->resume_resp);
571    event_free(st_h->resume_resp);
572    st_h->resume_resp = NULL;
573
574    LSQ_NOTICE("resume response to stream %"PRIu64,
575                                                lsquic_stream_id(st_h->stream));
576    prog_process_conns(st_h->server_ctx->prog);
577}
578
579
580static size_t
581bytes_left (lsquic_stream_ctx_t *st_h)
582{
583    if (s_pwritev)
584        return st_h->file_size - st_h->written;
585    else
586        return test_reader_size(st_h->reader.lsqr_ctx);
587}
588
589
590static ssize_t
591my_preadv (void *user_data, const struct iovec *iov, int iovcnt)
592{
593#if HAVE_PREADV
594    lsquic_stream_ctx_t *const st_h = user_data;
595    ssize_t nread = preadv(st_h->file_fd, iov, iovcnt, st_h->written);
596    LSQ_DEBUG("%s: wrote %zd bytes", __func__, (size_t) nread);
597    return nread;
598#else
599    return -1;
600#endif
601}
602
603
604static size_t
605pwritev_fallback_read (void *lsqr_ctx, void *buf, size_t count)
606{
607    lsquic_stream_ctx_t *const st_h = lsqr_ctx;
608    struct iovec iov;
609    size_t ntoread;
610
611    ntoread = st_h->file_size - st_h->written;
612    if (ntoread > count)
613        count = ntoread;
614    iov.iov_base = buf;
615    iov.iov_len = count;
616    return my_preadv(lsqr_ctx, &iov, 1);
617}
618
619
620static size_t
621pwritev_fallback_size (void *lsqr_ctx)
622{
623    lsquic_stream_ctx_t *const st_h = lsqr_ctx;
624    return st_h->file_size - st_h->written;
625}
626
627
628static void
629http_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
630{
631    if (st_h->flags & SH_HEADERS_SENT)
632    {
633        ssize_t nw;
634        if (bytes_left(st_h) > 0)
635        {
636            if (st_h->server_ctx->delay_resp_sec
637                    && !(st_h->flags & SH_DELAYED)
638                        && st_h->written > 10000000)
639            {
640                struct timeval delay = {
641                                .tv_sec = st_h->server_ctx->delay_resp_sec, };
642                st_h->resume_resp = event_new(st_h->server_ctx->prog->prog_eb,
643                            -1, EV_TIMEOUT, resume_response, st_h);
644                if (st_h->resume_resp)
645                {
646                    event_add(st_h->resume_resp, &delay);
647                    lsquic_stream_wantwrite(stream, 0);
648                    st_h->flags |= SH_DELAYED;
649                    LSQ_NOTICE("delay response of stream %"PRIu64" for %u seconds",
650                        lsquic_stream_id(stream), st_h->server_ctx->delay_resp_sec);
651                    return;
652                }
653                else
654                    LSQ_ERROR("cannot allocate event");
655            }
656            if (s_pwritev)
657            {
658                size_t to_write = bytes_left(st_h);
659                if (s_pwritev > 0 && (size_t) s_pwritev < to_write)
660                    to_write = s_pwritev;
661                nw = lsquic_stream_pwritev(stream, my_preadv, st_h, to_write);
662                if (nw == 0)
663                {
664                    struct lsquic_reader reader = {
665                        .lsqr_read = pwritev_fallback_read,
666                        .lsqr_size = pwritev_fallback_size,
667                        .lsqr_ctx = st_h,
668                    };
669                    nw = lsquic_stream_writef(stream, &reader);
670                }
671            }
672            else
673            {
674                nw = lsquic_stream_writef(stream, &st_h->reader);
675            }
676            if (nw < 0)
677            {
678                struct lsquic_conn *conn = lsquic_stream_conn(stream);
679                lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
680                if (conn_h->flags & RECEIVED_GOAWAY)
681                {
682                    LSQ_NOTICE("cannot write: goaway received");
683                    lsquic_stream_close(stream);
684                }
685                else
686                {
687                    LSQ_ERROR("write error: %s", strerror(errno));
688                    exit(1);
689                }
690            }
691            if (bytes_left(st_h) > 0)
692            {
693                st_h->written += (size_t) nw;
694                lsquic_stream_wantwrite(stream, 1);
695            }
696            else
697            {
698                lsquic_stream_shutdown(stream, 1);
699                lsquic_stream_wantread(stream, 1);
700            }
701        }
702        else
703        {
704            lsquic_stream_shutdown(stream, 1);
705            lsquic_stream_wantread(stream, 1);
706        }
707    }
708    else
709    {
710        if (0 != send_headers(stream, st_h))
711            exit(1);
712    }
713}
714
715
716struct capped_reader_ctx
717{
718    struct lsquic_reader *inner_reader;
719    size_t                nread;
720};
721
722
723static size_t
724capped_reader_size (void *void_ctx)
725{
726    struct capped_reader_ctx *const capped_reader_ctx = void_ctx;
727    struct lsquic_reader *const inner_reader = capped_reader_ctx->inner_reader;
728    size_t size;
729
730    size = inner_reader->lsqr_size(inner_reader->lsqr_ctx);
731    return MIN((size_t) (s_immediate_write - capped_reader_ctx->nread), size);
732}
733
734
735static size_t
736capped_reader_read (void *void_ctx, void *buf, size_t count)
737{
738    struct capped_reader_ctx *const capped_reader_ctx = void_ctx;
739    struct lsquic_reader *const inner_reader = capped_reader_ctx->inner_reader;
740    size_t size;
741
742    count = MIN(count, (size_t) (s_immediate_write - capped_reader_ctx->nread));
743    size = inner_reader->lsqr_read(inner_reader->lsqr_ctx, buf, count);
744    capped_reader_ctx->nread += size;
745    return size;
746}
747
748
749#if HAVE_OPEN_MEMSTREAM
750static void
751parse_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
752{
753    char *filename;
754    int s;
755    regex_t re;
756    regmatch_t matches[2];
757
758    s = regcomp(&re, "GET (.*) HTTP/1.[01]\r\n", REG_EXTENDED);
759    if (0 != s)
760    {
761        perror("regcomp");
762        exit(3);
763    }
764
765    s = regexec(&re, st_h->req_buf, 2, matches, 0);
766    if (0 != s)
767    {
768        LSQ_WARN("GET request could not be parsed: `%s'", st_h->req_buf);
769        regfree(&re);
770        return;
771    }
772
773    regfree(&re);
774
775    filename = malloc(strlen(st_h->server_ctx->document_root) + 1 +
776                                matches[1].rm_eo - matches[1].rm_so + 1);
777    strcpy(filename, st_h->server_ctx->document_root);
778    strcat(filename, "/");
779    strncat(filename, st_h->req_buf + matches[1].rm_so,
780                                        matches[1].rm_eo - matches[1].rm_so);
781
782    LSQ_INFO("filename to fetch: %s", filename);
783
784    st_h->req_filename = filename;
785    st_h->req_path     = strdup(filename);
786}
787
788
789static void
790process_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
791{
792    struct stat st;
793
794    if (s_pwritev)
795    {
796        st_h->file_fd = open(st_h->req_path, O_RDONLY);
797        if (st_h->file_fd < 0)
798        {
799            LSQ_ERROR("cannot open %s for reading: %s", st_h->req_path,
800                                                            strerror(errno));
801            exit(1);
802        }
803        if (fstat(st_h->file_fd, &st) < 0)
804        {
805            LSQ_ERROR("fstat: %s", strerror(errno));
806            exit(1);
807        }
808        st_h->file_size = st.st_size;
809    }
810    else
811    {
812        st_h->reader.lsqr_read = test_reader_read;
813        st_h->reader.lsqr_size = test_reader_size;
814        st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path);
815        if (!st_h->reader.lsqr_ctx)
816            exit(1);
817    }
818
819    if (s_immediate_write)
820    {
821        if (0 != send_headers(stream, st_h))
822            exit(1);
823
824        if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
825        {
826            struct capped_reader_ctx capped_reader_ctx =
827            {
828                .inner_reader = &st_h->reader,
829            };
830            struct lsquic_reader capped_reader =
831            {
832                .lsqr_read  = capped_reader_read,
833                .lsqr_size  = capped_reader_size,
834                .lsqr_ctx   = &capped_reader_ctx,
835            };
836            ssize_t nw;
837            nw = lsquic_stream_writef(stream, &capped_reader);
838            if (nw < 0)
839            {
840                LSQ_ERROR("write error: %s", strerror(errno));
841                exit(1);
842            }
843        }
844
845        if (test_reader_size(st_h->reader.lsqr_ctx) > 0)
846        {
847            lsquic_stream_flush(stream);
848            lsquic_stream_wantwrite(stream, 1);
849        }
850        else
851        {
852            lsquic_stream_shutdown(stream, 1);
853            lsquic_stream_wantread(stream, 1);
854        }
855    }
856    else
857        lsquic_stream_wantwrite(st_h->stream, 1);
858}
859
860
861static struct hset_fm      /* FM stands for Filesystem Mode */
862{
863    unsigned    id;
864    char       *path;
865} *
866new_hset_fm (const char *path)
867{
868    static unsigned hfm_id;
869    struct hset_fm *const hfm = malloc(sizeof(*hfm));
870    char *const str = strdup(path);
871    if (hfm && path)
872    {
873        hfm->id = hfm_id++;
874        hfm->path = str;
875        return hfm;
876    }
877    else
878    {
879        free(str);
880        free(hfm);
881        return NULL;
882    }
883}
884
885
886static void
887destroy_hset_fm (struct hset_fm *hfm)
888{
889    free(hfm->path);
890    free(hfm);
891}
892
893
894static int
895push_promise (lsquic_stream_ctx_t *st_h, lsquic_stream_t *stream)
896{
897    lsquic_conn_t *conn;
898    int s;
899    regex_t re;
900    regmatch_t matches[2];
901    struct hset_fm *hfm;
902    struct header_buf hbuf;
903
904    s = regcomp(&re, "\r\nHost: *([[:alnum:].][[:alnum:].]*)\r\n",
905                                                    REG_EXTENDED|REG_ICASE);
906    if (0 != s)
907    {
908        perror("regcomp");
909        exit(3);
910    }
911
912    s = regexec(&re, st_h->req_buf, 2, matches, 0);
913    if (0 != s)
914    {
915        LSQ_WARN("Could not find host header in request `%s'", st_h->req_buf);
916        regfree(&re);
917        return -1;
918    }
919    regfree(&re);
920
921    hfm = new_hset_fm(st_h->server_ctx->push_path);
922    if (!hfm)
923    {
924        LSQ_WARN("Could not allocate hfm");
925        return -1;
926    }
927
928#define V(v) (v), strlen(v)
929    hbuf.off = 0;
930    struct lsxpack_header headers_arr[6];
931    header_set_ptr(&headers_arr[0], &hbuf, V(":method"), V("GET"));
932    header_set_ptr(&headers_arr[1], &hbuf, V(":path"),
933                                            V(st_h->server_ctx->push_path));
934    header_set_ptr(&headers_arr[2], &hbuf, V(":authority"),
935        st_h->req_buf + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so);
936    header_set_ptr(&headers_arr[3], &hbuf, V(":scheme"), V("https"));
937    header_set_ptr(&headers_arr[4], &hbuf, V("x-some-header"),
938                                                        V("x-some-value"));
939    header_set_ptr(&headers_arr[5], &hbuf, V("x-kenny-status"),
940                        V("Oh my God!  They killed Kenny!!!  You bastards!"));
941    lsquic_http_headers_t headers = {
942        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
943        .headers = headers_arr,
944    };
945
946    conn = lsquic_stream_conn(stream);
947    s = lsquic_conn_push_stream(conn, hfm, stream, &headers);
948    if (0 == s)
949        LSQ_NOTICE("pushed stream successfully");
950    else
951    {
952        destroy_hset_fm(hfm);
953        LSQ_ERROR("could not push stream: %s", strerror(errno));
954    }
955
956    return 0;
957}
958
959
960static void
961http_server_on_read_pushed (struct lsquic_stream *stream,
962                                                    lsquic_stream_ctx_t *st_h)
963{
964    struct hset_fm *hfm;
965
966    hfm = lsquic_stream_get_hset(stream);
967    if (!hfm)
968    {
969        LSQ_ERROR("%s: error fetching hset: %s", __func__, strerror(errno));
970        lsquic_stream_close(stream);
971        return;
972    }
973
974    LSQ_INFO("got push request #%u for %s", hfm->id, hfm->path);
975    st_h->req_path = malloc(strlen(st_h->server_ctx->document_root) + 1 +
976                                strlen(hfm->path) + 1);
977    strcpy(st_h->req_path, st_h->server_ctx->document_root);
978    strcat(st_h->req_path, "/");
979    strcat(st_h->req_path, hfm->path);
980    st_h->req_filename = strdup(st_h->req_path);  /* XXX Only used for ends_with: drop it? */
981
982    process_request(stream, st_h);
983    free(st_h->req_buf);
984    lsquic_stream_shutdown(stream, 0);
985    destroy_hset_fm(hfm);
986}
987
988
989static void
990http_server_on_read_regular (struct lsquic_stream *stream,
991                                                    lsquic_stream_ctx_t *st_h)
992{
993    unsigned char buf[0x400];
994    ssize_t nread;
995    int s;
996
997    if (!st_h->req_fh)
998        st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz);
999
1000    nread = lsquic_stream_read(stream, buf, sizeof(buf));
1001    if (nread > 0)
1002        fwrite(buf, 1, nread, st_h->req_fh);
1003    else if (0 == nread)
1004    {
1005        fwrite("", 1, 1, st_h->req_fh);  /* NUL-terminate so that we can regex the string */
1006        fclose(st_h->req_fh);
1007        LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf);
1008        parse_request(stream, st_h);
1009        if (st_h->server_ctx->push_path &&
1010                0 != strcmp(st_h->req_path, st_h->server_ctx->push_path))
1011        {
1012            s = push_promise(st_h, stream);
1013            if (s != 0)
1014                exit(1);
1015        }
1016        process_request(stream, st_h);
1017        free(st_h->req_buf);
1018        lsquic_stream_shutdown(stream, 0);
1019    }
1020    else
1021    {
1022        LSQ_ERROR("error reading: %s", strerror(errno));
1023        lsquic_stream_close(stream);
1024    }
1025}
1026#endif
1027
1028
1029static void
1030http_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1031{
1032#if HAVE_OPEN_MEMSTREAM
1033    if (lsquic_stream_is_pushed(stream))
1034        http_server_on_read_pushed(stream, st_h);
1035    else
1036        http_server_on_read_regular(stream, st_h);
1037#else
1038    LSQ_ERROR("%s: open_memstream not supported\n", __func__);
1039    exit(1);
1040#endif
1041}
1042
1043
1044static void
1045http_server_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1046{
1047    free(st_h->req_filename);
1048    free(st_h->req_path);
1049    if (st_h->reader.lsqr_ctx)
1050        destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx);
1051#if HAVE_PREADV
1052    if (s_pwritev)
1053        close(st_h->file_fd);
1054#endif
1055    if (st_h->req)
1056        interop_server_hset_destroy(st_h->req);
1057    free(st_h);
1058    LSQ_INFO("%s called, has unacked data: %d", __func__,
1059                                lsquic_stream_has_unacked_data(stream));
1060}
1061
1062
1063const struct lsquic_stream_if http_server_if = {
1064    .on_new_conn            = http_server_on_new_conn,
1065    .on_conn_closed         = http_server_on_conn_closed,
1066    .on_new_stream          = http_server_on_new_stream,
1067    .on_read                = http_server_on_read,
1068    .on_write               = http_server_on_write,
1069    .on_close               = http_server_on_close,
1070    .on_goaway_received     = http_server_on_goaway,
1071};
1072
1073
1074#if HAVE_OPEN_MEMSTREAM
1075static void
1076hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1077{
1078    char tbuf[0x100], *buf;
1079    ssize_t nread;
1080    char *path, *end, *filename;
1081
1082    if (!st_h->req_fh)
1083        st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz);
1084
1085    nread = lsquic_stream_read(stream, tbuf, sizeof(tbuf));
1086    if (nread > 0)
1087    {
1088        fwrite(tbuf, 1, nread, st_h->req_fh);
1089        return;
1090    }
1091
1092    if (nread < 0)
1093    {
1094        LSQ_WARN("error reading request from stream: %s", strerror(errno));
1095        lsquic_stream_close(stream);
1096        return;
1097    }
1098
1099    fwrite("", 1, 1, st_h->req_fh);
1100    fclose(st_h->req_fh);
1101    LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf);
1102
1103    buf = st_h->req_buf;
1104    path = strchr(buf, ' ');
1105    if (!path)
1106    {
1107        LSQ_WARN("invalid request (no space character): `%s'", buf);
1108        lsquic_stream_close(stream);
1109        return;
1110    }
1111    if (!(path - buf == 3 && 0 == strncasecmp(buf, "GET", 3)))
1112    {
1113        LSQ_NOTICE("unsupported method `%.*s'", (int) (path - buf), buf);
1114        lsquic_stream_close(stream);
1115        return;
1116    }
1117    ++path;
1118    for (end = buf + st_h->req_sz - 1; end > path
1119                && (*end == '\0' || *end == '\r' || *end == '\n'); --end)
1120        *end = '\0';
1121    LSQ_NOTICE("parsed out request path: %s", path);
1122
1123    filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + strlen(path) + 1);
1124    strcpy(filename, st_h->server_ctx->document_root);
1125    strcat(filename, "/");
1126    strcat(filename, path);
1127    LSQ_NOTICE("file to fetch: %s", filename);
1128    /* XXX This copy pasta is getting a bit annoying now: two mallocs of the
1129     * same thing?
1130     */
1131    st_h->req_filename = filename;
1132    st_h->req_path = strdup(filename);
1133    st_h->reader.lsqr_read = test_reader_read;
1134    st_h->reader.lsqr_size = test_reader_size;
1135    st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path);
1136    if (!st_h->reader.lsqr_ctx)
1137    {
1138        lsquic_stream_close(stream);
1139        return;
1140    }
1141    lsquic_stream_shutdown(stream, 0);
1142    lsquic_stream_wantwrite(stream, 1);
1143}
1144
1145
1146static void
1147hq_server_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h)
1148{
1149    ssize_t nw;
1150
1151    nw = lsquic_stream_writef(stream, &st_h->reader);
1152    if (nw < 0)
1153    {
1154        struct lsquic_conn *conn = lsquic_stream_conn(stream);
1155        lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn);
1156        if (conn_h->flags & RECEIVED_GOAWAY)
1157        {
1158            LSQ_NOTICE("cannot write: goaway received");
1159            lsquic_stream_close(stream);
1160        }
1161        else
1162        {
1163            LSQ_ERROR("write error: %s", strerror(errno));
1164            lsquic_stream_close(stream);
1165        }
1166    }
1167    else if (bytes_left(st_h) > 0)
1168    {
1169        st_h->written += (size_t) nw;
1170        lsquic_stream_wantwrite(stream, 1);
1171    }
1172    else
1173    {
1174        lsquic_stream_shutdown(stream, 1);
1175        lsquic_stream_wantread(stream, 1);
1176    }
1177}
1178
1179
1180const struct lsquic_stream_if hq_server_if = {
1181    .on_new_conn            = http_server_on_new_conn,
1182    .on_conn_closed         = http_server_on_conn_closed,
1183    .on_new_stream          = http_server_on_new_stream,
1184    .on_read                = hq_server_on_read,
1185    .on_write               = hq_server_on_write,
1186    .on_close               = http_server_on_close,
1187};
1188#endif
1189
1190
1191#if HAVE_REGEX
1192struct req_map
1193{
1194    enum method             method;
1195    const char             *path;
1196    enum interop_handler    handler;
1197    const char             *status;
1198    enum {
1199        RM_WANTBODY     = 1 << 0,
1200        RM_REGEX        = 1 << 1,
1201        RM_COMPILED     = 1 << 2,
1202    }                       flags;
1203    regex_t                 re;
1204};
1205
1206
1207static struct req_map req_maps[] =
1208{
1209    { .method = GET, .path = "/", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1210    { .method = GET, .path = "/index.html", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1211    { .method = POST, .path = "/cgi-bin/md5sum.cgi", .handler = IOH_MD5SUM, .status = "200", .flags = RM_WANTBODY, },
1212    { .method = POST, .path = "/cgi-bin/verify-headers.cgi", .handler = IOH_VER_HEAD, .status = "200", .flags = RM_WANTBODY, },
1213    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1214    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1215    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1216    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1217    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1218    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1219    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1220    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1221};
1222
1223
1224#define MAX_MATCHES 5
1225
1226
1227static void
1228init_map_regexes (void)
1229{
1230    struct req_map *map;
1231
1232    for (map = req_maps; map < req_maps + sizeof(req_maps)
1233                                            / sizeof(req_maps[0]); ++map)
1234        if (map->flags & RM_REGEX)
1235        {
1236#ifndef NDEBUG
1237            int s;
1238            s =
1239#endif
1240            regcomp(&map->re, map->path, REG_EXTENDED|REG_ICASE);
1241            assert(0 == s);
1242            map->flags |= RM_COMPILED;
1243        }
1244}
1245
1246
1247static void
1248free_map_regexes (void)
1249{
1250    struct req_map *map;
1251
1252    for (map = req_maps; map < req_maps + sizeof(req_maps)
1253                                            / sizeof(req_maps[0]); ++map)
1254        if (map->flags & RM_COMPILED)
1255        {
1256            regfree(&map->re);
1257            map->flags &= ~RM_COMPILED;
1258        }
1259}
1260
1261
1262static const struct req_map *
1263find_handler (enum method method, const char *path, regmatch_t *matches)
1264{
1265    const struct req_map *map;
1266
1267    for (map = req_maps; map < req_maps + sizeof(req_maps)
1268                                            / sizeof(req_maps[0]); ++map)
1269        if (map->flags & RM_COMPILED)
1270        {
1271            if (0 == regexec(&map->re, path, MAX_MATCHES + 1, matches, 0))
1272                return map;
1273        }
1274        else if (0 == strcasecmp(path, map->path))
1275            return map;
1276
1277    return NULL;
1278}
1279
1280
1281static const char INDEX_HTML[] =
1282"<html>\n"
1283"   <head>\n"
1284"       <title>LiteSpeed IETF QUIC Server Index Page</title>\n"
1285"   </head>\n"
1286"   <body>\n"
1287"       <h1>LiteSpeed IETF QUIC Server Index Page</h1>\n"
1288"       <p>Hello!  Welcome to the interop.  Available services:\n"
1289"       <ul>\n"
1290"           <li><b>POST to /cgi-bin/md5sum.cgi</b>.  This will return\n"
1291"                   MD5 checksum of the request body.\n"
1292"           <li><b>GET /123K</b> or <b>GET /file-123K</b>.  This will return\n"
1293"                   requested number of payload in the form of repeating text\n"
1294"                   by Jerome K. Jerome.  The size specification must match\n"
1295"                   (\\d+)[KMG]? and the total size request must not exceed\n"
1296"                   2 gigabytes.  Then, you will get back that many bytes\n"
1297"                   of the <a\n"
1298"                       href=http://www.gutenberg.org/cache/epub/849/pg849.txt\n"
1299"                                                       >beloved classic</a>.\n"
1300"       </ul>\n"
1301"   </body>\n"
1302"</html>\n"
1303;
1304
1305
1306static size_t
1307read_md5 (void *ctx, const unsigned char *buf, size_t sz, int fin)
1308{
1309    struct lsquic_stream_ctx *st_h = ctx;
1310
1311    if (sz)
1312        MD5_Update(&st_h->interop_u.md5c.md5ctx, buf, sz);
1313
1314    if (fin)
1315        st_h->interop_u.md5c.done = 1;
1316
1317    return sz;
1318}
1319
1320
1321static void
1322http_server_interop_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1323{
1324#define ERROR_RESP(code, ...) do {  \
1325    LSQ_WARN(__VA_ARGS__);                                     \
1326    st_h->interop_handler = IOH_ERROR; \
1327    st_h->resp_status = #code;  \
1328    st_h->interop_u.err.resp.sz = snprintf(st_h->interop_u.err.buf, \
1329                            sizeof(st_h->interop_u.err.buf), __VA_ARGS__);  \
1330    if (st_h->interop_u.err.resp.sz >= sizeof(st_h->interop_u.err.buf))     \
1331        st_h->interop_u.err.resp.sz = sizeof(st_h->interop_u.err.buf) - 1;  \
1332    st_h->interop_u.err.resp.buf = st_h->interop_u.err.buf;                 \
1333    st_h->interop_u.err.resp.off = 0;                                       \
1334    goto err;                                                               \
1335} while (0)
1336
1337    const struct req_map *map;
1338    ssize_t nw;
1339    size_t need;
1340    unsigned len, i;
1341    struct interop_push_path *push_path;
1342    regmatch_t matches[MAX_MATCHES + 1];
1343    unsigned char md5sum[MD5_DIGEST_LENGTH];
1344    char md5str[ sizeof(md5sum) * 2 + 1 ];
1345    char byte[1];
1346
1347    if (!(st_h->flags & SH_HEADERS_READ))
1348    {
1349        st_h->flags |= SH_HEADERS_READ;
1350        st_h->req = lsquic_stream_get_hset(stream);
1351        if (!st_h->req)
1352            ERROR_RESP(500, "Internal error: cannot fetch header set from stream");
1353        else if (st_h->req->method == UNSET)
1354            ERROR_RESP(400, "Method is not specified");
1355        else if (!st_h->req->path)
1356            ERROR_RESP(400, "Path is not specified");
1357        else if (st_h->req->method == UNSUPPORTED)
1358            ERROR_RESP(501, "Method %s is not supported", st_h->req->method_str);
1359        else if (!(map = find_handler(st_h->req->method, st_h->req->path, matches)))
1360            ERROR_RESP(404, "No handler found for method: %s; path: %s",
1361                st_h->req->method_str, st_h->req->path);
1362        else
1363        {
1364            LSQ_INFO("found handler for %s %s", st_h->req->method_str, st_h->req->path);
1365            st_h->resp_status = map->status;
1366            st_h->interop_handler = map->handler;
1367            switch (map->handler)
1368            {
1369            case IOH_INDEX_HTML:
1370                st_h->interop_u.ihc.resp = (struct resp) { INDEX_HTML, sizeof(INDEX_HTML) - 1, 0, };
1371                break;
1372            case IOH_VER_HEAD:
1373                st_h->interop_u.vhc.resp = (struct resp) {
1374                        st_h->req->qif_str, st_h->req->qif_sz, 0, };
1375                break;
1376            case IOH_MD5SUM:
1377                MD5_Init(&st_h->interop_u.md5c.md5ctx);
1378                st_h->interop_u.md5c.done = 0;
1379                break;
1380            case IOH_GEN_FILE:
1381                STAILQ_INIT(&st_h->interop_u.gfc.push_paths);
1382                st_h->interop_u.gfc.remain = strtol(st_h->req->path + matches[1].rm_so, NULL, 10);
1383                if (matches[2].rm_so >= 0
1384                        && matches[2].rm_so < matches[2].rm_eo)
1385                {
1386                    switch (st_h->req->path[ matches[2].rm_so ])
1387                    {
1388                    case 'G':
1389                    case 'g':
1390                        st_h->interop_u.gfc.remain <<= 30;
1391                        break;
1392                    case 'M':
1393                    case 'm':
1394                        st_h->interop_u.gfc.remain <<= 20;
1395                        break;
1396                    case 'K':
1397                    case 'k':
1398                        st_h->interop_u.gfc.remain <<= 10;
1399                        break;
1400                    }
1401                }
1402                if (st_h->interop_u.gfc.remain > 2 * (1u << 30))
1403                    ERROR_RESP(406, "Response of %zd bytes is too long to generate",
1404                        st_h->interop_u.gfc.remain);
1405                st_h->interop_u.gfc.idle_off = 0;
1406                for (i = 3; i <= MAX_MATCHES; ++i)
1407                    if (matches[i].rm_so >= 0)
1408                    {
1409                        len = matches[i].rm_eo - matches[i].rm_so;
1410                        push_path = malloc(sizeof(*push_path) + len + 1);
1411                        memcpy(push_path->path, st_h->req->path
1412                            + matches[i].rm_so, len);
1413                        push_path->path[len] ='\0';
1414                        STAILQ_INSERT_TAIL(&st_h->interop_u.gfc.push_paths,
1415                                                                push_path, next);
1416                    }
1417                    else
1418                        break;
1419                break;
1420            default:
1421                /* TODO: implement this */
1422                assert(0);
1423                break;
1424            }
1425        }
1426
1427        if (!(map->flags & RM_WANTBODY))
1428        {
1429  err:
1430            lsquic_stream_shutdown(stream, 0);
1431            lsquic_stream_wantwrite(stream, 1);
1432        }
1433    }
1434    else
1435    {
1436        switch (st_h->interop_handler)
1437        {
1438        case IOH_MD5SUM:
1439            assert(!st_h->interop_u.md5c.done);
1440            nw = lsquic_stream_readf(stream, read_md5, st_h);
1441            if (nw < 0)
1442            {
1443                LSQ_ERROR("could not read from stream for MD5: %s", strerror(errno));
1444                exit(1);
1445            }
1446            if (nw == 0)
1447                st_h->interop_u.md5c.done = 1;
1448            if (st_h->interop_u.md5c.done)
1449            {
1450                MD5_Final(md5sum, &st_h->interop_u.md5c.md5ctx);
1451                lsquic_hexstr(md5sum, sizeof(md5sum), md5str, sizeof(md5str));
1452                snprintf(st_h->interop_u.md5c.resp_buf, sizeof(st_h->interop_u.md5c.resp_buf),
1453                    "<html><head><title>MD5 Checksum Result</title></head>\n"
1454                    "<body><h1>MD5 Checksum Result</h1>\n<p>"
1455                    "MD5 Checksum: <tt>%s</tt>\n</body></html>\n",
1456                    md5str);
1457                st_h->interop_u.md5c.resp.buf = st_h->interop_u.md5c.resp_buf;
1458                st_h->interop_u.md5c.resp.sz = strlen(st_h->interop_u.md5c.resp_buf);
1459                st_h->interop_u.md5c.resp.off = 0;
1460                lsquic_stream_shutdown(stream, 0);
1461                lsquic_stream_wantwrite(stream, 1);
1462            }
1463            break;
1464        case IOH_VER_HEAD:
1465            if (!st_h->interop_u.vhc.req_body)
1466            {
1467                st_h->interop_u.vhc.req_body = malloc(st_h->req->qif_sz);
1468                if (!st_h->interop_u.vhc.req_body)
1469                {
1470                    perror("malloc");
1471                    exit(1);
1472                }
1473            }
1474            need = st_h->req->qif_sz - st_h->interop_u.vhc.req_sz;
1475            if (need > 0)
1476            {
1477                nw = lsquic_stream_read(stream,
1478                        st_h->interop_u.vhc.req_body
1479                                        + st_h->interop_u.vhc.req_sz, need);
1480                if (nw > 0)
1481                    st_h->interop_u.vhc.req_sz += need;
1482                else if (nw == 0)
1483                {
1484                    LSQ_WARN("request body too short (does not match headers)");
1485                    lsquic_stream_shutdown(stream, 0);
1486                    lsquic_stream_wantwrite(stream, 1);
1487                }
1488                else
1489                {
1490                    LSQ_ERROR("error reading from stream");
1491                    exit(1);
1492                }
1493            }
1494            else
1495            {
1496                nw = lsquic_stream_read(stream, byte, sizeof(byte));
1497                if (nw == 0)
1498                {
1499                    if (0 == memcmp(st_h->req->qif_str,
1500                            st_h->interop_u.vhc.req_body, st_h->req->qif_sz))
1501                        LSQ_INFO("request headers and payload check out");
1502                    else
1503                        LSQ_WARN("request headers and payload are different");
1504                }
1505                else
1506                    LSQ_WARN("request body too long (does not match headers)");
1507                lsquic_stream_shutdown(stream, 0);
1508                lsquic_stream_wantwrite(stream, 1);
1509            }
1510            break;
1511        default:
1512            assert(0);
1513        }
1514    }
1515}
1516
1517
1518static int
1519send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h,
1520                    size_t content_len)
1521{
1522    char clbuf[0x20];
1523    struct header_buf hbuf;
1524
1525    snprintf(clbuf, sizeof(clbuf), "%zd", content_len);
1526
1527    hbuf.off = 0;
1528    struct lsxpack_header  headers_arr[4];
1529    header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V(st_h->resp_status));
1530    header_set_ptr(&headers_arr[1], &hbuf, V("server"), V(LITESPEED_ID));
1531    header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html"));
1532    header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf));
1533    lsquic_http_headers_t headers = {
1534        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
1535        .headers = headers_arr,
1536    };
1537
1538    return lsquic_stream_send_headers(st_h->stream, &headers, 0);
1539}
1540
1541#define MIN(a, b) ((a) < (b) ? (a) : (b))
1542
1543static size_t
1544idle_read (void *lsqr_ctx, void *buf, size_t count)
1545{
1546    struct gen_file_ctx *const gfc = lsqr_ctx;
1547    unsigned char *p = buf;
1548    unsigned char *const end = p + count;
1549    size_t towrite;
1550
1551    while (p < end && gfc->remain > 0)
1552    {
1553        towrite = MIN((unsigned) (end - p), IDLE_SIZE - gfc->idle_off);
1554        if (towrite > gfc->remain)
1555            towrite = gfc->remain;
1556        memcpy(p, on_being_idle + gfc->idle_off, towrite);
1557        gfc->idle_off += towrite;
1558        if (gfc->idle_off == IDLE_SIZE)
1559            gfc->idle_off = 0;
1560        p += towrite;
1561        gfc->remain -= towrite;
1562    }
1563
1564    return p - (unsigned char *) buf;
1565}
1566
1567
1568static size_t
1569idle_size (void *lsqr_ctx)
1570{
1571    struct gen_file_ctx *const gfc = lsqr_ctx;
1572
1573    return gfc->remain;
1574}
1575
1576
1577static struct req *
1578new_req (enum method method, const char *path, const char *authority)
1579{
1580    struct req *req;
1581
1582    req = malloc(sizeof(*req));
1583    if (!req)
1584        return NULL;
1585
1586    memset(req, 0, offsetof(struct req, decode_buf));
1587    req->method = method;
1588    req->path = strdup(path);
1589    req->authority_str = strdup(authority);
1590    if (!(req->path && req->authority_str))
1591    {
1592        free(req->path);
1593        free(req->authority_str);
1594        free(req);
1595        return NULL;
1596    }
1597
1598    return req;
1599}
1600
1601
1602static ssize_t
1603my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt)
1604{
1605    struct gen_file_ctx *const gfc = user_data;
1606    size_t nread, nr;
1607    int i;
1608
1609    nread = 0;
1610    for (i = 0; i < iovcnt; ++i)
1611    {
1612        nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len);
1613        nread += nr;
1614    }
1615
1616    return (ssize_t) nread;
1617}
1618
1619
1620static void
1621idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1622{
1623    struct gen_file_ctx *const gfc = &st_h->interop_u.gfc;
1624    struct interop_push_path *push_path;
1625    struct lsxpack_header header_arr[4];
1626    struct lsquic_http_headers headers;
1627    struct req *req;
1628    ssize_t nw;
1629    struct header_buf hbuf;
1630    struct lsquic_reader reader;
1631
1632    if (st_h->flags & SH_HEADERS_SENT)
1633    {
1634        if (s_pwritev)
1635        {
1636            nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc,
1637                                                            gfc->remain);
1638            if (nw == 0)
1639                goto with_reader;
1640        }
1641        else
1642        {
1643  with_reader:
1644            reader.lsqr_read = idle_read,
1645            reader.lsqr_size = idle_size,
1646            reader.lsqr_ctx = gfc,
1647            nw = lsquic_stream_writef(stream, &reader);
1648        }
1649        if (nw < 0)
1650        {
1651            LSQ_ERROR("error writing idle thoughts: %s", strerror(errno));
1652            exit(1);
1653        }
1654        if (gfc->remain == 0)
1655            lsquic_stream_shutdown(stream, 1);
1656    }
1657    else
1658    {
1659        if (st_h->req->authority_str)
1660            while ((push_path = STAILQ_FIRST(&gfc->push_paths)))
1661            {
1662                STAILQ_REMOVE_HEAD(&gfc->push_paths, next);
1663                LSQ_DEBUG("pushing promise for %s", push_path->path);
1664                hbuf.off = 0;
1665                header_set_ptr(&header_arr[0], &hbuf, V(":method"), V("GET"));
1666                header_set_ptr(&header_arr[1], &hbuf, V(":path"), V(push_path->path));
1667                header_set_ptr(&header_arr[2], &hbuf, V(":authority"), V(st_h->req->authority_str));
1668                header_set_ptr(&header_arr[3], &hbuf, V(":scheme"), V("https"));
1669                headers.headers = header_arr;
1670                headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
1671                req = new_req(GET, push_path->path, st_h->req->authority_str);
1672                if (req)
1673                {
1674                    if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream),
1675                                                            req, stream, &headers))
1676                    {
1677                        LSQ_WARN("stream push failed");
1678                        interop_server_hset_destroy(req);
1679                    }
1680                }
1681                else
1682                    LSQ_WARN("cannot allocate req for push");
1683                free(push_path);
1684            }
1685        if (0 == send_headers2(stream, st_h, gfc->remain))
1686            st_h->flags |= SH_HEADERS_SENT;
1687        else
1688        {
1689            LSQ_ERROR("cannot send headers: %s", strerror(errno));
1690            lsquic_stream_close(stream);
1691        }
1692    }
1693}
1694
1695
1696static void
1697http_server_interop_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1698{
1699    struct resp *resp;
1700    ssize_t nw;
1701
1702    switch (st_h->interop_handler)
1703    {
1704    case IOH_ERROR:
1705        resp = &st_h->interop_u.err.resp;
1706        goto reply;
1707    case IOH_INDEX_HTML:
1708        resp = &st_h->interop_u.ihc.resp;
1709        goto reply;
1710    case IOH_VER_HEAD:
1711        resp = &st_h->interop_u.vhc.resp;
1712        goto reply;
1713    case IOH_MD5SUM:
1714        resp = &st_h->interop_u.md5c.resp;
1715        goto reply;
1716    case IOH_GEN_FILE:
1717        idle_on_write(stream, st_h);
1718        return;
1719    default:
1720        assert(0);
1721        return;
1722    }
1723
1724  reply:
1725    assert(resp->sz);   /* We always need to send body */
1726    if (!(st_h->flags & SH_HEADERS_SENT))
1727    {
1728        send_headers2(stream, st_h, resp->sz);
1729        st_h->flags |= SH_HEADERS_SENT;
1730        return;
1731    }
1732
1733    nw = lsquic_stream_write(stream, resp->buf + resp->off, resp->sz - resp->off);
1734    if (nw < 0)
1735    {
1736        LSQ_ERROR("error writing to stream: %s", strerror(errno));
1737        lsquic_conn_abort(lsquic_stream_conn(stream));
1738        return;
1739    }
1740
1741    resp->off += nw;
1742    lsquic_stream_flush(stream);
1743    if (resp->off == resp->sz)
1744        lsquic_stream_shutdown(stream, 1);
1745}
1746
1747
1748const struct lsquic_stream_if interop_http_server_if = {
1749    .on_new_conn            = http_server_on_new_conn,
1750    .on_conn_closed         = http_server_on_conn_closed,
1751    .on_new_stream          = http_server_on_new_stream,
1752    .on_read                = http_server_interop_on_read,
1753    .on_write               = http_server_interop_on_write,
1754    .on_close               = http_server_on_close,
1755};
1756#endif /* HAVE_REGEX */
1757
1758
1759static void
1760usage (const char *prog)
1761{
1762    const char *const slash = strrchr(prog, '/');
1763    if (slash)
1764        prog = slash + 1;
1765    printf(
1766"Usage: %s [opts]\n"
1767"\n"
1768"Options:\n"
1769"   -r ROOT     Document root\n"
1770"   -p FILE     Push request with this path\n"
1771"   -w SIZE     Write immediately (LSWS mode).  Argument specifies maximum\n"
1772"                 size of the immediate write.\n"
1773#if HAVE_PREADV
1774"   -P SIZE     Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n"
1775"                 write to stream.  Positive SIZE indicate maximum value per\n"
1776"                 write; negative means always use remaining file size.\n"
1777"                 Incompatible with -w.\n"
1778#endif
1779"   -y DELAY    Delay response for this many seconds -- use for debugging\n"
1780"   -Q ALPN     Use hq mode; ALPN could be \"hq-29\", for example.\n"
1781            , prog);
1782}
1783
1784
1785static void *
1786interop_server_hset_create (void *hsi_ctx, lsquic_stream_t *stream,
1787                            int is_push_promise)
1788{
1789    struct req *req;
1790
1791    req = malloc(sizeof(struct req));
1792    memset(req, 0, offsetof(struct req, decode_buf));
1793
1794    return req;
1795}
1796
1797
1798static struct lsxpack_header *
1799interop_server_hset_prepare_decode (void *hset_p, struct lsxpack_header *xhdr,
1800                                                                size_t req_space)
1801{
1802    struct req *req = hset_p;
1803
1804    if (xhdr)
1805    {
1806        LSQ_WARN("we don't reallocate headers: can't give more");
1807        return NULL;
1808    }
1809
1810    if (req->flags & HAVE_XHDR)
1811    {
1812        if (req->decode_off + lsxpack_header_get_dec_size(&req->xhdr)
1813                                                    >= sizeof(req->decode_buf))
1814        {
1815            LSQ_WARN("Not enough room in header");
1816            return NULL;
1817        }
1818        req->decode_off += lsxpack_header_get_dec_size(&req->xhdr);
1819    }
1820    else
1821        req->flags |= HAVE_XHDR;
1822
1823    lsxpack_header_prepare_decode(&req->xhdr, req->decode_buf,
1824                req->decode_off, sizeof(req->decode_buf) - req->decode_off);
1825    return &req->xhdr;
1826}
1827
1828
1829static int
1830interop_server_hset_add_header (void *hset_p, struct lsxpack_header *xhdr)
1831{
1832    struct req *req = hset_p;
1833    const char *name, *value;
1834    unsigned name_len, value_len;
1835
1836    if (!xhdr)
1837    {
1838        if (req->pseudo_headers == (PH_AUTHORITY|PH_METHOD|PH_PATH))
1839            return 0;
1840        else
1841        {
1842            LSQ_INFO("%s: missing some pseudo-headers: 0x%X", __func__,
1843                req->pseudo_headers);
1844            return 1;
1845        }
1846    }
1847
1848    name = lsxpack_header_get_name(xhdr);
1849    value = lsxpack_header_get_value(xhdr);
1850    name_len = xhdr->name_len;
1851    value_len = xhdr->val_len;
1852
1853    req->qif_str = realloc(req->qif_str,
1854                        req->qif_sz + name_len + value_len + 2);
1855    if (!req->qif_str)
1856    {
1857        LSQ_ERROR("malloc failed");
1858        return -1;
1859    }
1860    memcpy(req->qif_str + req->qif_sz, name, name_len);
1861    req->qif_str[req->qif_sz + name_len] = '\t';
1862    memcpy(req->qif_str + req->qif_sz + name_len + 1, value, value_len);
1863    req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n';
1864    req->qif_sz += name_len + value_len + 2;
1865
1866    if (5 == name_len && 0 == strncmp(name, ":path", 5))
1867    {
1868        if (req->path)
1869            return 1;
1870        req->path = strndup(value, value_len);
1871        if (!req->path)
1872            return -1;
1873        req->pseudo_headers |= PH_PATH;
1874        return 0;
1875    }
1876
1877    if (7 == name_len && 0 == strncmp(name, ":method", 7))
1878    {
1879        if (req->method != UNSET)
1880            return 1;
1881        req->method_str = strndup(value, value_len);
1882        if (!req->method_str)
1883            return -1;
1884        if (0 == strcmp(req->method_str, "GET"))
1885            req->method = GET;
1886        else if (0 == strcmp(req->method_str, "POST"))
1887            req->method = POST;
1888        else
1889            req->method = UNSUPPORTED;
1890        req->pseudo_headers |= PH_METHOD;
1891        return 0;
1892    }
1893
1894    if (10 == name_len && 0 == strncmp(name, ":authority", 10))
1895    {
1896        req->authority_str = strndup(value, value_len);
1897        if (!req->authority_str)
1898            return -1;
1899        req->pseudo_headers |= PH_AUTHORITY;
1900        return 0;
1901    }
1902
1903    return 0;
1904}
1905
1906
1907static void
1908interop_server_hset_destroy (void *hset_p)
1909{
1910    struct req *req = hset_p;
1911    free(req->qif_str);
1912    free(req->path);
1913    free(req->method_str);
1914    free(req->authority_str);
1915    free(req);
1916}
1917
1918
1919static const struct lsquic_hset_if header_bypass_api =
1920{
1921    .hsi_create_header_set  = interop_server_hset_create,
1922    .hsi_prepare_decode     = interop_server_hset_prepare_decode,
1923    .hsi_process_header     = interop_server_hset_add_header,
1924    .hsi_discard_header_set = interop_server_hset_destroy,
1925};
1926
1927
1928int
1929main (int argc, char **argv)
1930{
1931    int opt, s;
1932    struct stat st;
1933    struct server_ctx server_ctx;
1934    struct prog prog;
1935    const char *const *alpn;
1936
1937#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX)
1938    fprintf(stderr, "cannot run server without regex or open_memstream\n");
1939    return 1;
1940#endif
1941
1942    memset(&server_ctx, 0, sizeof(server_ctx));
1943    TAILQ_INIT(&server_ctx.sports);
1944    server_ctx.prog = &prog;
1945
1946    prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
1947                                            &http_server_if, &server_ctx);
1948
1949    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h"
1950#if HAVE_OPEN_MEMSTREAM
1951                                                    "Q:"
1952#endif
1953                                                                        )))
1954    {
1955        switch (opt) {
1956        case 'n':
1957            server_ctx.max_conn = atoi(optarg);
1958            break;
1959        case 'p':
1960            server_ctx.push_path = optarg;
1961            break;
1962        case 'r':
1963            if (-1 == stat(optarg, &st))
1964            {
1965                perror("stat");
1966                exit(2);
1967            }
1968#ifndef WIN32
1969            if (!S_ISDIR(st.st_mode))
1970            {
1971                fprintf(stderr, "`%s' is not a directory\n", optarg);
1972                exit(2);
1973            }
1974#endif
1975            server_ctx.document_root = optarg;
1976            break;
1977        case 'w':
1978            s_immediate_write = atoi(optarg);
1979            break;
1980        case 'P':
1981#if HAVE_PREADV
1982            s_pwritev = strtoull(optarg, NULL, 10);
1983            break;
1984#else
1985            fprintf(stderr, "preadv is not supported on this platform, "
1986                                                        "cannot use -P\n");
1987            exit(EXIT_FAILURE);
1988#endif
1989        case 'y':
1990            server_ctx.delay_resp_sec = atoi(optarg);
1991            break;
1992        case 'h':
1993            usage(argv[0]);
1994            prog_print_common_options(&prog, stdout);
1995            exit(0);
1996#if HAVE_OPEN_MEMSTREAM
1997        case 'Q':
1998            /* XXX A bit hacky, as `prog' has already been initialized... */
1999            prog.prog_engine_flags &= ~LSENG_HTTP;
2000            prog.prog_api.ea_stream_if = &hq_server_if;
2001            add_alpn(optarg);
2002            break;
2003#endif
2004        default:
2005            if (0 != prog_set_opt(&prog, opt, optarg))
2006                exit(1);
2007        }
2008    }
2009
2010    if (!server_ctx.document_root)
2011    {
2012#if HAVE_REGEX
2013        LSQ_NOTICE("Document root is not set: start in Interop Mode");
2014        init_map_regexes();
2015        prog.prog_api.ea_stream_if = &interop_http_server_if;
2016        prog.prog_api.ea_hsi_if = &header_bypass_api;
2017        prog.prog_api.ea_hsi_ctx = NULL;
2018#else
2019        LSQ_ERROR("Document root is not set: use -r option");
2020        exit(EXIT_FAILURE);
2021#endif
2022    }
2023
2024    if (s_immediate_write && s_pwritev)
2025    {
2026        LSQ_ERROR("-w and -P are incompatible options");
2027        exit(EXIT_FAILURE);
2028    }
2029
2030    alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions);
2031    while (*alpn)
2032    {
2033        if (0 == add_alpn(*alpn))
2034            ++alpn;
2035        else
2036        {
2037            LSQ_ERROR("cannot add ALPN %s", *alpn);
2038            exit(EXIT_FAILURE);
2039        }
2040    }
2041
2042    if (0 != prog_prep(&prog))
2043    {
2044        LSQ_ERROR("could not prep");
2045        exit(EXIT_FAILURE);
2046    }
2047
2048    LSQ_DEBUG("entering event loop");
2049
2050    s = prog_run(&prog);
2051    prog_cleanup(&prog);
2052
2053#if HAVE_REGEX
2054    if (!server_ctx.document_root)
2055        free_map_regexes();
2056#endif
2057
2058    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
2059}
2060