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