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