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