##// END OF EJS Templates
httppeer: detect redirect to URL without query string (issue5860)...
Gregory Szorc -
r37851:6169d95d @24 stable
parent child Browse files
Show More
@@ -328,13 +328,24 b' def sendrequest(ui, opener, req):'
328 328
329 329 return res
330 330
331 class RedirectedRepoError(error.RepoError):
332 def __init__(self, msg, respurl):
333 super(RedirectedRepoError, self).__init__(msg)
334 self.respurl = respurl
335
331 336 def parsev1commandresponse(ui, baseurl, requrl, qs, resp, compressible,
332 337 allowcbor=False):
333 338 # record the url we got redirected to
339 redirected = False
334 340 respurl = pycompat.bytesurl(resp.geturl())
335 341 if respurl.endswith(qs):
336 342 respurl = respurl[:-len(qs)]
343 qsdropped = False
344 else:
345 qsdropped = True
346
337 347 if baseurl.rstrip('/') != respurl.rstrip('/'):
348 redirected = True
338 349 if not ui.quiet:
339 350 ui.warn(_('real URL is %s\n') % respurl)
340 351
@@ -351,10 +362,16 b' def parsev1commandresponse(ui, baseurl, '
351 362 # application/hg-changegroup. We don't support such old servers.
352 363 if not proto.startswith('application/mercurial-'):
353 364 ui.debug("requested URL: '%s'\n" % util.hidepassword(requrl))
354 raise error.RepoError(
355 _("'%s' does not appear to be an hg repository:\n"
356 "---%%<--- (%s)\n%s\n---%%<---\n")
357 % (safeurl, proto or 'no content-type', resp.read(1024)))
365 msg = _("'%s' does not appear to be an hg repository:\n"
366 "---%%<--- (%s)\n%s\n---%%<---\n") % (
367 safeurl, proto or 'no content-type', resp.read(1024))
368
369 # Some servers may strip the query string from the redirect. We
370 # raise a special error type so callers can react to this specially.
371 if redirected and qsdropped:
372 raise RedirectedRepoError(msg, respurl)
373 else:
374 raise error.RepoError(msg)
358 375
359 376 try:
360 377 subtype = proto.split('-', 1)[1]
@@ -434,8 +451,6 b' class httppeer(wireprotov1peer.wirepeer)'
434 451
435 452 # End of ipeercommands interface.
436 453
437 # look up capabilities only when needed
438
439 454 def _callstream(self, cmd, _compressible=False, **args):
440 455 args = pycompat.byteskwargs(args)
441 456
@@ -853,9 +868,29 b' def performhandshake(ui, url, opener, re'
853 868 req, requrl, qs = makev1commandrequest(ui, requestbuilder, caps,
854 869 capable, url, 'capabilities',
855 870 args)
856
857 871 resp = sendrequest(ui, opener, req)
858 872
873 # The server may redirect us to the repo root, stripping the
874 # ?cmd=capabilities query string from the URL. The server would likely
875 # return HTML in this case and ``parsev1commandresponse()`` would raise.
876 # We catch this special case and re-issue the capabilities request against
877 # the new URL.
878 #
879 # We should ideally not do this, as a redirect that drops the query
880 # string from the URL is arguably a server bug. (Garbage in, garbage out).
881 # However, Mercurial clients for several years appeared to handle this
882 # issue without behavior degradation. And according to issue 5860, it may
883 # be a longstanding bug in some server implementations. So we allow a
884 # redirect that drops the query string to "just work."
885 try:
886 respurl, ct, resp = parsev1commandresponse(ui, url, requrl, qs, resp,
887 compressible=False,
888 allowcbor=advertisev2)
889 except RedirectedRepoError as e:
890 req, requrl, qs = makev1commandrequest(ui, requestbuilder, caps,
891 capable, e.respurl,
892 'capabilities', args)
893 resp = sendrequest(ui, opener, req)
859 894 respurl, ct, resp = parsev1commandresponse(ui, url, requrl, qs, resp,
860 895 compressible=False,
861 896 allowcbor=advertisev2)
@@ -333,3 +333,394 b' Client with HTTPv2 enabled automatically'
333 333 response: [b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00']
334 334
335 335 $ killdaemons.py
336
337 HTTP client follows HTTP redirect on handshake to new repo
338
339 $ cd $TESTTMP
340
341 $ hg init redirector
342 $ hg init redirected
343 $ cd redirected
344 $ touch foo
345 $ hg -q commit -A -m initial
346 $ cd ..
347
348 $ cat > paths.conf << EOF
349 > [paths]
350 > / = $TESTTMP/*
351 > EOF
352
353 $ cat > redirectext.py << EOF
354 > from mercurial import extensions, wireprotoserver
355 > def wrappedcallhttp(orig, repo, req, res, proto, cmd):
356 > path = req.advertisedurl[len(req.advertisedbaseurl):]
357 > if not path.startswith(b'/redirector'):
358 > return orig(repo, req, res, proto, cmd)
359 > relpath = path[len(b'/redirector'):]
360 > res.status = b'301 Redirect'
361 > newurl = b'%s/redirected%s' % (req.baseurl, relpath)
362 > if not repo.ui.configbool('testing', 'redirectqs', True) and b'?' in newurl:
363 > newurl = newurl[0:newurl.index(b'?')]
364 > res.headers[b'Location'] = newurl
365 > res.headers[b'Content-Type'] = b'text/plain'
366 > res.setbodybytes(b'redirected')
367 > return True
368 >
369 > extensions.wrapfunction(wireprotoserver, '_callhttp', wrappedcallhttp)
370 > EOF
371
372 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
373 > --config server.compressionengines=zlib \
374 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
375 $ cat hg.pid > $DAEMON_PIDS
376
377 Verify our HTTP 301 is served properly
378
379 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
380 > httprequest GET /redirector?cmd=capabilities
381 > user-agent: test
382 > EOF
383 using raw connection to peer
384 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
385 s> Accept-Encoding: identity\r\n
386 s> user-agent: test\r\n
387 s> host: $LOCALIP:$HGPORT\r\n (glob)
388 s> \r\n
389 s> makefile('rb', None)
390 s> HTTP/1.1 301 Redirect\r\n
391 s> Server: testing stub value\r\n
392 s> Date: $HTTP_DATE$\r\n
393 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
394 s> Content-Type: text/plain\r\n
395 s> Content-Length: 10\r\n
396 s> \r\n
397 s> redirected
398 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
399 s> Accept-Encoding: identity\r\n
400 s> user-agent: test\r\n
401 s> host: $LOCALIP:$HGPORT\r\n (glob)
402 s> \r\n
403 s> makefile('rb', None)
404 s> HTTP/1.1 200 Script output follows\r\n
405 s> Server: testing stub value\r\n
406 s> Date: $HTTP_DATE$\r\n
407 s> Content-Type: application/mercurial-0.1\r\n
408 s> Content-Length: 453\r\n
409 s> \r\n
410 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
411
412 Test with the HTTP peer
413
414 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
415 > command heads
416 > EOF
417 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
418 s> Accept-Encoding: identity\r\n
419 s> accept: application/mercurial-0.1\r\n
420 s> host: $LOCALIP:$HGPORT\r\n (glob)
421 s> user-agent: Mercurial debugwireproto\r\n
422 s> \r\n
423 s> makefile('rb', None)
424 s> HTTP/1.1 301 Redirect\r\n
425 s> Server: testing stub value\r\n
426 s> Date: $HTTP_DATE$\r\n
427 s> Location: http://$LOCALIP:$HGPORT/redirected?cmd=capabilities\r\n (glob)
428 s> Content-Type: text/plain\r\n
429 s> Content-Length: 10\r\n
430 s> \r\n
431 s> redirected
432 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
433 s> Accept-Encoding: identity\r\n
434 s> accept: application/mercurial-0.1\r\n
435 s> host: $LOCALIP:$HGPORT\r\n (glob)
436 s> user-agent: Mercurial debugwireproto\r\n
437 s> \r\n
438 s> makefile('rb', None)
439 s> HTTP/1.1 200 Script output follows\r\n
440 s> Server: testing stub value\r\n
441 s> Date: $HTTP_DATE$\r\n
442 s> Content-Type: application/mercurial-0.1\r\n
443 s> Content-Length: 453\r\n
444 s> \r\n
445 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
446 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
447 sending heads command
448 s> GET /redirected?cmd=heads HTTP/1.1\r\n
449 s> Accept-Encoding: identity\r\n
450 s> vary: X-HgProto-1\r\n
451 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
452 s> accept: application/mercurial-0.1\r\n
453 s> host: $LOCALIP:$HGPORT\r\n (glob)
454 s> user-agent: Mercurial debugwireproto\r\n
455 s> \r\n
456 s> makefile('rb', None)
457 s> HTTP/1.1 200 Script output follows\r\n
458 s> Server: testing stub value\r\n
459 s> Date: $HTTP_DATE$\r\n
460 s> Content-Type: application/mercurial-0.1\r\n
461 s> Content-Length: 41\r\n
462 s> \r\n
463 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
464 response: [b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL']
465
466 $ killdaemons.py
467
468 Now test a variation where we strip the query string from the redirect URL.
469 (SCM Manager apparently did this and clients would recover from it)
470
471 $ hg --config extensions.redirect=$TESTTMP/redirectext.py \
472 > --config server.compressionengines=zlib \
473 > --config testing.redirectqs=false \
474 > serve --web-conf paths.conf --pid-file hg.pid -p $HGPORT -d
475 $ cat hg.pid > $DAEMON_PIDS
476
477 $ hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT << EOF
478 > httprequest GET /redirector?cmd=capabilities
479 > user-agent: test
480 > EOF
481 using raw connection to peer
482 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
483 s> Accept-Encoding: identity\r\n
484 s> user-agent: test\r\n
485 s> host: $LOCALIP:$HGPORT\r\n (glob)
486 s> \r\n
487 s> makefile('rb', None)
488 s> HTTP/1.1 301 Redirect\r\n
489 s> Server: testing stub value\r\n
490 s> Date: $HTTP_DATE$\r\n
491 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
492 s> Content-Type: text/plain\r\n
493 s> Content-Length: 10\r\n
494 s> \r\n
495 s> redirected
496 s> GET /redirected HTTP/1.1\r\n
497 s> Accept-Encoding: identity\r\n
498 s> user-agent: test\r\n
499 s> host: $LOCALIP:$HGPORT\r\n (glob)
500 s> \r\n
501 s> makefile('rb', None)
502 s> HTTP/1.1 200 Script output follows\r\n
503 s> Server: testing stub value\r\n
504 s> Date: $HTTP_DATE$\r\n
505 s> ETag: W/"*"\r\n (glob)
506 s> Content-Type: text/html; charset=ascii\r\n
507 s> Transfer-Encoding: chunked\r\n
508 s> \r\n
509 s> 414\r\n
510 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
511 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
512 s> <head>\n
513 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
514 s> <meta name="robots" content="index, nofollow" />\n
515 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
516 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
517 s> \n
518 s> <title>redirected: log</title>\n
519 s> <link rel="alternate" type="application/atom+xml"\n
520 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
521 s> <link rel="alternate" type="application/rss+xml"\n
522 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
523 s> </head>\n
524 s> <body>\n
525 s> \n
526 s> <div class="container">\n
527 s> <div class="menu">\n
528 s> <div class="logo">\n
529 s> <a href="https://mercurial-scm.org/">\n
530 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
531 s> </div>\n
532 s> <ul>\n
533 s> <li class="active">log</li>\n
534 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
535 s> <li><a href="/redirected/tags">tags</a></li>\n
536 s> <li><a href="
537 s> \r\n
538 s> 810\r\n
539 s> /redirected/bookmarks">bookmarks</a></li>\n
540 s> <li><a href="/redirected/branches">branches</a></li>\n
541 s> </ul>\n
542 s> <ul>\n
543 s> <li><a href="/redirected/rev/tip">changeset</a></li>\n
544 s> <li><a href="/redirected/file/tip">browse</a></li>\n
545 s> </ul>\n
546 s> <ul>\n
547 s> \n
548 s> </ul>\n
549 s> <ul>\n
550 s> <li><a href="/redirected/help">help</a></li>\n
551 s> </ul>\n
552 s> <div class="atom-logo">\n
553 s> <a href="/redirected/atom-log" title="subscribe to atom feed">\n
554 s> <img class="atom-logo" src="/redirected/static/feed-icon-14x14.png" alt="atom feed" />\n
555 s> </a>\n
556 s> </div>\n
557 s> </div>\n
558 s> \n
559 s> <div class="main">\n
560 s> <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/redirected">redirected</a> </h2>\n
561 s> <h3>log</h3>\n
562 s> \n
563 s> \n
564 s> <form class="search" action="/redirected/log">\n
565 s> \n
566 s> <p><input name="rev" id="search1" type="text" size="30" value="" /></p>\n
567 s> <div id="hint">Find changesets by keywords (author, files, the commit message), revision\n
568 s> number or hash, or <a href="/redirected/help/revsets">revset expression</a>.</div>\n
569 s> </form>\n
570 s> \n
571 s> <div class="navigate">\n
572 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
573 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
574 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
575 s> </div>\n
576 s> \n
577 s> <table class="bigtable">\n
578 s> <thead>\n
579 s> <tr>\n
580 s> <th class="age">age</th>\n
581 s> <th class="author">author</th>\n
582 s> <th class="description">description</th>\n
583 s> </tr>\n
584 s> </thead>\n
585 s> <tbody class="stripes2">\n
586 s> <tr>\n
587 s> <td class="age">Thu, 01 Jan 1970 00:00:00 +0000</td>\n
588 s> <td class="author">test</td>\n
589 s> <td class="description">\n
590 s> <a href="/redirected/rev/96ee1d7354c4">initial</a>\n
591 s> <span class="phase">draft</span> <span class="branchhead">default</span> <span class="tag">tip</span> \n
592 s> </td>\n
593 s> </tr>\n
594 s> \n
595 s> </tbody>\n
596 s> </table>\n
597 s> \n
598 s> <div class="navigate">\n
599 s> <a href="/redirected/shortlog/tip?revcount=30">less</a>\n
600 s> <a href="/redirected/shortlog/tip?revcount=120">more</a>\n
601 s> | rev 0: <a href="/redirected/shortlog/96ee1d7354c4">(0)</a> <a href="/redirected/shortlog/tip">tip</a> \n
602 s> </div>\n
603 s> \n
604 s> <script type="text/javascript">\n
605 s> ajaxScrollInit(\n
606 s> \'/redirected/shortlog/%next%\',\n
607 s> \'\', <!-- NEXTHASH\n
608 s> function (htmlText) {
609 s> \r\n
610 s> 14a\r\n
611 s> \n
612 s> var m = htmlText.match(/\'(\\w+)\', <!-- NEXTHASH/);\n
613 s> return m ? m[1] : null;\n
614 s> },\n
615 s> \'.bigtable > tbody\',\n
616 s> \'<tr class="%class%">\\\n
617 s> <td colspan="3" style="text-align: center;">%text%</td>\\\n
618 s> </tr>\'\n
619 s> );\n
620 s> </script>\n
621 s> \n
622 s> </div>\n
623 s> </div>\n
624 s> \n
625 s> \n
626 s> \n
627 s> </body>\n
628 s> </html>\n
629 s> \n
630 s> \r\n
631 s> 0\r\n
632 s> \r\n
633
634 $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT/redirector << EOF
635 > command heads
636 > EOF
637 s> GET /redirector?cmd=capabilities HTTP/1.1\r\n
638 s> Accept-Encoding: identity\r\n
639 s> accept: application/mercurial-0.1\r\n
640 s> host: $LOCALIP:$HGPORT\r\n (glob)
641 s> user-agent: Mercurial debugwireproto\r\n
642 s> \r\n
643 s> makefile('rb', None)
644 s> HTTP/1.1 301 Redirect\r\n
645 s> Server: testing stub value\r\n
646 s> Date: $HTTP_DATE$\r\n
647 s> Location: http://$LOCALIP:$HGPORT/redirected\r\n (glob)
648 s> Content-Type: text/plain\r\n
649 s> Content-Length: 10\r\n
650 s> \r\n
651 s> redirected
652 s> GET /redirected HTTP/1.1\r\n
653 s> Accept-Encoding: identity\r\n
654 s> accept: application/mercurial-0.1\r\n
655 s> host: $LOCALIP:$HGPORT\r\n (glob)
656 s> user-agent: Mercurial debugwireproto\r\n
657 s> \r\n
658 s> makefile('rb', None)
659 s> HTTP/1.1 200 Script output follows\r\n
660 s> Server: testing stub value\r\n
661 s> Date: $HTTP_DATE$\r\n
662 s> ETag: W/"*"\r\n (glob)
663 s> Content-Type: text/html; charset=ascii\r\n
664 s> Transfer-Encoding: chunked\r\n
665 s> \r\n
666 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
667 s> 414\r\n
668 s> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\n
669 s> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US">\n
670 s> <head>\n
671 s> <link rel="icon" href="/redirected/static/hgicon.png" type="image/png" />\n
672 s> <meta name="robots" content="index, nofollow" />\n
673 s> <link rel="stylesheet" href="/redirected/static/style-paper.css" type="text/css" />\n
674 s> <script type="text/javascript" src="/redirected/static/mercurial.js"></script>\n
675 s> \n
676 s> <title>redirected: log</title>\n
677 s> <link rel="alternate" type="application/atom+xml"\n
678 s> href="/redirected/atom-log" title="Atom feed for redirected" />\n
679 s> <link rel="alternate" type="application/rss+xml"\n
680 s> href="/redirected/rss-log" title="RSS feed for redirected" />\n
681 s> </head>\n
682 s> <body>\n
683 s> \n
684 s> <div class="container">\n
685 s> <div class="menu">\n
686 s> <div class="logo">\n
687 s> <a href="https://mercurial-scm.org/">\n
688 s> <img src="/redirected/static/hglogo.png" alt="mercurial" /></a>\n
689 s> </div>\n
690 s> <ul>\n
691 s> <li class="active">log</li>\n
692 s> <li><a href="/redirected/graph/tip">graph</a></li>\n
693 s> <li><a href="/redirected/tags">tags</a
694 s> GET /redirected?cmd=capabilities HTTP/1.1\r\n
695 s> Accept-Encoding: identity\r\n
696 s> accept: application/mercurial-0.1\r\n
697 s> host: $LOCALIP:$HGPORT\r\n (glob)
698 s> user-agent: Mercurial debugwireproto\r\n
699 s> \r\n
700 s> makefile('rb', None)
701 s> HTTP/1.1 200 Script output follows\r\n
702 s> Server: testing stub value\r\n
703 s> Date: $HTTP_DATE$\r\n
704 s> Content-Type: application/mercurial-0.1\r\n
705 s> Content-Length: 453\r\n
706 s> \r\n
707 real URL is http://$LOCALIP:$HGPORT/redirected (glob)
708 s> batch branchmap $USUAL_BUNDLE2_CAPS_SERVER$ changegroupsubset compression=$BUNDLE2_COMPRESSIONS$ getbundle httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx known lookup pushkey streamreqs=generaldelta,revlogv1 unbundle=HG10GZ,HG10BZ,HG10UN unbundlehash
709 sending heads command
710 s> GET /redirected?cmd=heads HTTP/1.1\r\n
711 s> Accept-Encoding: identity\r\n
712 s> vary: X-HgProto-1\r\n
713 s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$ partial-pull\r\n
714 s> accept: application/mercurial-0.1\r\n
715 s> host: $LOCALIP:$HGPORT\r\n (glob)
716 s> user-agent: Mercurial debugwireproto\r\n
717 s> \r\n
718 s> makefile('rb', None)
719 s> HTTP/1.1 200 Script output follows\r\n
720 s> Server: testing stub value\r\n
721 s> Date: $HTTP_DATE$\r\n
722 s> Content-Type: application/mercurial-0.1\r\n
723 s> Content-Length: 41\r\n
724 s> \r\n
725 s> 96ee1d7354c4ad7372047672c36a1f561e3a6a4c\n
726 response: [b'\x96\xee\x1dsT\xc4\xadsr\x04vr\xc3j\x1fV\x1e:jL']
General Comments 0
You need to be logged in to leave comments. Login now