http_server.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * http_server.c -- A simple HTTP/QUIC server
4 *
5 * It serves up files from the filesystem.
6 */
7
8#include <assert.h>
9#include <errno.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/queue.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <inttypes.h>
17
18#ifndef WIN32
19#include <netinet/in.h>
20#include <unistd.h>
21#include <fcntl.h>
22#else
23#include "vc_compat.h"
24#include "getopt.h"
25#endif
26
27#include <event2/event.h>
28
29#include <openssl/md5.h>
30
31#include "lsquic.h"
32#include "../src/liblsquic/lsquic_hash.h"
33#include "lsxpack_header.h"
34#include "test_config.h"
35#include "test_common.h"
36#include "test_cert.h"
37#include "prog.h"
38
39#if HAVE_REGEX
40#ifndef WIN32
41#include <regex.h>
42#else
43#include <pcreposix.h>
44#endif
45#endif
46
47#include "../src/liblsquic/lsquic_logger.h"
48#include "../src/liblsquic/lsquic_int_types.h"
49#include "../src/liblsquic/lsquic_util.h"
50
51#if HAVE_REGEX
52static const char on_being_idle[] =
53"ON BEING IDLE.\n"
54"\n"
55"Now, this is a subject on which I flatter myself I really am _au fait_.\n"
56"The gentleman who, when I was young, bathed me at wisdom's font for nine\n"
57"guineas a term--no extras--used to say he never knew a boy who could\n"
58"do less work in more time; and I remember my poor grandmother once\n"
59"incidentally observing, in the course of an instruction upon the use\n"
60"of the Prayer-book, that it was highly improbable that I should ever do\n"
61"much that I ought not to do, but that she felt convinced beyond a doubt\n"
62"that I should leave undone pretty well everything that I ought to do.\n"
63"\n"
64"I am afraid I have somewhat belied half the dear old lady's prophecy.\n"
65"Heaven help me! I have done a good many things that I ought not to have\n"
66"done, in spite of my laziness. But I have fully confirmed the accuracy\n"
67"of her judgment so far as neglecting much that I ought not to have\n"
68"neglected is concerned. Idling always has been my strong point. I take\n"
69"no credit to myself in the matter--it is a gift. Few possess it. There\n"
70"are plenty of lazy people and plenty of slow-coaches, but a genuine\n"
71"idler is a rarity. He is not a man who slouches about with his hands in\n"
72"his pockets. On the contrary, his most startling characteristic is that\n"
73"he is always intensely busy.\n"
74"\n"
75"It is impossible to enjoy idling thoroughly unless one has plenty of\n"
76"work to do. There is no fun in doing nothing when you have nothing to\n"
77"do. Wasting time is merely an occupation then, and a most exhausting\n"
78"one. Idleness, like kisses, to be sweet must be stolen.\n"
79"\n"
80"Many years ago, when I was a young man, I was taken very ill--I never\n"
81"could see myself that much was the matter with me, except that I had\n"
82"a beastly cold. But I suppose it was something very serious, for the\n"
83"doctor said that I ought to have come to him a month before, and that\n"
84"if it (whatever it was) had gone on for another week he would not have\n"
85"answered for the consequences. It is an extraordinary thing, but I\n"
86"never knew a doctor called into any case yet but what it transpired\n"
87"that another day's delay would have rendered cure hopeless. Our medical\n"
88"guide, philosopher, and friend is like the hero in a melodrama--he\n"
89"always comes upon the scene just, and only just, in the nick of time. It\n"
90"is Providence, that is what it is.\n"
91"\n"
92"Well, as I was saying, I was very ill and was ordered to Buxton for a\n"
93"month, with strict injunctions to do nothing whatever all the while\n"
94"that I was there. \"Rest is what you require,\" said the doctor, \"perfect\n"
95"rest.\"\n"
96"\n"
97"It seemed a delightful prospect. \"This man evidently understands my\n"
98"complaint,\" said I, and I pictured to myself a glorious time--a four\n"
99"weeks' _dolce far niente_ with a dash of illness in it. Not too much\n"
100"illness, but just illness enough--just sufficient to give it the flavor\n"
101"of suffering and make it poetical. I should get up late, sip chocolate,\n"
102"and have my breakfast in slippers and a dressing-gown. I should lie out\n"
103"in the garden in a hammock and read sentimental novels with a melancholy\n"
104"ending, until the books should fall from my listless hand, and I should\n"
105"recline there, dreamily gazing into the deep blue of the firmament,\n"
106"watching the fleecy clouds floating like white-sailed ships across\n"
107"its depths, and listening to the joyous song of the birds and the low\n"
108"rustling of the trees. Or, on becoming too weak to go out of doors,\n"
109"I should sit propped up with pillows at the open window of the\n"
110"ground-floor front, and look wasted and interesting, so that all the\n"
111"pretty girls would sigh as they passed by.\n"
112"\n"
113"And twice a day I should go down in a Bath chair to the Colonnade to\n"
114"drink the waters. Oh, those waters! I knew nothing about them then,\n"
115"and was rather taken with the idea. \"Drinking the waters\" sounded\n"
116"fashionable and Queen Anne-fied, and I thought I should like them. But,\n"
117"ugh! after the first three or four mornings! Sam Weller's description of\n"
118"them as \"having a taste of warm flat-irons\" conveys only a faint idea of\n"
119"their hideous nauseousness. If anything could make a sick man get well\n"
120"quickly, it would be the knowledge that he must drink a glassful of them\n"
121"every day until he was recovered. I drank them neat for six consecutive\n"
122"days, and they nearly killed me; but after then I adopted the plan of\n"
123"taking a stiff glass of brandy-and-water immediately on the top of them,\n"
124"and found much relief thereby. I have been informed since, by various\n"
125"eminent medical gentlemen, that the alcohol must have entirely\n"
126"counteracted the effects of the chalybeate properties contained in the\n"
127"water. I am glad I was lucky enough to hit upon the right thing.\n"
128"\n"
129"But \"drinking the waters\" was only a small portion of the torture I\n"
130"experienced during that memorable month--a month which was, without\n"
131"exception, the most miserable I have ever spent. During the best part of\n"
132"it I religiously followed the doctor's mandate and did nothing whatever,\n"
133"except moon about the house and garden and go out for two hours a day in\n"
134"a Bath chair. That did break the monotony to a certain extent. There is\n"
135"more excitement about Bath-chairing--especially if you are not used to\n"
136"the exhilarating exercise--than might appear to the casual observer. A\n"
137"sense of danger, such as a mere outsider might not understand, is ever\n"
138"present to the mind of the occupant. He feels convinced every minute\n"
139"that the whole concern is going over, a conviction which becomes\n"
140"especially lively whenever a ditch or a stretch of newly macadamized\n"
141"road comes in sight. Every vehicle that passes he expects is going to\n"
142"run into him; and he never finds himself ascending or descending a\n"
143"hill without immediately beginning to speculate upon his chances,\n"
144"supposing--as seems extremely probable--that the weak-kneed controller\n"
145"of his destiny should let go.\n"
146"\n"
147"But even this diversion failed to enliven after awhile, and the _ennui_\n"
148"became perfectly unbearable. I felt my mind giving way under it. It is\n"
149"not a strong mind, and I thought it would be unwise to tax it too far.\n"
150"So somewhere about the twentieth morning I got up early, had a good\n"
151"breakfast, and walked straight off to Hayfield, at the foot of the\n"
152"Kinder Scout--a pleasant, busy little town, reached through a lovely\n"
153"valley, and with two sweetly pretty women in it. At least they were\n"
154"sweetly pretty then; one passed me on the bridge and, I think, smiled;\n"
155"and the other was standing at an open door, making an unremunerative\n"
156"investment of kisses upon a red-faced baby. But it is years ago, and I\n"
157"dare say they have both grown stout and snappish since that time.\n"
158"Coming back, I saw an old man breaking stones, and it roused such strong\n"
159"longing in me to use my arms that I offered him a drink to let me take\n"
160"his place. He was a kindly old man and he humored me. I went for those\n"
161"stones with the accumulated energy of three weeks, and did more work in\n"
162"half an hour than he had done all day. But it did not make him jealous.\n"
163"\n"
164"Having taken the plunge, I went further and further into dissipation,\n"
165"going out for a long walk every morning and listening to the band in\n"
166"the pavilion every evening. But the days still passed slowly\n"
167"notwithstanding, and I was heartily glad when the last one came and I\n"
168"was being whirled away from gouty, consumptive Buxton to London with its\n"
169"stern work and life. I looked out of the carriage as we rushed through\n"
170"Hendon in the evening. The lurid glare overhanging the mighty city\n"
171"seemed to warm my heart, and when, later on, my cab rattled out of St.\n"
172"Pancras' station, the old familiar roar that came swelling up around me\n"
173"sounded the sweetest music I had heard for many a long day.\n"
174"\n"
175"I certainly did not enjoy that month's idling. I like idling when I\n"
176"ought not to be idling; not when it is the only thing I have to do. That\n"
177"is my pig-headed nature. The time when I like best to stand with my\n"
178"back to the fire, calculating how much I owe, is when my desk is heaped\n"
179"highest with letters that must be answered by the next post. When I like\n"
180"to dawdle longest over my dinner is when I have a heavy evening's work\n"
181"before me. And if, for some urgent reason, I ought to be up particularly\n"
182"early in the morning, it is then, more than at any other time, that I\n"
183"love to lie an extra half-hour in bed.\n"
184"\n"
185"Ah! how delicious it is to turn over and go to sleep again: \"just for\n"
186"five minutes.\" Is there any human being, I wonder, besides the hero of\n"
187"a Sunday-school \"tale for boys,\" who ever gets up willingly? There\n"
188"are some men to whom getting up at the proper time is an utter\n"
189"impossibility. If eight o'clock happens to be the time that they should\n"
190"turn out, then they lie till half-past. If circumstances change and\n"
191"half-past eight becomes early enough for them, then it is nine before\n"
192"they can rise. They are like the statesman of whom it was said that he\n"
193"was always punctually half an hour late. They try all manner of schemes.\n"
194"They buy alarm-clocks (artful contrivances that go off at the wrong time\n"
195"and alarm the wrong people). They tell Sarah Jane to knock at the door\n"
196"and call them, and Sarah Jane does knock at the door and does call them,\n"
197"and they grunt back \"awri\" and then go comfortably to sleep again. I\n"
198"knew one man who would actually get out and have a cold bath; and even\n"
199"that was of no use, for afterward he would jump into bed again to warm\n"
200"himself.\n"
201"\n"
202"I think myself that I could keep out of bed all right if I once got\n"
203"out. It is the wrenching away of the head from the pillow that I find so\n"
204"hard, and no amount of over-night determination makes it easier. I say\n"
205"to myself, after having wasted the whole evening, \"Well, I won't do\n"
206"any more work to-night; I'll get up early to-morrow morning;\" and I am\n"
207"thoroughly resolved to do so--then. In the morning, however, I feel less\n"
208"enthusiastic about the idea, and reflect that it would have been much\n"
209"better if I had stopped up last night. And then there is the trouble of\n"
210"dressing, and the more one thinks about that the more one wants to put\n"
211"it off.\n"
212"\n"
213"It is a strange thing this bed, this mimic grave, where we stretch our\n"
214"tired limbs and sink away so quietly into the silence and rest. \"O bed,\n"
215"O bed, delicious bed, that heaven on earth to the weary head,\" as sang\n"
216"poor Hood, you are a kind old nurse to us fretful boys and girls. Clever\n"
217"and foolish, naughty and good, you take us all in your motherly lap and\n"
218"hush our wayward crying. The strong man full of care--the sick man\n"
219"full of pain--the little maiden sobbing for her faithless lover--like\n"
220"children we lay our aching heads on your white bosom, and you gently\n"
221"soothe us off to by-by.\n"
222"\n"
223"Our trouble is sore indeed when you turn away and will not comfort us.\n"
224"How long the dawn seems coming when we cannot sleep! Oh! those hideous\n"
225"nights when we toss and turn in fever and pain, when we lie, like living\n"
226"men among the dead, staring out into the dark hours that drift so slowly\n"
227"between us and the light. And oh! those still more hideous nights when\n"
228"we sit by another in pain, when the low fire startles us every now and\n"
229"then with a falling cinder, and the tick of the clock seems a hammer\n"
230"beating out the life that we are watching.\n"
231"\n"
232"But enough of beds and bedrooms. I have kept to them too long, even for\n"
233"an idle fellow. Let us come out and have a smoke. That wastes time just\n"
234"as well and does not look so bad. Tobacco has been a blessing to us\n"
235"idlers. What the civil-service clerk before Sir Walter's time found\n"
236"to occupy their minds with it is hard to imagine. I attribute the\n"
237"quarrelsome nature of the Middle Ages young men entirely to the want of\n"
238"the soothing weed. They had no work to do and could not smoke, and\n"
239"the consequence was they were forever fighting and rowing. If, by any\n"
240"extraordinary chance, there was no war going, then they got up a deadly\n"
241"family feud with the next-door neighbor, and if, in spite of this, they\n"
242"still had a few spare moments on their hands, they occupied them with\n"
243"discussions as to whose sweetheart was the best looking, the arguments\n"
244"employed on both sides being battle-axes, clubs, etc. Questions of taste\n"
245"were soon decided in those days. When a twelfth-century youth fell in\n"
246"love he did not take three paces backward, gaze into her eyes, and tell\n"
247"her she was too beautiful to live. He said he would step outside and see\n"
248"about it. And if, when he got out, he met a man and broke his head--the\n"
249"other man's head, I mean--then that proved that his--the first\n"
250"fellow's--girl was a pretty girl. But if the other fellow broke _his_\n"
251"head--not his own, you know, but the other fellow's--the other fellow\n"
252"to the second fellow, that is, because of course the other fellow would\n"
253"only be the other fellow to him, not the first fellow who--well, if he\n"
254"broke his head, then _his_ girl--not the other fellow's, but the fellow\n"
255"who _was_ the--Look here, if A broke B's head, then A's girl was a\n"
256"pretty girl; but if B broke A's head, then A's girl wasn't a pretty\n"
257"girl, but B's girl was. That was their method of conducting art\n"
258"criticism.\n"
259"\n"
260"Nowadays we light a pipe and let the girls fight it out among\n"
261"themselves.\n"
262"\n"
263"They do it very well. They are getting to do all our work. They are\n"
264"doctors, and barristers, and artists. They manage theaters, and promote\n"
265"swindles, and edit newspapers. I am looking forward to the time when we\n"
266"men shall have nothing to do but lie in bed till twelve, read two novels\n"
267"a day, have nice little five-o'clock teas all to ourselves, and tax\n"
268"our brains with nothing more trying than discussions upon the latest\n"
269"patterns in trousers and arguments as to what Mr. Jones' coat was\n"
270"made of and whether it fitted him. It is a glorious prospect--for idle\n"
271"fellows.\n"
272"\n\n\n"
273;
274static const size_t IDLE_SIZE = sizeof(on_being_idle) - 1;
275#endif
276
277/* This is the "LSWS" mode: first write is performed immediately, outside
278 * of the on_write() callback.  This makes it possible to play with buffered
279 * packet queues.
280 */
281static int s_immediate_write;
282
283/* Use preadv(2) in conjuction with lsquic_stream_pwritev() to reduce
284 * number of system calls required to read from disk.  The actual value
285 * specifies maximum write size.  A negative value indicates always to use
286 * the remaining file size.
287 */
288static ssize_t s_pwritev;
289
290#define MIN(a, b) ((a) < (b) ? (a) : (b))
291#define V(v) (v), strlen(v)
292
293struct lsquic_conn_ctx;
294
295static void interop_server_hset_destroy (void *);
296
297
298struct server_ctx {
299    struct lsquic_conn_ctx  *conn_h;
300    lsquic_engine_t             *engine;
301    const char                  *document_root;
302    const char                  *push_path;
303    struct sport_head            sports;
304    struct prog                 *prog;
305    unsigned                     max_conn;
306    unsigned                     n_conn;
307    unsigned                     n_current_conns;
308    unsigned                     delay_resp_sec;
309};
310
311struct lsquic_conn_ctx {
312    lsquic_conn_t       *conn;
313    struct server_ctx   *server_ctx;
314    enum {
315        RECEIVED_GOAWAY = 1 << 0,
316    }                    flags;
317};
318
319
320static lsquic_conn_ctx_t *
321http_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn)
322{
323    struct server_ctx *server_ctx = stream_if_ctx;
324    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, has unacked data: %d", __func__,
1049                                lsquic_stream_has_unacked_data(stream));
1050}
1051
1052
1053const struct lsquic_stream_if http_server_if = {
1054    .on_new_conn            = http_server_on_new_conn,
1055    .on_conn_closed         = http_server_on_conn_closed,
1056    .on_new_stream          = http_server_on_new_stream,
1057    .on_read                = http_server_on_read,
1058    .on_write               = http_server_on_write,
1059    .on_close               = http_server_on_close,
1060    .on_goaway_received     = http_server_on_goaway,
1061};
1062
1063
1064#if HAVE_REGEX
1065struct req_map
1066{
1067    enum method             method;
1068    const char             *path;
1069    enum interop_handler    handler;
1070    const char             *status;
1071    enum {
1072        RM_WANTBODY     = 1 << 0,
1073        RM_REGEX        = 1 << 1,
1074        RM_COMPILED     = 1 << 2,
1075    }                       flags;
1076    regex_t                 re;
1077};
1078
1079
1080static struct req_map req_maps[] =
1081{
1082    { .method = GET, .path = "/", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1083    { .method = GET, .path = "/index.html", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, },
1084    { .method = POST, .path = "/cgi-bin/md5sum.cgi", .handler = IOH_MD5SUM, .status = "200", .flags = RM_WANTBODY, },
1085    { .method = POST, .path = "/cgi-bin/verify-headers.cgi", .handler = IOH_VER_HEAD, .status = "200", .flags = RM_WANTBODY, },
1086    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1087    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1088    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1089    { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1090    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1091    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1092    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1093    { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, },
1094};
1095
1096
1097#define MAX_MATCHES 5
1098
1099
1100static void
1101init_map_regexes (void)
1102{
1103    struct req_map *map;
1104
1105    for (map = req_maps; map < req_maps + sizeof(req_maps)
1106                                            / sizeof(req_maps[0]); ++map)
1107        if (map->flags & RM_REGEX)
1108        {
1109#ifndef NDEBUG
1110            int s;
1111            s =
1112#endif
1113            regcomp(&map->re, map->path, REG_EXTENDED|REG_ICASE);
1114            assert(0 == s);
1115            map->flags |= RM_COMPILED;
1116        }
1117}
1118
1119
1120static void
1121free_map_regexes (void)
1122{
1123    struct req_map *map;
1124
1125    for (map = req_maps; map < req_maps + sizeof(req_maps)
1126                                            / sizeof(req_maps[0]); ++map)
1127        if (map->flags & RM_COMPILED)
1128        {
1129            regfree(&map->re);
1130            map->flags &= ~RM_COMPILED;
1131        }
1132}
1133
1134
1135static const struct req_map *
1136find_handler (enum method method, const char *path, regmatch_t *matches)
1137{
1138    const struct req_map *map;
1139
1140    for (map = req_maps; map < req_maps + sizeof(req_maps)
1141                                            / sizeof(req_maps[0]); ++map)
1142        if (map->flags & RM_COMPILED)
1143        {
1144            if (0 == regexec(&map->re, path, MAX_MATCHES + 1, matches, 0))
1145                return map;
1146        }
1147        else if (0 == strcasecmp(path, map->path))
1148            return map;
1149
1150    return NULL;
1151}
1152
1153
1154static const char INDEX_HTML[] =
1155"<html>\n"
1156"   <head>\n"
1157"       <title>LiteSpeed IETF QUIC Server Index Page</title>\n"
1158"   </head>\n"
1159"   <body>\n"
1160"       <h1>LiteSpeed IETF QUIC Server Index Page</h1>\n"
1161"       <p>Hello!  Welcome to the interop.  Available services:\n"
1162"       <ul>\n"
1163"           <li><b>POST to /cgi-bin/md5sum.cgi</b>.  This will return\n"
1164"                   MD5 checksum of the request body.\n"
1165"           <li><b>GET /123K</b> or <b>GET /file-123K</b>.  This will return\n"
1166"                   requested number of payload in the form of repeating text\n"
1167"                   by Jerome K. Jerome.  The size specification must match\n"
1168"                   (\\d+)[KMG]? and the total size request must not exceed\n"
1169"                   2 gigabytes.  Then, you will get back that many bytes\n"
1170"                   of the <a\n"
1171"                       href=http://www.gutenberg.org/cache/epub/849/pg849.txt\n"
1172"                                                       >beloved classic</a>.\n"
1173"       </ul>\n"
1174"   </body>\n"
1175"</html>\n"
1176;
1177
1178
1179static size_t
1180read_md5 (void *ctx, const unsigned char *buf, size_t sz, int fin)
1181{
1182    struct lsquic_stream_ctx *st_h = ctx;
1183
1184    if (sz)
1185        MD5_Update(&st_h->interop_u.md5c.md5ctx, buf, sz);
1186
1187    if (fin)
1188        st_h->interop_u.md5c.done = 1;
1189
1190    return sz;
1191}
1192
1193
1194static void
1195http_server_interop_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1196{
1197#define ERROR_RESP(code, ...) do {  \
1198    LSQ_WARN(__VA_ARGS__);                                     \
1199    st_h->interop_handler = IOH_ERROR; \
1200    st_h->resp_status = #code;  \
1201    st_h->interop_u.err.resp.sz = snprintf(st_h->interop_u.err.buf, \
1202                            sizeof(st_h->interop_u.err.buf), __VA_ARGS__);  \
1203    if (st_h->interop_u.err.resp.sz >= sizeof(st_h->interop_u.err.buf))     \
1204        st_h->interop_u.err.resp.sz = sizeof(st_h->interop_u.err.buf) - 1;  \
1205    st_h->interop_u.err.resp.buf = st_h->interop_u.err.buf;                 \
1206    st_h->interop_u.err.resp.off = 0;                                       \
1207    goto err;                                                               \
1208} while (0)
1209
1210    const struct req_map *map;
1211    ssize_t nw;
1212    size_t need;
1213    unsigned len, i;
1214    struct interop_push_path *push_path;
1215    regmatch_t matches[MAX_MATCHES + 1];
1216    unsigned char md5sum[MD5_DIGEST_LENGTH];
1217    char md5str[ sizeof(md5sum) * 2 + 1 ];
1218    char byte[1];
1219
1220    if (!(st_h->flags & SH_HEADERS_READ))
1221    {
1222        st_h->flags |= SH_HEADERS_READ;
1223        st_h->req = lsquic_stream_get_hset(stream);
1224        if (!st_h->req)
1225            ERROR_RESP(500, "Internal error: cannot fetch header set from stream");
1226        else if (st_h->req->method == UNSET)
1227            ERROR_RESP(400, "Method is not specified");
1228        else if (!st_h->req->path)
1229            ERROR_RESP(400, "Path is not specified");
1230        else if (st_h->req->method == UNSUPPORTED)
1231            ERROR_RESP(501, "Method %s is not supported", st_h->req->method_str);
1232        else if (!(map = find_handler(st_h->req->method, st_h->req->path, matches)))
1233            ERROR_RESP(404, "No handler found for method: %s; path: %s",
1234                st_h->req->method_str, st_h->req->path);
1235        else
1236        {
1237            LSQ_INFO("found handler for %s %s", st_h->req->method_str, st_h->req->path);
1238            st_h->resp_status = map->status;
1239            st_h->interop_handler = map->handler;
1240            switch (map->handler)
1241            {
1242            case IOH_INDEX_HTML:
1243                st_h->interop_u.ihc.resp = (struct resp) { INDEX_HTML, sizeof(INDEX_HTML) - 1, 0, };
1244                break;
1245            case IOH_VER_HEAD:
1246                st_h->interop_u.vhc.resp = (struct resp) {
1247                        st_h->req->qif_str, st_h->req->qif_sz, 0, };
1248                break;
1249            case IOH_MD5SUM:
1250                MD5_Init(&st_h->interop_u.md5c.md5ctx);
1251                st_h->interop_u.md5c.done = 0;
1252                break;
1253            case IOH_GEN_FILE:
1254                STAILQ_INIT(&st_h->interop_u.gfc.push_paths);
1255                st_h->interop_u.gfc.remain = strtol(st_h->req->path + matches[1].rm_so, NULL, 10);
1256                if (matches[2].rm_so >= 0
1257                        && matches[2].rm_so < matches[2].rm_eo)
1258                {
1259                    switch (st_h->req->path[ matches[2].rm_so ])
1260                    {
1261                    case 'G':
1262                    case 'g':
1263                        st_h->interop_u.gfc.remain <<= 30;
1264                        break;
1265                    case 'M':
1266                    case 'm':
1267                        st_h->interop_u.gfc.remain <<= 20;
1268                        break;
1269                    case 'K':
1270                    case 'k':
1271                        st_h->interop_u.gfc.remain <<= 10;
1272                        break;
1273                    }
1274                }
1275                if (st_h->interop_u.gfc.remain > 2 * (1u << 30))
1276                    ERROR_RESP(406, "Response of %zd bytes is too long to generate",
1277                        st_h->interop_u.gfc.remain);
1278                st_h->interop_u.gfc.idle_off = 0;
1279                for (i = 3; i <= MAX_MATCHES; ++i)
1280                    if (matches[i].rm_so >= 0)
1281                    {
1282                        len = matches[i].rm_eo - matches[i].rm_so;
1283                        push_path = malloc(sizeof(*push_path) + len + 1);
1284                        memcpy(push_path->path, st_h->req->path
1285                            + matches[i].rm_so, len);
1286                        push_path->path[len] ='\0';
1287                        STAILQ_INSERT_TAIL(&st_h->interop_u.gfc.push_paths,
1288                                                                push_path, next);
1289                    }
1290                    else
1291                        break;
1292                break;
1293            default:
1294                /* TODO: implement this */
1295                assert(0);
1296                break;
1297            }
1298        }
1299
1300        if (!(map->flags & RM_WANTBODY))
1301        {
1302  err:
1303            lsquic_stream_shutdown(stream, 0);
1304            lsquic_stream_wantwrite(stream, 1);
1305        }
1306    }
1307    else
1308    {
1309        switch (st_h->interop_handler)
1310        {
1311        case IOH_MD5SUM:
1312            assert(!st_h->interop_u.md5c.done);
1313            nw = lsquic_stream_readf(stream, read_md5, st_h);
1314            if (nw < 0)
1315            {
1316                LSQ_ERROR("could not read from stream for MD5: %s", strerror(errno));
1317                exit(1);
1318            }
1319            if (nw == 0)
1320                st_h->interop_u.md5c.done = 1;
1321            if (st_h->interop_u.md5c.done)
1322            {
1323                MD5_Final(md5sum, &st_h->interop_u.md5c.md5ctx);
1324                lsquic_hexstr(md5sum, sizeof(md5sum), md5str, sizeof(md5str));
1325                snprintf(st_h->interop_u.md5c.resp_buf, sizeof(st_h->interop_u.md5c.resp_buf),
1326                    "<html><head><title>MD5 Checksum Result</title></head>\n"
1327                    "<body><h1>MD5 Checksum Result</h1>\n<p>"
1328                    "MD5 Checksum: <tt>%s</tt>\n</body></html>\n",
1329                    md5str);
1330                st_h->interop_u.md5c.resp.buf = st_h->interop_u.md5c.resp_buf;
1331                st_h->interop_u.md5c.resp.sz = strlen(st_h->interop_u.md5c.resp_buf);
1332                st_h->interop_u.md5c.resp.off = 0;
1333                lsquic_stream_shutdown(stream, 0);
1334                lsquic_stream_wantwrite(stream, 1);
1335            }
1336            break;
1337        case IOH_VER_HEAD:
1338            if (!st_h->interop_u.vhc.req_body)
1339            {
1340                st_h->interop_u.vhc.req_body = malloc(st_h->req->qif_sz);
1341                if (!st_h->interop_u.vhc.req_body)
1342                {
1343                    perror("malloc");
1344                    exit(1);
1345                }
1346            }
1347            need = st_h->req->qif_sz - st_h->interop_u.vhc.req_sz;
1348            if (need > 0)
1349            {
1350                nw = lsquic_stream_read(stream,
1351                        st_h->interop_u.vhc.req_body
1352                                        + st_h->interop_u.vhc.req_sz, need);
1353                if (nw > 0)
1354                    st_h->interop_u.vhc.req_sz += need;
1355                else if (nw == 0)
1356                {
1357                    LSQ_WARN("request body too short (does not match headers)");
1358                    lsquic_stream_shutdown(stream, 0);
1359                    lsquic_stream_wantwrite(stream, 1);
1360                }
1361                else
1362                {
1363                    LSQ_ERROR("error reading from stream");
1364                    exit(1);
1365                }
1366            }
1367            else
1368            {
1369                nw = lsquic_stream_read(stream, byte, sizeof(byte));
1370                if (nw == 0)
1371                {
1372                    if (0 == memcmp(st_h->req->qif_str,
1373                            st_h->interop_u.vhc.req_body, st_h->req->qif_sz))
1374                        LSQ_INFO("request headers and payload check out");
1375                    else
1376                        LSQ_WARN("request headers and payload are different");
1377                }
1378                else
1379                    LSQ_WARN("request body too long (does not match headers)");
1380                lsquic_stream_shutdown(stream, 0);
1381                lsquic_stream_wantwrite(stream, 1);
1382            }
1383            break;
1384        default:
1385            assert(0);
1386        }
1387    }
1388}
1389
1390
1391static int
1392send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h,
1393                    size_t content_len)
1394{
1395    char clbuf[0x20];
1396    struct header_buf hbuf;
1397
1398    snprintf(clbuf, sizeof(clbuf), "%zd", content_len);
1399
1400    hbuf.off = 0;
1401    struct lsxpack_header  headers_arr[4];
1402    header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V(st_h->resp_status));
1403    header_set_ptr(&headers_arr[1], &hbuf, V("server"), V(LITESPEED_ID));
1404    header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html"));
1405    header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf));
1406    lsquic_http_headers_t headers = {
1407        .count = sizeof(headers_arr) / sizeof(headers_arr[0]),
1408        .headers = headers_arr,
1409    };
1410
1411    return lsquic_stream_send_headers(st_h->stream, &headers, 0);
1412}
1413
1414#define MIN(a, b) ((a) < (b) ? (a) : (b))
1415
1416static size_t
1417idle_read (void *lsqr_ctx, void *buf, size_t count)
1418{
1419    struct gen_file_ctx *const gfc = lsqr_ctx;
1420    unsigned char *p = buf;
1421    unsigned char *const end = p + count;
1422    size_t towrite;
1423
1424    while (p < end && gfc->remain > 0)
1425    {
1426        towrite = MIN((unsigned) (end - p), IDLE_SIZE - gfc->idle_off);
1427        if (towrite > gfc->remain)
1428            towrite = gfc->remain;
1429        memcpy(p, on_being_idle + gfc->idle_off, towrite);
1430        gfc->idle_off += towrite;
1431        if (gfc->idle_off == IDLE_SIZE)
1432            gfc->idle_off = 0;
1433        p += towrite;
1434        gfc->remain -= towrite;
1435    }
1436
1437    return p - (unsigned char *) buf;
1438}
1439
1440
1441static size_t
1442idle_size (void *lsqr_ctx)
1443{
1444    struct gen_file_ctx *const gfc = lsqr_ctx;
1445
1446    return gfc->remain;
1447}
1448
1449
1450static struct req *
1451new_req (enum method method, const char *path, const char *authority)
1452{
1453    struct req *req;
1454
1455    req = malloc(sizeof(*req));
1456    if (!req)
1457        return NULL;
1458
1459    memset(req, 0, offsetof(struct req, decode_buf));
1460    req->method = method;
1461    req->path = strdup(path);
1462    req->authority_str = strdup(authority);
1463    if (!(req->path && req->authority_str))
1464    {
1465        free(req->path);
1466        free(req->authority_str);
1467        free(req);
1468        return NULL;
1469    }
1470
1471    return req;
1472}
1473
1474
1475static ssize_t
1476my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt)
1477{
1478    struct gen_file_ctx *const gfc = user_data;
1479    size_t nread, nr;
1480    int i;
1481
1482    nread = 0;
1483    for (i = 0; i < iovcnt; ++i)
1484    {
1485        nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len);
1486        nread += nr;
1487    }
1488
1489    return (ssize_t) nread;
1490}
1491
1492
1493static void
1494idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1495{
1496    struct gen_file_ctx *const gfc = &st_h->interop_u.gfc;
1497    struct interop_push_path *push_path;
1498    struct lsxpack_header header_arr[4];
1499    struct lsquic_http_headers headers;
1500    struct req *req;
1501    ssize_t nw;
1502    struct header_buf hbuf;
1503    struct lsquic_reader reader;
1504
1505    if (st_h->flags & SH_HEADERS_SENT)
1506    {
1507        if (s_pwritev)
1508        {
1509            nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc,
1510                                                            gfc->remain);
1511            if (nw == 0)
1512                goto with_reader;
1513        }
1514        else
1515        {
1516  with_reader:
1517            reader.lsqr_read = idle_read,
1518            reader.lsqr_size = idle_size,
1519            reader.lsqr_ctx = gfc,
1520            nw = lsquic_stream_writef(stream, &reader);
1521        }
1522        if (nw < 0)
1523        {
1524            LSQ_ERROR("error writing idle thoughts: %s", strerror(errno));
1525            exit(1);
1526        }
1527        if (gfc->remain == 0)
1528            lsquic_stream_shutdown(stream, 1);
1529    }
1530    else
1531    {
1532        if (st_h->req->authority_str)
1533            while ((push_path = STAILQ_FIRST(&gfc->push_paths)))
1534            {
1535                STAILQ_REMOVE_HEAD(&gfc->push_paths, next);
1536                LSQ_DEBUG("pushing promise for %s", push_path->path);
1537                hbuf.off = 0;
1538                header_set_ptr(&header_arr[0], &hbuf, V(":method"), V("GET"));
1539                header_set_ptr(&header_arr[1], &hbuf, V(":path"), V(push_path->path));
1540                header_set_ptr(&header_arr[2], &hbuf, V(":authority"), V(st_h->req->authority_str));
1541                header_set_ptr(&header_arr[3], &hbuf, V(":scheme"), V("https"));
1542                headers.headers = header_arr;
1543                headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
1544                req = new_req(GET, push_path->path, st_h->req->authority_str);
1545                if (req)
1546                {
1547                    if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream),
1548                                                            req, stream, &headers))
1549                    {
1550                        LSQ_WARN("stream push failed");
1551                        interop_server_hset_destroy(req);
1552                    }
1553                }
1554                else
1555                    LSQ_WARN("cannot allocate req for push");
1556                free(push_path);
1557            }
1558        if (0 == send_headers2(stream, st_h, gfc->remain))
1559            st_h->flags |= SH_HEADERS_SENT;
1560        else
1561        {
1562            LSQ_ERROR("cannot send headers: %s", strerror(errno));
1563            lsquic_stream_close(stream);
1564        }
1565    }
1566}
1567
1568
1569static void
1570http_server_interop_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
1571{
1572    struct resp *resp;
1573    ssize_t nw;
1574
1575    switch (st_h->interop_handler)
1576    {
1577    case IOH_ERROR:
1578        resp = &st_h->interop_u.err.resp;
1579        goto reply;
1580    case IOH_INDEX_HTML:
1581        resp = &st_h->interop_u.ihc.resp;
1582        goto reply;
1583    case IOH_VER_HEAD:
1584        resp = &st_h->interop_u.vhc.resp;
1585        goto reply;
1586    case IOH_MD5SUM:
1587        resp = &st_h->interop_u.md5c.resp;
1588        goto reply;
1589    case IOH_GEN_FILE:
1590        idle_on_write(stream, st_h);
1591        return;
1592    default:
1593        assert(0);
1594        return;
1595    }
1596
1597  reply:
1598    assert(resp->sz);   /* We always need to send body */
1599    if (!(st_h->flags & SH_HEADERS_SENT))
1600    {
1601        send_headers2(stream, st_h, resp->sz);
1602        st_h->flags |= SH_HEADERS_SENT;
1603        return;
1604    }
1605
1606    nw = lsquic_stream_write(stream, resp->buf + resp->off, resp->sz - resp->off);
1607    if (nw < 0)
1608    {
1609        LSQ_ERROR("error writing to stream: %s", strerror(errno));
1610        lsquic_conn_abort(lsquic_stream_conn(stream));
1611        return;
1612    }
1613
1614    resp->off += nw;
1615    lsquic_stream_flush(stream);
1616    if (resp->off == resp->sz)
1617        lsquic_stream_shutdown(stream, 1);
1618}
1619
1620
1621const struct lsquic_stream_if interop_http_server_if = {
1622    .on_new_conn            = http_server_on_new_conn,
1623    .on_conn_closed         = http_server_on_conn_closed,
1624    .on_new_stream          = http_server_on_new_stream,
1625    .on_read                = http_server_interop_on_read,
1626    .on_write               = http_server_interop_on_write,
1627    .on_close               = http_server_on_close,
1628};
1629#endif /* HAVE_REGEX */
1630
1631
1632static void
1633usage (const char *prog)
1634{
1635    const char *const slash = strrchr(prog, '/');
1636    if (slash)
1637        prog = slash + 1;
1638    printf(
1639"Usage: %s [opts]\n"
1640"\n"
1641"Options:\n"
1642"   -r ROOT     Document root\n"
1643"   -p FILE     Push request with this path\n"
1644"   -w SIZE     Write immediately (LSWS mode).  Argument specifies maximum\n"
1645"                 size of the immediate write.\n"
1646#if HAVE_PREADV
1647"   -P SIZE     Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n"
1648"                 write to stream.  Positive SIZE indicate maximum value per\n"
1649"                 write; negative means always use remaining file size.\n"
1650"                 Incompatible with -w.\n"
1651#endif
1652"   -y DELAY    Delay response for this many seconds -- use for debugging\n"
1653            , prog);
1654}
1655
1656
1657static void *
1658interop_server_hset_create (void *hsi_ctx, lsquic_stream_t *stream,
1659                            int is_push_promise)
1660{
1661    struct req *req;
1662
1663    req = malloc(sizeof(struct req));
1664    memset(req, 0, offsetof(struct req, decode_buf));
1665
1666    return req;
1667}
1668
1669
1670static struct lsxpack_header *
1671interop_server_hset_prepare_decode (void *hset_p, struct lsxpack_header *xhdr,
1672                                                                size_t req_space)
1673{
1674    struct req *req = hset_p;
1675
1676    if (xhdr)
1677    {
1678        LSQ_WARN("we don't reallocate headers: can't give more");
1679        return NULL;
1680    }
1681
1682    if (req->flags & HAVE_XHDR)
1683    {
1684        if (req->decode_off + lsxpack_header_get_dec_size(&req->xhdr)
1685                                                    >= sizeof(req->decode_buf))
1686        {
1687            LSQ_WARN("Not enough room in header");
1688            return NULL;
1689        }
1690        req->decode_off += lsxpack_header_get_dec_size(&req->xhdr);
1691    }
1692    else
1693        req->flags |= HAVE_XHDR;
1694
1695    lsxpack_header_prepare_decode(&req->xhdr, req->decode_buf,
1696                req->decode_off, sizeof(req->decode_buf) - req->decode_off);
1697    return &req->xhdr;
1698}
1699
1700
1701static int
1702interop_server_hset_add_header (void *hset_p, struct lsxpack_header *xhdr)
1703{
1704    struct req *req = hset_p;
1705    const char *name, *value;
1706    unsigned name_len, value_len;
1707
1708    if (!xhdr)
1709        return 0;
1710
1711    name = lsxpack_header_get_name(xhdr);
1712    value = lsxpack_header_get_value(xhdr);
1713    name_len = xhdr->name_len;
1714    value_len = xhdr->val_len;
1715
1716    req->qif_str = realloc(req->qif_str,
1717                        req->qif_sz + name_len + value_len + 2);
1718    if (!req->qif_str)
1719    {
1720        LSQ_ERROR("malloc failed");
1721        return -1;
1722    }
1723    memcpy(req->qif_str + req->qif_sz, name, name_len);
1724    req->qif_str[req->qif_sz + name_len] = '\t';
1725    memcpy(req->qif_str + req->qif_sz + name_len + 1, value, value_len);
1726    req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n';
1727    req->qif_sz += name_len + value_len + 2;
1728
1729    if (5 == name_len && 0 == strncmp(name, ":path", 5))
1730    {
1731        if (req->path)
1732            return 1;
1733        req->path = strndup(value, value_len);
1734        if (!req->path)
1735            return -1;
1736        return 0;
1737    }
1738
1739    if (7 == name_len && 0 == strncmp(name, ":method", 7))
1740    {
1741        if (req->method != UNSET)
1742            return 1;
1743        req->method_str = strndup(value, value_len);
1744        if (!req->method_str)
1745            return -1;
1746        if (0 == strcmp(req->method_str, "GET"))
1747            req->method = GET;
1748        else if (0 == strcmp(req->method_str, "POST"))
1749            req->method = POST;
1750        else
1751            req->method = UNSUPPORTED;
1752        return 0;
1753    }
1754
1755    if (10 == name_len && 0 == strncmp(name, ":authority", 10))
1756    {
1757        req->authority_str = strndup(value, value_len);
1758        if (!req->authority_str)
1759            return -1;
1760        return 0;
1761    }
1762
1763    return 0;
1764}
1765
1766
1767static void
1768interop_server_hset_destroy (void *hset_p)
1769{
1770    struct req *req = hset_p;
1771    free(req->qif_str);
1772    free(req->path);
1773    free(req->method_str);
1774    free(req->authority_str);
1775    free(req);
1776}
1777
1778
1779static const struct lsquic_hset_if header_bypass_api =
1780{
1781    .hsi_create_header_set  = interop_server_hset_create,
1782    .hsi_prepare_decode     = interop_server_hset_prepare_decode,
1783    .hsi_process_header     = interop_server_hset_add_header,
1784    .hsi_discard_header_set = interop_server_hset_destroy,
1785};
1786
1787
1788int
1789main (int argc, char **argv)
1790{
1791    int opt, s;
1792    struct stat st;
1793    struct server_ctx server_ctx;
1794    struct prog prog;
1795    const char *const *alpn;
1796
1797#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX)
1798    fprintf(stderr, "cannot run server without regex or open_memstream\n");
1799    return 1;
1800#endif
1801
1802    memset(&server_ctx, 0, sizeof(server_ctx));
1803    TAILQ_INIT(&server_ctx.sports);
1804    server_ctx.prog = &prog;
1805
1806    prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports,
1807                                            &http_server_if, &server_ctx);
1808
1809    while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h")))
1810    {
1811        switch (opt) {
1812        case 'n':
1813            server_ctx.max_conn = atoi(optarg);
1814            break;
1815        case 'p':
1816            server_ctx.push_path = optarg;
1817            break;
1818        case 'r':
1819            if (-1 == stat(optarg, &st))
1820            {
1821                perror("stat");
1822                exit(2);
1823            }
1824#ifndef WIN32
1825            if (!S_ISDIR(st.st_mode))
1826            {
1827                fprintf(stderr, "`%s' is not a directory\n", optarg);
1828                exit(2);
1829            }
1830#endif
1831            server_ctx.document_root = optarg;
1832            break;
1833        case 'w':
1834            s_immediate_write = atoi(optarg);
1835            break;
1836        case 'P':
1837#if HAVE_PREADV
1838            s_pwritev = strtoull(optarg, NULL, 10);
1839            break;
1840#else
1841            fprintf(stderr, "preadv is not supported on this platform, "
1842                                                        "cannot use -P\n");
1843            exit(EXIT_FAILURE);
1844#endif
1845        case 'y':
1846            server_ctx.delay_resp_sec = atoi(optarg);
1847            break;
1848        case 'h':
1849            usage(argv[0]);
1850            prog_print_common_options(&prog, stdout);
1851            exit(0);
1852        default:
1853            if (0 != prog_set_opt(&prog, opt, optarg))
1854                exit(1);
1855        }
1856    }
1857
1858    if (!server_ctx.document_root)
1859    {
1860#if HAVE_REGEX
1861        LSQ_NOTICE("Document root is not set: start in Interop Mode");
1862        init_map_regexes();
1863        prog.prog_api.ea_stream_if = &interop_http_server_if;
1864        prog.prog_api.ea_hsi_if = &header_bypass_api;
1865        prog.prog_api.ea_hsi_ctx = NULL;
1866#else
1867        LSQ_ERROR("Document root is not set: use -r option");
1868        exit(EXIT_FAILURE);
1869#endif
1870    }
1871
1872    if (s_immediate_write && s_pwritev)
1873    {
1874        LSQ_ERROR("-w and -P are incompatible options");
1875        exit(EXIT_FAILURE);
1876    }
1877
1878    alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions);
1879    while (*alpn)
1880    {
1881        if (0 == add_alpn(*alpn))
1882            ++alpn;
1883        else
1884        {
1885            LSQ_ERROR("cannot add ALPN %s", *alpn);
1886            exit(EXIT_FAILURE);
1887        }
1888    }
1889
1890    if (0 != prog_prep(&prog))
1891    {
1892        LSQ_ERROR("could not prep");
1893        exit(EXIT_FAILURE);
1894    }
1895
1896    LSQ_DEBUG("entering event loop");
1897
1898    s = prog_run(&prog);
1899    prog_cleanup(&prog);
1900
1901#if HAVE_REGEX
1902    if (!server_ctx.document_root)
1903        free_map_regexes();
1904#endif
1905
1906    exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE);
1907}
1908