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