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