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