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