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