http_server.c revision c2faf032
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#if HAVE_OPEN_MEMSTREAM 1070static void 1071hq_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h) 1072{ 1073 char tbuf[0x100], *buf; 1074 ssize_t nread; 1075 char *path, *end, *filename; 1076 1077 if (!st_h->req_fh) 1078 st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz); 1079 1080 nread = lsquic_stream_read(stream, tbuf, sizeof(tbuf)); 1081 if (nread > 0) 1082 { 1083 fwrite(tbuf, 1, nread, st_h->req_fh); 1084 return; 1085 } 1086 1087 if (nread < 0) 1088 { 1089 LSQ_WARN("error reading request from stream: %s", strerror(errno)); 1090 lsquic_stream_close(stream); 1091 return; 1092 } 1093 1094 fwrite("", 1, 1, st_h->req_fh); 1095 fclose(st_h->req_fh); 1096 LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf); 1097 1098 buf = st_h->req_buf; 1099 path = strchr(buf, ' '); 1100 if (!path) 1101 { 1102 LSQ_WARN("invalid request (no space character): `%s'", buf); 1103 lsquic_stream_close(stream); 1104 return; 1105 } 1106 if (!(path - buf == 3 && 0 == strncasecmp(buf, "GET", 3))) 1107 { 1108 LSQ_NOTICE("unsupported method `%.*s'", (int) (path - buf), buf); 1109 lsquic_stream_close(stream); 1110 return; 1111 } 1112 ++path; 1113 for (end = buf + st_h->req_sz - 1; end > path 1114 && (*end == '\0' || *end == '\r' || *end == '\n'); --end) 1115 *end = '\0'; 1116 LSQ_NOTICE("parsed out request path: %s", path); 1117 1118 filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + strlen(path) + 1); 1119 strcpy(filename, st_h->server_ctx->document_root); 1120 strcat(filename, "/"); 1121 strcat(filename, path); 1122 LSQ_NOTICE("file to fetch: %s", filename); 1123 /* XXX This copy pasta is getting a bit annoying now: two mallocs of the 1124 * same thing? 1125 */ 1126 st_h->req_filename = filename; 1127 st_h->req_path = strdup(filename); 1128 st_h->reader.lsqr_read = test_reader_read; 1129 st_h->reader.lsqr_size = test_reader_size; 1130 st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path); 1131 if (!st_h->reader.lsqr_ctx) 1132 { 1133 lsquic_stream_close(stream); 1134 return; 1135 } 1136 lsquic_stream_shutdown(stream, 0); 1137 lsquic_stream_wantwrite(stream, 1); 1138} 1139 1140 1141static void 1142hq_server_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h) 1143{ 1144 ssize_t nw; 1145 1146 nw = lsquic_stream_writef(stream, &st_h->reader); 1147 if (nw < 0) 1148 { 1149 struct lsquic_conn *conn = lsquic_stream_conn(stream); 1150 lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn); 1151 if (conn_h->flags & RECEIVED_GOAWAY) 1152 { 1153 LSQ_NOTICE("cannot write: goaway received"); 1154 lsquic_stream_close(stream); 1155 } 1156 else 1157 { 1158 LSQ_ERROR("write error: %s", strerror(errno)); 1159 lsquic_stream_close(stream); 1160 } 1161 } 1162 else if (bytes_left(st_h) > 0) 1163 { 1164 st_h->written += (size_t) nw; 1165 lsquic_stream_wantwrite(stream, 1); 1166 } 1167 else 1168 { 1169 lsquic_stream_shutdown(stream, 1); 1170 lsquic_stream_wantread(stream, 1); 1171 } 1172} 1173 1174 1175const struct lsquic_stream_if hq_server_if = { 1176 .on_new_conn = http_server_on_new_conn, 1177 .on_conn_closed = http_server_on_conn_closed, 1178 .on_new_stream = http_server_on_new_stream, 1179 .on_read = hq_server_on_read, 1180 .on_write = hq_server_on_write, 1181 .on_close = http_server_on_close, 1182}; 1183#endif 1184 1185 1186#if HAVE_REGEX 1187struct req_map 1188{ 1189 enum method method; 1190 const char *path; 1191 enum interop_handler handler; 1192 const char *status; 1193 enum { 1194 RM_WANTBODY = 1 << 0, 1195 RM_REGEX = 1 << 1, 1196 RM_COMPILED = 1 << 2, 1197 } flags; 1198 regex_t re; 1199}; 1200 1201 1202static struct req_map req_maps[] = 1203{ 1204 { .method = GET, .path = "/", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, }, 1205 { .method = GET, .path = "/index.html", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, }, 1206 { .method = POST, .path = "/cgi-bin/md5sum.cgi", .handler = IOH_MD5SUM, .status = "200", .flags = RM_WANTBODY, }, 1207 { .method = POST, .path = "/cgi-bin/verify-headers.cgi", .handler = IOH_VER_HEAD, .status = "200", .flags = RM_WANTBODY, }, 1208 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1209 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1210 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1211 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1212 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1213 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1214 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1215 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1216}; 1217 1218 1219#define MAX_MATCHES 5 1220 1221 1222static void 1223init_map_regexes (void) 1224{ 1225 struct req_map *map; 1226 1227 for (map = req_maps; map < req_maps + sizeof(req_maps) 1228 / sizeof(req_maps[0]); ++map) 1229 if (map->flags & RM_REGEX) 1230 { 1231#ifndef NDEBUG 1232 int s; 1233 s = 1234#endif 1235 regcomp(&map->re, map->path, REG_EXTENDED|REG_ICASE); 1236 assert(0 == s); 1237 map->flags |= RM_COMPILED; 1238 } 1239} 1240 1241 1242static void 1243free_map_regexes (void) 1244{ 1245 struct req_map *map; 1246 1247 for (map = req_maps; map < req_maps + sizeof(req_maps) 1248 / sizeof(req_maps[0]); ++map) 1249 if (map->flags & RM_COMPILED) 1250 { 1251 regfree(&map->re); 1252 map->flags &= ~RM_COMPILED; 1253 } 1254} 1255 1256 1257static const struct req_map * 1258find_handler (enum method method, const char *path, regmatch_t *matches) 1259{ 1260 const struct req_map *map; 1261 1262 for (map = req_maps; map < req_maps + sizeof(req_maps) 1263 / sizeof(req_maps[0]); ++map) 1264 if (map->flags & RM_COMPILED) 1265 { 1266 if (0 == regexec(&map->re, path, MAX_MATCHES + 1, matches, 0)) 1267 return map; 1268 } 1269 else if (0 == strcasecmp(path, map->path)) 1270 return map; 1271 1272 return NULL; 1273} 1274 1275 1276static const char INDEX_HTML[] = 1277"<html>\n" 1278" <head>\n" 1279" <title>LiteSpeed IETF QUIC Server Index Page</title>\n" 1280" </head>\n" 1281" <body>\n" 1282" <h1>LiteSpeed IETF QUIC Server Index Page</h1>\n" 1283" <p>Hello! Welcome to the interop. Available services:\n" 1284" <ul>\n" 1285" <li><b>POST to /cgi-bin/md5sum.cgi</b>. This will return\n" 1286" MD5 checksum of the request body.\n" 1287" <li><b>GET /123K</b> or <b>GET /file-123K</b>. This will return\n" 1288" requested number of payload in the form of repeating text\n" 1289" by Jerome K. Jerome. The size specification must match\n" 1290" (\\d+)[KMG]? and the total size request must not exceed\n" 1291" 2 gigabytes. Then, you will get back that many bytes\n" 1292" of the <a\n" 1293" href=http://www.gutenberg.org/cache/epub/849/pg849.txt\n" 1294" >beloved classic</a>.\n" 1295" </ul>\n" 1296" </body>\n" 1297"</html>\n" 1298; 1299 1300 1301static size_t 1302read_md5 (void *ctx, const unsigned char *buf, size_t sz, int fin) 1303{ 1304 struct lsquic_stream_ctx *st_h = ctx; 1305 1306 if (sz) 1307 MD5_Update(&st_h->interop_u.md5c.md5ctx, buf, sz); 1308 1309 if (fin) 1310 st_h->interop_u.md5c.done = 1; 1311 1312 return sz; 1313} 1314 1315 1316static void 1317http_server_interop_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1318{ 1319#define ERROR_RESP(code, ...) do { \ 1320 LSQ_WARN(__VA_ARGS__); \ 1321 st_h->interop_handler = IOH_ERROR; \ 1322 st_h->resp_status = #code; \ 1323 st_h->interop_u.err.resp.sz = snprintf(st_h->interop_u.err.buf, \ 1324 sizeof(st_h->interop_u.err.buf), __VA_ARGS__); \ 1325 if (st_h->interop_u.err.resp.sz >= sizeof(st_h->interop_u.err.buf)) \ 1326 st_h->interop_u.err.resp.sz = sizeof(st_h->interop_u.err.buf) - 1; \ 1327 st_h->interop_u.err.resp.buf = st_h->interop_u.err.buf; \ 1328 st_h->interop_u.err.resp.off = 0; \ 1329 goto err; \ 1330} while (0) 1331 1332 const struct req_map *map; 1333 ssize_t nw; 1334 size_t need; 1335 unsigned len, i; 1336 struct interop_push_path *push_path; 1337 regmatch_t matches[MAX_MATCHES + 1]; 1338 unsigned char md5sum[MD5_DIGEST_LENGTH]; 1339 char md5str[ sizeof(md5sum) * 2 + 1 ]; 1340 char byte[1]; 1341 1342 if (!(st_h->flags & SH_HEADERS_READ)) 1343 { 1344 st_h->flags |= SH_HEADERS_READ; 1345 st_h->req = lsquic_stream_get_hset(stream); 1346 if (!st_h->req) 1347 ERROR_RESP(500, "Internal error: cannot fetch header set from stream"); 1348 else if (st_h->req->method == UNSET) 1349 ERROR_RESP(400, "Method is not specified"); 1350 else if (!st_h->req->path) 1351 ERROR_RESP(400, "Path is not specified"); 1352 else if (st_h->req->method == UNSUPPORTED) 1353 ERROR_RESP(501, "Method %s is not supported", st_h->req->method_str); 1354 else if (!(map = find_handler(st_h->req->method, st_h->req->path, matches))) 1355 ERROR_RESP(404, "No handler found for method: %s; path: %s", 1356 st_h->req->method_str, st_h->req->path); 1357 else 1358 { 1359 LSQ_INFO("found handler for %s %s", st_h->req->method_str, st_h->req->path); 1360 st_h->resp_status = map->status; 1361 st_h->interop_handler = map->handler; 1362 switch (map->handler) 1363 { 1364 case IOH_INDEX_HTML: 1365 st_h->interop_u.ihc.resp = (struct resp) { INDEX_HTML, sizeof(INDEX_HTML) - 1, 0, }; 1366 break; 1367 case IOH_VER_HEAD: 1368 st_h->interop_u.vhc.resp = (struct resp) { 1369 st_h->req->qif_str, st_h->req->qif_sz, 0, }; 1370 break; 1371 case IOH_MD5SUM: 1372 MD5_Init(&st_h->interop_u.md5c.md5ctx); 1373 st_h->interop_u.md5c.done = 0; 1374 break; 1375 case IOH_GEN_FILE: 1376 STAILQ_INIT(&st_h->interop_u.gfc.push_paths); 1377 st_h->interop_u.gfc.remain = strtol(st_h->req->path + matches[1].rm_so, NULL, 10); 1378 if (matches[2].rm_so >= 0 1379 && matches[2].rm_so < matches[2].rm_eo) 1380 { 1381 switch (st_h->req->path[ matches[2].rm_so ]) 1382 { 1383 case 'G': 1384 case 'g': 1385 st_h->interop_u.gfc.remain <<= 30; 1386 break; 1387 case 'M': 1388 case 'm': 1389 st_h->interop_u.gfc.remain <<= 20; 1390 break; 1391 case 'K': 1392 case 'k': 1393 st_h->interop_u.gfc.remain <<= 10; 1394 break; 1395 } 1396 } 1397 if (st_h->interop_u.gfc.remain > 2 * (1u << 30)) 1398 ERROR_RESP(406, "Response of %zd bytes is too long to generate", 1399 st_h->interop_u.gfc.remain); 1400 st_h->interop_u.gfc.idle_off = 0; 1401 for (i = 3; i <= MAX_MATCHES; ++i) 1402 if (matches[i].rm_so >= 0) 1403 { 1404 len = matches[i].rm_eo - matches[i].rm_so; 1405 push_path = malloc(sizeof(*push_path) + len + 1); 1406 memcpy(push_path->path, st_h->req->path 1407 + matches[i].rm_so, len); 1408 push_path->path[len] ='\0'; 1409 STAILQ_INSERT_TAIL(&st_h->interop_u.gfc.push_paths, 1410 push_path, next); 1411 } 1412 else 1413 break; 1414 break; 1415 default: 1416 /* TODO: implement this */ 1417 assert(0); 1418 break; 1419 } 1420 } 1421 1422 if (!(map->flags & RM_WANTBODY)) 1423 { 1424 err: 1425 lsquic_stream_shutdown(stream, 0); 1426 lsquic_stream_wantwrite(stream, 1); 1427 } 1428 } 1429 else 1430 { 1431 switch (st_h->interop_handler) 1432 { 1433 case IOH_MD5SUM: 1434 assert(!st_h->interop_u.md5c.done); 1435 nw = lsquic_stream_readf(stream, read_md5, st_h); 1436 if (nw < 0) 1437 { 1438 LSQ_ERROR("could not read from stream for MD5: %s", strerror(errno)); 1439 exit(1); 1440 } 1441 if (nw == 0) 1442 st_h->interop_u.md5c.done = 1; 1443 if (st_h->interop_u.md5c.done) 1444 { 1445 MD5_Final(md5sum, &st_h->interop_u.md5c.md5ctx); 1446 lsquic_hexstr(md5sum, sizeof(md5sum), md5str, sizeof(md5str)); 1447 snprintf(st_h->interop_u.md5c.resp_buf, sizeof(st_h->interop_u.md5c.resp_buf), 1448 "<html><head><title>MD5 Checksum Result</title></head>\n" 1449 "<body><h1>MD5 Checksum Result</h1>\n<p>" 1450 "MD5 Checksum: <tt>%s</tt>\n</body></html>\n", 1451 md5str); 1452 st_h->interop_u.md5c.resp.buf = st_h->interop_u.md5c.resp_buf; 1453 st_h->interop_u.md5c.resp.sz = strlen(st_h->interop_u.md5c.resp_buf); 1454 st_h->interop_u.md5c.resp.off = 0; 1455 lsquic_stream_shutdown(stream, 0); 1456 lsquic_stream_wantwrite(stream, 1); 1457 } 1458 break; 1459 case IOH_VER_HEAD: 1460 if (!st_h->interop_u.vhc.req_body) 1461 { 1462 st_h->interop_u.vhc.req_body = malloc(st_h->req->qif_sz); 1463 if (!st_h->interop_u.vhc.req_body) 1464 { 1465 perror("malloc"); 1466 exit(1); 1467 } 1468 } 1469 need = st_h->req->qif_sz - st_h->interop_u.vhc.req_sz; 1470 if (need > 0) 1471 { 1472 nw = lsquic_stream_read(stream, 1473 st_h->interop_u.vhc.req_body 1474 + st_h->interop_u.vhc.req_sz, need); 1475 if (nw > 0) 1476 st_h->interop_u.vhc.req_sz += need; 1477 else if (nw == 0) 1478 { 1479 LSQ_WARN("request body too short (does not match headers)"); 1480 lsquic_stream_shutdown(stream, 0); 1481 lsquic_stream_wantwrite(stream, 1); 1482 } 1483 else 1484 { 1485 LSQ_ERROR("error reading from stream"); 1486 exit(1); 1487 } 1488 } 1489 else 1490 { 1491 nw = lsquic_stream_read(stream, byte, sizeof(byte)); 1492 if (nw == 0) 1493 { 1494 if (0 == memcmp(st_h->req->qif_str, 1495 st_h->interop_u.vhc.req_body, st_h->req->qif_sz)) 1496 LSQ_INFO("request headers and payload check out"); 1497 else 1498 LSQ_WARN("request headers and payload are different"); 1499 } 1500 else 1501 LSQ_WARN("request body too long (does not match headers)"); 1502 lsquic_stream_shutdown(stream, 0); 1503 lsquic_stream_wantwrite(stream, 1); 1504 } 1505 break; 1506 default: 1507 assert(0); 1508 } 1509 } 1510} 1511 1512 1513static int 1514send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h, 1515 size_t content_len) 1516{ 1517 char clbuf[0x20]; 1518 struct header_buf hbuf; 1519 1520 snprintf(clbuf, sizeof(clbuf), "%zd", content_len); 1521 1522 hbuf.off = 0; 1523 struct lsxpack_header headers_arr[4]; 1524 header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V(st_h->resp_status)); 1525 header_set_ptr(&headers_arr[1], &hbuf, V("server"), V(LITESPEED_ID)); 1526 header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html")); 1527 header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf)); 1528 lsquic_http_headers_t headers = { 1529 .count = sizeof(headers_arr) / sizeof(headers_arr[0]), 1530 .headers = headers_arr, 1531 }; 1532 1533 return lsquic_stream_send_headers(st_h->stream, &headers, 0); 1534} 1535 1536#define MIN(a, b) ((a) < (b) ? (a) : (b)) 1537 1538static size_t 1539idle_read (void *lsqr_ctx, void *buf, size_t count) 1540{ 1541 struct gen_file_ctx *const gfc = lsqr_ctx; 1542 unsigned char *p = buf; 1543 unsigned char *const end = p + count; 1544 size_t towrite; 1545 1546 while (p < end && gfc->remain > 0) 1547 { 1548 towrite = MIN((unsigned) (end - p), IDLE_SIZE - gfc->idle_off); 1549 if (towrite > gfc->remain) 1550 towrite = gfc->remain; 1551 memcpy(p, on_being_idle + gfc->idle_off, towrite); 1552 gfc->idle_off += towrite; 1553 if (gfc->idle_off == IDLE_SIZE) 1554 gfc->idle_off = 0; 1555 p += towrite; 1556 gfc->remain -= towrite; 1557 } 1558 1559 return p - (unsigned char *) buf; 1560} 1561 1562 1563static size_t 1564idle_size (void *lsqr_ctx) 1565{ 1566 struct gen_file_ctx *const gfc = lsqr_ctx; 1567 1568 return gfc->remain; 1569} 1570 1571 1572static struct req * 1573new_req (enum method method, const char *path, const char *authority) 1574{ 1575 struct req *req; 1576 1577 req = malloc(sizeof(*req)); 1578 if (!req) 1579 return NULL; 1580 1581 memset(req, 0, offsetof(struct req, decode_buf)); 1582 req->method = method; 1583 req->path = strdup(path); 1584 req->authority_str = strdup(authority); 1585 if (!(req->path && req->authority_str)) 1586 { 1587 free(req->path); 1588 free(req->authority_str); 1589 free(req); 1590 return NULL; 1591 } 1592 1593 return req; 1594} 1595 1596 1597static ssize_t 1598my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt) 1599{ 1600 struct gen_file_ctx *const gfc = user_data; 1601 size_t nread, nr; 1602 int i; 1603 1604 nread = 0; 1605 for (i = 0; i < iovcnt; ++i) 1606 { 1607 nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len); 1608 nread += nr; 1609 } 1610 1611 return (ssize_t) nread; 1612} 1613 1614 1615static void 1616idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1617{ 1618 struct gen_file_ctx *const gfc = &st_h->interop_u.gfc; 1619 struct interop_push_path *push_path; 1620 struct lsxpack_header header_arr[4]; 1621 struct lsquic_http_headers headers; 1622 struct req *req; 1623 ssize_t nw; 1624 struct header_buf hbuf; 1625 struct lsquic_reader reader; 1626 1627 if (st_h->flags & SH_HEADERS_SENT) 1628 { 1629 if (s_pwritev) 1630 { 1631 nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc, 1632 gfc->remain); 1633 if (nw == 0) 1634 goto with_reader; 1635 } 1636 else 1637 { 1638 with_reader: 1639 reader.lsqr_read = idle_read, 1640 reader.lsqr_size = idle_size, 1641 reader.lsqr_ctx = gfc, 1642 nw = lsquic_stream_writef(stream, &reader); 1643 } 1644 if (nw < 0) 1645 { 1646 LSQ_ERROR("error writing idle thoughts: %s", strerror(errno)); 1647 exit(1); 1648 } 1649 if (gfc->remain == 0) 1650 lsquic_stream_shutdown(stream, 1); 1651 } 1652 else 1653 { 1654 if (st_h->req->authority_str) 1655 while ((push_path = STAILQ_FIRST(&gfc->push_paths))) 1656 { 1657 STAILQ_REMOVE_HEAD(&gfc->push_paths, next); 1658 LSQ_DEBUG("pushing promise for %s", push_path->path); 1659 hbuf.off = 0; 1660 header_set_ptr(&header_arr[0], &hbuf, V(":method"), V("GET")); 1661 header_set_ptr(&header_arr[1], &hbuf, V(":path"), V(push_path->path)); 1662 header_set_ptr(&header_arr[2], &hbuf, V(":authority"), V(st_h->req->authority_str)); 1663 header_set_ptr(&header_arr[3], &hbuf, V(":scheme"), V("https")); 1664 headers.headers = header_arr; 1665 headers.count = sizeof(header_arr) / sizeof(header_arr[0]); 1666 req = new_req(GET, push_path->path, st_h->req->authority_str); 1667 if (req) 1668 { 1669 if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream), 1670 req, stream, &headers)) 1671 { 1672 LSQ_WARN("stream push failed"); 1673 interop_server_hset_destroy(req); 1674 } 1675 } 1676 else 1677 LSQ_WARN("cannot allocate req for push"); 1678 free(push_path); 1679 } 1680 if (0 == send_headers2(stream, st_h, gfc->remain)) 1681 st_h->flags |= SH_HEADERS_SENT; 1682 else 1683 { 1684 LSQ_ERROR("cannot send headers: %s", strerror(errno)); 1685 lsquic_stream_close(stream); 1686 } 1687 } 1688} 1689 1690 1691static void 1692http_server_interop_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1693{ 1694 struct resp *resp; 1695 ssize_t nw; 1696 1697 switch (st_h->interop_handler) 1698 { 1699 case IOH_ERROR: 1700 resp = &st_h->interop_u.err.resp; 1701 goto reply; 1702 case IOH_INDEX_HTML: 1703 resp = &st_h->interop_u.ihc.resp; 1704 goto reply; 1705 case IOH_VER_HEAD: 1706 resp = &st_h->interop_u.vhc.resp; 1707 goto reply; 1708 case IOH_MD5SUM: 1709 resp = &st_h->interop_u.md5c.resp; 1710 goto reply; 1711 case IOH_GEN_FILE: 1712 idle_on_write(stream, st_h); 1713 return; 1714 default: 1715 assert(0); 1716 return; 1717 } 1718 1719 reply: 1720 assert(resp->sz); /* We always need to send body */ 1721 if (!(st_h->flags & SH_HEADERS_SENT)) 1722 { 1723 send_headers2(stream, st_h, resp->sz); 1724 st_h->flags |= SH_HEADERS_SENT; 1725 return; 1726 } 1727 1728 nw = lsquic_stream_write(stream, resp->buf + resp->off, resp->sz - resp->off); 1729 if (nw < 0) 1730 { 1731 LSQ_ERROR("error writing to stream: %s", strerror(errno)); 1732 lsquic_conn_abort(lsquic_stream_conn(stream)); 1733 return; 1734 } 1735 1736 resp->off += nw; 1737 lsquic_stream_flush(stream); 1738 if (resp->off == resp->sz) 1739 lsquic_stream_shutdown(stream, 1); 1740} 1741 1742 1743const struct lsquic_stream_if interop_http_server_if = { 1744 .on_new_conn = http_server_on_new_conn, 1745 .on_conn_closed = http_server_on_conn_closed, 1746 .on_new_stream = http_server_on_new_stream, 1747 .on_read = http_server_interop_on_read, 1748 .on_write = http_server_interop_on_write, 1749 .on_close = http_server_on_close, 1750}; 1751#endif /* HAVE_REGEX */ 1752 1753 1754static void 1755usage (const char *prog) 1756{ 1757 const char *const slash = strrchr(prog, '/'); 1758 if (slash) 1759 prog = slash + 1; 1760 printf( 1761"Usage: %s [opts]\n" 1762"\n" 1763"Options:\n" 1764" -r ROOT Document root\n" 1765" -p FILE Push request with this path\n" 1766" -w SIZE Write immediately (LSWS mode). Argument specifies maximum\n" 1767" size of the immediate write.\n" 1768#if HAVE_PREADV 1769" -P SIZE Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n" 1770" write to stream. Positive SIZE indicate maximum value per\n" 1771" write; negative means always use remaining file size.\n" 1772" Incompatible with -w.\n" 1773#endif 1774" -y DELAY Delay response for this many seconds -- use for debugging\n" 1775" -Q ALPN Use hq mode; ALPN could be \"hq-29\", for example.\n" 1776 , prog); 1777} 1778 1779 1780static void * 1781interop_server_hset_create (void *hsi_ctx, lsquic_stream_t *stream, 1782 int is_push_promise) 1783{ 1784 struct req *req; 1785 1786 req = malloc(sizeof(struct req)); 1787 memset(req, 0, offsetof(struct req, decode_buf)); 1788 1789 return req; 1790} 1791 1792 1793static struct lsxpack_header * 1794interop_server_hset_prepare_decode (void *hset_p, struct lsxpack_header *xhdr, 1795 size_t req_space) 1796{ 1797 struct req *req = hset_p; 1798 1799 if (xhdr) 1800 { 1801 LSQ_WARN("we don't reallocate headers: can't give more"); 1802 return NULL; 1803 } 1804 1805 if (req->flags & HAVE_XHDR) 1806 { 1807 if (req->decode_off + lsxpack_header_get_dec_size(&req->xhdr) 1808 >= sizeof(req->decode_buf)) 1809 { 1810 LSQ_WARN("Not enough room in header"); 1811 return NULL; 1812 } 1813 req->decode_off += lsxpack_header_get_dec_size(&req->xhdr); 1814 } 1815 else 1816 req->flags |= HAVE_XHDR; 1817 1818 lsxpack_header_prepare_decode(&req->xhdr, req->decode_buf, 1819 req->decode_off, sizeof(req->decode_buf) - req->decode_off); 1820 return &req->xhdr; 1821} 1822 1823 1824static int 1825interop_server_hset_add_header (void *hset_p, struct lsxpack_header *xhdr) 1826{ 1827 struct req *req = hset_p; 1828 const char *name, *value; 1829 unsigned name_len, value_len; 1830 1831 if (!xhdr) 1832 return 0; 1833 1834 name = lsxpack_header_get_name(xhdr); 1835 value = lsxpack_header_get_value(xhdr); 1836 name_len = xhdr->name_len; 1837 value_len = xhdr->val_len; 1838 1839 req->qif_str = realloc(req->qif_str, 1840 req->qif_sz + name_len + value_len + 2); 1841 if (!req->qif_str) 1842 { 1843 LSQ_ERROR("malloc failed"); 1844 return -1; 1845 } 1846 memcpy(req->qif_str + req->qif_sz, name, name_len); 1847 req->qif_str[req->qif_sz + name_len] = '\t'; 1848 memcpy(req->qif_str + req->qif_sz + name_len + 1, value, value_len); 1849 req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n'; 1850 req->qif_sz += name_len + value_len + 2; 1851 1852 if (5 == name_len && 0 == strncmp(name, ":path", 5)) 1853 { 1854 if (req->path) 1855 return 1; 1856 req->path = strndup(value, value_len); 1857 if (!req->path) 1858 return -1; 1859 return 0; 1860 } 1861 1862 if (7 == name_len && 0 == strncmp(name, ":method", 7)) 1863 { 1864 if (req->method != UNSET) 1865 return 1; 1866 req->method_str = strndup(value, value_len); 1867 if (!req->method_str) 1868 return -1; 1869 if (0 == strcmp(req->method_str, "GET")) 1870 req->method = GET; 1871 else if (0 == strcmp(req->method_str, "POST")) 1872 req->method = POST; 1873 else 1874 req->method = UNSUPPORTED; 1875 return 0; 1876 } 1877 1878 if (10 == name_len && 0 == strncmp(name, ":authority", 10)) 1879 { 1880 req->authority_str = strndup(value, value_len); 1881 if (!req->authority_str) 1882 return -1; 1883 return 0; 1884 } 1885 1886 return 0; 1887} 1888 1889 1890static void 1891interop_server_hset_destroy (void *hset_p) 1892{ 1893 struct req *req = hset_p; 1894 free(req->qif_str); 1895 free(req->path); 1896 free(req->method_str); 1897 free(req->authority_str); 1898 free(req); 1899} 1900 1901 1902static const struct lsquic_hset_if header_bypass_api = 1903{ 1904 .hsi_create_header_set = interop_server_hset_create, 1905 .hsi_prepare_decode = interop_server_hset_prepare_decode, 1906 .hsi_process_header = interop_server_hset_add_header, 1907 .hsi_discard_header_set = interop_server_hset_destroy, 1908}; 1909 1910 1911int 1912main (int argc, char **argv) 1913{ 1914 int opt, s; 1915 struct stat st; 1916 struct server_ctx server_ctx; 1917 struct prog prog; 1918 const char *const *alpn; 1919 1920#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX) 1921 fprintf(stderr, "cannot run server without regex or open_memstream\n"); 1922 return 1; 1923#endif 1924 1925 memset(&server_ctx, 0, sizeof(server_ctx)); 1926 TAILQ_INIT(&server_ctx.sports); 1927 server_ctx.prog = &prog; 1928 1929 prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports, 1930 &http_server_if, &server_ctx); 1931 1932 while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h" 1933#if HAVE_OPEN_MEMSTREAM 1934 "Q:" 1935#endif 1936 ))) 1937 { 1938 switch (opt) { 1939 case 'n': 1940 server_ctx.max_conn = atoi(optarg); 1941 break; 1942 case 'p': 1943 server_ctx.push_path = optarg; 1944 break; 1945 case 'r': 1946 if (-1 == stat(optarg, &st)) 1947 { 1948 perror("stat"); 1949 exit(2); 1950 } 1951#ifndef WIN32 1952 if (!S_ISDIR(st.st_mode)) 1953 { 1954 fprintf(stderr, "`%s' is not a directory\n", optarg); 1955 exit(2); 1956 } 1957#endif 1958 server_ctx.document_root = optarg; 1959 break; 1960 case 'w': 1961 s_immediate_write = atoi(optarg); 1962 break; 1963 case 'P': 1964#if HAVE_PREADV 1965 s_pwritev = strtoull(optarg, NULL, 10); 1966 break; 1967#else 1968 fprintf(stderr, "preadv is not supported on this platform, " 1969 "cannot use -P\n"); 1970 exit(EXIT_FAILURE); 1971#endif 1972 case 'y': 1973 server_ctx.delay_resp_sec = atoi(optarg); 1974 break; 1975 case 'h': 1976 usage(argv[0]); 1977 prog_print_common_options(&prog, stdout); 1978 exit(0); 1979#if HAVE_OPEN_MEMSTREAM 1980 case 'Q': 1981 /* XXX A bit hacky, as `prog' has already been initialized... */ 1982 prog.prog_engine_flags &= ~LSENG_HTTP; 1983 prog.prog_api.ea_stream_if = &hq_server_if; 1984 add_alpn(optarg); 1985 break; 1986#endif 1987 default: 1988 if (0 != prog_set_opt(&prog, opt, optarg)) 1989 exit(1); 1990 } 1991 } 1992 1993 if (!server_ctx.document_root) 1994 { 1995#if HAVE_REGEX 1996 LSQ_NOTICE("Document root is not set: start in Interop Mode"); 1997 init_map_regexes(); 1998 prog.prog_api.ea_stream_if = &interop_http_server_if; 1999 prog.prog_api.ea_hsi_if = &header_bypass_api; 2000 prog.prog_api.ea_hsi_ctx = NULL; 2001#else 2002 LSQ_ERROR("Document root is not set: use -r option"); 2003 exit(EXIT_FAILURE); 2004#endif 2005 } 2006 2007 if (s_immediate_write && s_pwritev) 2008 { 2009 LSQ_ERROR("-w and -P are incompatible options"); 2010 exit(EXIT_FAILURE); 2011 } 2012 2013 alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions); 2014 while (*alpn) 2015 { 2016 if (0 == add_alpn(*alpn)) 2017 ++alpn; 2018 else 2019 { 2020 LSQ_ERROR("cannot add ALPN %s", *alpn); 2021 exit(EXIT_FAILURE); 2022 } 2023 } 2024 2025 if (0 != prog_prep(&prog)) 2026 { 2027 LSQ_ERROR("could not prep"); 2028 exit(EXIT_FAILURE); 2029 } 2030 2031 LSQ_DEBUG("entering event loop"); 2032 2033 s = prog_run(&prog); 2034 prog_cleanup(&prog); 2035 2036#if HAVE_REGEX 2037 if (!server_ctx.document_root) 2038 free_map_regexes(); 2039#endif 2040 2041 exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE); 2042} 2043