http_server.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * http_server.c -- A simple HTTP/QUIC server 4 * 5 * It serves up files from the filesystem. 6 */ 7 8#include <assert.h> 9#include <errno.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <sys/queue.h> 14#include <sys/types.h> 15#include <sys/stat.h> 16#include <inttypes.h> 17 18#ifndef WIN32 19#include <netinet/in.h> 20#include <unistd.h> 21#include <fcntl.h> 22#else 23#include "vc_compat.h" 24#include "getopt.h" 25#endif 26 27#include <event2/event.h> 28 29#include <openssl/md5.h> 30 31#include "lsquic.h" 32#include "../src/liblsquic/lsquic_hash.h" 33#include "lsxpack_header.h" 34#include "test_config.h" 35#include "test_common.h" 36#include "test_cert.h" 37#include "prog.h" 38 39#if HAVE_REGEX 40#ifndef WIN32 41#include <regex.h> 42#else 43#include <pcreposix.h> 44#endif 45#endif 46 47#include "../src/liblsquic/lsquic_logger.h" 48#include "../src/liblsquic/lsquic_int_types.h" 49#include "../src/liblsquic/lsquic_util.h" 50 51#if HAVE_REGEX 52static const char on_being_idle[] = 53"ON BEING IDLE.\n" 54"\n" 55"Now, this is a subject on which I flatter myself I really am _au fait_.\n" 56"The gentleman who, when I was young, bathed me at wisdom's font for nine\n" 57"guineas a term--no extras--used to say he never knew a boy who could\n" 58"do less work in more time; and I remember my poor grandmother once\n" 59"incidentally observing, in the course of an instruction upon the use\n" 60"of the Prayer-book, that it was highly improbable that I should ever do\n" 61"much that I ought not to do, but that she felt convinced beyond a doubt\n" 62"that I should leave undone pretty well everything that I ought to do.\n" 63"\n" 64"I am afraid I have somewhat belied half the dear old lady's prophecy.\n" 65"Heaven help me! I have done a good many things that I ought not to have\n" 66"done, in spite of my laziness. But I have fully confirmed the accuracy\n" 67"of her judgment so far as neglecting much that I ought not to have\n" 68"neglected is concerned. Idling always has been my strong point. I take\n" 69"no credit to myself in the matter--it is a gift. Few possess it. There\n" 70"are plenty of lazy people and plenty of slow-coaches, but a genuine\n" 71"idler is a rarity. He is not a man who slouches about with his hands in\n" 72"his pockets. On the contrary, his most startling characteristic is that\n" 73"he is always intensely busy.\n" 74"\n" 75"It is impossible to enjoy idling thoroughly unless one has plenty of\n" 76"work to do. There is no fun in doing nothing when you have nothing to\n" 77"do. Wasting time is merely an occupation then, and a most exhausting\n" 78"one. Idleness, like kisses, to be sweet must be stolen.\n" 79"\n" 80"Many years ago, when I was a young man, I was taken very ill--I never\n" 81"could see myself that much was the matter with me, except that I had\n" 82"a beastly cold. But I suppose it was something very serious, for the\n" 83"doctor said that I ought to have come to him a month before, and that\n" 84"if it (whatever it was) had gone on for another week he would not have\n" 85"answered for the consequences. It is an extraordinary thing, but I\n" 86"never knew a doctor called into any case yet but what it transpired\n" 87"that another day's delay would have rendered cure hopeless. Our medical\n" 88"guide, philosopher, and friend is like the hero in a melodrama--he\n" 89"always comes upon the scene just, and only just, in the nick of time. It\n" 90"is Providence, that is what it is.\n" 91"\n" 92"Well, as I was saying, I was very ill and was ordered to Buxton for a\n" 93"month, with strict injunctions to do nothing whatever all the while\n" 94"that I was there. \"Rest is what you require,\" said the doctor, \"perfect\n" 95"rest.\"\n" 96"\n" 97"It seemed a delightful prospect. \"This man evidently understands my\n" 98"complaint,\" said I, and I pictured to myself a glorious time--a four\n" 99"weeks' _dolce far niente_ with a dash of illness in it. Not too much\n" 100"illness, but just illness enough--just sufficient to give it the flavor\n" 101"of suffering and make it poetical. I should get up late, sip chocolate,\n" 102"and have my breakfast in slippers and a dressing-gown. I should lie out\n" 103"in the garden in a hammock and read sentimental novels with a melancholy\n" 104"ending, until the books should fall from my listless hand, and I should\n" 105"recline there, dreamily gazing into the deep blue of the firmament,\n" 106"watching the fleecy clouds floating like white-sailed ships across\n" 107"its depths, and listening to the joyous song of the birds and the low\n" 108"rustling of the trees. Or, on becoming too weak to go out of doors,\n" 109"I should sit propped up with pillows at the open window of the\n" 110"ground-floor front, and look wasted and interesting, so that all the\n" 111"pretty girls would sigh as they passed by.\n" 112"\n" 113"And twice a day I should go down in a Bath chair to the Colonnade to\n" 114"drink the waters. Oh, those waters! I knew nothing about them then,\n" 115"and was rather taken with the idea. \"Drinking the waters\" sounded\n" 116"fashionable and Queen Anne-fied, and I thought I should like them. But,\n" 117"ugh! after the first three or four mornings! Sam Weller's description of\n" 118"them as \"having a taste of warm flat-irons\" conveys only a faint idea of\n" 119"their hideous nauseousness. If anything could make a sick man get well\n" 120"quickly, it would be the knowledge that he must drink a glassful of them\n" 121"every day until he was recovered. I drank them neat for six consecutive\n" 122"days, and they nearly killed me; but after then I adopted the plan of\n" 123"taking a stiff glass of brandy-and-water immediately on the top of them,\n" 124"and found much relief thereby. I have been informed since, by various\n" 125"eminent medical gentlemen, that the alcohol must have entirely\n" 126"counteracted the effects of the chalybeate properties contained in the\n" 127"water. I am glad I was lucky enough to hit upon the right thing.\n" 128"\n" 129"But \"drinking the waters\" was only a small portion of the torture I\n" 130"experienced during that memorable month--a month which was, without\n" 131"exception, the most miserable I have ever spent. During the best part of\n" 132"it I religiously followed the doctor's mandate and did nothing whatever,\n" 133"except moon about the house and garden and go out for two hours a day in\n" 134"a Bath chair. That did break the monotony to a certain extent. There is\n" 135"more excitement about Bath-chairing--especially if you are not used to\n" 136"the exhilarating exercise--than might appear to the casual observer. A\n" 137"sense of danger, such as a mere outsider might not understand, is ever\n" 138"present to the mind of the occupant. He feels convinced every minute\n" 139"that the whole concern is going over, a conviction which becomes\n" 140"especially lively whenever a ditch or a stretch of newly macadamized\n" 141"road comes in sight. Every vehicle that passes he expects is going to\n" 142"run into him; and he never finds himself ascending or descending a\n" 143"hill without immediately beginning to speculate upon his chances,\n" 144"supposing--as seems extremely probable--that the weak-kneed controller\n" 145"of his destiny should let go.\n" 146"\n" 147"But even this diversion failed to enliven after awhile, and the _ennui_\n" 148"became perfectly unbearable. I felt my mind giving way under it. It is\n" 149"not a strong mind, and I thought it would be unwise to tax it too far.\n" 150"So somewhere about the twentieth morning I got up early, had a good\n" 151"breakfast, and walked straight off to Hayfield, at the foot of the\n" 152"Kinder Scout--a pleasant, busy little town, reached through a lovely\n" 153"valley, and with two sweetly pretty women in it. At least they were\n" 154"sweetly pretty then; one passed me on the bridge and, I think, smiled;\n" 155"and the other was standing at an open door, making an unremunerative\n" 156"investment of kisses upon a red-faced baby. But it is years ago, and I\n" 157"dare say they have both grown stout and snappish since that time.\n" 158"Coming back, I saw an old man breaking stones, and it roused such strong\n" 159"longing in me to use my arms that I offered him a drink to let me take\n" 160"his place. He was a kindly old man and he humored me. I went for those\n" 161"stones with the accumulated energy of three weeks, and did more work in\n" 162"half an hour than he had done all day. But it did not make him jealous.\n" 163"\n" 164"Having taken the plunge, I went further and further into dissipation,\n" 165"going out for a long walk every morning and listening to the band in\n" 166"the pavilion every evening. But the days still passed slowly\n" 167"notwithstanding, and I was heartily glad when the last one came and I\n" 168"was being whirled away from gouty, consumptive Buxton to London with its\n" 169"stern work and life. I looked out of the carriage as we rushed through\n" 170"Hendon in the evening. The lurid glare overhanging the mighty city\n" 171"seemed to warm my heart, and when, later on, my cab rattled out of St.\n" 172"Pancras' station, the old familiar roar that came swelling up around me\n" 173"sounded the sweetest music I had heard for many a long day.\n" 174"\n" 175"I certainly did not enjoy that month's idling. I like idling when I\n" 176"ought not to be idling; not when it is the only thing I have to do. That\n" 177"is my pig-headed nature. The time when I like best to stand with my\n" 178"back to the fire, calculating how much I owe, is when my desk is heaped\n" 179"highest with letters that must be answered by the next post. When I like\n" 180"to dawdle longest over my dinner is when I have a heavy evening's work\n" 181"before me. And if, for some urgent reason, I ought to be up particularly\n" 182"early in the morning, it is then, more than at any other time, that I\n" 183"love to lie an extra half-hour in bed.\n" 184"\n" 185"Ah! how delicious it is to turn over and go to sleep again: \"just for\n" 186"five minutes.\" Is there any human being, I wonder, besides the hero of\n" 187"a Sunday-school \"tale for boys,\" who ever gets up willingly? There\n" 188"are some men to whom getting up at the proper time is an utter\n" 189"impossibility. If eight o'clock happens to be the time that they should\n" 190"turn out, then they lie till half-past. If circumstances change and\n" 191"half-past eight becomes early enough for them, then it is nine before\n" 192"they can rise. They are like the statesman of whom it was said that he\n" 193"was always punctually half an hour late. They try all manner of schemes.\n" 194"They buy alarm-clocks (artful contrivances that go off at the wrong time\n" 195"and alarm the wrong people). They tell Sarah Jane to knock at the door\n" 196"and call them, and Sarah Jane does knock at the door and does call them,\n" 197"and they grunt back \"awri\" and then go comfortably to sleep again. I\n" 198"knew one man who would actually get out and have a cold bath; and even\n" 199"that was of no use, for afterward he would jump into bed again to warm\n" 200"himself.\n" 201"\n" 202"I think myself that I could keep out of bed all right if I once got\n" 203"out. It is the wrenching away of the head from the pillow that I find so\n" 204"hard, and no amount of over-night determination makes it easier. I say\n" 205"to myself, after having wasted the whole evening, \"Well, I won't do\n" 206"any more work to-night; I'll get up early to-morrow morning;\" and I am\n" 207"thoroughly resolved to do so--then. In the morning, however, I feel less\n" 208"enthusiastic about the idea, and reflect that it would have been much\n" 209"better if I had stopped up last night. And then there is the trouble of\n" 210"dressing, and the more one thinks about that the more one wants to put\n" 211"it off.\n" 212"\n" 213"It is a strange thing this bed, this mimic grave, where we stretch our\n" 214"tired limbs and sink away so quietly into the silence and rest. \"O bed,\n" 215"O bed, delicious bed, that heaven on earth to the weary head,\" as sang\n" 216"poor Hood, you are a kind old nurse to us fretful boys and girls. Clever\n" 217"and foolish, naughty and good, you take us all in your motherly lap and\n" 218"hush our wayward crying. The strong man full of care--the sick man\n" 219"full of pain--the little maiden sobbing for her faithless lover--like\n" 220"children we lay our aching heads on your white bosom, and you gently\n" 221"soothe us off to by-by.\n" 222"\n" 223"Our trouble is sore indeed when you turn away and will not comfort us.\n" 224"How long the dawn seems coming when we cannot sleep! Oh! those hideous\n" 225"nights when we toss and turn in fever and pain, when we lie, like living\n" 226"men among the dead, staring out into the dark hours that drift so slowly\n" 227"between us and the light. And oh! those still more hideous nights when\n" 228"we sit by another in pain, when the low fire startles us every now and\n" 229"then with a falling cinder, and the tick of the clock seems a hammer\n" 230"beating out the life that we are watching.\n" 231"\n" 232"But enough of beds and bedrooms. I have kept to them too long, even for\n" 233"an idle fellow. Let us come out and have a smoke. That wastes time just\n" 234"as well and does not look so bad. Tobacco has been a blessing to us\n" 235"idlers. What the civil-service clerk before Sir Walter's time found\n" 236"to occupy their minds with it is hard to imagine. I attribute the\n" 237"quarrelsome nature of the Middle Ages young men entirely to the want of\n" 238"the soothing weed. They had no work to do and could not smoke, and\n" 239"the consequence was they were forever fighting and rowing. If, by any\n" 240"extraordinary chance, there was no war going, then they got up a deadly\n" 241"family feud with the next-door neighbor, and if, in spite of this, they\n" 242"still had a few spare moments on their hands, they occupied them with\n" 243"discussions as to whose sweetheart was the best looking, the arguments\n" 244"employed on both sides being battle-axes, clubs, etc. Questions of taste\n" 245"were soon decided in those days. When a twelfth-century youth fell in\n" 246"love he did not take three paces backward, gaze into her eyes, and tell\n" 247"her she was too beautiful to live. He said he would step outside and see\n" 248"about it. And if, when he got out, he met a man and broke his head--the\n" 249"other man's head, I mean--then that proved that his--the first\n" 250"fellow's--girl was a pretty girl. But if the other fellow broke _his_\n" 251"head--not his own, you know, but the other fellow's--the other fellow\n" 252"to the second fellow, that is, because of course the other fellow would\n" 253"only be the other fellow to him, not the first fellow who--well, if he\n" 254"broke his head, then _his_ girl--not the other fellow's, but the fellow\n" 255"who _was_ the--Look here, if A broke B's head, then A's girl was a\n" 256"pretty girl; but if B broke A's head, then A's girl wasn't a pretty\n" 257"girl, but B's girl was. That was their method of conducting art\n" 258"criticism.\n" 259"\n" 260"Nowadays we light a pipe and let the girls fight it out among\n" 261"themselves.\n" 262"\n" 263"They do it very well. They are getting to do all our work. They are\n" 264"doctors, and barristers, and artists. They manage theaters, and promote\n" 265"swindles, and edit newspapers. I am looking forward to the time when we\n" 266"men shall have nothing to do but lie in bed till twelve, read two novels\n" 267"a day, have nice little five-o'clock teas all to ourselves, and tax\n" 268"our brains with nothing more trying than discussions upon the latest\n" 269"patterns in trousers and arguments as to what Mr. Jones' coat was\n" 270"made of and whether it fitted him. It is a glorious prospect--for idle\n" 271"fellows.\n" 272"\n\n\n" 273; 274static const size_t IDLE_SIZE = sizeof(on_being_idle) - 1; 275#endif 276 277/* This is the "LSWS" mode: first write is performed immediately, outside 278 * of the on_write() callback. This makes it possible to play with buffered 279 * packet queues. 280 */ 281static int s_immediate_write; 282 283/* Use preadv(2) in conjuction with lsquic_stream_pwritev() to reduce 284 * number of system calls required to read from disk. The actual value 285 * specifies maximum write size. A negative value indicates always to use 286 * the remaining file size. 287 */ 288static ssize_t s_pwritev; 289 290#define MIN(a, b) ((a) < (b) ? (a) : (b)) 291#define V(v) (v), strlen(v) 292 293struct lsquic_conn_ctx; 294 295static void interop_server_hset_destroy (void *); 296 297 298struct server_ctx { 299 struct lsquic_conn_ctx *conn_h; 300 lsquic_engine_t *engine; 301 const char *document_root; 302 const char *push_path; 303 struct sport_head sports; 304 struct prog *prog; 305 unsigned max_conn; 306 unsigned n_conn; 307 unsigned n_current_conns; 308 unsigned delay_resp_sec; 309}; 310 311struct lsquic_conn_ctx { 312 lsquic_conn_t *conn; 313 struct server_ctx *server_ctx; 314 enum { 315 RECEIVED_GOAWAY = 1 << 0, 316 } flags; 317}; 318 319 320static lsquic_conn_ctx_t * 321http_server_on_new_conn (void *stream_if_ctx, lsquic_conn_t *conn) 322{ 323 struct server_ctx *server_ctx = stream_if_ctx; 324 lsquic_conn_ctx_t *conn_h = malloc(sizeof(*conn_h)); 325 conn_h->conn = conn; 326 conn_h->server_ctx = server_ctx; 327 server_ctx->conn_h = conn_h; 328 ++server_ctx->n_current_conns; 329 return conn_h; 330} 331 332 333static void 334http_server_on_goaway (lsquic_conn_t *conn) 335{ 336 lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn); 337 conn_h->flags |= RECEIVED_GOAWAY; 338 LSQ_INFO("received GOAWAY"); 339} 340 341 342static void 343http_server_on_conn_closed (lsquic_conn_t *conn) 344{ 345 static int stopped; 346 lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn); 347 LSQ_INFO("Connection closed"); 348 --conn_h->server_ctx->n_current_conns; 349 if ((conn_h->server_ctx->prog->prog_flags & PROG_FLAG_COOLDOWN) 350 && 0 == conn_h->server_ctx->n_current_conns) 351 { 352 if (!stopped) 353 { 354 stopped = 1; 355 prog_stop(conn_h->server_ctx->prog); 356 } 357 } 358 if (conn_h->server_ctx->max_conn > 0) 359 { 360 ++conn_h->server_ctx->n_conn; 361 LSQ_NOTICE("Connection closed, remaining: %d", 362 conn_h->server_ctx->max_conn - conn_h->server_ctx->n_conn); 363 if (conn_h->server_ctx->n_conn >= conn_h->server_ctx->max_conn) 364 { 365 if (!stopped) 366 { 367 stopped = 1; 368 prog_stop(conn_h->server_ctx->prog); 369 } 370 } 371 } 372 /* No provision is made to stop HTTP server */ 373 free(conn_h); 374} 375 376 377struct resp 378{ 379 const char *buf; 380 size_t sz; 381 size_t off; 382}; 383 384 385struct index_html_ctx 386{ 387 struct resp resp; 388}; 389 390 391struct ver_head_ctx 392{ 393 struct resp resp; 394 unsigned char *req_body; 395 size_t req_sz; /* Expect it to be the same as qif_sz */ 396}; 397 398 399struct md5sum_ctx 400{ 401 char resp_buf[0x100]; 402 MD5_CTX md5ctx; 403 struct resp resp; 404 int done; 405}; 406 407 408struct req 409{ 410 enum method { 411 UNSET, GET, POST, UNSUPPORTED, 412 } method; 413 enum { 414 HAVE_XHDR = 1 << 0, 415 } flags; 416 char *path; 417 char *method_str; 418 char *authority_str; 419 char *qif_str; 420 size_t qif_sz; 421 struct lsxpack_header 422 xhdr; 423 size_t decode_off; 424 char decode_buf[MIN(LSXPACK_MAX_STRLEN + 1, 64 * 1024)]; 425}; 426 427 428struct interop_push_path 429{ 430 STAILQ_ENTRY(interop_push_path) next; 431 char path[0]; 432}; 433 434 435struct gen_file_ctx 436{ 437 STAILQ_HEAD(, interop_push_path) push_paths; 438 size_t remain; 439 unsigned idle_off; 440}; 441 442 443struct lsquic_stream_ctx { 444 lsquic_stream_t *stream; 445 struct server_ctx *server_ctx; 446 FILE *req_fh; 447 char *req_buf; 448 char *req_filename; 449 char *req_path; 450 size_t req_sz; 451 enum { 452 SH_HEADERS_SENT = (1 << 0), 453 SH_DELAYED = (1 << 1), 454 SH_HEADERS_READ = (1 << 2), 455 } flags; 456 struct lsquic_reader reader; 457 int file_fd; /* Used by pwritev */ 458 459 /* Fields below are used by interop callbacks: */ 460 enum interop_handler { 461 IOH_ERROR, 462 IOH_INDEX_HTML, 463 IOH_MD5SUM, 464 IOH_VER_HEAD, 465 IOH_GEN_FILE, 466 IOH_ECHO, 467 } interop_handler; 468 struct req *req; 469 const char *resp_status; 470 union { 471 struct index_html_ctx ihc; 472 struct ver_head_ctx vhc; 473 struct md5sum_ctx md5c; 474 struct gen_file_ctx gfc; 475 struct { 476 char buf[0x100]; 477 struct resp resp; 478 } err; 479 } interop_u; 480 struct event *resume_resp; 481 size_t written; 482 size_t file_size; /* Used by pwritev */ 483}; 484 485 486static lsquic_stream_ctx_t * 487http_server_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 488{ 489 lsquic_stream_ctx_t *st_h = calloc(1, sizeof(*st_h)); 490 st_h->stream = stream; 491 st_h->server_ctx = stream_if_ctx; 492 lsquic_stream_wantread(stream, 1); 493 return st_h; 494} 495 496 497static int 498ends_with (const char *filename, const char *ext) 499{ 500 const char *where; 501 502 where = strstr(filename, ext); 503 return where 504 && strlen(where) == strlen(ext); 505} 506 507 508static const char * 509select_content_type (lsquic_stream_ctx_t *st_h) 510{ 511 if ( ends_with(st_h->req_filename, ".html")) 512 return "text/html"; 513 else if (ends_with(st_h->req_filename, ".png")) 514 return "image/png"; 515 else if (ends_with(st_h->req_filename, ".css")) 516 return "text/css"; 517 else if (ends_with(st_h->req_filename, ".gif")) 518 return "image/gif"; 519 else if (ends_with(st_h->req_filename, ".txt")) 520 return "text/plain"; 521 else 522 return "application/octet-stream"; 523} 524 525 526static int 527send_headers (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h) 528{ 529 const char *content_type; 530 struct header_buf hbuf; 531 532 content_type = select_content_type(st_h); 533 struct lsxpack_header headers_arr[2]; 534 535 hbuf.off = 0; 536 header_set_ptr(&headers_arr[0], &hbuf, ":status", 7, "200", 3); 537 header_set_ptr(&headers_arr[1], &hbuf, "content-type", 12, 538 content_type, strlen(content_type)); 539 lsquic_http_headers_t headers = { 540 .count = sizeof(headers_arr) / sizeof(headers_arr[0]), 541 .headers = headers_arr, 542 }; 543 if (0 != lsquic_stream_send_headers(stream, &headers, 0)) 544 { 545 LSQ_ERROR("cannot send headers: %s", strerror(errno)); 546 return -1; 547 } 548 549 st_h->flags |= SH_HEADERS_SENT; 550 return 0; 551} 552 553 554static void 555resume_response (evutil_socket_t fd, short what, void *arg) 556{ 557 struct lsquic_stream_ctx *const st_h = arg; 558 559 lsquic_stream_wantwrite(st_h->stream, 1); 560 event_del(st_h->resume_resp); 561 event_free(st_h->resume_resp); 562 st_h->resume_resp = NULL; 563 564 LSQ_NOTICE("resume response to stream %"PRIu64, 565 lsquic_stream_id(st_h->stream)); 566 prog_process_conns(st_h->server_ctx->prog); 567} 568 569 570static size_t 571bytes_left (lsquic_stream_ctx_t *st_h) 572{ 573 if (s_pwritev) 574 return st_h->file_size - st_h->written; 575 else 576 return test_reader_size(st_h->reader.lsqr_ctx); 577} 578 579 580static ssize_t 581my_preadv (void *user_data, const struct iovec *iov, int iovcnt) 582{ 583#if HAVE_PREADV 584 lsquic_stream_ctx_t *const st_h = user_data; 585 ssize_t nread = preadv(st_h->file_fd, iov, iovcnt, st_h->written); 586 LSQ_DEBUG("%s: wrote %zd bytes", __func__, (size_t) nread); 587 return nread; 588#else 589 return -1; 590#endif 591} 592 593 594static size_t 595pwritev_fallback_read (void *lsqr_ctx, void *buf, size_t count) 596{ 597 lsquic_stream_ctx_t *const st_h = lsqr_ctx; 598 struct iovec iov; 599 size_t ntoread; 600 601 ntoread = st_h->file_size - st_h->written; 602 if (ntoread > count) 603 count = ntoread; 604 iov.iov_base = buf; 605 iov.iov_len = count; 606 return my_preadv(lsqr_ctx, &iov, 1); 607} 608 609 610static size_t 611pwritev_fallback_size (void *lsqr_ctx) 612{ 613 lsquic_stream_ctx_t *const st_h = lsqr_ctx; 614 return st_h->file_size - st_h->written; 615} 616 617 618static void 619http_server_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 620{ 621 if (st_h->flags & SH_HEADERS_SENT) 622 { 623 ssize_t nw; 624 if (bytes_left(st_h) > 0) 625 { 626 if (st_h->server_ctx->delay_resp_sec 627 && !(st_h->flags & SH_DELAYED) 628 && st_h->written > 10000000) 629 { 630 struct timeval delay = { 631 .tv_sec = st_h->server_ctx->delay_resp_sec, }; 632 st_h->resume_resp = event_new(st_h->server_ctx->prog->prog_eb, 633 -1, EV_TIMEOUT, resume_response, st_h); 634 if (st_h->resume_resp) 635 { 636 event_add(st_h->resume_resp, &delay); 637 lsquic_stream_wantwrite(stream, 0); 638 st_h->flags |= SH_DELAYED; 639 LSQ_NOTICE("delay response of stream %"PRIu64" for %u seconds", 640 lsquic_stream_id(stream), st_h->server_ctx->delay_resp_sec); 641 return; 642 } 643 else 644 LSQ_ERROR("cannot allocate event"); 645 } 646 if (s_pwritev) 647 { 648 size_t to_write = bytes_left(st_h); 649 if (s_pwritev > 0 && (size_t) s_pwritev < to_write) 650 to_write = s_pwritev; 651 nw = lsquic_stream_pwritev(stream, my_preadv, st_h, to_write); 652 if (nw == 0) 653 { 654 struct lsquic_reader reader = { 655 .lsqr_read = pwritev_fallback_read, 656 .lsqr_size = pwritev_fallback_size, 657 .lsqr_ctx = st_h, 658 }; 659 nw = lsquic_stream_writef(stream, &reader); 660 } 661 } 662 else 663 { 664 nw = lsquic_stream_writef(stream, &st_h->reader); 665 } 666 if (nw < 0) 667 { 668 struct lsquic_conn *conn = lsquic_stream_conn(stream); 669 lsquic_conn_ctx_t *conn_h = lsquic_conn_get_ctx(conn); 670 if (conn_h->flags & RECEIVED_GOAWAY) 671 { 672 LSQ_NOTICE("cannot write: goaway received"); 673 lsquic_stream_close(stream); 674 } 675 else 676 { 677 LSQ_ERROR("write error: %s", strerror(errno)); 678 exit(1); 679 } 680 } 681 if (bytes_left(st_h) > 0) 682 { 683 st_h->written += (size_t) nw; 684 lsquic_stream_wantwrite(stream, 1); 685 } 686 else 687 { 688 lsquic_stream_shutdown(stream, 1); 689 lsquic_stream_wantread(stream, 1); 690 } 691 } 692 else 693 { 694 lsquic_stream_shutdown(stream, 1); 695 lsquic_stream_wantread(stream, 1); 696 } 697 } 698 else 699 { 700 if (0 != send_headers(stream, st_h)) 701 exit(1); 702 } 703} 704 705 706struct capped_reader_ctx 707{ 708 struct lsquic_reader *inner_reader; 709 size_t nread; 710}; 711 712 713static size_t 714capped_reader_size (void *void_ctx) 715{ 716 struct capped_reader_ctx *const capped_reader_ctx = void_ctx; 717 struct lsquic_reader *const inner_reader = capped_reader_ctx->inner_reader; 718 size_t size; 719 720 size = inner_reader->lsqr_size(inner_reader->lsqr_ctx); 721 return MIN((size_t) (s_immediate_write - capped_reader_ctx->nread), size); 722} 723 724 725static size_t 726capped_reader_read (void *void_ctx, void *buf, size_t count) 727{ 728 struct capped_reader_ctx *const capped_reader_ctx = void_ctx; 729 struct lsquic_reader *const inner_reader = capped_reader_ctx->inner_reader; 730 size_t size; 731 732 count = MIN(count, (size_t) (s_immediate_write - capped_reader_ctx->nread)); 733 size = inner_reader->lsqr_read(inner_reader->lsqr_ctx, buf, count); 734 capped_reader_ctx->nread += size; 735 return size; 736} 737 738 739#if HAVE_OPEN_MEMSTREAM 740static void 741parse_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h) 742{ 743 char *filename; 744 int s; 745 regex_t re; 746 regmatch_t matches[2]; 747 748 s = regcomp(&re, "GET (.*) HTTP/1.[01]\r\n", REG_EXTENDED); 749 if (0 != s) 750 { 751 perror("regcomp"); 752 exit(3); 753 } 754 755 s = regexec(&re, st_h->req_buf, 2, matches, 0); 756 if (0 != s) 757 { 758 LSQ_WARN("GET request could not be parsed: `%s'", st_h->req_buf); 759 regfree(&re); 760 return; 761 } 762 763 regfree(&re); 764 765 filename = malloc(strlen(st_h->server_ctx->document_root) + 1 + 766 matches[1].rm_eo - matches[1].rm_so + 1); 767 strcpy(filename, st_h->server_ctx->document_root); 768 strcat(filename, "/"); 769 strncat(filename, st_h->req_buf + matches[1].rm_so, 770 matches[1].rm_eo - matches[1].rm_so); 771 772 LSQ_INFO("filename to fetch: %s", filename); 773 774 st_h->req_filename = filename; 775 st_h->req_path = strdup(filename); 776} 777 778 779static void 780process_request (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h) 781{ 782 struct stat st; 783 784 if (s_pwritev) 785 { 786 st_h->file_fd = open(st_h->req_path, O_RDONLY); 787 if (st_h->file_fd < 0) 788 { 789 LSQ_ERROR("cannot open %s for reading: %s", st_h->req_path, 790 strerror(errno)); 791 exit(1); 792 } 793 if (fstat(st_h->file_fd, &st) < 0) 794 { 795 LSQ_ERROR("fstat: %s", strerror(errno)); 796 exit(1); 797 } 798 st_h->file_size = st.st_size; 799 } 800 else 801 { 802 st_h->reader.lsqr_read = test_reader_read; 803 st_h->reader.lsqr_size = test_reader_size; 804 st_h->reader.lsqr_ctx = create_lsquic_reader_ctx(st_h->req_path); 805 if (!st_h->reader.lsqr_ctx) 806 exit(1); 807 } 808 809 if (s_immediate_write) 810 { 811 if (0 != send_headers(stream, st_h)) 812 exit(1); 813 814 if (test_reader_size(st_h->reader.lsqr_ctx) > 0) 815 { 816 struct capped_reader_ctx capped_reader_ctx = 817 { 818 .inner_reader = &st_h->reader, 819 }; 820 struct lsquic_reader capped_reader = 821 { 822 .lsqr_read = capped_reader_read, 823 .lsqr_size = capped_reader_size, 824 .lsqr_ctx = &capped_reader_ctx, 825 }; 826 ssize_t nw; 827 nw = lsquic_stream_writef(stream, &capped_reader); 828 if (nw < 0) 829 { 830 LSQ_ERROR("write error: %s", strerror(errno)); 831 exit(1); 832 } 833 } 834 835 if (test_reader_size(st_h->reader.lsqr_ctx) > 0) 836 { 837 lsquic_stream_flush(stream); 838 lsquic_stream_wantwrite(stream, 1); 839 } 840 else 841 { 842 lsquic_stream_shutdown(stream, 1); 843 lsquic_stream_wantread(stream, 1); 844 } 845 } 846 else 847 lsquic_stream_wantwrite(st_h->stream, 1); 848} 849 850 851static struct hset_fm /* FM stands for Filesystem Mode */ 852{ 853 unsigned id; 854 char *path; 855} * 856new_hset_fm (const char *path) 857{ 858 static unsigned hfm_id; 859 struct hset_fm *const hfm = malloc(sizeof(*hfm)); 860 char *const str = strdup(path); 861 if (hfm && path) 862 { 863 hfm->id = hfm_id++; 864 hfm->path = str; 865 return hfm; 866 } 867 else 868 { 869 free(str); 870 free(hfm); 871 return NULL; 872 } 873} 874 875 876static void 877destroy_hset_fm (struct hset_fm *hfm) 878{ 879 free(hfm->path); 880 free(hfm); 881} 882 883 884static int 885push_promise (lsquic_stream_ctx_t *st_h, lsquic_stream_t *stream) 886{ 887 lsquic_conn_t *conn; 888 int s; 889 regex_t re; 890 regmatch_t matches[2]; 891 struct hset_fm *hfm; 892 struct header_buf hbuf; 893 894 s = regcomp(&re, "\r\nHost: *([[:alnum:].][[:alnum:].]*)\r\n", 895 REG_EXTENDED|REG_ICASE); 896 if (0 != s) 897 { 898 perror("regcomp"); 899 exit(3); 900 } 901 902 s = regexec(&re, st_h->req_buf, 2, matches, 0); 903 if (0 != s) 904 { 905 LSQ_WARN("Could not find host header in request `%s'", st_h->req_buf); 906 regfree(&re); 907 return -1; 908 } 909 regfree(&re); 910 911 hfm = new_hset_fm(st_h->server_ctx->push_path); 912 if (!hfm) 913 { 914 LSQ_WARN("Could not allocate hfm"); 915 return -1; 916 } 917 918#define V(v) (v), strlen(v) 919 hbuf.off = 0; 920 struct lsxpack_header headers_arr[6]; 921 header_set_ptr(&headers_arr[0], &hbuf, V(":method"), V("GET")); 922 header_set_ptr(&headers_arr[1], &hbuf, V(":path"), 923 V(st_h->server_ctx->push_path)); 924 header_set_ptr(&headers_arr[2], &hbuf, V(":authority"), 925 st_h->req_buf + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); 926 header_set_ptr(&headers_arr[3], &hbuf, V(":scheme"), V("https")); 927 header_set_ptr(&headers_arr[4], &hbuf, V("x-some-header"), 928 V("x-some-value")); 929 header_set_ptr(&headers_arr[5], &hbuf, V("x-kenny-status"), 930 V("Oh my God! They killed Kenny!!! You bastards!")); 931 lsquic_http_headers_t headers = { 932 .count = sizeof(headers_arr) / sizeof(headers_arr[0]), 933 .headers = headers_arr, 934 }; 935 936 conn = lsquic_stream_conn(stream); 937 s = lsquic_conn_push_stream(conn, hfm, stream, &headers); 938 if (0 == s) 939 LSQ_NOTICE("pushed stream successfully"); 940 else 941 { 942 destroy_hset_fm(hfm); 943 LSQ_ERROR("could not push stream: %s", strerror(errno)); 944 } 945 946 return 0; 947} 948 949 950static void 951http_server_on_read_pushed (struct lsquic_stream *stream, 952 lsquic_stream_ctx_t *st_h) 953{ 954 struct hset_fm *hfm; 955 956 hfm = lsquic_stream_get_hset(stream); 957 if (!hfm) 958 { 959 LSQ_ERROR("%s: error fetching hset: %s", __func__, strerror(errno)); 960 lsquic_stream_close(stream); 961 return; 962 } 963 964 LSQ_INFO("got push request #%u for %s", hfm->id, hfm->path); 965 st_h->req_path = malloc(strlen(st_h->server_ctx->document_root) + 1 + 966 strlen(hfm->path) + 1); 967 strcpy(st_h->req_path, st_h->server_ctx->document_root); 968 strcat(st_h->req_path, "/"); 969 strcat(st_h->req_path, hfm->path); 970 st_h->req_filename = strdup(st_h->req_path); /* XXX Only used for ends_with: drop it? */ 971 972 process_request(stream, st_h); 973 free(st_h->req_buf); 974 lsquic_stream_shutdown(stream, 0); 975 destroy_hset_fm(hfm); 976} 977 978 979static void 980http_server_on_read_regular (struct lsquic_stream *stream, 981 lsquic_stream_ctx_t *st_h) 982{ 983 unsigned char buf[0x400]; 984 ssize_t nread; 985 int s; 986 987 if (!st_h->req_fh) 988 st_h->req_fh = open_memstream(&st_h->req_buf, &st_h->req_sz); 989 990 nread = lsquic_stream_read(stream, buf, sizeof(buf)); 991 if (nread > 0) 992 fwrite(buf, 1, nread, st_h->req_fh); 993 else if (0 == nread) 994 { 995 fwrite("", 1, 1, st_h->req_fh); /* NUL-terminate so that we can regex the string */ 996 fclose(st_h->req_fh); 997 LSQ_INFO("got request: `%.*s'", (int) st_h->req_sz, st_h->req_buf); 998 parse_request(stream, st_h); 999 if (st_h->server_ctx->push_path && 1000 0 != strcmp(st_h->req_path, st_h->server_ctx->push_path)) 1001 { 1002 s = push_promise(st_h, stream); 1003 if (s != 0) 1004 exit(1); 1005 } 1006 process_request(stream, st_h); 1007 free(st_h->req_buf); 1008 lsquic_stream_shutdown(stream, 0); 1009 } 1010 else 1011 { 1012 LSQ_ERROR("error reading: %s", strerror(errno)); 1013 lsquic_stream_close(stream); 1014 } 1015} 1016#endif 1017 1018 1019static void 1020http_server_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *st_h) 1021{ 1022#if HAVE_OPEN_MEMSTREAM 1023 if (lsquic_stream_is_pushed(stream)) 1024 http_server_on_read_pushed(stream, st_h); 1025 else 1026 http_server_on_read_regular(stream, st_h); 1027#else 1028 LSQ_ERROR("%s: open_memstream not supported\n", __func__); 1029 exit(1); 1030#endif 1031} 1032 1033 1034static void 1035http_server_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1036{ 1037 free(st_h->req_filename); 1038 free(st_h->req_path); 1039 if (st_h->reader.lsqr_ctx) 1040 destroy_lsquic_reader_ctx(st_h->reader.lsqr_ctx); 1041#if HAVE_PREADV 1042 if (s_pwritev) 1043 close(st_h->file_fd); 1044#endif 1045 if (st_h->req) 1046 interop_server_hset_destroy(st_h->req); 1047 free(st_h); 1048 LSQ_INFO("%s called, has unacked data: %d", __func__, 1049 lsquic_stream_has_unacked_data(stream)); 1050} 1051 1052 1053const struct lsquic_stream_if http_server_if = { 1054 .on_new_conn = http_server_on_new_conn, 1055 .on_conn_closed = http_server_on_conn_closed, 1056 .on_new_stream = http_server_on_new_stream, 1057 .on_read = http_server_on_read, 1058 .on_write = http_server_on_write, 1059 .on_close = http_server_on_close, 1060 .on_goaway_received = http_server_on_goaway, 1061}; 1062 1063 1064#if HAVE_REGEX 1065struct req_map 1066{ 1067 enum method method; 1068 const char *path; 1069 enum interop_handler handler; 1070 const char *status; 1071 enum { 1072 RM_WANTBODY = 1 << 0, 1073 RM_REGEX = 1 << 1, 1074 RM_COMPILED = 1 << 2, 1075 } flags; 1076 regex_t re; 1077}; 1078 1079 1080static struct req_map req_maps[] = 1081{ 1082 { .method = GET, .path = "/", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, }, 1083 { .method = GET, .path = "/index.html", .handler = IOH_INDEX_HTML, .status = "200", .flags = 0, }, 1084 { .method = POST, .path = "/cgi-bin/md5sum.cgi", .handler = IOH_MD5SUM, .status = "200", .flags = RM_WANTBODY, }, 1085 { .method = POST, .path = "/cgi-bin/verify-headers.cgi", .handler = IOH_VER_HEAD, .status = "200", .flags = RM_WANTBODY, }, 1086 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1087 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1088 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1089 { .method = GET, .path = "^/([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1090 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1091 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1092 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1093 { .method = GET, .path = "^/file-([0-9][0-9]*)([KMG]?)\\?push=([^&]*)&push=([^&]*)&push=([^&]*)$", .handler = IOH_GEN_FILE, .status = "200", .flags = RM_REGEX, }, 1094}; 1095 1096 1097#define MAX_MATCHES 5 1098 1099 1100static void 1101init_map_regexes (void) 1102{ 1103 struct req_map *map; 1104 1105 for (map = req_maps; map < req_maps + sizeof(req_maps) 1106 / sizeof(req_maps[0]); ++map) 1107 if (map->flags & RM_REGEX) 1108 { 1109#ifndef NDEBUG 1110 int s; 1111 s = 1112#endif 1113 regcomp(&map->re, map->path, REG_EXTENDED|REG_ICASE); 1114 assert(0 == s); 1115 map->flags |= RM_COMPILED; 1116 } 1117} 1118 1119 1120static void 1121free_map_regexes (void) 1122{ 1123 struct req_map *map; 1124 1125 for (map = req_maps; map < req_maps + sizeof(req_maps) 1126 / sizeof(req_maps[0]); ++map) 1127 if (map->flags & RM_COMPILED) 1128 { 1129 regfree(&map->re); 1130 map->flags &= ~RM_COMPILED; 1131 } 1132} 1133 1134 1135static const struct req_map * 1136find_handler (enum method method, const char *path, regmatch_t *matches) 1137{ 1138 const struct req_map *map; 1139 1140 for (map = req_maps; map < req_maps + sizeof(req_maps) 1141 / sizeof(req_maps[0]); ++map) 1142 if (map->flags & RM_COMPILED) 1143 { 1144 if (0 == regexec(&map->re, path, MAX_MATCHES + 1, matches, 0)) 1145 return map; 1146 } 1147 else if (0 == strcasecmp(path, map->path)) 1148 return map; 1149 1150 return NULL; 1151} 1152 1153 1154static const char INDEX_HTML[] = 1155"<html>\n" 1156" <head>\n" 1157" <title>LiteSpeed IETF QUIC Server Index Page</title>\n" 1158" </head>\n" 1159" <body>\n" 1160" <h1>LiteSpeed IETF QUIC Server Index Page</h1>\n" 1161" <p>Hello! Welcome to the interop. Available services:\n" 1162" <ul>\n" 1163" <li><b>POST to /cgi-bin/md5sum.cgi</b>. This will return\n" 1164" MD5 checksum of the request body.\n" 1165" <li><b>GET /123K</b> or <b>GET /file-123K</b>. This will return\n" 1166" requested number of payload in the form of repeating text\n" 1167" by Jerome K. Jerome. The size specification must match\n" 1168" (\\d+)[KMG]? and the total size request must not exceed\n" 1169" 2 gigabytes. Then, you will get back that many bytes\n" 1170" of the <a\n" 1171" href=http://www.gutenberg.org/cache/epub/849/pg849.txt\n" 1172" >beloved classic</a>.\n" 1173" </ul>\n" 1174" </body>\n" 1175"</html>\n" 1176; 1177 1178 1179static size_t 1180read_md5 (void *ctx, const unsigned char *buf, size_t sz, int fin) 1181{ 1182 struct lsquic_stream_ctx *st_h = ctx; 1183 1184 if (sz) 1185 MD5_Update(&st_h->interop_u.md5c.md5ctx, buf, sz); 1186 1187 if (fin) 1188 st_h->interop_u.md5c.done = 1; 1189 1190 return sz; 1191} 1192 1193 1194static void 1195http_server_interop_on_read (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1196{ 1197#define ERROR_RESP(code, ...) do { \ 1198 LSQ_WARN(__VA_ARGS__); \ 1199 st_h->interop_handler = IOH_ERROR; \ 1200 st_h->resp_status = #code; \ 1201 st_h->interop_u.err.resp.sz = snprintf(st_h->interop_u.err.buf, \ 1202 sizeof(st_h->interop_u.err.buf), __VA_ARGS__); \ 1203 if (st_h->interop_u.err.resp.sz >= sizeof(st_h->interop_u.err.buf)) \ 1204 st_h->interop_u.err.resp.sz = sizeof(st_h->interop_u.err.buf) - 1; \ 1205 st_h->interop_u.err.resp.buf = st_h->interop_u.err.buf; \ 1206 st_h->interop_u.err.resp.off = 0; \ 1207 goto err; \ 1208} while (0) 1209 1210 const struct req_map *map; 1211 ssize_t nw; 1212 size_t need; 1213 unsigned len, i; 1214 struct interop_push_path *push_path; 1215 regmatch_t matches[MAX_MATCHES + 1]; 1216 unsigned char md5sum[MD5_DIGEST_LENGTH]; 1217 char md5str[ sizeof(md5sum) * 2 + 1 ]; 1218 char byte[1]; 1219 1220 if (!(st_h->flags & SH_HEADERS_READ)) 1221 { 1222 st_h->flags |= SH_HEADERS_READ; 1223 st_h->req = lsquic_stream_get_hset(stream); 1224 if (!st_h->req) 1225 ERROR_RESP(500, "Internal error: cannot fetch header set from stream"); 1226 else if (st_h->req->method == UNSET) 1227 ERROR_RESP(400, "Method is not specified"); 1228 else if (!st_h->req->path) 1229 ERROR_RESP(400, "Path is not specified"); 1230 else if (st_h->req->method == UNSUPPORTED) 1231 ERROR_RESP(501, "Method %s is not supported", st_h->req->method_str); 1232 else if (!(map = find_handler(st_h->req->method, st_h->req->path, matches))) 1233 ERROR_RESP(404, "No handler found for method: %s; path: %s", 1234 st_h->req->method_str, st_h->req->path); 1235 else 1236 { 1237 LSQ_INFO("found handler for %s %s", st_h->req->method_str, st_h->req->path); 1238 st_h->resp_status = map->status; 1239 st_h->interop_handler = map->handler; 1240 switch (map->handler) 1241 { 1242 case IOH_INDEX_HTML: 1243 st_h->interop_u.ihc.resp = (struct resp) { INDEX_HTML, sizeof(INDEX_HTML) - 1, 0, }; 1244 break; 1245 case IOH_VER_HEAD: 1246 st_h->interop_u.vhc.resp = (struct resp) { 1247 st_h->req->qif_str, st_h->req->qif_sz, 0, }; 1248 break; 1249 case IOH_MD5SUM: 1250 MD5_Init(&st_h->interop_u.md5c.md5ctx); 1251 st_h->interop_u.md5c.done = 0; 1252 break; 1253 case IOH_GEN_FILE: 1254 STAILQ_INIT(&st_h->interop_u.gfc.push_paths); 1255 st_h->interop_u.gfc.remain = strtol(st_h->req->path + matches[1].rm_so, NULL, 10); 1256 if (matches[2].rm_so >= 0 1257 && matches[2].rm_so < matches[2].rm_eo) 1258 { 1259 switch (st_h->req->path[ matches[2].rm_so ]) 1260 { 1261 case 'G': 1262 case 'g': 1263 st_h->interop_u.gfc.remain <<= 30; 1264 break; 1265 case 'M': 1266 case 'm': 1267 st_h->interop_u.gfc.remain <<= 20; 1268 break; 1269 case 'K': 1270 case 'k': 1271 st_h->interop_u.gfc.remain <<= 10; 1272 break; 1273 } 1274 } 1275 if (st_h->interop_u.gfc.remain > 2 * (1u << 30)) 1276 ERROR_RESP(406, "Response of %zd bytes is too long to generate", 1277 st_h->interop_u.gfc.remain); 1278 st_h->interop_u.gfc.idle_off = 0; 1279 for (i = 3; i <= MAX_MATCHES; ++i) 1280 if (matches[i].rm_so >= 0) 1281 { 1282 len = matches[i].rm_eo - matches[i].rm_so; 1283 push_path = malloc(sizeof(*push_path) + len + 1); 1284 memcpy(push_path->path, st_h->req->path 1285 + matches[i].rm_so, len); 1286 push_path->path[len] ='\0'; 1287 STAILQ_INSERT_TAIL(&st_h->interop_u.gfc.push_paths, 1288 push_path, next); 1289 } 1290 else 1291 break; 1292 break; 1293 default: 1294 /* TODO: implement this */ 1295 assert(0); 1296 break; 1297 } 1298 } 1299 1300 if (!(map->flags & RM_WANTBODY)) 1301 { 1302 err: 1303 lsquic_stream_shutdown(stream, 0); 1304 lsquic_stream_wantwrite(stream, 1); 1305 } 1306 } 1307 else 1308 { 1309 switch (st_h->interop_handler) 1310 { 1311 case IOH_MD5SUM: 1312 assert(!st_h->interop_u.md5c.done); 1313 nw = lsquic_stream_readf(stream, read_md5, st_h); 1314 if (nw < 0) 1315 { 1316 LSQ_ERROR("could not read from stream for MD5: %s", strerror(errno)); 1317 exit(1); 1318 } 1319 if (nw == 0) 1320 st_h->interop_u.md5c.done = 1; 1321 if (st_h->interop_u.md5c.done) 1322 { 1323 MD5_Final(md5sum, &st_h->interop_u.md5c.md5ctx); 1324 lsquic_hexstr(md5sum, sizeof(md5sum), md5str, sizeof(md5str)); 1325 snprintf(st_h->interop_u.md5c.resp_buf, sizeof(st_h->interop_u.md5c.resp_buf), 1326 "<html><head><title>MD5 Checksum Result</title></head>\n" 1327 "<body><h1>MD5 Checksum Result</h1>\n<p>" 1328 "MD5 Checksum: <tt>%s</tt>\n</body></html>\n", 1329 md5str); 1330 st_h->interop_u.md5c.resp.buf = st_h->interop_u.md5c.resp_buf; 1331 st_h->interop_u.md5c.resp.sz = strlen(st_h->interop_u.md5c.resp_buf); 1332 st_h->interop_u.md5c.resp.off = 0; 1333 lsquic_stream_shutdown(stream, 0); 1334 lsquic_stream_wantwrite(stream, 1); 1335 } 1336 break; 1337 case IOH_VER_HEAD: 1338 if (!st_h->interop_u.vhc.req_body) 1339 { 1340 st_h->interop_u.vhc.req_body = malloc(st_h->req->qif_sz); 1341 if (!st_h->interop_u.vhc.req_body) 1342 { 1343 perror("malloc"); 1344 exit(1); 1345 } 1346 } 1347 need = st_h->req->qif_sz - st_h->interop_u.vhc.req_sz; 1348 if (need > 0) 1349 { 1350 nw = lsquic_stream_read(stream, 1351 st_h->interop_u.vhc.req_body 1352 + st_h->interop_u.vhc.req_sz, need); 1353 if (nw > 0) 1354 st_h->interop_u.vhc.req_sz += need; 1355 else if (nw == 0) 1356 { 1357 LSQ_WARN("request body too short (does not match headers)"); 1358 lsquic_stream_shutdown(stream, 0); 1359 lsquic_stream_wantwrite(stream, 1); 1360 } 1361 else 1362 { 1363 LSQ_ERROR("error reading from stream"); 1364 exit(1); 1365 } 1366 } 1367 else 1368 { 1369 nw = lsquic_stream_read(stream, byte, sizeof(byte)); 1370 if (nw == 0) 1371 { 1372 if (0 == memcmp(st_h->req->qif_str, 1373 st_h->interop_u.vhc.req_body, st_h->req->qif_sz)) 1374 LSQ_INFO("request headers and payload check out"); 1375 else 1376 LSQ_WARN("request headers and payload are different"); 1377 } 1378 else 1379 LSQ_WARN("request body too long (does not match headers)"); 1380 lsquic_stream_shutdown(stream, 0); 1381 lsquic_stream_wantwrite(stream, 1); 1382 } 1383 break; 1384 default: 1385 assert(0); 1386 } 1387 } 1388} 1389 1390 1391static int 1392send_headers2 (struct lsquic_stream *stream, struct lsquic_stream_ctx *st_h, 1393 size_t content_len) 1394{ 1395 char clbuf[0x20]; 1396 struct header_buf hbuf; 1397 1398 snprintf(clbuf, sizeof(clbuf), "%zd", content_len); 1399 1400 hbuf.off = 0; 1401 struct lsxpack_header headers_arr[4]; 1402 header_set_ptr(&headers_arr[0], &hbuf, V(":status"), V(st_h->resp_status)); 1403 header_set_ptr(&headers_arr[1], &hbuf, V("server"), V(LITESPEED_ID)); 1404 header_set_ptr(&headers_arr[2], &hbuf, V("content-type"), V("text/html")); 1405 header_set_ptr(&headers_arr[3], &hbuf, V("content-length"), V(clbuf)); 1406 lsquic_http_headers_t headers = { 1407 .count = sizeof(headers_arr) / sizeof(headers_arr[0]), 1408 .headers = headers_arr, 1409 }; 1410 1411 return lsquic_stream_send_headers(st_h->stream, &headers, 0); 1412} 1413 1414#define MIN(a, b) ((a) < (b) ? (a) : (b)) 1415 1416static size_t 1417idle_read (void *lsqr_ctx, void *buf, size_t count) 1418{ 1419 struct gen_file_ctx *const gfc = lsqr_ctx; 1420 unsigned char *p = buf; 1421 unsigned char *const end = p + count; 1422 size_t towrite; 1423 1424 while (p < end && gfc->remain > 0) 1425 { 1426 towrite = MIN((unsigned) (end - p), IDLE_SIZE - gfc->idle_off); 1427 if (towrite > gfc->remain) 1428 towrite = gfc->remain; 1429 memcpy(p, on_being_idle + gfc->idle_off, towrite); 1430 gfc->idle_off += towrite; 1431 if (gfc->idle_off == IDLE_SIZE) 1432 gfc->idle_off = 0; 1433 p += towrite; 1434 gfc->remain -= towrite; 1435 } 1436 1437 return p - (unsigned char *) buf; 1438} 1439 1440 1441static size_t 1442idle_size (void *lsqr_ctx) 1443{ 1444 struct gen_file_ctx *const gfc = lsqr_ctx; 1445 1446 return gfc->remain; 1447} 1448 1449 1450static struct req * 1451new_req (enum method method, const char *path, const char *authority) 1452{ 1453 struct req *req; 1454 1455 req = malloc(sizeof(*req)); 1456 if (!req) 1457 return NULL; 1458 1459 memset(req, 0, offsetof(struct req, decode_buf)); 1460 req->method = method; 1461 req->path = strdup(path); 1462 req->authority_str = strdup(authority); 1463 if (!(req->path && req->authority_str)) 1464 { 1465 free(req->path); 1466 free(req->authority_str); 1467 free(req); 1468 return NULL; 1469 } 1470 1471 return req; 1472} 1473 1474 1475static ssize_t 1476my_interop_preadv (void *user_data, const struct iovec *iov, int iovcnt) 1477{ 1478 struct gen_file_ctx *const gfc = user_data; 1479 size_t nread, nr; 1480 int i; 1481 1482 nread = 0; 1483 for (i = 0; i < iovcnt; ++i) 1484 { 1485 nr = idle_read(gfc, iov[i].iov_base, iov[i].iov_len); 1486 nread += nr; 1487 } 1488 1489 return (ssize_t) nread; 1490} 1491 1492 1493static void 1494idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1495{ 1496 struct gen_file_ctx *const gfc = &st_h->interop_u.gfc; 1497 struct interop_push_path *push_path; 1498 struct lsxpack_header header_arr[4]; 1499 struct lsquic_http_headers headers; 1500 struct req *req; 1501 ssize_t nw; 1502 struct header_buf hbuf; 1503 struct lsquic_reader reader; 1504 1505 if (st_h->flags & SH_HEADERS_SENT) 1506 { 1507 if (s_pwritev) 1508 { 1509 nw = lsquic_stream_pwritev(stream, my_interop_preadv, gfc, 1510 gfc->remain); 1511 if (nw == 0) 1512 goto with_reader; 1513 } 1514 else 1515 { 1516 with_reader: 1517 reader.lsqr_read = idle_read, 1518 reader.lsqr_size = idle_size, 1519 reader.lsqr_ctx = gfc, 1520 nw = lsquic_stream_writef(stream, &reader); 1521 } 1522 if (nw < 0) 1523 { 1524 LSQ_ERROR("error writing idle thoughts: %s", strerror(errno)); 1525 exit(1); 1526 } 1527 if (gfc->remain == 0) 1528 lsquic_stream_shutdown(stream, 1); 1529 } 1530 else 1531 { 1532 if (st_h->req->authority_str) 1533 while ((push_path = STAILQ_FIRST(&gfc->push_paths))) 1534 { 1535 STAILQ_REMOVE_HEAD(&gfc->push_paths, next); 1536 LSQ_DEBUG("pushing promise for %s", push_path->path); 1537 hbuf.off = 0; 1538 header_set_ptr(&header_arr[0], &hbuf, V(":method"), V("GET")); 1539 header_set_ptr(&header_arr[1], &hbuf, V(":path"), V(push_path->path)); 1540 header_set_ptr(&header_arr[2], &hbuf, V(":authority"), V(st_h->req->authority_str)); 1541 header_set_ptr(&header_arr[3], &hbuf, V(":scheme"), V("https")); 1542 headers.headers = header_arr; 1543 headers.count = sizeof(header_arr) / sizeof(header_arr[0]); 1544 req = new_req(GET, push_path->path, st_h->req->authority_str); 1545 if (req) 1546 { 1547 if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream), 1548 req, stream, &headers)) 1549 { 1550 LSQ_WARN("stream push failed"); 1551 interop_server_hset_destroy(req); 1552 } 1553 } 1554 else 1555 LSQ_WARN("cannot allocate req for push"); 1556 free(push_path); 1557 } 1558 if (0 == send_headers2(stream, st_h, gfc->remain)) 1559 st_h->flags |= SH_HEADERS_SENT; 1560 else 1561 { 1562 LSQ_ERROR("cannot send headers: %s", strerror(errno)); 1563 lsquic_stream_close(stream); 1564 } 1565 } 1566} 1567 1568 1569static void 1570http_server_interop_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 1571{ 1572 struct resp *resp; 1573 ssize_t nw; 1574 1575 switch (st_h->interop_handler) 1576 { 1577 case IOH_ERROR: 1578 resp = &st_h->interop_u.err.resp; 1579 goto reply; 1580 case IOH_INDEX_HTML: 1581 resp = &st_h->interop_u.ihc.resp; 1582 goto reply; 1583 case IOH_VER_HEAD: 1584 resp = &st_h->interop_u.vhc.resp; 1585 goto reply; 1586 case IOH_MD5SUM: 1587 resp = &st_h->interop_u.md5c.resp; 1588 goto reply; 1589 case IOH_GEN_FILE: 1590 idle_on_write(stream, st_h); 1591 return; 1592 default: 1593 assert(0); 1594 return; 1595 } 1596 1597 reply: 1598 assert(resp->sz); /* We always need to send body */ 1599 if (!(st_h->flags & SH_HEADERS_SENT)) 1600 { 1601 send_headers2(stream, st_h, resp->sz); 1602 st_h->flags |= SH_HEADERS_SENT; 1603 return; 1604 } 1605 1606 nw = lsquic_stream_write(stream, resp->buf + resp->off, resp->sz - resp->off); 1607 if (nw < 0) 1608 { 1609 LSQ_ERROR("error writing to stream: %s", strerror(errno)); 1610 lsquic_conn_abort(lsquic_stream_conn(stream)); 1611 return; 1612 } 1613 1614 resp->off += nw; 1615 lsquic_stream_flush(stream); 1616 if (resp->off == resp->sz) 1617 lsquic_stream_shutdown(stream, 1); 1618} 1619 1620 1621const struct lsquic_stream_if interop_http_server_if = { 1622 .on_new_conn = http_server_on_new_conn, 1623 .on_conn_closed = http_server_on_conn_closed, 1624 .on_new_stream = http_server_on_new_stream, 1625 .on_read = http_server_interop_on_read, 1626 .on_write = http_server_interop_on_write, 1627 .on_close = http_server_on_close, 1628}; 1629#endif /* HAVE_REGEX */ 1630 1631 1632static void 1633usage (const char *prog) 1634{ 1635 const char *const slash = strrchr(prog, '/'); 1636 if (slash) 1637 prog = slash + 1; 1638 printf( 1639"Usage: %s [opts]\n" 1640"\n" 1641"Options:\n" 1642" -r ROOT Document root\n" 1643" -p FILE Push request with this path\n" 1644" -w SIZE Write immediately (LSWS mode). Argument specifies maximum\n" 1645" size of the immediate write.\n" 1646#if HAVE_PREADV 1647" -P SIZE Use preadv(2) to read from disk and lsquic_stream_pwritev() to\n" 1648" write to stream. Positive SIZE indicate maximum value per\n" 1649" write; negative means always use remaining file size.\n" 1650" Incompatible with -w.\n" 1651#endif 1652" -y DELAY Delay response for this many seconds -- use for debugging\n" 1653 , prog); 1654} 1655 1656 1657static void * 1658interop_server_hset_create (void *hsi_ctx, lsquic_stream_t *stream, 1659 int is_push_promise) 1660{ 1661 struct req *req; 1662 1663 req = malloc(sizeof(struct req)); 1664 memset(req, 0, offsetof(struct req, decode_buf)); 1665 1666 return req; 1667} 1668 1669 1670static struct lsxpack_header * 1671interop_server_hset_prepare_decode (void *hset_p, struct lsxpack_header *xhdr, 1672 size_t req_space) 1673{ 1674 struct req *req = hset_p; 1675 1676 if (xhdr) 1677 { 1678 LSQ_WARN("we don't reallocate headers: can't give more"); 1679 return NULL; 1680 } 1681 1682 if (req->flags & HAVE_XHDR) 1683 { 1684 if (req->decode_off + lsxpack_header_get_dec_size(&req->xhdr) 1685 >= sizeof(req->decode_buf)) 1686 { 1687 LSQ_WARN("Not enough room in header"); 1688 return NULL; 1689 } 1690 req->decode_off += lsxpack_header_get_dec_size(&req->xhdr); 1691 } 1692 else 1693 req->flags |= HAVE_XHDR; 1694 1695 lsxpack_header_prepare_decode(&req->xhdr, req->decode_buf, 1696 req->decode_off, sizeof(req->decode_buf) - req->decode_off); 1697 return &req->xhdr; 1698} 1699 1700 1701static int 1702interop_server_hset_add_header (void *hset_p, struct lsxpack_header *xhdr) 1703{ 1704 struct req *req = hset_p; 1705 const char *name, *value; 1706 unsigned name_len, value_len; 1707 1708 if (!xhdr) 1709 return 0; 1710 1711 name = lsxpack_header_get_name(xhdr); 1712 value = lsxpack_header_get_value(xhdr); 1713 name_len = xhdr->name_len; 1714 value_len = xhdr->val_len; 1715 1716 req->qif_str = realloc(req->qif_str, 1717 req->qif_sz + name_len + value_len + 2); 1718 if (!req->qif_str) 1719 { 1720 LSQ_ERROR("malloc failed"); 1721 return -1; 1722 } 1723 memcpy(req->qif_str + req->qif_sz, name, name_len); 1724 req->qif_str[req->qif_sz + name_len] = '\t'; 1725 memcpy(req->qif_str + req->qif_sz + name_len + 1, value, value_len); 1726 req->qif_str[req->qif_sz + name_len + 1 + value_len] = '\n'; 1727 req->qif_sz += name_len + value_len + 2; 1728 1729 if (5 == name_len && 0 == strncmp(name, ":path", 5)) 1730 { 1731 if (req->path) 1732 return 1; 1733 req->path = strndup(value, value_len); 1734 if (!req->path) 1735 return -1; 1736 return 0; 1737 } 1738 1739 if (7 == name_len && 0 == strncmp(name, ":method", 7)) 1740 { 1741 if (req->method != UNSET) 1742 return 1; 1743 req->method_str = strndup(value, value_len); 1744 if (!req->method_str) 1745 return -1; 1746 if (0 == strcmp(req->method_str, "GET")) 1747 req->method = GET; 1748 else if (0 == strcmp(req->method_str, "POST")) 1749 req->method = POST; 1750 else 1751 req->method = UNSUPPORTED; 1752 return 0; 1753 } 1754 1755 if (10 == name_len && 0 == strncmp(name, ":authority", 10)) 1756 { 1757 req->authority_str = strndup(value, value_len); 1758 if (!req->authority_str) 1759 return -1; 1760 return 0; 1761 } 1762 1763 return 0; 1764} 1765 1766 1767static void 1768interop_server_hset_destroy (void *hset_p) 1769{ 1770 struct req *req = hset_p; 1771 free(req->qif_str); 1772 free(req->path); 1773 free(req->method_str); 1774 free(req->authority_str); 1775 free(req); 1776} 1777 1778 1779static const struct lsquic_hset_if header_bypass_api = 1780{ 1781 .hsi_create_header_set = interop_server_hset_create, 1782 .hsi_prepare_decode = interop_server_hset_prepare_decode, 1783 .hsi_process_header = interop_server_hset_add_header, 1784 .hsi_discard_header_set = interop_server_hset_destroy, 1785}; 1786 1787 1788int 1789main (int argc, char **argv) 1790{ 1791 int opt, s; 1792 struct stat st; 1793 struct server_ctx server_ctx; 1794 struct prog prog; 1795 const char *const *alpn; 1796 1797#if !(HAVE_OPEN_MEMSTREAM || HAVE_REGEX) 1798 fprintf(stderr, "cannot run server without regex or open_memstream\n"); 1799 return 1; 1800#endif 1801 1802 memset(&server_ctx, 0, sizeof(server_ctx)); 1803 TAILQ_INIT(&server_ctx.sports); 1804 server_ctx.prog = &prog; 1805 1806 prog_init(&prog, LSENG_SERVER|LSENG_HTTP, &server_ctx.sports, 1807 &http_server_if, &server_ctx); 1808 1809 while (-1 != (opt = getopt(argc, argv, PROG_OPTS "y:Y:n:p:r:w:P:h"))) 1810 { 1811 switch (opt) { 1812 case 'n': 1813 server_ctx.max_conn = atoi(optarg); 1814 break; 1815 case 'p': 1816 server_ctx.push_path = optarg; 1817 break; 1818 case 'r': 1819 if (-1 == stat(optarg, &st)) 1820 { 1821 perror("stat"); 1822 exit(2); 1823 } 1824#ifndef WIN32 1825 if (!S_ISDIR(st.st_mode)) 1826 { 1827 fprintf(stderr, "`%s' is not a directory\n", optarg); 1828 exit(2); 1829 } 1830#endif 1831 server_ctx.document_root = optarg; 1832 break; 1833 case 'w': 1834 s_immediate_write = atoi(optarg); 1835 break; 1836 case 'P': 1837#if HAVE_PREADV 1838 s_pwritev = strtoull(optarg, NULL, 10); 1839 break; 1840#else 1841 fprintf(stderr, "preadv is not supported on this platform, " 1842 "cannot use -P\n"); 1843 exit(EXIT_FAILURE); 1844#endif 1845 case 'y': 1846 server_ctx.delay_resp_sec = atoi(optarg); 1847 break; 1848 case 'h': 1849 usage(argv[0]); 1850 prog_print_common_options(&prog, stdout); 1851 exit(0); 1852 default: 1853 if (0 != prog_set_opt(&prog, opt, optarg)) 1854 exit(1); 1855 } 1856 } 1857 1858 if (!server_ctx.document_root) 1859 { 1860#if HAVE_REGEX 1861 LSQ_NOTICE("Document root is not set: start in Interop Mode"); 1862 init_map_regexes(); 1863 prog.prog_api.ea_stream_if = &interop_http_server_if; 1864 prog.prog_api.ea_hsi_if = &header_bypass_api; 1865 prog.prog_api.ea_hsi_ctx = NULL; 1866#else 1867 LSQ_ERROR("Document root is not set: use -r option"); 1868 exit(EXIT_FAILURE); 1869#endif 1870 } 1871 1872 if (s_immediate_write && s_pwritev) 1873 { 1874 LSQ_ERROR("-w and -P are incompatible options"); 1875 exit(EXIT_FAILURE); 1876 } 1877 1878 alpn = lsquic_get_h3_alpns(prog.prog_settings.es_versions); 1879 while (*alpn) 1880 { 1881 if (0 == add_alpn(*alpn)) 1882 ++alpn; 1883 else 1884 { 1885 LSQ_ERROR("cannot add ALPN %s", *alpn); 1886 exit(EXIT_FAILURE); 1887 } 1888 } 1889 1890 if (0 != prog_prep(&prog)) 1891 { 1892 LSQ_ERROR("could not prep"); 1893 exit(EXIT_FAILURE); 1894 } 1895 1896 LSQ_DEBUG("entering event loop"); 1897 1898 s = prog_run(&prog); 1899 prog_cleanup(&prog); 1900 1901#if HAVE_REGEX 1902 if (!server_ctx.document_root) 1903 free_map_regexes(); 1904#endif 1905 1906 exit(0 == s ? EXIT_SUCCESS : EXIT_FAILURE); 1907} 1908