##// END OF EJS Templates
merge with security patches
Kevin Bullock -
r36898:8bba684e merge 4.5.2 stable
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (1502 lines changed) Show them Hide them
@@ -0,0 +1,1502 b''
1 #require killdaemons
2
3 $ cat > fakeremoteuser.py << EOF
4 > import os
5 > from mercurial.hgweb import hgweb_mod
6 > from mercurial import wireproto
7 > class testenvhgweb(hgweb_mod.hgweb):
8 > def __call__(self, env, respond):
9 > # Allow REMOTE_USER to define authenticated user.
10 > if r'REMOTE_USER' in os.environ:
11 > env[r'REMOTE_USER'] = os.environ[r'REMOTE_USER']
12 > # Allow REQUEST_METHOD to override HTTP method
13 > if r'REQUEST_METHOD' in os.environ:
14 > env[r'REQUEST_METHOD'] = os.environ[r'REQUEST_METHOD']
15 > return super(testenvhgweb, self).__call__(env, respond)
16 > hgweb_mod.hgweb = testenvhgweb
17 >
18 > @wireproto.wireprotocommand('customreadnoperm')
19 > def customread(repo, proto):
20 > return b'read-only command no defined permissions\n'
21 > @wireproto.wireprotocommand('customwritenoperm')
22 > def customwritenoperm(repo, proto):
23 > return b'write command no defined permissions\n'
24 > wireproto.permissions['customreadwithperm'] = 'pull'
25 > @wireproto.wireprotocommand('customreadwithperm')
26 > def customreadwithperm(repo, proto):
27 > return b'read-only command w/ defined permissions\n'
28 > wireproto.permissions['customwritewithperm'] = 'push'
29 > @wireproto.wireprotocommand('customwritewithperm')
30 > def customwritewithperm(repo, proto):
31 > return b'write command w/ defined permissions\n'
32 > EOF
33
34 $ cat >> $HGRCPATH << EOF
35 > [extensions]
36 > fakeremoteuser = $TESTTMP/fakeremoteuser.py
37 > strip =
38 > EOF
39
40 $ hg init test
41 $ cd test
42 $ echo a > a
43 $ hg ci -Ama
44 adding a
45 $ cd ..
46 $ hg clone test test2
47 updating to branch default
48 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
49 $ cd test2
50 $ echo a >> a
51 $ hg ci -mb
52 $ hg book bm -r 0
53 $ cd ../test
54
55 web.deny_read=* prevents access to wire protocol for all users
56
57 $ cat > .hg/hgrc <<EOF
58 > [web]
59 > deny_read = *
60 > EOF
61
62 $ hg serve -p $HGPORT -d --pid-file hg.pid
63 $ cat hg.pid > $DAEMON_PIDS
64
65 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities'
66 401 read not authorized
67
68 0
69 read not authorized
70 [1]
71
72 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=stream_out'
73 401 read not authorized
74
75 0
76 read not authorized
77 [1]
78
79 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
80 401 read not authorized
81
82 0
83 read not authorized
84 [1]
85
86 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
87 401 read not authorized
88
89 0
90 read not authorized
91 [1]
92
93 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
94 401 read not authorized
95
96 0
97 read not authorized
98 [1]
99
100 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
101 401 read not authorized
102
103 0
104 read not authorized
105 [1]
106
107 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
108 401 read not authorized
109
110 0
111 read not authorized
112 [1]
113
114 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
115 401 read not authorized
116
117 0
118 read not authorized
119 [1]
120
121 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
122 pulling from http://localhost:$HGPORT/
123 abort: authorization failed
124 [255]
125
126 $ killdaemons.py
127
128 web.deny_read=* with REMOTE_USER set still locks out clients
129
130 $ REMOTE_USER=authed_user hg serve -p $HGPORT -d --pid-file hg.pid
131 $ cat hg.pid > $DAEMON_PIDS
132
133 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities'
134 401 read not authorized
135
136 0
137 read not authorized
138 [1]
139
140 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=stream_out'
141 401 read not authorized
142
143 0
144 read not authorized
145 [1]
146
147 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
148 401 read not authorized
149
150 0
151 read not authorized
152 [1]
153
154 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
155 401 read not authorized
156
157 0
158 read not authorized
159 [1]
160
161 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
162 401 read not authorized
163
164 0
165 read not authorized
166 [1]
167
168 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
169 401 read not authorized
170
171 0
172 read not authorized
173 [1]
174
175 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
176 401 read not authorized
177
178 0
179 read not authorized
180 [1]
181
182 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
183 pulling from http://localhost:$HGPORT/
184 abort: authorization failed
185 [255]
186
187 $ killdaemons.py
188
189 web.deny_read=<user> denies access to unauthenticated user
190
191 $ cat > .hg/hgrc <<EOF
192 > [web]
193 > deny_read = baduser1,baduser2
194 > EOF
195
196 $ hg serve -p $HGPORT -d --pid-file hg.pid
197 $ cat hg.pid > $DAEMON_PIDS
198
199 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
200 401 read not authorized
201
202 0
203 read not authorized
204 [1]
205
206 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
207 401 read not authorized
208
209 0
210 read not authorized
211 [1]
212
213 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
214 401 read not authorized
215
216 0
217 read not authorized
218 [1]
219
220 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
221 401 read not authorized
222
223 0
224 read not authorized
225 [1]
226
227 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
228 401 read not authorized
229
230 0
231 read not authorized
232 [1]
233
234 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
235 401 read not authorized
236
237 0
238 read not authorized
239 [1]
240
241 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
242 pulling from http://localhost:$HGPORT/
243 abort: authorization failed
244 [255]
245
246 $ killdaemons.py
247
248 web.deny_read=<user> denies access to users in deny list
249
250 $ REMOTE_USER=baduser2 hg serve -p $HGPORT -d --pid-file hg.pid
251 $ cat hg.pid > $DAEMON_PIDS
252
253 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
254 401 read not authorized
255
256 0
257 read not authorized
258 [1]
259
260 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
261 401 read not authorized
262
263 0
264 read not authorized
265 [1]
266
267 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
268 401 read not authorized
269
270 0
271 read not authorized
272 [1]
273
274 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
275 401 read not authorized
276
277 0
278 read not authorized
279 [1]
280
281 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
282 401 read not authorized
283
284 0
285 read not authorized
286 [1]
287
288 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
289 401 read not authorized
290
291 0
292 read not authorized
293 [1]
294
295 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
296 pulling from http://localhost:$HGPORT/
297 abort: authorization failed
298 [255]
299
300 $ killdaemons.py
301
302 web.deny_read=<user> allows access to authenticated users not in list
303
304 $ REMOTE_USER=gooduser hg serve -p $HGPORT -d --pid-file hg.pid
305 $ cat hg.pid > $DAEMON_PIDS
306
307 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
308 200 Script output follows
309
310 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
311 publishing True (no-eol)
312
313 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
314 200 Script output follows
315
316 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
317 publishing True (no-eol)
318
319 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
320 405 push requires POST request
321
322 0
323 push requires POST request
324 [1]
325
326 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
327 200 Script output follows
328
329 read-only command w/ defined permissions
330
331 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
332 405 push requires POST request
333
334 0
335 push requires POST request
336 [1]
337
338 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
339 405 push requires POST request
340
341 0
342 push requires POST request
343 [1]
344
345 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
346 pulling from http://localhost:$HGPORT/
347 searching for changes
348 no changes found
349
350 $ killdaemons.py
351
352 web.allow_read=* allows reads for unauthenticated users
353
354 $ cat > .hg/hgrc <<EOF
355 > [web]
356 > allow_read = *
357 > EOF
358
359 $ hg serve -p $HGPORT -d --pid-file hg.pid
360 $ cat hg.pid > $DAEMON_PIDS
361
362 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
363 200 Script output follows
364
365 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
366 publishing True (no-eol)
367
368 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
369 200 Script output follows
370
371 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
372 publishing True (no-eol)
373
374 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
375 405 push requires POST request
376
377 0
378 push requires POST request
379 [1]
380
381 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
382 200 Script output follows
383
384 read-only command w/ defined permissions
385
386 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
387 405 push requires POST request
388
389 0
390 push requires POST request
391 [1]
392
393 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
394 405 push requires POST request
395
396 0
397 push requires POST request
398 [1]
399
400 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
401 pulling from http://localhost:$HGPORT/
402 searching for changes
403 no changes found
404
405 $ killdaemons.py
406
407 web.allow_read=* allows read for authenticated user
408
409 $ REMOTE_USER=authed_user hg serve -p $HGPORT -d --pid-file hg.pid
410 $ cat hg.pid > $DAEMON_PIDS
411
412 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
413 200 Script output follows
414
415 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
416 publishing True (no-eol)
417
418 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
419 200 Script output follows
420
421 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
422 publishing True (no-eol)
423
424 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
425 405 push requires POST request
426
427 0
428 push requires POST request
429 [1]
430
431 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
432 200 Script output follows
433
434 read-only command w/ defined permissions
435
436 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
437 405 push requires POST request
438
439 0
440 push requires POST request
441 [1]
442
443 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
444 405 push requires POST request
445
446 0
447 push requires POST request
448 [1]
449
450 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
451 pulling from http://localhost:$HGPORT/
452 searching for changes
453 no changes found
454
455 $ killdaemons.py
456
457 web.allow_read=<user> does not allow unauthenticated users to read
458
459 $ cat > .hg/hgrc <<EOF
460 > [web]
461 > allow_read = gooduser
462 > EOF
463
464 $ hg serve -p $HGPORT -d --pid-file hg.pid
465 $ cat hg.pid > $DAEMON_PIDS
466
467 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
468 401 read not authorized
469
470 0
471 read not authorized
472 [1]
473
474 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
475 401 read not authorized
476
477 0
478 read not authorized
479 [1]
480
481 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
482 401 read not authorized
483
484 0
485 read not authorized
486 [1]
487
488 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
489 401 read not authorized
490
491 0
492 read not authorized
493 [1]
494
495 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
496 401 read not authorized
497
498 0
499 read not authorized
500 [1]
501
502 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
503 401 read not authorized
504
505 0
506 read not authorized
507 [1]
508
509 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
510 pulling from http://localhost:$HGPORT/
511 abort: authorization failed
512 [255]
513
514 $ killdaemons.py
515
516 web.allow_read=<user> does not allow user not in list to read
517
518 $ REMOTE_USER=baduser hg serve -p $HGPORT -d --pid-file hg.pid
519 $ cat hg.pid > $DAEMON_PIDS
520
521 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
522 401 read not authorized
523
524 0
525 read not authorized
526 [1]
527
528 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
529 401 read not authorized
530
531 0
532 read not authorized
533 [1]
534
535 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
536 401 read not authorized
537
538 0
539 read not authorized
540 [1]
541
542 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
543 401 read not authorized
544
545 0
546 read not authorized
547 [1]
548
549 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
550 401 read not authorized
551
552 0
553 read not authorized
554 [1]
555
556 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
557 401 read not authorized
558
559 0
560 read not authorized
561 [1]
562
563 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
564 pulling from http://localhost:$HGPORT/
565 abort: authorization failed
566 [255]
567
568 $ killdaemons.py
569
570 web.allow_read=<user> allows read from user in list
571
572 $ REMOTE_USER=gooduser hg serve -p $HGPORT -d --pid-file hg.pid
573 $ cat hg.pid > $DAEMON_PIDS
574
575 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
576 200 Script output follows
577
578 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
579 publishing True (no-eol)
580
581 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
582 200 Script output follows
583
584 cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b 1
585 publishing True (no-eol)
586
587 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
588 405 push requires POST request
589
590 0
591 push requires POST request
592 [1]
593
594 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
595 200 Script output follows
596
597 read-only command w/ defined permissions
598
599 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
600 405 push requires POST request
601
602 0
603 push requires POST request
604 [1]
605
606 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
607 405 push requires POST request
608
609 0
610 push requires POST request
611 [1]
612
613 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
614 pulling from http://localhost:$HGPORT/
615 searching for changes
616 no changes found
617
618 $ killdaemons.py
619
620 web.deny_read takes precedence over web.allow_read
621
622 $ cat > .hg/hgrc <<EOF
623 > [web]
624 > allow_read = baduser
625 > deny_read = baduser
626 > EOF
627
628 $ REMOTE_USER=baduser hg serve -p $HGPORT -d --pid-file hg.pid
629 $ cat hg.pid > $DAEMON_PIDS
630
631 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
632 401 read not authorized
633
634 0
635 read not authorized
636 [1]
637
638 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
639 401 read not authorized
640
641 0
642 read not authorized
643 [1]
644
645 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
646 401 read not authorized
647
648 0
649 read not authorized
650 [1]
651
652 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
653 401 read not authorized
654
655 0
656 read not authorized
657 [1]
658
659 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
660 401 read not authorized
661
662 0
663 read not authorized
664 [1]
665
666 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
667 401 read not authorized
668
669 0
670 read not authorized
671 [1]
672
673 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
674 pulling from http://localhost:$HGPORT/
675 abort: authorization failed
676 [255]
677
678 $ killdaemons.py
679
680 web.allow-pull=false denies read access to repo
681
682 $ cat > .hg/hgrc <<EOF
683 > [web]
684 > allow-pull = false
685 > EOF
686
687 $ hg serve -p $HGPORT -d --pid-file hg.pid
688 $ cat hg.pid > $DAEMON_PIDS
689
690 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=capabilities'
691 401 pull not authorized
692
693 0
694 pull not authorized
695 [1]
696
697 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=listkeys' --requestheader 'x-hgarg-1=namespace=phases'
698 401 pull not authorized
699
700 0
701 pull not authorized
702 [1]
703
704 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=listkeys+namespace%3Dphases'
705 401 pull not authorized
706
707 0
708 pull not authorized
709 [1]
710
711 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
712 405 push requires POST request
713
714 0
715 push requires POST request
716 [1]
717
718 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
719 401 pull not authorized
720
721 0
722 pull not authorized
723 [1]
724
725 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
726 405 push requires POST request
727
728 0
729 push requires POST request
730 [1]
731
732 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
733 405 push requires POST request
734
735 0
736 push requires POST request
737 [1]
738
739 $ hg --cwd ../test2 pull http://localhost:$HGPORT/
740 pulling from http://localhost:$HGPORT/
741 abort: authorization failed
742 [255]
743
744 $ killdaemons.py
745
746 Attempting a write command with HTTP GET fails
747
748 $ cat > .hg/hgrc <<EOF
749 > EOF
750
751 $ REQUEST_METHOD=GET hg serve -p $HGPORT -d --pid-file hg.pid
752 $ cat hg.pid > $DAEMON_PIDS
753
754 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
755 405 push requires POST request
756
757 0
758 push requires POST request
759 [1]
760
761 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
762 405 push requires POST request
763
764 0
765 push requires POST request
766 [1]
767
768 $ hg bookmarks
769 no bookmarks set
770 $ hg bookmark -d bm
771 abort: bookmark 'bm' does not exist
772 [255]
773
774 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
775 405 push requires POST request
776
777 0
778 push requires POST request
779 [1]
780
781 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
782 405 push requires POST request
783
784 0
785 push requires POST request
786 [1]
787
788 $ killdaemons.py
789
790 Attempting a write command with an unknown HTTP verb fails
791
792 $ REQUEST_METHOD=someverb hg serve -p $HGPORT -d --pid-file hg.pid
793 $ cat hg.pid > $DAEMON_PIDS
794
795 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
796 405 push requires POST request
797
798 0
799 push requires POST request
800 [1]
801
802 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
803 405 push requires POST request
804
805 0
806 push requires POST request
807 [1]
808
809 $ hg bookmarks
810 no bookmarks set
811 $ hg bookmark -d bm
812 abort: bookmark 'bm' does not exist
813 [255]
814
815 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
816 405 push requires POST request
817
818 0
819 push requires POST request
820 [1]
821
822 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
823 405 push requires POST request
824
825 0
826 push requires POST request
827 [1]
828
829 $ killdaemons.py
830
831 Pushing on a plaintext channel is disabled by default
832
833 $ cat > .hg/hgrc <<EOF
834 > EOF
835
836 $ REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
837 $ cat hg.pid > $DAEMON_PIDS
838
839 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
840 403 ssl required
841
842 0
843 ssl required
844 [1]
845
846 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
847 403 ssl required
848
849 0
850 ssl required
851 [1]
852
853 $ hg bookmarks
854 no bookmarks set
855
856 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
857 403 ssl required
858
859 0
860 ssl required
861 [1]
862
863 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
864 403 ssl required
865
866 0
867 ssl required
868 [1]
869
870 Reset server to remove REQUEST_METHOD hack to test hg client
871
872 $ killdaemons.py
873 $ hg serve -p $HGPORT -d --pid-file hg.pid
874 $ cat hg.pid > $DAEMON_PIDS
875
876 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
877 pushing to http://localhost:$HGPORT/
878 searching for changes
879 no changes found
880 abort: HTTP Error 403: ssl required
881 [255]
882
883 $ hg --cwd ../test2 push http://localhost:$HGPORT/
884 pushing to http://localhost:$HGPORT/
885 searching for changes
886 abort: HTTP Error 403: ssl required
887 [255]
888
889 $ killdaemons.py
890
891 web.deny_push=* denies pushing to unauthenticated users
892
893 $ cat > .hg/hgrc <<EOF
894 > [web]
895 > push_ssl = false
896 > deny_push = *
897 > EOF
898
899 $ REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
900 $ cat hg.pid > $DAEMON_PIDS
901
902 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
903 401 push not authorized
904
905 0
906 push not authorized
907 [1]
908
909 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
910 401 push not authorized
911
912 0
913 push not authorized
914 [1]
915
916 $ hg bookmarks
917 no bookmarks set
918
919 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
920 401 push not authorized
921
922 0
923 push not authorized
924 [1]
925
926 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
927 401 push not authorized
928
929 0
930 push not authorized
931 [1]
932
933 Reset server to remove REQUEST_METHOD hack to test hg client
934
935 $ killdaemons.py
936 $ hg serve -p $HGPORT -d --pid-file hg.pid
937 $ cat hg.pid > $DAEMON_PIDS
938
939 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
940 pushing to http://localhost:$HGPORT/
941 searching for changes
942 no changes found
943 abort: authorization failed
944 [255]
945
946 $ hg --cwd ../test2 push http://localhost:$HGPORT/
947 pushing to http://localhost:$HGPORT/
948 searching for changes
949 abort: authorization failed
950 [255]
951
952 $ killdaemons.py
953
954 web.deny_push=* denies pushing to authenticated users
955
956 $ REMOTE_USER=someuser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
957 $ cat hg.pid > $DAEMON_PIDS
958
959 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
960 401 push not authorized
961
962 0
963 push not authorized
964 [1]
965
966 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
967 401 push not authorized
968
969 0
970 push not authorized
971 [1]
972
973 $ hg bookmarks
974 no bookmarks set
975
976 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
977 401 push not authorized
978
979 0
980 push not authorized
981 [1]
982
983 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
984 401 push not authorized
985
986 0
987 push not authorized
988 [1]
989
990 Reset server to remove REQUEST_METHOD hack to test hg client
991
992 $ killdaemons.py
993 $ REMOTE_USER=someuser hg serve -p $HGPORT -d --pid-file hg.pid
994 $ cat hg.pid > $DAEMON_PIDS
995
996 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
997 pushing to http://localhost:$HGPORT/
998 searching for changes
999 no changes found
1000 abort: authorization failed
1001 [255]
1002
1003 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1004 pushing to http://localhost:$HGPORT/
1005 searching for changes
1006 abort: authorization failed
1007 [255]
1008
1009 $ killdaemons.py
1010
1011 web.deny_push=<user> denies pushing to user in list
1012
1013 $ cat > .hg/hgrc <<EOF
1014 > [web]
1015 > push_ssl = false
1016 > deny_push = baduser
1017 > EOF
1018
1019 $ REMOTE_USER=baduser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1020 $ cat hg.pid > $DAEMON_PIDS
1021
1022 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1023 401 push not authorized
1024
1025 0
1026 push not authorized
1027 [1]
1028
1029 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1030 401 push not authorized
1031
1032 0
1033 push not authorized
1034 [1]
1035
1036 $ hg bookmarks
1037 no bookmarks set
1038
1039 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1040 401 push not authorized
1041
1042 0
1043 push not authorized
1044 [1]
1045
1046 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1047 401 push not authorized
1048
1049 0
1050 push not authorized
1051 [1]
1052
1053 Reset server to remove REQUEST_METHOD hack to test hg client
1054
1055 $ killdaemons.py
1056 $ REMOTE_USER=baduser hg serve -p $HGPORT -d --pid-file hg.pid
1057 $ cat hg.pid > $DAEMON_PIDS
1058
1059 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1060 pushing to http://localhost:$HGPORT/
1061 searching for changes
1062 no changes found
1063 abort: authorization failed
1064 [255]
1065
1066 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1067 pushing to http://localhost:$HGPORT/
1068 searching for changes
1069 abort: authorization failed
1070 [255]
1071
1072 $ killdaemons.py
1073
1074 web.deny_push=<user> denies pushing to user not in list because allow-push isn't set
1075
1076 $ REMOTE_USER=gooduser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1077 $ cat hg.pid > $DAEMON_PIDS
1078
1079 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1080 401 push not authorized
1081
1082 0
1083 push not authorized
1084 [1]
1085
1086 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1087 401 push not authorized
1088
1089 0
1090 push not authorized
1091 [1]
1092
1093 $ hg bookmarks
1094 no bookmarks set
1095
1096 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1097 401 push not authorized
1098
1099 0
1100 push not authorized
1101 [1]
1102
1103 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1104 401 push not authorized
1105
1106 0
1107 push not authorized
1108 [1]
1109
1110 Reset server to remove REQUEST_METHOD hack to test hg client
1111
1112 $ killdaemons.py
1113 $ REMOTE_USER=gooduser hg serve -p $HGPORT -d --pid-file hg.pid
1114 $ cat hg.pid > $DAEMON_PIDS
1115
1116 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1117 pushing to http://localhost:$HGPORT/
1118 searching for changes
1119 no changes found
1120 abort: authorization failed
1121 [255]
1122
1123 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1124 pushing to http://localhost:$HGPORT/
1125 searching for changes
1126 abort: authorization failed
1127 [255]
1128
1129 $ killdaemons.py
1130
1131 web.allow-push=* allows pushes from unauthenticated users
1132
1133 $ cat > .hg/hgrc <<EOF
1134 > [web]
1135 > push_ssl = false
1136 > allow-push = *
1137 > EOF
1138
1139 $ REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1140 $ cat hg.pid > $DAEMON_PIDS
1141
1142 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1143 200 Script output follows
1144
1145 1
1146
1147 $ hg bookmarks
1148 bm 0:cb9a9f314b8b
1149 $ hg book -d bm
1150
1151 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1152 200 Script output follows
1153
1154 write command no defined permissions
1155
1156 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1157 200 Script output follows
1158
1159 write command w/ defined permissions
1160
1161 Reset server to remove REQUEST_METHOD hack to test hg client
1162
1163 $ killdaemons.py
1164 $ hg serve -p $HGPORT -d --pid-file hg.pid
1165 $ cat hg.pid > $DAEMON_PIDS
1166
1167 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1168 pushing to http://localhost:$HGPORT/
1169 searching for changes
1170 no changes found
1171 exporting bookmark bm
1172 [1]
1173
1174 $ hg book -d bm
1175
1176 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1177 pushing to http://localhost:$HGPORT/
1178 searching for changes
1179 remote: adding changesets
1180 remote: adding manifests
1181 remote: adding file changes
1182 remote: added 1 changesets with 1 changes to 1 files
1183
1184 $ hg strip -r 1:
1185 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
1186
1187 $ killdaemons.py
1188
1189 web.allow-push=* allows pushes from authenticated users
1190
1191 $ REMOTE_USER=someuser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1192 $ cat hg.pid > $DAEMON_PIDS
1193
1194 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1195 200 Script output follows
1196
1197 1
1198
1199 $ hg bookmarks
1200 bm 0:cb9a9f314b8b
1201 $ hg book -d bm
1202
1203 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1204 200 Script output follows
1205
1206 write command no defined permissions
1207
1208 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1209 200 Script output follows
1210
1211 write command w/ defined permissions
1212
1213 Reset server to remove REQUEST_METHOD hack to test hg client
1214
1215 $ killdaemons.py
1216 $ REMOTE_USER=someuser hg serve -p $HGPORT -d --pid-file hg.pid
1217 $ cat hg.pid > $DAEMON_PIDS
1218
1219 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1220 pushing to http://localhost:$HGPORT/
1221 searching for changes
1222 no changes found
1223 exporting bookmark bm
1224 [1]
1225
1226 $ hg book -d bm
1227
1228 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1229 pushing to http://localhost:$HGPORT/
1230 searching for changes
1231 remote: adding changesets
1232 remote: adding manifests
1233 remote: adding file changes
1234 remote: added 1 changesets with 1 changes to 1 files
1235
1236 $ hg strip -r 1:
1237 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
1238
1239 $ killdaemons.py
1240
1241 web.allow-push=<user> denies push to user not in list
1242
1243 $ cat > .hg/hgrc <<EOF
1244 > [web]
1245 > push_ssl = false
1246 > allow-push = gooduser
1247 > EOF
1248
1249 $ REMOTE_USER=baduser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1250 $ cat hg.pid > $DAEMON_PIDS
1251
1252 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1253 401 push not authorized
1254
1255 0
1256 push not authorized
1257 [1]
1258
1259 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1260 401 push not authorized
1261
1262 0
1263 push not authorized
1264 [1]
1265
1266 $ hg bookmarks
1267 no bookmarks set
1268
1269 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1270 401 push not authorized
1271
1272 0
1273 push not authorized
1274 [1]
1275
1276 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1277 401 push not authorized
1278
1279 0
1280 push not authorized
1281 [1]
1282
1283 Reset server to remove REQUEST_METHOD hack to test hg client
1284
1285 $ killdaemons.py
1286 $ REMOTE_USER=baduser hg serve -p $HGPORT -d --pid-file hg.pid
1287 $ cat hg.pid > $DAEMON_PIDS
1288
1289 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1290 pushing to http://localhost:$HGPORT/
1291 searching for changes
1292 no changes found
1293 abort: authorization failed
1294 [255]
1295
1296 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1297 pushing to http://localhost:$HGPORT/
1298 searching for changes
1299 abort: authorization failed
1300 [255]
1301
1302 $ killdaemons.py
1303
1304 web.allow-push=<user> allows push from user in list
1305
1306 $ REMOTE_USER=gooduser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1307 $ cat hg.pid > $DAEMON_PIDS
1308
1309 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1310 200 Script output follows
1311
1312 1
1313
1314 $ hg bookmarks
1315 bm 0:cb9a9f314b8b
1316 $ hg book -d bm
1317
1318 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1319 200 Script output follows
1320
1321 1
1322
1323 $ hg bookmarks
1324 bm 0:cb9a9f314b8b
1325 $ hg book -d bm
1326
1327 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1328 200 Script output follows
1329
1330 write command no defined permissions
1331
1332 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1333 200 Script output follows
1334
1335 write command w/ defined permissions
1336
1337 Reset server to remove REQUEST_METHOD hack to test hg client
1338
1339 $ killdaemons.py
1340 $ REMOTE_USER=gooduser hg serve -p $HGPORT -d --pid-file hg.pid
1341 $ cat hg.pid > $DAEMON_PIDS
1342
1343 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1344 pushing to http://localhost:$HGPORT/
1345 searching for changes
1346 no changes found
1347 exporting bookmark bm
1348 [1]
1349
1350 $ hg book -d bm
1351
1352 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1353 pushing to http://localhost:$HGPORT/
1354 searching for changes
1355 remote: adding changesets
1356 remote: adding manifests
1357 remote: adding file changes
1358 remote: added 1 changesets with 1 changes to 1 files
1359
1360 $ hg strip -r 1:
1361 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
1362
1363 $ killdaemons.py
1364
1365 web.deny_push takes precedence over web.allow_push
1366
1367 $ cat > .hg/hgrc <<EOF
1368 > [web]
1369 > push_ssl = false
1370 > allow-push = someuser
1371 > deny_push = someuser
1372 > EOF
1373
1374 $ REMOTE_USER=someuser REQUEST_METHOD=POST hg serve -p $HGPORT -d --pid-file hg.pid
1375 $ cat hg.pid > $DAEMON_PIDS
1376
1377 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1378 401 push not authorized
1379
1380 0
1381 push not authorized
1382 [1]
1383
1384 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1385 401 push not authorized
1386
1387 0
1388 push not authorized
1389 [1]
1390
1391 $ hg bookmarks
1392 no bookmarks set
1393
1394 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1395 401 push not authorized
1396
1397 0
1398 push not authorized
1399 [1]
1400
1401 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1402 401 push not authorized
1403
1404 0
1405 push not authorized
1406 [1]
1407
1408 Reset server to remove REQUEST_METHOD hack to test hg client
1409
1410 $ killdaemons.py
1411 $ REMOTE_USER=someuser hg serve -p $HGPORT -d --pid-file hg.pid
1412 $ cat hg.pid > $DAEMON_PIDS
1413
1414 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1415 pushing to http://localhost:$HGPORT/
1416 searching for changes
1417 no changes found
1418 abort: authorization failed
1419 [255]
1420
1421 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1422 pushing to http://localhost:$HGPORT/
1423 searching for changes
1424 abort: authorization failed
1425 [255]
1426
1427 $ killdaemons.py
1428
1429 web.allow-push has no effect if web.deny_read is set
1430
1431 $ cat > .hg/hgrc <<EOF
1432 > [web]
1433 > push_ssl = false
1434 > allow-push = *
1435 > deny_read = *
1436 > EOF
1437
1438 $ REQUEST_METHOD=POST REMOTE_USER=someuser hg serve -p $HGPORT -d --pid-file hg.pid
1439 $ cat hg.pid > $DAEMON_PIDS
1440
1441 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=pushkey' --requestheader 'x-hgarg-1=namespace=bookmarks&key=bm&old=&new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1442 401 read not authorized
1443
1444 0
1445 read not authorized
1446 [1]
1447
1448 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=batch' --requestheader 'x-hgarg-1=cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'
1449 401 read not authorized
1450
1451 0
1452 read not authorized
1453 [1]
1454
1455 $ hg bookmarks
1456 no bookmarks set
1457
1458 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadnoperm'
1459 401 read not authorized
1460
1461 0
1462 read not authorized
1463 [1]
1464
1465 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customreadwithperm'
1466 401 read not authorized
1467
1468 0
1469 read not authorized
1470 [1]
1471
1472 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritenoperm'
1473 401 read not authorized
1474
1475 0
1476 read not authorized
1477 [1]
1478
1479 $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=customwritewithperm'
1480 401 read not authorized
1481
1482 0
1483 read not authorized
1484 [1]
1485
1486 Reset server to remove REQUEST_METHOD hack to test hg client
1487
1488 $ killdaemons.py
1489 $ REMOTE_USER=someuser hg serve -p $HGPORT -d --pid-file hg.pid
1490 $ cat hg.pid > $DAEMON_PIDS
1491
1492 $ hg --cwd ../test2 push -B bm http://localhost:$HGPORT/
1493 pushing to http://localhost:$HGPORT/
1494 abort: authorization failed
1495 [255]
1496
1497 $ hg --cwd ../test2 push http://localhost:$HGPORT/
1498 pushing to http://localhost:$HGPORT/
1499 abort: authorization failed
1500 [255]
1501
1502 $ killdaemons.py
@@ -1,205 +1,205 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''setup for largefiles extension: uisetup'''
9 '''setup for largefiles extension: uisetup'''
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 from mercurial.i18n import _
12 from mercurial.i18n import _
13
13
14 from mercurial.hgweb import (
14 from mercurial.hgweb import (
15 hgweb_mod,
16 webcommands,
15 webcommands,
17 )
16 )
18
17
19 from mercurial import (
18 from mercurial import (
20 archival,
19 archival,
21 cmdutil,
20 cmdutil,
22 commands,
21 commands,
23 copies,
22 copies,
24 exchange,
23 exchange,
25 extensions,
24 extensions,
26 filemerge,
25 filemerge,
27 hg,
26 hg,
28 httppeer,
27 httppeer,
29 merge,
28 merge,
30 scmutil,
29 scmutil,
31 sshpeer,
30 sshpeer,
32 subrepo,
31 subrepo,
33 upgrade,
32 upgrade,
34 url,
33 url,
35 wireproto,
34 wireproto,
36 )
35 )
37
36
38 from . import (
37 from . import (
39 overrides,
38 overrides,
40 proto,
39 proto,
41 )
40 )
42
41
43 def uisetup(ui):
42 def uisetup(ui):
44 # Disable auto-status for some commands which assume that all
43 # Disable auto-status for some commands which assume that all
45 # files in the result are under Mercurial's control
44 # files in the result are under Mercurial's control
46
45
47 entry = extensions.wrapcommand(commands.table, 'add',
46 entry = extensions.wrapcommand(commands.table, 'add',
48 overrides.overrideadd)
47 overrides.overrideadd)
49 addopt = [('', 'large', None, _('add as largefile')),
48 addopt = [('', 'large', None, _('add as largefile')),
50 ('', 'normal', None, _('add as normal file')),
49 ('', 'normal', None, _('add as normal file')),
51 ('', 'lfsize', '', _('add all files above this size '
50 ('', 'lfsize', '', _('add all files above this size '
52 '(in megabytes) as largefiles '
51 '(in megabytes) as largefiles '
53 '(default: 10)'))]
52 '(default: 10)'))]
54 entry[1].extend(addopt)
53 entry[1].extend(addopt)
55
54
56 # The scmutil function is called both by the (trivial) addremove command,
55 # The scmutil function is called both by the (trivial) addremove command,
57 # and in the process of handling commit -A (issue3542)
56 # and in the process of handling commit -A (issue3542)
58 extensions.wrapfunction(scmutil, 'addremove', overrides.scmutiladdremove)
57 extensions.wrapfunction(scmutil, 'addremove', overrides.scmutiladdremove)
59 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
58 extensions.wrapfunction(cmdutil, 'add', overrides.cmdutiladd)
60 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
59 extensions.wrapfunction(cmdutil, 'remove', overrides.cmdutilremove)
61 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
60 extensions.wrapfunction(cmdutil, 'forget', overrides.cmdutilforget)
62
61
63 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
62 extensions.wrapfunction(copies, 'pathcopies', overrides.copiespathcopies)
64
63
65 extensions.wrapfunction(upgrade, 'preservedrequirements',
64 extensions.wrapfunction(upgrade, 'preservedrequirements',
66 overrides.upgraderequirements)
65 overrides.upgraderequirements)
67
66
68 extensions.wrapfunction(upgrade, 'supporteddestrequirements',
67 extensions.wrapfunction(upgrade, 'supporteddestrequirements',
69 overrides.upgraderequirements)
68 overrides.upgraderequirements)
70
69
71 # Subrepos call status function
70 # Subrepos call status function
72 entry = extensions.wrapcommand(commands.table, 'status',
71 entry = extensions.wrapcommand(commands.table, 'status',
73 overrides.overridestatus)
72 overrides.overridestatus)
74 extensions.wrapfunction(subrepo.hgsubrepo, 'status',
73 extensions.wrapfunction(subrepo.hgsubrepo, 'status',
75 overrides.overridestatusfn)
74 overrides.overridestatusfn)
76
75
77 entry = extensions.wrapcommand(commands.table, 'log',
76 entry = extensions.wrapcommand(commands.table, 'log',
78 overrides.overridelog)
77 overrides.overridelog)
79 entry = extensions.wrapcommand(commands.table, 'rollback',
78 entry = extensions.wrapcommand(commands.table, 'rollback',
80 overrides.overriderollback)
79 overrides.overriderollback)
81 entry = extensions.wrapcommand(commands.table, 'verify',
80 entry = extensions.wrapcommand(commands.table, 'verify',
82 overrides.overrideverify)
81 overrides.overrideverify)
83
82
84 verifyopt = [('', 'large', None,
83 verifyopt = [('', 'large', None,
85 _('verify that all largefiles in current revision exists')),
84 _('verify that all largefiles in current revision exists')),
86 ('', 'lfa', None,
85 ('', 'lfa', None,
87 _('verify largefiles in all revisions, not just current')),
86 _('verify largefiles in all revisions, not just current')),
88 ('', 'lfc', None,
87 ('', 'lfc', None,
89 _('verify local largefile contents, not just existence'))]
88 _('verify local largefile contents, not just existence'))]
90 entry[1].extend(verifyopt)
89 entry[1].extend(verifyopt)
91
90
92 entry = extensions.wrapcommand(commands.table, 'debugstate',
91 entry = extensions.wrapcommand(commands.table, 'debugstate',
93 overrides.overridedebugstate)
92 overrides.overridedebugstate)
94 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
93 debugstateopt = [('', 'large', None, _('display largefiles dirstate'))]
95 entry[1].extend(debugstateopt)
94 entry[1].extend(debugstateopt)
96
95
97 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
96 outgoing = lambda orgfunc, *arg, **kwargs: orgfunc(*arg, **kwargs)
98 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
97 entry = extensions.wrapcommand(commands.table, 'outgoing', outgoing)
99 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
98 outgoingopt = [('', 'large', None, _('display outgoing largefiles'))]
100 entry[1].extend(outgoingopt)
99 entry[1].extend(outgoingopt)
101 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
100 cmdutil.outgoinghooks.add('largefiles', overrides.outgoinghook)
102 entry = extensions.wrapcommand(commands.table, 'summary',
101 entry = extensions.wrapcommand(commands.table, 'summary',
103 overrides.overridesummary)
102 overrides.overridesummary)
104 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
103 summaryopt = [('', 'large', None, _('display outgoing largefiles'))]
105 entry[1].extend(summaryopt)
104 entry[1].extend(summaryopt)
106 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
105 cmdutil.summaryremotehooks.add('largefiles', overrides.summaryremotehook)
107
106
108 entry = extensions.wrapcommand(commands.table, 'pull',
107 entry = extensions.wrapcommand(commands.table, 'pull',
109 overrides.overridepull)
108 overrides.overridepull)
110 pullopt = [('', 'all-largefiles', None,
109 pullopt = [('', 'all-largefiles', None,
111 _('download all pulled versions of largefiles (DEPRECATED)')),
110 _('download all pulled versions of largefiles (DEPRECATED)')),
112 ('', 'lfrev', [],
111 ('', 'lfrev', [],
113 _('download largefiles for these revisions'), _('REV'))]
112 _('download largefiles for these revisions'), _('REV'))]
114 entry[1].extend(pullopt)
113 entry[1].extend(pullopt)
115
114
116 entry = extensions.wrapcommand(commands.table, 'push',
115 entry = extensions.wrapcommand(commands.table, 'push',
117 overrides.overridepush)
116 overrides.overridepush)
118 pushopt = [('', 'lfrev', [],
117 pushopt = [('', 'lfrev', [],
119 _('upload largefiles for these revisions'), _('REV'))]
118 _('upload largefiles for these revisions'), _('REV'))]
120 entry[1].extend(pushopt)
119 entry[1].extend(pushopt)
121 extensions.wrapfunction(exchange, 'pushoperation',
120 extensions.wrapfunction(exchange, 'pushoperation',
122 overrides.exchangepushoperation)
121 overrides.exchangepushoperation)
123
122
124 entry = extensions.wrapcommand(commands.table, 'clone',
123 entry = extensions.wrapcommand(commands.table, 'clone',
125 overrides.overrideclone)
124 overrides.overrideclone)
126 cloneopt = [('', 'all-largefiles', None,
125 cloneopt = [('', 'all-largefiles', None,
127 _('download all versions of all largefiles'))]
126 _('download all versions of all largefiles'))]
128 entry[1].extend(cloneopt)
127 entry[1].extend(cloneopt)
129 extensions.wrapfunction(hg, 'clone', overrides.hgclone)
128 extensions.wrapfunction(hg, 'clone', overrides.hgclone)
130 extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
129 extensions.wrapfunction(hg, 'postshare', overrides.hgpostshare)
131
130
132 entry = extensions.wrapcommand(commands.table, 'cat',
131 entry = extensions.wrapcommand(commands.table, 'cat',
133 overrides.overridecat)
132 overrides.overridecat)
134 extensions.wrapfunction(merge, '_checkunknownfile',
133 extensions.wrapfunction(merge, '_checkunknownfile',
135 overrides.overridecheckunknownfile)
134 overrides.overridecheckunknownfile)
136 extensions.wrapfunction(merge, 'calculateupdates',
135 extensions.wrapfunction(merge, 'calculateupdates',
137 overrides.overridecalculateupdates)
136 overrides.overridecalculateupdates)
138 extensions.wrapfunction(merge, 'recordupdates',
137 extensions.wrapfunction(merge, 'recordupdates',
139 overrides.mergerecordupdates)
138 overrides.mergerecordupdates)
140 extensions.wrapfunction(merge, 'update', overrides.mergeupdate)
139 extensions.wrapfunction(merge, 'update', overrides.mergeupdate)
141 extensions.wrapfunction(filemerge, '_filemerge',
140 extensions.wrapfunction(filemerge, '_filemerge',
142 overrides.overridefilemerge)
141 overrides.overridefilemerge)
143 extensions.wrapfunction(cmdutil, 'copy', overrides.overridecopy)
142 extensions.wrapfunction(cmdutil, 'copy', overrides.overridecopy)
144
143
145 # Summary calls dirty on the subrepos
144 # Summary calls dirty on the subrepos
146 extensions.wrapfunction(subrepo.hgsubrepo, 'dirty', overrides.overridedirty)
145 extensions.wrapfunction(subrepo.hgsubrepo, 'dirty', overrides.overridedirty)
147
146
148 extensions.wrapfunction(cmdutil, 'revert', overrides.overriderevert)
147 extensions.wrapfunction(cmdutil, 'revert', overrides.overriderevert)
149
148
150 extensions.wrapcommand(commands.table, 'archive',
149 extensions.wrapcommand(commands.table, 'archive',
151 overrides.overridearchivecmd)
150 overrides.overridearchivecmd)
152 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
151 extensions.wrapfunction(archival, 'archive', overrides.overridearchive)
153 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
152 extensions.wrapfunction(subrepo.hgsubrepo, 'archive',
154 overrides.hgsubrepoarchive)
153 overrides.hgsubrepoarchive)
155 extensions.wrapfunction(webcommands, 'archive', overrides.hgwebarchive)
154 extensions.wrapfunction(webcommands, 'archive', overrides.hgwebarchive)
156 extensions.wrapfunction(cmdutil, 'bailifchanged',
155 extensions.wrapfunction(cmdutil, 'bailifchanged',
157 overrides.overridebailifchanged)
156 overrides.overridebailifchanged)
158
157
159 extensions.wrapfunction(cmdutil, 'postcommitstatus',
158 extensions.wrapfunction(cmdutil, 'postcommitstatus',
160 overrides.postcommitstatus)
159 overrides.postcommitstatus)
161 extensions.wrapfunction(scmutil, 'marktouched',
160 extensions.wrapfunction(scmutil, 'marktouched',
162 overrides.scmutilmarktouched)
161 overrides.scmutilmarktouched)
163
162
164 extensions.wrapfunction(url, 'open',
163 extensions.wrapfunction(url, 'open',
165 overrides.openlargefile)
164 overrides.openlargefile)
166
165
167 # create the new wireproto commands ...
166 # create the new wireproto commands ...
168 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
167 wireproto.commands['putlfile'] = (proto.putlfile, 'sha')
169 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
168 wireproto.commands['getlfile'] = (proto.getlfile, 'sha')
170 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
169 wireproto.commands['statlfile'] = (proto.statlfile, 'sha')
171
170
172 # ... and wrap some existing ones
171 # ... and wrap some existing ones
173 wireproto.commands['heads'] = (proto.heads, '')
172 wireproto.commands['heads'] = (proto.heads, '')
174 wireproto.commands['lheads'] = (wireproto.heads, '')
173 wireproto.commands['lheads'] = (wireproto.heads, '')
175
174
176 # make putlfile behave the same as push and {get,stat}lfile behave
175 # make putlfile behave the same as push and {get,stat}lfile behave
177 # the same as pull w.r.t. permissions checks
176 # the same as pull w.r.t. permissions checks
178 hgweb_mod.perms['putlfile'] = 'push'
177 wireproto.permissions['putlfile'] = 'push'
179 hgweb_mod.perms['getlfile'] = 'pull'
178 wireproto.permissions['getlfile'] = 'pull'
180 hgweb_mod.perms['statlfile'] = 'pull'
179 wireproto.permissions['statlfile'] = 'pull'
180 wireproto.permissions['lheads'] = 'pull'
181
181
182 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
182 extensions.wrapfunction(webcommands, 'decodepath', overrides.decodepath)
183
183
184 extensions.wrapfunction(wireproto, '_capabilities', proto._capabilities)
184 extensions.wrapfunction(wireproto, '_capabilities', proto._capabilities)
185
185
186 # can't do this in reposetup because it needs to have happened before
186 # can't do this in reposetup because it needs to have happened before
187 # wirerepo.__init__ is called
187 # wirerepo.__init__ is called
188 proto.ssholdcallstream = sshpeer.sshpeer._callstream
188 proto.ssholdcallstream = sshpeer.sshpeer._callstream
189 proto.httpoldcallstream = httppeer.httppeer._callstream
189 proto.httpoldcallstream = httppeer.httppeer._callstream
190 sshpeer.sshpeer._callstream = proto.sshrepocallstream
190 sshpeer.sshpeer._callstream = proto.sshrepocallstream
191 httppeer.httppeer._callstream = proto.httprepocallstream
191 httppeer.httppeer._callstream = proto.httprepocallstream
192
192
193 # override some extensions' stuff as well
193 # override some extensions' stuff as well
194 for name, module in extensions.extensions():
194 for name, module in extensions.extensions():
195 if name == 'purge':
195 if name == 'purge':
196 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
196 extensions.wrapcommand(getattr(module, 'cmdtable'), 'purge',
197 overrides.overridepurge)
197 overrides.overridepurge)
198 if name == 'rebase':
198 if name == 'rebase':
199 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
199 extensions.wrapcommand(getattr(module, 'cmdtable'), 'rebase',
200 overrides.overriderebase)
200 overrides.overriderebase)
201 extensions.wrapfunction(module, 'rebase',
201 extensions.wrapfunction(module, 'rebase',
202 overrides.overriderebase)
202 overrides.overriderebase)
203 if name == 'transplant':
203 if name == 'transplant':
204 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
204 extensions.wrapcommand(getattr(module, 'cmdtable'), 'transplant',
205 overrides.overridetransplant)
205 overrides.overridetransplant)
@@ -1,491 +1,490 b''
1 # hgweb/hgweb_mod.py - Web interface for a repository.
1 # hgweb/hgweb_mod.py - Web interface for a repository.
2 #
2 #
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 import contextlib
11 import contextlib
12 import os
12 import os
13
13
14 from .common import (
14 from .common import (
15 ErrorResponse,
15 ErrorResponse,
16 HTTP_BAD_REQUEST,
16 HTTP_BAD_REQUEST,
17 HTTP_NOT_FOUND,
17 HTTP_NOT_FOUND,
18 HTTP_NOT_MODIFIED,
18 HTTP_NOT_MODIFIED,
19 HTTP_OK,
19 HTTP_OK,
20 HTTP_SERVER_ERROR,
20 HTTP_SERVER_ERROR,
21 caching,
21 caching,
22 cspvalues,
22 cspvalues,
23 permhooks,
23 permhooks,
24 )
24 )
25 from .request import wsgirequest
25 from .request import wsgirequest
26
26
27 from .. import (
27 from .. import (
28 encoding,
28 encoding,
29 error,
29 error,
30 hg,
30 hg,
31 hook,
31 hook,
32 profiling,
32 profiling,
33 pycompat,
33 pycompat,
34 repoview,
34 repoview,
35 templatefilters,
35 templatefilters,
36 templater,
36 templater,
37 ui as uimod,
37 ui as uimod,
38 util,
38 util,
39 wireproto,
39 )
40 )
40
41
41 from . import (
42 from . import (
42 protocol,
43 protocol,
43 webcommands,
44 webcommands,
44 webutil,
45 webutil,
45 wsgicgi,
46 wsgicgi,
46 )
47 )
47
48
48 perms = {
49 # Aliased for API compatibility.
49 'changegroup': 'pull',
50 perms = wireproto.permissions
50 'changegroupsubset': 'pull',
51 'getbundle': 'pull',
52 'stream_out': 'pull',
53 'listkeys': 'pull',
54 'unbundle': 'push',
55 'pushkey': 'push',
56 }
57
51
58 archivespecs = util.sortdict((
52 archivespecs = util.sortdict((
59 ('zip', ('application/zip', 'zip', '.zip', None)),
53 ('zip', ('application/zip', 'zip', '.zip', None)),
60 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
54 ('gz', ('application/x-gzip', 'tgz', '.tar.gz', None)),
61 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
55 ('bz2', ('application/x-bzip2', 'tbz2', '.tar.bz2', None)),
62 ))
56 ))
63
57
64 def getstyle(req, configfn, templatepath):
58 def getstyle(req, configfn, templatepath):
65 fromreq = req.form.get('style', [None])[0]
59 fromreq = req.form.get('style', [None])[0]
66 if fromreq is not None:
60 if fromreq is not None:
67 fromreq = pycompat.sysbytes(fromreq)
61 fromreq = pycompat.sysbytes(fromreq)
68 styles = (
62 styles = (
69 fromreq,
63 fromreq,
70 configfn('web', 'style'),
64 configfn('web', 'style'),
71 'paper',
65 'paper',
72 )
66 )
73 return styles, templater.stylemap(styles, templatepath)
67 return styles, templater.stylemap(styles, templatepath)
74
68
75 def makebreadcrumb(url, prefix=''):
69 def makebreadcrumb(url, prefix=''):
76 '''Return a 'URL breadcrumb' list
70 '''Return a 'URL breadcrumb' list
77
71
78 A 'URL breadcrumb' is a list of URL-name pairs,
72 A 'URL breadcrumb' is a list of URL-name pairs,
79 corresponding to each of the path items on a URL.
73 corresponding to each of the path items on a URL.
80 This can be used to create path navigation entries.
74 This can be used to create path navigation entries.
81 '''
75 '''
82 if url.endswith('/'):
76 if url.endswith('/'):
83 url = url[:-1]
77 url = url[:-1]
84 if prefix:
78 if prefix:
85 url = '/' + prefix + url
79 url = '/' + prefix + url
86 relpath = url
80 relpath = url
87 if relpath.startswith('/'):
81 if relpath.startswith('/'):
88 relpath = relpath[1:]
82 relpath = relpath[1:]
89
83
90 breadcrumb = []
84 breadcrumb = []
91 urlel = url
85 urlel = url
92 pathitems = [''] + relpath.split('/')
86 pathitems = [''] + relpath.split('/')
93 for pathel in reversed(pathitems):
87 for pathel in reversed(pathitems):
94 if not pathel or not urlel:
88 if not pathel or not urlel:
95 break
89 break
96 breadcrumb.append({'url': urlel, 'name': pathel})
90 breadcrumb.append({'url': urlel, 'name': pathel})
97 urlel = os.path.dirname(urlel)
91 urlel = os.path.dirname(urlel)
98 return reversed(breadcrumb)
92 return reversed(breadcrumb)
99
93
100 class requestcontext(object):
94 class requestcontext(object):
101 """Holds state/context for an individual request.
95 """Holds state/context for an individual request.
102
96
103 Servers can be multi-threaded. Holding state on the WSGI application
97 Servers can be multi-threaded. Holding state on the WSGI application
104 is prone to race conditions. Instances of this class exist to hold
98 is prone to race conditions. Instances of this class exist to hold
105 mutable and race-free state for requests.
99 mutable and race-free state for requests.
106 """
100 """
107 def __init__(self, app, repo):
101 def __init__(self, app, repo):
108 self.repo = repo
102 self.repo = repo
109 self.reponame = app.reponame
103 self.reponame = app.reponame
110
104
111 self.archivespecs = archivespecs
105 self.archivespecs = archivespecs
112
106
113 self.maxchanges = self.configint('web', 'maxchanges')
107 self.maxchanges = self.configint('web', 'maxchanges')
114 self.stripecount = self.configint('web', 'stripes')
108 self.stripecount = self.configint('web', 'stripes')
115 self.maxshortchanges = self.configint('web', 'maxshortchanges')
109 self.maxshortchanges = self.configint('web', 'maxshortchanges')
116 self.maxfiles = self.configint('web', 'maxfiles')
110 self.maxfiles = self.configint('web', 'maxfiles')
117 self.allowpull = self.configbool('web', 'allow-pull')
111 self.allowpull = self.configbool('web', 'allow-pull')
118
112
119 # we use untrusted=False to prevent a repo owner from using
113 # we use untrusted=False to prevent a repo owner from using
120 # web.templates in .hg/hgrc to get access to any file readable
114 # web.templates in .hg/hgrc to get access to any file readable
121 # by the user running the CGI script
115 # by the user running the CGI script
122 self.templatepath = self.config('web', 'templates', untrusted=False)
116 self.templatepath = self.config('web', 'templates', untrusted=False)
123
117
124 # This object is more expensive to build than simple config values.
118 # This object is more expensive to build than simple config values.
125 # It is shared across requests. The app will replace the object
119 # It is shared across requests. The app will replace the object
126 # if it is updated. Since this is a reference and nothing should
120 # if it is updated. Since this is a reference and nothing should
127 # modify the underlying object, it should be constant for the lifetime
121 # modify the underlying object, it should be constant for the lifetime
128 # of the request.
122 # of the request.
129 self.websubtable = app.websubtable
123 self.websubtable = app.websubtable
130
124
131 self.csp, self.nonce = cspvalues(self.repo.ui)
125 self.csp, self.nonce = cspvalues(self.repo.ui)
132
126
133 # Trust the settings from the .hg/hgrc files by default.
127 # Trust the settings from the .hg/hgrc files by default.
134 def config(self, section, name, default=uimod._unset, untrusted=True):
128 def config(self, section, name, default=uimod._unset, untrusted=True):
135 return self.repo.ui.config(section, name, default,
129 return self.repo.ui.config(section, name, default,
136 untrusted=untrusted)
130 untrusted=untrusted)
137
131
138 def configbool(self, section, name, default=uimod._unset, untrusted=True):
132 def configbool(self, section, name, default=uimod._unset, untrusted=True):
139 return self.repo.ui.configbool(section, name, default,
133 return self.repo.ui.configbool(section, name, default,
140 untrusted=untrusted)
134 untrusted=untrusted)
141
135
142 def configint(self, section, name, default=uimod._unset, untrusted=True):
136 def configint(self, section, name, default=uimod._unset, untrusted=True):
143 return self.repo.ui.configint(section, name, default,
137 return self.repo.ui.configint(section, name, default,
144 untrusted=untrusted)
138 untrusted=untrusted)
145
139
146 def configlist(self, section, name, default=uimod._unset, untrusted=True):
140 def configlist(self, section, name, default=uimod._unset, untrusted=True):
147 return self.repo.ui.configlist(section, name, default,
141 return self.repo.ui.configlist(section, name, default,
148 untrusted=untrusted)
142 untrusted=untrusted)
149
143
150 def archivelist(self, nodeid):
144 def archivelist(self, nodeid):
151 allowed = self.configlist('web', 'allow_archive')
145 allowed = self.configlist('web', 'allow_archive')
152 for typ, spec in self.archivespecs.iteritems():
146 for typ, spec in self.archivespecs.iteritems():
153 if typ in allowed or self.configbool('web', 'allow%s' % typ):
147 if typ in allowed or self.configbool('web', 'allow%s' % typ):
154 yield {'type': typ, 'extension': spec[2], 'node': nodeid}
148 yield {'type': typ, 'extension': spec[2], 'node': nodeid}
155
149
156 def templater(self, req):
150 def templater(self, req):
157 # determine scheme, port and server name
151 # determine scheme, port and server name
158 # this is needed to create absolute urls
152 # this is needed to create absolute urls
159
153
160 proto = req.env.get('wsgi.url_scheme')
154 proto = req.env.get('wsgi.url_scheme')
161 if proto == 'https':
155 if proto == 'https':
162 proto = 'https'
156 proto = 'https'
163 default_port = '443'
157 default_port = '443'
164 else:
158 else:
165 proto = 'http'
159 proto = 'http'
166 default_port = '80'
160 default_port = '80'
167
161
168 port = req.env[r'SERVER_PORT']
162 port = req.env[r'SERVER_PORT']
169 port = port != default_port and (r':' + port) or r''
163 port = port != default_port and (r':' + port) or r''
170 urlbase = r'%s://%s%s' % (proto, req.env[r'SERVER_NAME'], port)
164 urlbase = r'%s://%s%s' % (proto, req.env[r'SERVER_NAME'], port)
171 logourl = self.config('web', 'logourl')
165 logourl = self.config('web', 'logourl')
172 logoimg = self.config('web', 'logoimg')
166 logoimg = self.config('web', 'logoimg')
173 staticurl = self.config('web', 'staticurl') or req.url + 'static/'
167 staticurl = self.config('web', 'staticurl') or req.url + 'static/'
174 if not staticurl.endswith('/'):
168 if not staticurl.endswith('/'):
175 staticurl += '/'
169 staticurl += '/'
176
170
177 # some functions for the templater
171 # some functions for the templater
178
172
179 def motd(**map):
173 def motd(**map):
180 yield self.config('web', 'motd')
174 yield self.config('web', 'motd')
181
175
182 # figure out which style to use
176 # figure out which style to use
183
177
184 vars = {}
178 vars = {}
185 styles, (style, mapfile) = getstyle(req, self.config,
179 styles, (style, mapfile) = getstyle(req, self.config,
186 self.templatepath)
180 self.templatepath)
187 if style == styles[0]:
181 if style == styles[0]:
188 vars['style'] = style
182 vars['style'] = style
189
183
190 start = '&' if req.url[-1] == r'?' else '?'
184 start = '&' if req.url[-1] == r'?' else '?'
191 sessionvars = webutil.sessionvars(vars, start)
185 sessionvars = webutil.sessionvars(vars, start)
192
186
193 if not self.reponame:
187 if not self.reponame:
194 self.reponame = (self.config('web', 'name', '')
188 self.reponame = (self.config('web', 'name', '')
195 or req.env.get('REPO_NAME')
189 or req.env.get('REPO_NAME')
196 or req.url.strip('/') or self.repo.root)
190 or req.url.strip('/') or self.repo.root)
197
191
198 def websubfilter(text):
192 def websubfilter(text):
199 return templatefilters.websub(text, self.websubtable)
193 return templatefilters.websub(text, self.websubtable)
200
194
201 # create the templater
195 # create the templater
202
196
203 defaults = {
197 defaults = {
204 'url': req.url,
198 'url': req.url,
205 'logourl': logourl,
199 'logourl': logourl,
206 'logoimg': logoimg,
200 'logoimg': logoimg,
207 'staticurl': staticurl,
201 'staticurl': staticurl,
208 'urlbase': urlbase,
202 'urlbase': urlbase,
209 'repo': self.reponame,
203 'repo': self.reponame,
210 'encoding': encoding.encoding,
204 'encoding': encoding.encoding,
211 'motd': motd,
205 'motd': motd,
212 'sessionvars': sessionvars,
206 'sessionvars': sessionvars,
213 'pathdef': makebreadcrumb(req.url),
207 'pathdef': makebreadcrumb(req.url),
214 'style': style,
208 'style': style,
215 'nonce': self.nonce,
209 'nonce': self.nonce,
216 }
210 }
217 tmpl = templater.templater.frommapfile(mapfile,
211 tmpl = templater.templater.frommapfile(mapfile,
218 filters={'websub': websubfilter},
212 filters={'websub': websubfilter},
219 defaults=defaults)
213 defaults=defaults)
220 return tmpl
214 return tmpl
221
215
222
216
223 class hgweb(object):
217 class hgweb(object):
224 """HTTP server for individual repositories.
218 """HTTP server for individual repositories.
225
219
226 Instances of this class serve HTTP responses for a particular
220 Instances of this class serve HTTP responses for a particular
227 repository.
221 repository.
228
222
229 Instances are typically used as WSGI applications.
223 Instances are typically used as WSGI applications.
230
224
231 Some servers are multi-threaded. On these servers, there may
225 Some servers are multi-threaded. On these servers, there may
232 be multiple active threads inside __call__.
226 be multiple active threads inside __call__.
233 """
227 """
234 def __init__(self, repo, name=None, baseui=None):
228 def __init__(self, repo, name=None, baseui=None):
235 if isinstance(repo, str):
229 if isinstance(repo, str):
236 if baseui:
230 if baseui:
237 u = baseui.copy()
231 u = baseui.copy()
238 else:
232 else:
239 u = uimod.ui.load()
233 u = uimod.ui.load()
240 r = hg.repository(u, repo)
234 r = hg.repository(u, repo)
241 else:
235 else:
242 # we trust caller to give us a private copy
236 # we trust caller to give us a private copy
243 r = repo
237 r = repo
244
238
245 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
239 r.ui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
246 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
240 r.baseui.setconfig('ui', 'report_untrusted', 'off', 'hgweb')
247 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
241 r.ui.setconfig('ui', 'nontty', 'true', 'hgweb')
248 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
242 r.baseui.setconfig('ui', 'nontty', 'true', 'hgweb')
249 # resolve file patterns relative to repo root
243 # resolve file patterns relative to repo root
250 r.ui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
244 r.ui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
251 r.baseui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
245 r.baseui.setconfig('ui', 'forcecwd', r.root, 'hgweb')
252 # displaying bundling progress bar while serving feel wrong and may
246 # displaying bundling progress bar while serving feel wrong and may
253 # break some wsgi implementation.
247 # break some wsgi implementation.
254 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
248 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
255 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
249 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
256 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))]
250 self._repos = [hg.cachedlocalrepo(self._webifyrepo(r))]
257 self._lastrepo = self._repos[0]
251 self._lastrepo = self._repos[0]
258 hook.redirect(True)
252 hook.redirect(True)
259 self.reponame = name
253 self.reponame = name
260
254
261 def _webifyrepo(self, repo):
255 def _webifyrepo(self, repo):
262 repo = getwebview(repo)
256 repo = getwebview(repo)
263 self.websubtable = webutil.getwebsubs(repo)
257 self.websubtable = webutil.getwebsubs(repo)
264 return repo
258 return repo
265
259
266 @contextlib.contextmanager
260 @contextlib.contextmanager
267 def _obtainrepo(self):
261 def _obtainrepo(self):
268 """Obtain a repo unique to the caller.
262 """Obtain a repo unique to the caller.
269
263
270 Internally we maintain a stack of cachedlocalrepo instances
264 Internally we maintain a stack of cachedlocalrepo instances
271 to be handed out. If one is available, we pop it and return it,
265 to be handed out. If one is available, we pop it and return it,
272 ensuring it is up to date in the process. If one is not available,
266 ensuring it is up to date in the process. If one is not available,
273 we clone the most recently used repo instance and return it.
267 we clone the most recently used repo instance and return it.
274
268
275 It is currently possible for the stack to grow without bounds
269 It is currently possible for the stack to grow without bounds
276 if the server allows infinite threads. However, servers should
270 if the server allows infinite threads. However, servers should
277 have a thread limit, thus establishing our limit.
271 have a thread limit, thus establishing our limit.
278 """
272 """
279 if self._repos:
273 if self._repos:
280 cached = self._repos.pop()
274 cached = self._repos.pop()
281 r, created = cached.fetch()
275 r, created = cached.fetch()
282 else:
276 else:
283 cached = self._lastrepo.copy()
277 cached = self._lastrepo.copy()
284 r, created = cached.fetch()
278 r, created = cached.fetch()
285 if created:
279 if created:
286 r = self._webifyrepo(r)
280 r = self._webifyrepo(r)
287
281
288 self._lastrepo = cached
282 self._lastrepo = cached
289 self.mtime = cached.mtime
283 self.mtime = cached.mtime
290 try:
284 try:
291 yield r
285 yield r
292 finally:
286 finally:
293 self._repos.append(cached)
287 self._repos.append(cached)
294
288
295 def run(self):
289 def run(self):
296 """Start a server from CGI environment.
290 """Start a server from CGI environment.
297
291
298 Modern servers should be using WSGI and should avoid this
292 Modern servers should be using WSGI and should avoid this
299 method, if possible.
293 method, if possible.
300 """
294 """
301 if not encoding.environ.get('GATEWAY_INTERFACE',
295 if not encoding.environ.get('GATEWAY_INTERFACE',
302 '').startswith("CGI/1."):
296 '').startswith("CGI/1."):
303 raise RuntimeError("This function is only intended to be "
297 raise RuntimeError("This function is only intended to be "
304 "called while running as a CGI script.")
298 "called while running as a CGI script.")
305 wsgicgi.launch(self)
299 wsgicgi.launch(self)
306
300
307 def __call__(self, env, respond):
301 def __call__(self, env, respond):
308 """Run the WSGI application.
302 """Run the WSGI application.
309
303
310 This may be called by multiple threads.
304 This may be called by multiple threads.
311 """
305 """
312 req = wsgirequest(env, respond)
306 req = wsgirequest(env, respond)
313 return self.run_wsgi(req)
307 return self.run_wsgi(req)
314
308
315 def run_wsgi(self, req):
309 def run_wsgi(self, req):
316 """Internal method to run the WSGI application.
310 """Internal method to run the WSGI application.
317
311
318 This is typically only called by Mercurial. External consumers
312 This is typically only called by Mercurial. External consumers
319 should be using instances of this class as the WSGI application.
313 should be using instances of this class as the WSGI application.
320 """
314 """
321 with self._obtainrepo() as repo:
315 with self._obtainrepo() as repo:
322 profile = repo.ui.configbool('profiling', 'enabled')
316 profile = repo.ui.configbool('profiling', 'enabled')
323 with profiling.profile(repo.ui, enabled=profile):
317 with profiling.profile(repo.ui, enabled=profile):
324 for r in self._runwsgi(req, repo):
318 for r in self._runwsgi(req, repo):
325 yield r
319 yield r
326
320
327 def _runwsgi(self, req, repo):
321 def _runwsgi(self, req, repo):
328 rctx = requestcontext(self, repo)
322 rctx = requestcontext(self, repo)
329
323
330 # This state is global across all threads.
324 # This state is global across all threads.
331 encoding.encoding = rctx.config('web', 'encoding')
325 encoding.encoding = rctx.config('web', 'encoding')
332 rctx.repo.ui.environ = req.env
326 rctx.repo.ui.environ = req.env
333
327
334 if rctx.csp:
328 if rctx.csp:
335 # hgwebdir may have added CSP header. Since we generate our own,
329 # hgwebdir may have added CSP header. Since we generate our own,
336 # replace it.
330 # replace it.
337 req.headers = [h for h in req.headers
331 req.headers = [h for h in req.headers
338 if h[0] != 'Content-Security-Policy']
332 if h[0] != 'Content-Security-Policy']
339 req.headers.append(('Content-Security-Policy', rctx.csp))
333 req.headers.append(('Content-Security-Policy', rctx.csp))
340
334
341 # work with CGI variables to create coherent structure
335 # work with CGI variables to create coherent structure
342 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
336 # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME
343
337
344 req.url = req.env[r'SCRIPT_NAME']
338 req.url = req.env[r'SCRIPT_NAME']
345 if not req.url.endswith('/'):
339 if not req.url.endswith('/'):
346 req.url += '/'
340 req.url += '/'
347 if req.env.get('REPO_NAME'):
341 if req.env.get('REPO_NAME'):
348 req.url += req.env[r'REPO_NAME'] + r'/'
342 req.url += req.env[r'REPO_NAME'] + r'/'
349
343
350 if r'PATH_INFO' in req.env:
344 if r'PATH_INFO' in req.env:
351 parts = req.env[r'PATH_INFO'].strip('/').split('/')
345 parts = req.env[r'PATH_INFO'].strip('/').split('/')
352 repo_parts = req.env.get(r'REPO_NAME', r'').split(r'/')
346 repo_parts = req.env.get(r'REPO_NAME', r'').split(r'/')
353 if parts[:len(repo_parts)] == repo_parts:
347 if parts[:len(repo_parts)] == repo_parts:
354 parts = parts[len(repo_parts):]
348 parts = parts[len(repo_parts):]
355 query = '/'.join(parts)
349 query = '/'.join(parts)
356 else:
350 else:
357 query = req.env[r'QUERY_STRING'].partition(r'&')[0]
351 query = req.env[r'QUERY_STRING'].partition(r'&')[0]
358 query = query.partition(r';')[0]
352 query = query.partition(r';')[0]
359
353
360 # process this if it's a protocol request
354 # process this if it's a protocol request
361 # protocol bits don't need to create any URLs
355 # protocol bits don't need to create any URLs
362 # and the clients always use the old URL structure
356 # and the clients always use the old URL structure
363
357
364 cmd = pycompat.sysbytes(req.form.get(r'cmd', [r''])[0])
358 cmd = pycompat.sysbytes(req.form.get(r'cmd', [r''])[0])
365 if protocol.iscmd(cmd):
359 if protocol.iscmd(cmd):
366 try:
360 try:
367 if query:
361 if query:
368 raise ErrorResponse(HTTP_NOT_FOUND)
362 raise ErrorResponse(HTTP_NOT_FOUND)
369 if cmd in perms:
363
370 self.check_perm(rctx, req, perms[cmd])
364 req.checkperm = lambda op: self.check_perm(rctx, req, op)
365 # Assume commands with no defined permissions are writes /
366 # for pushes. This is the safest from a security perspective
367 # because it doesn't allow commands with undefined semantics
368 # from bypassing permissions checks.
369 req.checkperm(perms.get(cmd, 'push'))
371 return protocol.call(rctx.repo, req, cmd)
370 return protocol.call(rctx.repo, req, cmd)
372 except ErrorResponse as inst:
371 except ErrorResponse as inst:
373 # A client that sends unbundle without 100-continue will
372 # A client that sends unbundle without 100-continue will
374 # break if we respond early.
373 # break if we respond early.
375 if (cmd == 'unbundle' and
374 if (cmd == 'unbundle' and
376 (req.env.get('HTTP_EXPECT',
375 (req.env.get('HTTP_EXPECT',
377 '').lower() != '100-continue') or
376 '').lower() != '100-continue') or
378 req.env.get('X-HgHttp2', '')):
377 req.env.get('X-HgHttp2', '')):
379 req.drain()
378 req.drain()
380 else:
379 else:
381 req.headers.append((r'Connection', r'Close'))
380 req.headers.append((r'Connection', r'Close'))
382 req.respond(inst, protocol.HGTYPE,
381 req.respond(inst, protocol.HGTYPE,
383 body='0\n%s\n' % inst)
382 body='0\n%s\n' % inst)
384 return ''
383 return ''
385
384
386 # translate user-visible url structure to internal structure
385 # translate user-visible url structure to internal structure
387
386
388 args = query.split('/', 2)
387 args = query.split('/', 2)
389 if r'cmd' not in req.form and args and args[0]:
388 if r'cmd' not in req.form and args and args[0]:
390 cmd = args.pop(0)
389 cmd = args.pop(0)
391 style = cmd.rfind('-')
390 style = cmd.rfind('-')
392 if style != -1:
391 if style != -1:
393 req.form['style'] = [cmd[:style]]
392 req.form['style'] = [cmd[:style]]
394 cmd = cmd[style + 1:]
393 cmd = cmd[style + 1:]
395
394
396 # avoid accepting e.g. style parameter as command
395 # avoid accepting e.g. style parameter as command
397 if util.safehasattr(webcommands, cmd):
396 if util.safehasattr(webcommands, cmd):
398 req.form[r'cmd'] = [cmd]
397 req.form[r'cmd'] = [cmd]
399
398
400 if cmd == 'static':
399 if cmd == 'static':
401 req.form['file'] = ['/'.join(args)]
400 req.form['file'] = ['/'.join(args)]
402 else:
401 else:
403 if args and args[0]:
402 if args and args[0]:
404 node = args.pop(0).replace('%2F', '/')
403 node = args.pop(0).replace('%2F', '/')
405 req.form['node'] = [node]
404 req.form['node'] = [node]
406 if args:
405 if args:
407 req.form['file'] = args
406 req.form['file'] = args
408
407
409 ua = req.env.get('HTTP_USER_AGENT', '')
408 ua = req.env.get('HTTP_USER_AGENT', '')
410 if cmd == 'rev' and 'mercurial' in ua:
409 if cmd == 'rev' and 'mercurial' in ua:
411 req.form['style'] = ['raw']
410 req.form['style'] = ['raw']
412
411
413 if cmd == 'archive':
412 if cmd == 'archive':
414 fn = req.form['node'][0]
413 fn = req.form['node'][0]
415 for type_, spec in rctx.archivespecs.iteritems():
414 for type_, spec in rctx.archivespecs.iteritems():
416 ext = spec[2]
415 ext = spec[2]
417 if fn.endswith(ext):
416 if fn.endswith(ext):
418 req.form['node'] = [fn[:-len(ext)]]
417 req.form['node'] = [fn[:-len(ext)]]
419 req.form['type'] = [type_]
418 req.form['type'] = [type_]
420
419
421 # process the web interface request
420 # process the web interface request
422
421
423 try:
422 try:
424 tmpl = rctx.templater(req)
423 tmpl = rctx.templater(req)
425 ctype = tmpl('mimetype', encoding=encoding.encoding)
424 ctype = tmpl('mimetype', encoding=encoding.encoding)
426 ctype = templater.stringify(ctype)
425 ctype = templater.stringify(ctype)
427
426
428 # check read permissions non-static content
427 # check read permissions non-static content
429 if cmd != 'static':
428 if cmd != 'static':
430 self.check_perm(rctx, req, None)
429 self.check_perm(rctx, req, None)
431
430
432 if cmd == '':
431 if cmd == '':
433 req.form[r'cmd'] = [tmpl.cache['default']]
432 req.form[r'cmd'] = [tmpl.cache['default']]
434 cmd = req.form[r'cmd'][0]
433 cmd = req.form[r'cmd'][0]
435
434
436 # Don't enable caching if using a CSP nonce because then it wouldn't
435 # Don't enable caching if using a CSP nonce because then it wouldn't
437 # be a nonce.
436 # be a nonce.
438 if rctx.configbool('web', 'cache') and not rctx.nonce:
437 if rctx.configbool('web', 'cache') and not rctx.nonce:
439 caching(self, req) # sets ETag header or raises NOT_MODIFIED
438 caching(self, req) # sets ETag header or raises NOT_MODIFIED
440 if cmd not in webcommands.__all__:
439 if cmd not in webcommands.__all__:
441 msg = 'no such method: %s' % cmd
440 msg = 'no such method: %s' % cmd
442 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
441 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
443 elif cmd == 'file' and r'raw' in req.form.get(r'style', []):
442 elif cmd == 'file' and r'raw' in req.form.get(r'style', []):
444 rctx.ctype = ctype
443 rctx.ctype = ctype
445 content = webcommands.rawfile(rctx, req, tmpl)
444 content = webcommands.rawfile(rctx, req, tmpl)
446 else:
445 else:
447 content = getattr(webcommands, cmd)(rctx, req, tmpl)
446 content = getattr(webcommands, cmd)(rctx, req, tmpl)
448 req.respond(HTTP_OK, ctype)
447 req.respond(HTTP_OK, ctype)
449
448
450 return content
449 return content
451
450
452 except (error.LookupError, error.RepoLookupError) as err:
451 except (error.LookupError, error.RepoLookupError) as err:
453 req.respond(HTTP_NOT_FOUND, ctype)
452 req.respond(HTTP_NOT_FOUND, ctype)
454 msg = str(err)
453 msg = str(err)
455 if (util.safehasattr(err, 'name') and
454 if (util.safehasattr(err, 'name') and
456 not isinstance(err, error.ManifestLookupError)):
455 not isinstance(err, error.ManifestLookupError)):
457 msg = 'revision not found: %s' % err.name
456 msg = 'revision not found: %s' % err.name
458 return tmpl('error', error=msg)
457 return tmpl('error', error=msg)
459 except (error.RepoError, error.RevlogError) as inst:
458 except (error.RepoError, error.RevlogError) as inst:
460 req.respond(HTTP_SERVER_ERROR, ctype)
459 req.respond(HTTP_SERVER_ERROR, ctype)
461 return tmpl('error', error=str(inst))
460 return tmpl('error', error=str(inst))
462 except ErrorResponse as inst:
461 except ErrorResponse as inst:
463 req.respond(inst, ctype)
462 req.respond(inst, ctype)
464 if inst.code == HTTP_NOT_MODIFIED:
463 if inst.code == HTTP_NOT_MODIFIED:
465 # Not allowed to return a body on a 304
464 # Not allowed to return a body on a 304
466 return ['']
465 return ['']
467 return tmpl('error', error=str(inst))
466 return tmpl('error', error=str(inst))
468
467
469 def check_perm(self, rctx, req, op):
468 def check_perm(self, rctx, req, op):
470 for permhook in permhooks:
469 for permhook in permhooks:
471 permhook(rctx, req, op)
470 permhook(rctx, req, op)
472
471
473 def getwebview(repo):
472 def getwebview(repo):
474 """The 'web.view' config controls changeset filter to hgweb. Possible
473 """The 'web.view' config controls changeset filter to hgweb. Possible
475 values are ``served``, ``visible`` and ``all``. Default is ``served``.
474 values are ``served``, ``visible`` and ``all``. Default is ``served``.
476 The ``served`` filter only shows changesets that can be pulled from the
475 The ``served`` filter only shows changesets that can be pulled from the
477 hgweb instance. The``visible`` filter includes secret changesets but
476 hgweb instance. The``visible`` filter includes secret changesets but
478 still excludes "hidden" one.
477 still excludes "hidden" one.
479
478
480 See the repoview module for details.
479 See the repoview module for details.
481
480
482 The option has been around undocumented since Mercurial 2.5, but no
481 The option has been around undocumented since Mercurial 2.5, but no
483 user ever asked about it. So we better keep it undocumented for now."""
482 user ever asked about it. So we better keep it undocumented for now."""
484 # experimental config: web.view
483 # experimental config: web.view
485 viewconfig = repo.ui.config('web', 'view', untrusted=True)
484 viewconfig = repo.ui.config('web', 'view', untrusted=True)
486 if viewconfig == 'all':
485 if viewconfig == 'all':
487 return repo.unfiltered()
486 return repo.unfiltered()
488 elif viewconfig in repoview.filtertable:
487 elif viewconfig in repoview.filtertable:
489 return repo.filtered(viewconfig)
488 return repo.filtered(viewconfig)
490 else:
489 else:
491 return repo.filtered('served')
490 return repo.filtered('served')
@@ -1,201 +1,202 b''
1 #
1 #
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
2 # Copyright 21 May 2005 - (c) 2005 Jake Edge <jake@edge2.net>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import cgi
10 import cgi
11 import struct
11 import struct
12
12
13 from .common import (
13 from .common import (
14 HTTP_OK,
14 HTTP_OK,
15 )
15 )
16
16
17 from .. import (
17 from .. import (
18 error,
18 error,
19 pycompat,
19 pycompat,
20 util,
20 util,
21 wireproto,
21 wireproto,
22 )
22 )
23 stringio = util.stringio
23 stringio = util.stringio
24
24
25 urlerr = util.urlerr
25 urlerr = util.urlerr
26 urlreq = util.urlreq
26 urlreq = util.urlreq
27
27
28 HGTYPE = 'application/mercurial-0.1'
28 HGTYPE = 'application/mercurial-0.1'
29 HGTYPE2 = 'application/mercurial-0.2'
29 HGTYPE2 = 'application/mercurial-0.2'
30 HGERRTYPE = 'application/hg-error'
30 HGERRTYPE = 'application/hg-error'
31
31
32 def decodevaluefromheaders(req, headerprefix):
32 def decodevaluefromheaders(req, headerprefix):
33 """Decode a long value from multiple HTTP request headers.
33 """Decode a long value from multiple HTTP request headers.
34
34
35 Returns the value as a bytes, not a str.
35 Returns the value as a bytes, not a str.
36 """
36 """
37 chunks = []
37 chunks = []
38 i = 1
38 i = 1
39 prefix = headerprefix.upper().replace(r'-', r'_')
39 prefix = headerprefix.upper().replace(r'-', r'_')
40 while True:
40 while True:
41 v = req.env.get(r'HTTP_%s_%d' % (prefix, i))
41 v = req.env.get(r'HTTP_%s_%d' % (prefix, i))
42 if v is None:
42 if v is None:
43 break
43 break
44 chunks.append(pycompat.bytesurl(v))
44 chunks.append(pycompat.bytesurl(v))
45 i += 1
45 i += 1
46
46
47 return ''.join(chunks)
47 return ''.join(chunks)
48
48
49 class webproto(wireproto.abstractserverproto):
49 class webproto(wireproto.abstractserverproto):
50 def __init__(self, req, ui):
50 def __init__(self, req, ui):
51 self.req = req
51 self.req = req
52 self.response = ''
52 self.response = ''
53 self.ui = ui
53 self.ui = ui
54 self.name = 'http'
54 self.name = 'http'
55 self.checkperm = req.checkperm
55
56
56 def getargs(self, args):
57 def getargs(self, args):
57 knownargs = self._args()
58 knownargs = self._args()
58 data = {}
59 data = {}
59 keys = args.split()
60 keys = args.split()
60 for k in keys:
61 for k in keys:
61 if k == '*':
62 if k == '*':
62 star = {}
63 star = {}
63 for key in knownargs.keys():
64 for key in knownargs.keys():
64 if key != 'cmd' and key not in keys:
65 if key != 'cmd' and key not in keys:
65 star[key] = knownargs[key][0]
66 star[key] = knownargs[key][0]
66 data['*'] = star
67 data['*'] = star
67 else:
68 else:
68 data[k] = knownargs[k][0]
69 data[k] = knownargs[k][0]
69 return [data[k] for k in keys]
70 return [data[k] for k in keys]
70 def _args(self):
71 def _args(self):
71 args = self.req.form.copy()
72 args = self.req.form.copy()
72 if pycompat.ispy3:
73 if pycompat.ispy3:
73 args = {k.encode('ascii'): [v.encode('ascii') for v in vs]
74 args = {k.encode('ascii'): [v.encode('ascii') for v in vs]
74 for k, vs in args.items()}
75 for k, vs in args.items()}
75 postlen = int(self.req.env.get(r'HTTP_X_HGARGS_POST', 0))
76 postlen = int(self.req.env.get(r'HTTP_X_HGARGS_POST', 0))
76 if postlen:
77 if postlen:
77 args.update(cgi.parse_qs(
78 args.update(cgi.parse_qs(
78 self.req.read(postlen), keep_blank_values=True))
79 self.req.read(postlen), keep_blank_values=True))
79 return args
80 return args
80
81
81 argvalue = decodevaluefromheaders(self.req, r'X-HgArg')
82 argvalue = decodevaluefromheaders(self.req, r'X-HgArg')
82 args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
83 args.update(cgi.parse_qs(argvalue, keep_blank_values=True))
83 return args
84 return args
84 def getfile(self, fp):
85 def getfile(self, fp):
85 length = int(self.req.env[r'CONTENT_LENGTH'])
86 length = int(self.req.env[r'CONTENT_LENGTH'])
86 # If httppostargs is used, we need to read Content-Length
87 # If httppostargs is used, we need to read Content-Length
87 # minus the amount that was consumed by args.
88 # minus the amount that was consumed by args.
88 length -= int(self.req.env.get(r'HTTP_X_HGARGS_POST', 0))
89 length -= int(self.req.env.get(r'HTTP_X_HGARGS_POST', 0))
89 for s in util.filechunkiter(self.req, limit=length):
90 for s in util.filechunkiter(self.req, limit=length):
90 fp.write(s)
91 fp.write(s)
91 def redirect(self):
92 def redirect(self):
92 self.oldio = self.ui.fout, self.ui.ferr
93 self.oldio = self.ui.fout, self.ui.ferr
93 self.ui.ferr = self.ui.fout = stringio()
94 self.ui.ferr = self.ui.fout = stringio()
94 def restore(self):
95 def restore(self):
95 val = self.ui.fout.getvalue()
96 val = self.ui.fout.getvalue()
96 self.ui.ferr, self.ui.fout = self.oldio
97 self.ui.ferr, self.ui.fout = self.oldio
97 return val
98 return val
98
99
99 def _client(self):
100 def _client(self):
100 return 'remote:%s:%s:%s' % (
101 return 'remote:%s:%s:%s' % (
101 self.req.env.get('wsgi.url_scheme') or 'http',
102 self.req.env.get('wsgi.url_scheme') or 'http',
102 urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
103 urlreq.quote(self.req.env.get('REMOTE_HOST', '')),
103 urlreq.quote(self.req.env.get('REMOTE_USER', '')))
104 urlreq.quote(self.req.env.get('REMOTE_USER', '')))
104
105
105 def responsetype(self, prefer_uncompressed):
106 def responsetype(self, prefer_uncompressed):
106 """Determine the appropriate response type and compression settings.
107 """Determine the appropriate response type and compression settings.
107
108
108 Returns a tuple of (mediatype, compengine, engineopts).
109 Returns a tuple of (mediatype, compengine, engineopts).
109 """
110 """
110 # Determine the response media type and compression engine based
111 # Determine the response media type and compression engine based
111 # on the request parameters.
112 # on the request parameters.
112 protocaps = decodevaluefromheaders(self.req, r'X-HgProto').split(' ')
113 protocaps = decodevaluefromheaders(self.req, r'X-HgProto').split(' ')
113
114
114 if '0.2' in protocaps:
115 if '0.2' in protocaps:
115 # All clients are expected to support uncompressed data.
116 # All clients are expected to support uncompressed data.
116 if prefer_uncompressed:
117 if prefer_uncompressed:
117 return HGTYPE2, util._noopengine(), {}
118 return HGTYPE2, util._noopengine(), {}
118
119
119 # Default as defined by wire protocol spec.
120 # Default as defined by wire protocol spec.
120 compformats = ['zlib', 'none']
121 compformats = ['zlib', 'none']
121 for cap in protocaps:
122 for cap in protocaps:
122 if cap.startswith('comp='):
123 if cap.startswith('comp='):
123 compformats = cap[5:].split(',')
124 compformats = cap[5:].split(',')
124 break
125 break
125
126
126 # Now find an agreed upon compression format.
127 # Now find an agreed upon compression format.
127 for engine in wireproto.supportedcompengines(self.ui, self,
128 for engine in wireproto.supportedcompengines(self.ui, self,
128 util.SERVERROLE):
129 util.SERVERROLE):
129 if engine.wireprotosupport().name in compformats:
130 if engine.wireprotosupport().name in compformats:
130 opts = {}
131 opts = {}
131 level = self.ui.configint('server',
132 level = self.ui.configint('server',
132 '%slevel' % engine.name())
133 '%slevel' % engine.name())
133 if level is not None:
134 if level is not None:
134 opts['level'] = level
135 opts['level'] = level
135
136
136 return HGTYPE2, engine, opts
137 return HGTYPE2, engine, opts
137
138
138 # No mutually supported compression format. Fall back to the
139 # No mutually supported compression format. Fall back to the
139 # legacy protocol.
140 # legacy protocol.
140
141
141 # Don't allow untrusted settings because disabling compression or
142 # Don't allow untrusted settings because disabling compression or
142 # setting a very high compression level could lead to flooding
143 # setting a very high compression level could lead to flooding
143 # the server's network or CPU.
144 # the server's network or CPU.
144 opts = {'level': self.ui.configint('server', 'zliblevel')}
145 opts = {'level': self.ui.configint('server', 'zliblevel')}
145 return HGTYPE, util.compengines['zlib'], opts
146 return HGTYPE, util.compengines['zlib'], opts
146
147
147 def iscmd(cmd):
148 def iscmd(cmd):
148 return cmd in wireproto.commands
149 return cmd in wireproto.commands
149
150
150 def call(repo, req, cmd):
151 def call(repo, req, cmd):
151 p = webproto(req, repo.ui)
152 p = webproto(req, repo.ui)
152
153
153 def genversion2(gen, engine, engineopts):
154 def genversion2(gen, engine, engineopts):
154 # application/mercurial-0.2 always sends a payload header
155 # application/mercurial-0.2 always sends a payload header
155 # identifying the compression engine.
156 # identifying the compression engine.
156 name = engine.wireprotosupport().name
157 name = engine.wireprotosupport().name
157 assert 0 < len(name) < 256
158 assert 0 < len(name) < 256
158 yield struct.pack('B', len(name))
159 yield struct.pack('B', len(name))
159 yield name
160 yield name
160
161
161 for chunk in gen:
162 for chunk in gen:
162 yield chunk
163 yield chunk
163
164
164 rsp = wireproto.dispatch(repo, p, cmd)
165 rsp = wireproto.dispatch(repo, p, cmd)
165 if isinstance(rsp, bytes):
166 if isinstance(rsp, bytes):
166 req.respond(HTTP_OK, HGTYPE, body=rsp)
167 req.respond(HTTP_OK, HGTYPE, body=rsp)
167 return []
168 return []
168 elif isinstance(rsp, wireproto.streamres_legacy):
169 elif isinstance(rsp, wireproto.streamres_legacy):
169 gen = rsp.gen
170 gen = rsp.gen
170 req.respond(HTTP_OK, HGTYPE)
171 req.respond(HTTP_OK, HGTYPE)
171 return gen
172 return gen
172 elif isinstance(rsp, wireproto.streamres):
173 elif isinstance(rsp, wireproto.streamres):
173 gen = rsp.gen
174 gen = rsp.gen
174
175
175 # This code for compression should not be streamres specific. It
176 # This code for compression should not be streamres specific. It
176 # is here because we only compress streamres at the moment.
177 # is here because we only compress streamres at the moment.
177 mediatype, engine, engineopts = p.responsetype(rsp.prefer_uncompressed)
178 mediatype, engine, engineopts = p.responsetype(rsp.prefer_uncompressed)
178 gen = engine.compressstream(gen, engineopts)
179 gen = engine.compressstream(gen, engineopts)
179
180
180 if mediatype == HGTYPE2:
181 if mediatype == HGTYPE2:
181 gen = genversion2(gen, engine, engineopts)
182 gen = genversion2(gen, engine, engineopts)
182
183
183 req.respond(HTTP_OK, mediatype)
184 req.respond(HTTP_OK, mediatype)
184 return gen
185 return gen
185 elif isinstance(rsp, wireproto.pushres):
186 elif isinstance(rsp, wireproto.pushres):
186 val = p.restore()
187 val = p.restore()
187 rsp = '%d\n%s' % (rsp.res, val)
188 rsp = '%d\n%s' % (rsp.res, val)
188 req.respond(HTTP_OK, HGTYPE, body=rsp)
189 req.respond(HTTP_OK, HGTYPE, body=rsp)
189 return []
190 return []
190 elif isinstance(rsp, wireproto.pusherr):
191 elif isinstance(rsp, wireproto.pusherr):
191 # drain the incoming bundle
192 # drain the incoming bundle
192 req.drain()
193 req.drain()
193 p.restore()
194 p.restore()
194 rsp = '0\n%s\n' % rsp.res
195 rsp = '0\n%s\n' % rsp.res
195 req.respond(HTTP_OK, HGTYPE, body=rsp)
196 req.respond(HTTP_OK, HGTYPE, body=rsp)
196 return []
197 return []
197 elif isinstance(rsp, wireproto.ooberror):
198 elif isinstance(rsp, wireproto.ooberror):
198 rsp = rsp.message
199 rsp = rsp.message
199 req.respond(HTTP_OK, HGERRTYPE, body=rsp)
200 req.respond(HTTP_OK, HGERRTYPE, body=rsp)
200 return []
201 return []
201 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
202 raise error.ProgrammingError('hgweb.protocol internal failure', rsp)
@@ -1,1076 +1,1111 b''
1 # wireproto.py - generic wire protocol support functions
1 # wireproto.py - generic wire protocol support functions
2 #
2 #
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import hashlib
10 import hashlib
11 import os
11 import os
12 import tempfile
12 import tempfile
13
13
14 from .i18n import _
14 from .i18n import _
15 from .node import (
15 from .node import (
16 bin,
16 bin,
17 hex,
17 hex,
18 nullid,
18 nullid,
19 )
19 )
20
20
21 from . import (
21 from . import (
22 bundle2,
22 bundle2,
23 changegroup as changegroupmod,
23 changegroup as changegroupmod,
24 discovery,
24 discovery,
25 encoding,
25 encoding,
26 error,
26 error,
27 exchange,
27 exchange,
28 peer,
28 peer,
29 pushkey as pushkeymod,
29 pushkey as pushkeymod,
30 pycompat,
30 pycompat,
31 repository,
31 repository,
32 streamclone,
32 streamclone,
33 util,
33 util,
34 )
34 )
35
35
36 urlerr = util.urlerr
36 urlerr = util.urlerr
37 urlreq = util.urlreq
37 urlreq = util.urlreq
38
38
39 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
39 bundle2requiredmain = _('incompatible Mercurial client; bundle2 required')
40 bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
40 bundle2requiredhint = _('see https://www.mercurial-scm.org/wiki/'
41 'IncompatibleClient')
41 'IncompatibleClient')
42 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
42 bundle2required = '%s\n(%s)\n' % (bundle2requiredmain, bundle2requiredhint)
43
43
44 class abstractserverproto(object):
44 class abstractserverproto(object):
45 """abstract class that summarizes the protocol API
45 """abstract class that summarizes the protocol API
46
46
47 Used as reference and documentation.
47 Used as reference and documentation.
48 """
48 """
49
49
50 def getargs(self, args):
50 def getargs(self, args):
51 """return the value for arguments in <args>
51 """return the value for arguments in <args>
52
52
53 returns a list of values (same order as <args>)"""
53 returns a list of values (same order as <args>)"""
54 raise NotImplementedError()
54 raise NotImplementedError()
55
55
56 def getfile(self, fp):
56 def getfile(self, fp):
57 """write the whole content of a file into a file like object
57 """write the whole content of a file into a file like object
58
58
59 The file is in the form::
59 The file is in the form::
60
60
61 (<chunk-size>\n<chunk>)+0\n
61 (<chunk-size>\n<chunk>)+0\n
62
62
63 chunk size is the ascii version of the int.
63 chunk size is the ascii version of the int.
64 """
64 """
65 raise NotImplementedError()
65 raise NotImplementedError()
66
66
67 def redirect(self):
67 def redirect(self):
68 """may setup interception for stdout and stderr
68 """may setup interception for stdout and stderr
69
69
70 See also the `restore` method."""
70 See also the `restore` method."""
71 raise NotImplementedError()
71 raise NotImplementedError()
72
72
73 # If the `redirect` function does install interception, the `restore`
73 # If the `redirect` function does install interception, the `restore`
74 # function MUST be defined. If interception is not used, this function
74 # function MUST be defined. If interception is not used, this function
75 # MUST NOT be defined.
75 # MUST NOT be defined.
76 #
76 #
77 # left commented here on purpose
77 # left commented here on purpose
78 #
78 #
79 #def restore(self):
79 #def restore(self):
80 # """reinstall previous stdout and stderr and return intercepted stdout
80 # """reinstall previous stdout and stderr and return intercepted stdout
81 # """
81 # """
82 # raise NotImplementedError()
82 # raise NotImplementedError()
83
83
84 class remoteiterbatcher(peer.iterbatcher):
84 class remoteiterbatcher(peer.iterbatcher):
85 def __init__(self, remote):
85 def __init__(self, remote):
86 super(remoteiterbatcher, self).__init__()
86 super(remoteiterbatcher, self).__init__()
87 self._remote = remote
87 self._remote = remote
88
88
89 def __getattr__(self, name):
89 def __getattr__(self, name):
90 # Validate this method is batchable, since submit() only supports
90 # Validate this method is batchable, since submit() only supports
91 # batchable methods.
91 # batchable methods.
92 fn = getattr(self._remote, name)
92 fn = getattr(self._remote, name)
93 if not getattr(fn, 'batchable', None):
93 if not getattr(fn, 'batchable', None):
94 raise error.ProgrammingError('Attempted to batch a non-batchable '
94 raise error.ProgrammingError('Attempted to batch a non-batchable '
95 'call to %r' % name)
95 'call to %r' % name)
96
96
97 return super(remoteiterbatcher, self).__getattr__(name)
97 return super(remoteiterbatcher, self).__getattr__(name)
98
98
99 def submit(self):
99 def submit(self):
100 """Break the batch request into many patch calls and pipeline them.
100 """Break the batch request into many patch calls and pipeline them.
101
101
102 This is mostly valuable over http where request sizes can be
102 This is mostly valuable over http where request sizes can be
103 limited, but can be used in other places as well.
103 limited, but can be used in other places as well.
104 """
104 """
105 # 2-tuple of (command, arguments) that represents what will be
105 # 2-tuple of (command, arguments) that represents what will be
106 # sent over the wire.
106 # sent over the wire.
107 requests = []
107 requests = []
108
108
109 # 4-tuple of (command, final future, @batchable generator, remote
109 # 4-tuple of (command, final future, @batchable generator, remote
110 # future).
110 # future).
111 results = []
111 results = []
112
112
113 for command, args, opts, finalfuture in self.calls:
113 for command, args, opts, finalfuture in self.calls:
114 mtd = getattr(self._remote, command)
114 mtd = getattr(self._remote, command)
115 batchable = mtd.batchable(mtd.__self__, *args, **opts)
115 batchable = mtd.batchable(mtd.__self__, *args, **opts)
116
116
117 commandargs, fremote = next(batchable)
117 commandargs, fremote = next(batchable)
118 assert fremote
118 assert fremote
119 requests.append((command, commandargs))
119 requests.append((command, commandargs))
120 results.append((command, finalfuture, batchable, fremote))
120 results.append((command, finalfuture, batchable, fremote))
121
121
122 if requests:
122 if requests:
123 self._resultiter = self._remote._submitbatch(requests)
123 self._resultiter = self._remote._submitbatch(requests)
124
124
125 self._results = results
125 self._results = results
126
126
127 def results(self):
127 def results(self):
128 for command, finalfuture, batchable, remotefuture in self._results:
128 for command, finalfuture, batchable, remotefuture in self._results:
129 # Get the raw result, set it in the remote future, feed it
129 # Get the raw result, set it in the remote future, feed it
130 # back into the @batchable generator so it can be decoded, and
130 # back into the @batchable generator so it can be decoded, and
131 # set the result on the final future to this value.
131 # set the result on the final future to this value.
132 remoteresult = next(self._resultiter)
132 remoteresult = next(self._resultiter)
133 remotefuture.set(remoteresult)
133 remotefuture.set(remoteresult)
134 finalfuture.set(next(batchable))
134 finalfuture.set(next(batchable))
135
135
136 # Verify our @batchable generators only emit 2 values.
136 # Verify our @batchable generators only emit 2 values.
137 try:
137 try:
138 next(batchable)
138 next(batchable)
139 except StopIteration:
139 except StopIteration:
140 pass
140 pass
141 else:
141 else:
142 raise error.ProgrammingError('%s @batchable generator emitted '
142 raise error.ProgrammingError('%s @batchable generator emitted '
143 'unexpected value count' % command)
143 'unexpected value count' % command)
144
144
145 yield finalfuture.value
145 yield finalfuture.value
146
146
147 # Forward a couple of names from peer to make wireproto interactions
147 # Forward a couple of names from peer to make wireproto interactions
148 # slightly more sensible.
148 # slightly more sensible.
149 batchable = peer.batchable
149 batchable = peer.batchable
150 future = peer.future
150 future = peer.future
151
151
152 # list of nodes encoding / decoding
152 # list of nodes encoding / decoding
153
153
154 def decodelist(l, sep=' '):
154 def decodelist(l, sep=' '):
155 if l:
155 if l:
156 return [bin(v) for v in l.split(sep)]
156 return [bin(v) for v in l.split(sep)]
157 return []
157 return []
158
158
159 def encodelist(l, sep=' '):
159 def encodelist(l, sep=' '):
160 try:
160 try:
161 return sep.join(map(hex, l))
161 return sep.join(map(hex, l))
162 except TypeError:
162 except TypeError:
163 raise
163 raise
164
164
165 # batched call argument encoding
165 # batched call argument encoding
166
166
167 def escapearg(plain):
167 def escapearg(plain):
168 return (plain
168 return (plain
169 .replace(':', ':c')
169 .replace(':', ':c')
170 .replace(',', ':o')
170 .replace(',', ':o')
171 .replace(';', ':s')
171 .replace(';', ':s')
172 .replace('=', ':e'))
172 .replace('=', ':e'))
173
173
174 def unescapearg(escaped):
174 def unescapearg(escaped):
175 return (escaped
175 return (escaped
176 .replace(':e', '=')
176 .replace(':e', '=')
177 .replace(':s', ';')
177 .replace(':s', ';')
178 .replace(':o', ',')
178 .replace(':o', ',')
179 .replace(':c', ':'))
179 .replace(':c', ':'))
180
180
181 def encodebatchcmds(req):
181 def encodebatchcmds(req):
182 """Return a ``cmds`` argument value for the ``batch`` command."""
182 """Return a ``cmds`` argument value for the ``batch`` command."""
183 cmds = []
183 cmds = []
184 for op, argsdict in req:
184 for op, argsdict in req:
185 # Old servers didn't properly unescape argument names. So prevent
185 # Old servers didn't properly unescape argument names. So prevent
186 # the sending of argument names that may not be decoded properly by
186 # the sending of argument names that may not be decoded properly by
187 # servers.
187 # servers.
188 assert all(escapearg(k) == k for k in argsdict)
188 assert all(escapearg(k) == k for k in argsdict)
189
189
190 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
190 args = ','.join('%s=%s' % (escapearg(k), escapearg(v))
191 for k, v in argsdict.iteritems())
191 for k, v in argsdict.iteritems())
192 cmds.append('%s %s' % (op, args))
192 cmds.append('%s %s' % (op, args))
193
193
194 return ';'.join(cmds)
194 return ';'.join(cmds)
195
195
196 # mapping of options accepted by getbundle and their types
196 # mapping of options accepted by getbundle and their types
197 #
197 #
198 # Meant to be extended by extensions. It is extensions responsibility to ensure
198 # Meant to be extended by extensions. It is extensions responsibility to ensure
199 # such options are properly processed in exchange.getbundle.
199 # such options are properly processed in exchange.getbundle.
200 #
200 #
201 # supported types are:
201 # supported types are:
202 #
202 #
203 # :nodes: list of binary nodes
203 # :nodes: list of binary nodes
204 # :csv: list of comma-separated values
204 # :csv: list of comma-separated values
205 # :scsv: list of comma-separated values return as set
205 # :scsv: list of comma-separated values return as set
206 # :plain: string with no transformation needed.
206 # :plain: string with no transformation needed.
207 gboptsmap = {'heads': 'nodes',
207 gboptsmap = {'heads': 'nodes',
208 'bookmarks': 'boolean',
208 'bookmarks': 'boolean',
209 'common': 'nodes',
209 'common': 'nodes',
210 'obsmarkers': 'boolean',
210 'obsmarkers': 'boolean',
211 'phases': 'boolean',
211 'phases': 'boolean',
212 'bundlecaps': 'scsv',
212 'bundlecaps': 'scsv',
213 'listkeys': 'csv',
213 'listkeys': 'csv',
214 'cg': 'boolean',
214 'cg': 'boolean',
215 'cbattempted': 'boolean',
215 'cbattempted': 'boolean',
216 'stream': 'boolean',
216 'stream': 'boolean',
217 }
217 }
218
218
219 # client side
219 # client side
220
220
221 class wirepeer(repository.legacypeer):
221 class wirepeer(repository.legacypeer):
222 """Client-side interface for communicating with a peer repository.
222 """Client-side interface for communicating with a peer repository.
223
223
224 Methods commonly call wire protocol commands of the same name.
224 Methods commonly call wire protocol commands of the same name.
225
225
226 See also httppeer.py and sshpeer.py for protocol-specific
226 See also httppeer.py and sshpeer.py for protocol-specific
227 implementations of this interface.
227 implementations of this interface.
228 """
228 """
229 # Begin of basewirepeer interface.
229 # Begin of basewirepeer interface.
230
230
231 def iterbatch(self):
231 def iterbatch(self):
232 return remoteiterbatcher(self)
232 return remoteiterbatcher(self)
233
233
234 @batchable
234 @batchable
235 def lookup(self, key):
235 def lookup(self, key):
236 self.requirecap('lookup', _('look up remote revision'))
236 self.requirecap('lookup', _('look up remote revision'))
237 f = future()
237 f = future()
238 yield {'key': encoding.fromlocal(key)}, f
238 yield {'key': encoding.fromlocal(key)}, f
239 d = f.value
239 d = f.value
240 success, data = d[:-1].split(" ", 1)
240 success, data = d[:-1].split(" ", 1)
241 if int(success):
241 if int(success):
242 yield bin(data)
242 yield bin(data)
243 else:
243 else:
244 self._abort(error.RepoError(data))
244 self._abort(error.RepoError(data))
245
245
246 @batchable
246 @batchable
247 def heads(self):
247 def heads(self):
248 f = future()
248 f = future()
249 yield {}, f
249 yield {}, f
250 d = f.value
250 d = f.value
251 try:
251 try:
252 yield decodelist(d[:-1])
252 yield decodelist(d[:-1])
253 except ValueError:
253 except ValueError:
254 self._abort(error.ResponseError(_("unexpected response:"), d))
254 self._abort(error.ResponseError(_("unexpected response:"), d))
255
255
256 @batchable
256 @batchable
257 def known(self, nodes):
257 def known(self, nodes):
258 f = future()
258 f = future()
259 yield {'nodes': encodelist(nodes)}, f
259 yield {'nodes': encodelist(nodes)}, f
260 d = f.value
260 d = f.value
261 try:
261 try:
262 yield [bool(int(b)) for b in d]
262 yield [bool(int(b)) for b in d]
263 except ValueError:
263 except ValueError:
264 self._abort(error.ResponseError(_("unexpected response:"), d))
264 self._abort(error.ResponseError(_("unexpected response:"), d))
265
265
266 @batchable
266 @batchable
267 def branchmap(self):
267 def branchmap(self):
268 f = future()
268 f = future()
269 yield {}, f
269 yield {}, f
270 d = f.value
270 d = f.value
271 try:
271 try:
272 branchmap = {}
272 branchmap = {}
273 for branchpart in d.splitlines():
273 for branchpart in d.splitlines():
274 branchname, branchheads = branchpart.split(' ', 1)
274 branchname, branchheads = branchpart.split(' ', 1)
275 branchname = encoding.tolocal(urlreq.unquote(branchname))
275 branchname = encoding.tolocal(urlreq.unquote(branchname))
276 branchheads = decodelist(branchheads)
276 branchheads = decodelist(branchheads)
277 branchmap[branchname] = branchheads
277 branchmap[branchname] = branchheads
278 yield branchmap
278 yield branchmap
279 except TypeError:
279 except TypeError:
280 self._abort(error.ResponseError(_("unexpected response:"), d))
280 self._abort(error.ResponseError(_("unexpected response:"), d))
281
281
282 @batchable
282 @batchable
283 def listkeys(self, namespace):
283 def listkeys(self, namespace):
284 if not self.capable('pushkey'):
284 if not self.capable('pushkey'):
285 yield {}, None
285 yield {}, None
286 f = future()
286 f = future()
287 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
287 self.ui.debug('preparing listkeys for "%s"\n' % namespace)
288 yield {'namespace': encoding.fromlocal(namespace)}, f
288 yield {'namespace': encoding.fromlocal(namespace)}, f
289 d = f.value
289 d = f.value
290 self.ui.debug('received listkey for "%s": %i bytes\n'
290 self.ui.debug('received listkey for "%s": %i bytes\n'
291 % (namespace, len(d)))
291 % (namespace, len(d)))
292 yield pushkeymod.decodekeys(d)
292 yield pushkeymod.decodekeys(d)
293
293
294 @batchable
294 @batchable
295 def pushkey(self, namespace, key, old, new):
295 def pushkey(self, namespace, key, old, new):
296 if not self.capable('pushkey'):
296 if not self.capable('pushkey'):
297 yield False, None
297 yield False, None
298 f = future()
298 f = future()
299 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
299 self.ui.debug('preparing pushkey for "%s:%s"\n' % (namespace, key))
300 yield {'namespace': encoding.fromlocal(namespace),
300 yield {'namespace': encoding.fromlocal(namespace),
301 'key': encoding.fromlocal(key),
301 'key': encoding.fromlocal(key),
302 'old': encoding.fromlocal(old),
302 'old': encoding.fromlocal(old),
303 'new': encoding.fromlocal(new)}, f
303 'new': encoding.fromlocal(new)}, f
304 d = f.value
304 d = f.value
305 d, output = d.split('\n', 1)
305 d, output = d.split('\n', 1)
306 try:
306 try:
307 d = bool(int(d))
307 d = bool(int(d))
308 except ValueError:
308 except ValueError:
309 raise error.ResponseError(
309 raise error.ResponseError(
310 _('push failed (unexpected response):'), d)
310 _('push failed (unexpected response):'), d)
311 for l in output.splitlines(True):
311 for l in output.splitlines(True):
312 self.ui.status(_('remote: '), l)
312 self.ui.status(_('remote: '), l)
313 yield d
313 yield d
314
314
315 def stream_out(self):
315 def stream_out(self):
316 return self._callstream('stream_out')
316 return self._callstream('stream_out')
317
317
318 def getbundle(self, source, **kwargs):
318 def getbundle(self, source, **kwargs):
319 kwargs = pycompat.byteskwargs(kwargs)
319 kwargs = pycompat.byteskwargs(kwargs)
320 self.requirecap('getbundle', _('look up remote changes'))
320 self.requirecap('getbundle', _('look up remote changes'))
321 opts = {}
321 opts = {}
322 bundlecaps = kwargs.get('bundlecaps')
322 bundlecaps = kwargs.get('bundlecaps')
323 if bundlecaps is not None:
323 if bundlecaps is not None:
324 kwargs['bundlecaps'] = sorted(bundlecaps)
324 kwargs['bundlecaps'] = sorted(bundlecaps)
325 else:
325 else:
326 bundlecaps = () # kwargs could have it to None
326 bundlecaps = () # kwargs could have it to None
327 for key, value in kwargs.iteritems():
327 for key, value in kwargs.iteritems():
328 if value is None:
328 if value is None:
329 continue
329 continue
330 keytype = gboptsmap.get(key)
330 keytype = gboptsmap.get(key)
331 if keytype is None:
331 if keytype is None:
332 raise error.ProgrammingError(
332 raise error.ProgrammingError(
333 'Unexpectedly None keytype for key %s' % key)
333 'Unexpectedly None keytype for key %s' % key)
334 elif keytype == 'nodes':
334 elif keytype == 'nodes':
335 value = encodelist(value)
335 value = encodelist(value)
336 elif keytype in ('csv', 'scsv'):
336 elif keytype in ('csv', 'scsv'):
337 value = ','.join(value)
337 value = ','.join(value)
338 elif keytype == 'boolean':
338 elif keytype == 'boolean':
339 value = '%i' % bool(value)
339 value = '%i' % bool(value)
340 elif keytype != 'plain':
340 elif keytype != 'plain':
341 raise KeyError('unknown getbundle option type %s'
341 raise KeyError('unknown getbundle option type %s'
342 % keytype)
342 % keytype)
343 opts[key] = value
343 opts[key] = value
344 f = self._callcompressable("getbundle", **pycompat.strkwargs(opts))
344 f = self._callcompressable("getbundle", **pycompat.strkwargs(opts))
345 if any((cap.startswith('HG2') for cap in bundlecaps)):
345 if any((cap.startswith('HG2') for cap in bundlecaps)):
346 return bundle2.getunbundler(self.ui, f)
346 return bundle2.getunbundler(self.ui, f)
347 else:
347 else:
348 return changegroupmod.cg1unpacker(f, 'UN')
348 return changegroupmod.cg1unpacker(f, 'UN')
349
349
350 def unbundle(self, cg, heads, url):
350 def unbundle(self, cg, heads, url):
351 '''Send cg (a readable file-like object representing the
351 '''Send cg (a readable file-like object representing the
352 changegroup to push, typically a chunkbuffer object) to the
352 changegroup to push, typically a chunkbuffer object) to the
353 remote server as a bundle.
353 remote server as a bundle.
354
354
355 When pushing a bundle10 stream, return an integer indicating the
355 When pushing a bundle10 stream, return an integer indicating the
356 result of the push (see changegroup.apply()).
356 result of the push (see changegroup.apply()).
357
357
358 When pushing a bundle20 stream, return a bundle20 stream.
358 When pushing a bundle20 stream, return a bundle20 stream.
359
359
360 `url` is the url the client thinks it's pushing to, which is
360 `url` is the url the client thinks it's pushing to, which is
361 visible to hooks.
361 visible to hooks.
362 '''
362 '''
363
363
364 if heads != ['force'] and self.capable('unbundlehash'):
364 if heads != ['force'] and self.capable('unbundlehash'):
365 heads = encodelist(['hashed',
365 heads = encodelist(['hashed',
366 hashlib.sha1(''.join(sorted(heads))).digest()])
366 hashlib.sha1(''.join(sorted(heads))).digest()])
367 else:
367 else:
368 heads = encodelist(heads)
368 heads = encodelist(heads)
369
369
370 if util.safehasattr(cg, 'deltaheader'):
370 if util.safehasattr(cg, 'deltaheader'):
371 # this a bundle10, do the old style call sequence
371 # this a bundle10, do the old style call sequence
372 ret, output = self._callpush("unbundle", cg, heads=heads)
372 ret, output = self._callpush("unbundle", cg, heads=heads)
373 if ret == "":
373 if ret == "":
374 raise error.ResponseError(
374 raise error.ResponseError(
375 _('push failed:'), output)
375 _('push failed:'), output)
376 try:
376 try:
377 ret = int(ret)
377 ret = int(ret)
378 except ValueError:
378 except ValueError:
379 raise error.ResponseError(
379 raise error.ResponseError(
380 _('push failed (unexpected response):'), ret)
380 _('push failed (unexpected response):'), ret)
381
381
382 for l in output.splitlines(True):
382 for l in output.splitlines(True):
383 self.ui.status(_('remote: '), l)
383 self.ui.status(_('remote: '), l)
384 else:
384 else:
385 # bundle2 push. Send a stream, fetch a stream.
385 # bundle2 push. Send a stream, fetch a stream.
386 stream = self._calltwowaystream('unbundle', cg, heads=heads)
386 stream = self._calltwowaystream('unbundle', cg, heads=heads)
387 ret = bundle2.getunbundler(self.ui, stream)
387 ret = bundle2.getunbundler(self.ui, stream)
388 return ret
388 return ret
389
389
390 # End of basewirepeer interface.
390 # End of basewirepeer interface.
391
391
392 # Begin of baselegacywirepeer interface.
392 # Begin of baselegacywirepeer interface.
393
393
394 def branches(self, nodes):
394 def branches(self, nodes):
395 n = encodelist(nodes)
395 n = encodelist(nodes)
396 d = self._call("branches", nodes=n)
396 d = self._call("branches", nodes=n)
397 try:
397 try:
398 br = [tuple(decodelist(b)) for b in d.splitlines()]
398 br = [tuple(decodelist(b)) for b in d.splitlines()]
399 return br
399 return br
400 except ValueError:
400 except ValueError:
401 self._abort(error.ResponseError(_("unexpected response:"), d))
401 self._abort(error.ResponseError(_("unexpected response:"), d))
402
402
403 def between(self, pairs):
403 def between(self, pairs):
404 batch = 8 # avoid giant requests
404 batch = 8 # avoid giant requests
405 r = []
405 r = []
406 for i in xrange(0, len(pairs), batch):
406 for i in xrange(0, len(pairs), batch):
407 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
407 n = " ".join([encodelist(p, '-') for p in pairs[i:i + batch]])
408 d = self._call("between", pairs=n)
408 d = self._call("between", pairs=n)
409 try:
409 try:
410 r.extend(l and decodelist(l) or [] for l in d.splitlines())
410 r.extend(l and decodelist(l) or [] for l in d.splitlines())
411 except ValueError:
411 except ValueError:
412 self._abort(error.ResponseError(_("unexpected response:"), d))
412 self._abort(error.ResponseError(_("unexpected response:"), d))
413 return r
413 return r
414
414
415 def changegroup(self, nodes, kind):
415 def changegroup(self, nodes, kind):
416 n = encodelist(nodes)
416 n = encodelist(nodes)
417 f = self._callcompressable("changegroup", roots=n)
417 f = self._callcompressable("changegroup", roots=n)
418 return changegroupmod.cg1unpacker(f, 'UN')
418 return changegroupmod.cg1unpacker(f, 'UN')
419
419
420 def changegroupsubset(self, bases, heads, kind):
420 def changegroupsubset(self, bases, heads, kind):
421 self.requirecap('changegroupsubset', _('look up remote changes'))
421 self.requirecap('changegroupsubset', _('look up remote changes'))
422 bases = encodelist(bases)
422 bases = encodelist(bases)
423 heads = encodelist(heads)
423 heads = encodelist(heads)
424 f = self._callcompressable("changegroupsubset",
424 f = self._callcompressable("changegroupsubset",
425 bases=bases, heads=heads)
425 bases=bases, heads=heads)
426 return changegroupmod.cg1unpacker(f, 'UN')
426 return changegroupmod.cg1unpacker(f, 'UN')
427
427
428 # End of baselegacywirepeer interface.
428 # End of baselegacywirepeer interface.
429
429
430 def _submitbatch(self, req):
430 def _submitbatch(self, req):
431 """run batch request <req> on the server
431 """run batch request <req> on the server
432
432
433 Returns an iterator of the raw responses from the server.
433 Returns an iterator of the raw responses from the server.
434 """
434 """
435 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
435 rsp = self._callstream("batch", cmds=encodebatchcmds(req))
436 chunk = rsp.read(1024)
436 chunk = rsp.read(1024)
437 work = [chunk]
437 work = [chunk]
438 while chunk:
438 while chunk:
439 while ';' not in chunk and chunk:
439 while ';' not in chunk and chunk:
440 chunk = rsp.read(1024)
440 chunk = rsp.read(1024)
441 work.append(chunk)
441 work.append(chunk)
442 merged = ''.join(work)
442 merged = ''.join(work)
443 while ';' in merged:
443 while ';' in merged:
444 one, merged = merged.split(';', 1)
444 one, merged = merged.split(';', 1)
445 yield unescapearg(one)
445 yield unescapearg(one)
446 chunk = rsp.read(1024)
446 chunk = rsp.read(1024)
447 work = [merged, chunk]
447 work = [merged, chunk]
448 yield unescapearg(''.join(work))
448 yield unescapearg(''.join(work))
449
449
450 def _submitone(self, op, args):
450 def _submitone(self, op, args):
451 return self._call(op, **pycompat.strkwargs(args))
451 return self._call(op, **pycompat.strkwargs(args))
452
452
453 def debugwireargs(self, one, two, three=None, four=None, five=None):
453 def debugwireargs(self, one, two, three=None, four=None, five=None):
454 # don't pass optional arguments left at their default value
454 # don't pass optional arguments left at their default value
455 opts = {}
455 opts = {}
456 if three is not None:
456 if three is not None:
457 opts[r'three'] = three
457 opts[r'three'] = three
458 if four is not None:
458 if four is not None:
459 opts[r'four'] = four
459 opts[r'four'] = four
460 return self._call('debugwireargs', one=one, two=two, **opts)
460 return self._call('debugwireargs', one=one, two=two, **opts)
461
461
462 def _call(self, cmd, **args):
462 def _call(self, cmd, **args):
463 """execute <cmd> on the server
463 """execute <cmd> on the server
464
464
465 The command is expected to return a simple string.
465 The command is expected to return a simple string.
466
466
467 returns the server reply as a string."""
467 returns the server reply as a string."""
468 raise NotImplementedError()
468 raise NotImplementedError()
469
469
470 def _callstream(self, cmd, **args):
470 def _callstream(self, cmd, **args):
471 """execute <cmd> on the server
471 """execute <cmd> on the server
472
472
473 The command is expected to return a stream. Note that if the
473 The command is expected to return a stream. Note that if the
474 command doesn't return a stream, _callstream behaves
474 command doesn't return a stream, _callstream behaves
475 differently for ssh and http peers.
475 differently for ssh and http peers.
476
476
477 returns the server reply as a file like object.
477 returns the server reply as a file like object.
478 """
478 """
479 raise NotImplementedError()
479 raise NotImplementedError()
480
480
481 def _callcompressable(self, cmd, **args):
481 def _callcompressable(self, cmd, **args):
482 """execute <cmd> on the server
482 """execute <cmd> on the server
483
483
484 The command is expected to return a stream.
484 The command is expected to return a stream.
485
485
486 The stream may have been compressed in some implementations. This
486 The stream may have been compressed in some implementations. This
487 function takes care of the decompression. This is the only difference
487 function takes care of the decompression. This is the only difference
488 with _callstream.
488 with _callstream.
489
489
490 returns the server reply as a file like object.
490 returns the server reply as a file like object.
491 """
491 """
492 raise NotImplementedError()
492 raise NotImplementedError()
493
493
494 def _callpush(self, cmd, fp, **args):
494 def _callpush(self, cmd, fp, **args):
495 """execute a <cmd> on server
495 """execute a <cmd> on server
496
496
497 The command is expected to be related to a push. Push has a special
497 The command is expected to be related to a push. Push has a special
498 return method.
498 return method.
499
499
500 returns the server reply as a (ret, output) tuple. ret is either
500 returns the server reply as a (ret, output) tuple. ret is either
501 empty (error) or a stringified int.
501 empty (error) or a stringified int.
502 """
502 """
503 raise NotImplementedError()
503 raise NotImplementedError()
504
504
505 def _calltwowaystream(self, cmd, fp, **args):
505 def _calltwowaystream(self, cmd, fp, **args):
506 """execute <cmd> on server
506 """execute <cmd> on server
507
507
508 The command will send a stream to the server and get a stream in reply.
508 The command will send a stream to the server and get a stream in reply.
509 """
509 """
510 raise NotImplementedError()
510 raise NotImplementedError()
511
511
512 def _abort(self, exception):
512 def _abort(self, exception):
513 """clearly abort the wire protocol connection and raise the exception
513 """clearly abort the wire protocol connection and raise the exception
514 """
514 """
515 raise NotImplementedError()
515 raise NotImplementedError()
516
516
517 # server side
517 # server side
518
518
519 # wire protocol command can either return a string or one of these classes.
519 # wire protocol command can either return a string or one of these classes.
520 class streamres(object):
520 class streamres(object):
521 """wireproto reply: binary stream
521 """wireproto reply: binary stream
522
522
523 The call was successful and the result is a stream.
523 The call was successful and the result is a stream.
524
524
525 Accepts a generator containing chunks of data to be sent to the client.
525 Accepts a generator containing chunks of data to be sent to the client.
526
526
527 ``prefer_uncompressed`` indicates that the data is expected to be
527 ``prefer_uncompressed`` indicates that the data is expected to be
528 uncompressable and that the stream should therefore use the ``none``
528 uncompressable and that the stream should therefore use the ``none``
529 engine.
529 engine.
530 """
530 """
531 def __init__(self, gen=None, prefer_uncompressed=False):
531 def __init__(self, gen=None, prefer_uncompressed=False):
532 self.gen = gen
532 self.gen = gen
533 self.prefer_uncompressed = prefer_uncompressed
533 self.prefer_uncompressed = prefer_uncompressed
534
534
535 class streamres_legacy(object):
535 class streamres_legacy(object):
536 """wireproto reply: uncompressed binary stream
536 """wireproto reply: uncompressed binary stream
537
537
538 The call was successful and the result is a stream.
538 The call was successful and the result is a stream.
539
539
540 Accepts a generator containing chunks of data to be sent to the client.
540 Accepts a generator containing chunks of data to be sent to the client.
541
541
542 Like ``streamres``, but sends an uncompressed data for "version 1" clients
542 Like ``streamres``, but sends an uncompressed data for "version 1" clients
543 using the application/mercurial-0.1 media type.
543 using the application/mercurial-0.1 media type.
544 """
544 """
545 def __init__(self, gen=None):
545 def __init__(self, gen=None):
546 self.gen = gen
546 self.gen = gen
547
547
548 class pushres(object):
548 class pushres(object):
549 """wireproto reply: success with simple integer return
549 """wireproto reply: success with simple integer return
550
550
551 The call was successful and returned an integer contained in `self.res`.
551 The call was successful and returned an integer contained in `self.res`.
552 """
552 """
553 def __init__(self, res):
553 def __init__(self, res):
554 self.res = res
554 self.res = res
555
555
556 class pusherr(object):
556 class pusherr(object):
557 """wireproto reply: failure
557 """wireproto reply: failure
558
558
559 The call failed. The `self.res` attribute contains the error message.
559 The call failed. The `self.res` attribute contains the error message.
560 """
560 """
561 def __init__(self, res):
561 def __init__(self, res):
562 self.res = res
562 self.res = res
563
563
564 class ooberror(object):
564 class ooberror(object):
565 """wireproto reply: failure of a batch of operation
565 """wireproto reply: failure of a batch of operation
566
566
567 Something failed during a batch call. The error message is stored in
567 Something failed during a batch call. The error message is stored in
568 `self.message`.
568 `self.message`.
569 """
569 """
570 def __init__(self, message):
570 def __init__(self, message):
571 self.message = message
571 self.message = message
572
572
573 def getdispatchrepo(repo, proto, command):
573 def getdispatchrepo(repo, proto, command):
574 """Obtain the repo used for processing wire protocol commands.
574 """Obtain the repo used for processing wire protocol commands.
575
575
576 The intent of this function is to serve as a monkeypatch point for
576 The intent of this function is to serve as a monkeypatch point for
577 extensions that need commands to operate on different repo views under
577 extensions that need commands to operate on different repo views under
578 specialized circumstances.
578 specialized circumstances.
579 """
579 """
580 return repo.filtered('served')
580 return repo.filtered('served')
581
581
582 def dispatch(repo, proto, command):
582 def dispatch(repo, proto, command):
583 repo = getdispatchrepo(repo, proto, command)
583 repo = getdispatchrepo(repo, proto, command)
584 func, spec = commands[command]
584 func, spec = commands[command]
585 args = proto.getargs(spec)
585 args = proto.getargs(spec)
586 return func(repo, proto, *args)
586 return func(repo, proto, *args)
587
587
588 def options(cmd, keys, others):
588 def options(cmd, keys, others):
589 opts = {}
589 opts = {}
590 for k in keys:
590 for k in keys:
591 if k in others:
591 if k in others:
592 opts[k] = others[k]
592 opts[k] = others[k]
593 del others[k]
593 del others[k]
594 if others:
594 if others:
595 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
595 util.stderr.write("warning: %s ignored unexpected arguments %s\n"
596 % (cmd, ",".join(others)))
596 % (cmd, ",".join(others)))
597 return opts
597 return opts
598
598
599 def bundle1allowed(repo, action):
599 def bundle1allowed(repo, action):
600 """Whether a bundle1 operation is allowed from the server.
600 """Whether a bundle1 operation is allowed from the server.
601
601
602 Priority is:
602 Priority is:
603
603
604 1. server.bundle1gd.<action> (if generaldelta active)
604 1. server.bundle1gd.<action> (if generaldelta active)
605 2. server.bundle1.<action>
605 2. server.bundle1.<action>
606 3. server.bundle1gd (if generaldelta active)
606 3. server.bundle1gd (if generaldelta active)
607 4. server.bundle1
607 4. server.bundle1
608 """
608 """
609 ui = repo.ui
609 ui = repo.ui
610 gd = 'generaldelta' in repo.requirements
610 gd = 'generaldelta' in repo.requirements
611
611
612 if gd:
612 if gd:
613 v = ui.configbool('server', 'bundle1gd.%s' % action)
613 v = ui.configbool('server', 'bundle1gd.%s' % action)
614 if v is not None:
614 if v is not None:
615 return v
615 return v
616
616
617 v = ui.configbool('server', 'bundle1.%s' % action)
617 v = ui.configbool('server', 'bundle1.%s' % action)
618 if v is not None:
618 if v is not None:
619 return v
619 return v
620
620
621 if gd:
621 if gd:
622 v = ui.configbool('server', 'bundle1gd')
622 v = ui.configbool('server', 'bundle1gd')
623 if v is not None:
623 if v is not None:
624 return v
624 return v
625
625
626 return ui.configbool('server', 'bundle1')
626 return ui.configbool('server', 'bundle1')
627
627
628 def supportedcompengines(ui, proto, role):
628 def supportedcompengines(ui, proto, role):
629 """Obtain the list of supported compression engines for a request."""
629 """Obtain the list of supported compression engines for a request."""
630 assert role in (util.CLIENTROLE, util.SERVERROLE)
630 assert role in (util.CLIENTROLE, util.SERVERROLE)
631
631
632 compengines = util.compengines.supportedwireengines(role)
632 compengines = util.compengines.supportedwireengines(role)
633
633
634 # Allow config to override default list and ordering.
634 # Allow config to override default list and ordering.
635 if role == util.SERVERROLE:
635 if role == util.SERVERROLE:
636 configengines = ui.configlist('server', 'compressionengines')
636 configengines = ui.configlist('server', 'compressionengines')
637 config = 'server.compressionengines'
637 config = 'server.compressionengines'
638 else:
638 else:
639 # This is currently implemented mainly to facilitate testing. In most
639 # This is currently implemented mainly to facilitate testing. In most
640 # cases, the server should be in charge of choosing a compression engine
640 # cases, the server should be in charge of choosing a compression engine
641 # because a server has the most to lose from a sub-optimal choice. (e.g.
641 # because a server has the most to lose from a sub-optimal choice. (e.g.
642 # CPU DoS due to an expensive engine or a network DoS due to poor
642 # CPU DoS due to an expensive engine or a network DoS due to poor
643 # compression ratio).
643 # compression ratio).
644 configengines = ui.configlist('experimental',
644 configengines = ui.configlist('experimental',
645 'clientcompressionengines')
645 'clientcompressionengines')
646 config = 'experimental.clientcompressionengines'
646 config = 'experimental.clientcompressionengines'
647
647
648 # No explicit config. Filter out the ones that aren't supposed to be
648 # No explicit config. Filter out the ones that aren't supposed to be
649 # advertised and return default ordering.
649 # advertised and return default ordering.
650 if not configengines:
650 if not configengines:
651 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
651 attr = 'serverpriority' if role == util.SERVERROLE else 'clientpriority'
652 return [e for e in compengines
652 return [e for e in compengines
653 if getattr(e.wireprotosupport(), attr) > 0]
653 if getattr(e.wireprotosupport(), attr) > 0]
654
654
655 # If compression engines are listed in the config, assume there is a good
655 # If compression engines are listed in the config, assume there is a good
656 # reason for it (like server operators wanting to achieve specific
656 # reason for it (like server operators wanting to achieve specific
657 # performance characteristics). So fail fast if the config references
657 # performance characteristics). So fail fast if the config references
658 # unusable compression engines.
658 # unusable compression engines.
659 validnames = set(e.name() for e in compengines)
659 validnames = set(e.name() for e in compengines)
660 invalidnames = set(e for e in configengines if e not in validnames)
660 invalidnames = set(e for e in configengines if e not in validnames)
661 if invalidnames:
661 if invalidnames:
662 raise error.Abort(_('invalid compression engine defined in %s: %s') %
662 raise error.Abort(_('invalid compression engine defined in %s: %s') %
663 (config, ', '.join(sorted(invalidnames))))
663 (config, ', '.join(sorted(invalidnames))))
664
664
665 compengines = [e for e in compengines if e.name() in configengines]
665 compengines = [e for e in compengines if e.name() in configengines]
666 compengines = sorted(compengines,
666 compengines = sorted(compengines,
667 key=lambda e: configengines.index(e.name()))
667 key=lambda e: configengines.index(e.name()))
668
668
669 if not compengines:
669 if not compengines:
670 raise error.Abort(_('%s config option does not specify any known '
670 raise error.Abort(_('%s config option does not specify any known '
671 'compression engines') % config,
671 'compression engines') % config,
672 hint=_('usable compression engines: %s') %
672 hint=_('usable compression engines: %s') %
673 ', '.sorted(validnames))
673 ', '.sorted(validnames))
674
674
675 return compengines
675 return compengines
676
676
677 # list of commands
677 # list of commands
678 commands = {}
678 commands = {}
679
679
680 # Maps wire protocol name to operation type. This is used for permissions
681 # checking. All defined @wireiprotocommand should have an entry in this
682 # dict.
683 permissions = {}
684
680 def wireprotocommand(name, args=''):
685 def wireprotocommand(name, args=''):
681 """decorator for wire protocol command"""
686 """decorator for wire protocol command"""
682 def register(func):
687 def register(func):
683 commands[name] = (func, args)
688 commands[name] = (func, args)
684 return func
689 return func
685 return register
690 return register
686
691
692 # TODO define a more appropriate permissions type to use for this.
693 permissions['batch'] = 'pull'
687 @wireprotocommand('batch', 'cmds *')
694 @wireprotocommand('batch', 'cmds *')
688 def batch(repo, proto, cmds, others):
695 def batch(repo, proto, cmds, others):
689 repo = repo.filtered("served")
696 repo = repo.filtered("served")
690 res = []
697 res = []
691 for pair in cmds.split(';'):
698 for pair in cmds.split(';'):
692 op, args = pair.split(' ', 1)
699 op, args = pair.split(' ', 1)
693 vals = {}
700 vals = {}
694 for a in args.split(','):
701 for a in args.split(','):
695 if a:
702 if a:
696 n, v = a.split('=')
703 n, v = a.split('=')
697 vals[unescapearg(n)] = unescapearg(v)
704 vals[unescapearg(n)] = unescapearg(v)
698 func, spec = commands[op]
705 func, spec = commands[op]
706
707 # If the protocol supports permissions checking, perform that
708 # checking on each batched command.
709 # TODO formalize permission checking as part of protocol interface.
710 if util.safehasattr(proto, 'checkperm'):
711 # Assume commands with no defined permissions are writes / for
712 # pushes. This is the safest from a security perspective because
713 # it doesn't allow commands with undefined semantics from
714 # bypassing permissions checks.
715 proto.checkperm(permissions.get(op, 'push'))
716
699 if spec:
717 if spec:
700 keys = spec.split()
718 keys = spec.split()
701 data = {}
719 data = {}
702 for k in keys:
720 for k in keys:
703 if k == '*':
721 if k == '*':
704 star = {}
722 star = {}
705 for key in vals.keys():
723 for key in vals.keys():
706 if key not in keys:
724 if key not in keys:
707 star[key] = vals[key]
725 star[key] = vals[key]
708 data['*'] = star
726 data['*'] = star
709 else:
727 else:
710 data[k] = vals[k]
728 data[k] = vals[k]
711 result = func(repo, proto, *[data[k] for k in keys])
729 result = func(repo, proto, *[data[k] for k in keys])
712 else:
730 else:
713 result = func(repo, proto)
731 result = func(repo, proto)
714 if isinstance(result, ooberror):
732 if isinstance(result, ooberror):
715 return result
733 return result
716 res.append(escapearg(result))
734 res.append(escapearg(result))
717 return ';'.join(res)
735 return ';'.join(res)
718
736
737 permissions['between'] = 'pull'
719 @wireprotocommand('between', 'pairs')
738 @wireprotocommand('between', 'pairs')
720 def between(repo, proto, pairs):
739 def between(repo, proto, pairs):
721 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
740 pairs = [decodelist(p, '-') for p in pairs.split(" ")]
722 r = []
741 r = []
723 for b in repo.between(pairs):
742 for b in repo.between(pairs):
724 r.append(encodelist(b) + "\n")
743 r.append(encodelist(b) + "\n")
725 return "".join(r)
744 return "".join(r)
726
745
746 permissions['branchmap'] = 'pull'
727 @wireprotocommand('branchmap')
747 @wireprotocommand('branchmap')
728 def branchmap(repo, proto):
748 def branchmap(repo, proto):
729 branchmap = repo.branchmap()
749 branchmap = repo.branchmap()
730 heads = []
750 heads = []
731 for branch, nodes in branchmap.iteritems():
751 for branch, nodes in branchmap.iteritems():
732 branchname = urlreq.quote(encoding.fromlocal(branch))
752 branchname = urlreq.quote(encoding.fromlocal(branch))
733 branchnodes = encodelist(nodes)
753 branchnodes = encodelist(nodes)
734 heads.append('%s %s' % (branchname, branchnodes))
754 heads.append('%s %s' % (branchname, branchnodes))
735 return '\n'.join(heads)
755 return '\n'.join(heads)
736
756
757 permissions['branches'] = 'pull'
737 @wireprotocommand('branches', 'nodes')
758 @wireprotocommand('branches', 'nodes')
738 def branches(repo, proto, nodes):
759 def branches(repo, proto, nodes):
739 nodes = decodelist(nodes)
760 nodes = decodelist(nodes)
740 r = []
761 r = []
741 for b in repo.branches(nodes):
762 for b in repo.branches(nodes):
742 r.append(encodelist(b) + "\n")
763 r.append(encodelist(b) + "\n")
743 return "".join(r)
764 return "".join(r)
744
765
766 permissions['clonebundles'] = 'pull'
745 @wireprotocommand('clonebundles', '')
767 @wireprotocommand('clonebundles', '')
746 def clonebundles(repo, proto):
768 def clonebundles(repo, proto):
747 """Server command for returning info for available bundles to seed clones.
769 """Server command for returning info for available bundles to seed clones.
748
770
749 Clients will parse this response and determine what bundle to fetch.
771 Clients will parse this response and determine what bundle to fetch.
750
772
751 Extensions may wrap this command to filter or dynamically emit data
773 Extensions may wrap this command to filter or dynamically emit data
752 depending on the request. e.g. you could advertise URLs for the closest
774 depending on the request. e.g. you could advertise URLs for the closest
753 data center given the client's IP address.
775 data center given the client's IP address.
754 """
776 """
755 return repo.vfs.tryread('clonebundles.manifest')
777 return repo.vfs.tryread('clonebundles.manifest')
756
778
757 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
779 wireprotocaps = ['lookup', 'changegroupsubset', 'branchmap', 'pushkey',
758 'known', 'getbundle', 'unbundlehash', 'batch']
780 'known', 'getbundle', 'unbundlehash', 'batch']
759
781
760 def _capabilities(repo, proto):
782 def _capabilities(repo, proto):
761 """return a list of capabilities for a repo
783 """return a list of capabilities for a repo
762
784
763 This function exists to allow extensions to easily wrap capabilities
785 This function exists to allow extensions to easily wrap capabilities
764 computation
786 computation
765
787
766 - returns a lists: easy to alter
788 - returns a lists: easy to alter
767 - change done here will be propagated to both `capabilities` and `hello`
789 - change done here will be propagated to both `capabilities` and `hello`
768 command without any other action needed.
790 command without any other action needed.
769 """
791 """
770 # copy to prevent modification of the global list
792 # copy to prevent modification of the global list
771 caps = list(wireprotocaps)
793 caps = list(wireprotocaps)
772 if streamclone.allowservergeneration(repo):
794 if streamclone.allowservergeneration(repo):
773 if repo.ui.configbool('server', 'preferuncompressed'):
795 if repo.ui.configbool('server', 'preferuncompressed'):
774 caps.append('stream-preferred')
796 caps.append('stream-preferred')
775 requiredformats = repo.requirements & repo.supportedformats
797 requiredformats = repo.requirements & repo.supportedformats
776 # if our local revlogs are just revlogv1, add 'stream' cap
798 # if our local revlogs are just revlogv1, add 'stream' cap
777 if not requiredformats - {'revlogv1'}:
799 if not requiredformats - {'revlogv1'}:
778 caps.append('stream')
800 caps.append('stream')
779 # otherwise, add 'streamreqs' detailing our local revlog format
801 # otherwise, add 'streamreqs' detailing our local revlog format
780 else:
802 else:
781 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
803 caps.append('streamreqs=%s' % ','.join(sorted(requiredformats)))
782 if repo.ui.configbool('experimental', 'bundle2-advertise'):
804 if repo.ui.configbool('experimental', 'bundle2-advertise'):
783 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role='server'))
805 capsblob = bundle2.encodecaps(bundle2.getrepocaps(repo, role='server'))
784 caps.append('bundle2=' + urlreq.quote(capsblob))
806 caps.append('bundle2=' + urlreq.quote(capsblob))
785 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
807 caps.append('unbundle=%s' % ','.join(bundle2.bundlepriority))
786
808
787 if proto.name == 'http':
809 if proto.name == 'http':
788 caps.append('httpheader=%d' %
810 caps.append('httpheader=%d' %
789 repo.ui.configint('server', 'maxhttpheaderlen'))
811 repo.ui.configint('server', 'maxhttpheaderlen'))
790 if repo.ui.configbool('experimental', 'httppostargs'):
812 if repo.ui.configbool('experimental', 'httppostargs'):
791 caps.append('httppostargs')
813 caps.append('httppostargs')
792
814
793 # FUTURE advertise 0.2rx once support is implemented
815 # FUTURE advertise 0.2rx once support is implemented
794 # FUTURE advertise minrx and mintx after consulting config option
816 # FUTURE advertise minrx and mintx after consulting config option
795 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
817 caps.append('httpmediatype=0.1rx,0.1tx,0.2tx')
796
818
797 compengines = supportedcompengines(repo.ui, proto, util.SERVERROLE)
819 compengines = supportedcompengines(repo.ui, proto, util.SERVERROLE)
798 if compengines:
820 if compengines:
799 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
821 comptypes = ','.join(urlreq.quote(e.wireprotosupport().name)
800 for e in compengines)
822 for e in compengines)
801 caps.append('compression=%s' % comptypes)
823 caps.append('compression=%s' % comptypes)
802
824
803 return caps
825 return caps
804
826
805 # If you are writing an extension and consider wrapping this function. Wrap
827 # If you are writing an extension and consider wrapping this function. Wrap
806 # `_capabilities` instead.
828 # `_capabilities` instead.
829 permissions['capabilities'] = 'pull'
807 @wireprotocommand('capabilities')
830 @wireprotocommand('capabilities')
808 def capabilities(repo, proto):
831 def capabilities(repo, proto):
809 return ' '.join(_capabilities(repo, proto))
832 return ' '.join(_capabilities(repo, proto))
810
833
834 permissions['changegroup'] = 'pull'
811 @wireprotocommand('changegroup', 'roots')
835 @wireprotocommand('changegroup', 'roots')
812 def changegroup(repo, proto, roots):
836 def changegroup(repo, proto, roots):
813 nodes = decodelist(roots)
837 nodes = decodelist(roots)
814 outgoing = discovery.outgoing(repo, missingroots=nodes,
838 outgoing = discovery.outgoing(repo, missingroots=nodes,
815 missingheads=repo.heads())
839 missingheads=repo.heads())
816 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
840 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
817 gen = iter(lambda: cg.read(32768), '')
841 gen = iter(lambda: cg.read(32768), '')
818 return streamres(gen=gen)
842 return streamres(gen=gen)
819
843
844 permissions['changegroupsubset'] = 'pull'
820 @wireprotocommand('changegroupsubset', 'bases heads')
845 @wireprotocommand('changegroupsubset', 'bases heads')
821 def changegroupsubset(repo, proto, bases, heads):
846 def changegroupsubset(repo, proto, bases, heads):
822 bases = decodelist(bases)
847 bases = decodelist(bases)
823 heads = decodelist(heads)
848 heads = decodelist(heads)
824 outgoing = discovery.outgoing(repo, missingroots=bases,
849 outgoing = discovery.outgoing(repo, missingroots=bases,
825 missingheads=heads)
850 missingheads=heads)
826 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
851 cg = changegroupmod.makechangegroup(repo, outgoing, '01', 'serve')
827 gen = iter(lambda: cg.read(32768), '')
852 gen = iter(lambda: cg.read(32768), '')
828 return streamres(gen=gen)
853 return streamres(gen=gen)
829
854
855 permissions['debugwireargs'] = 'pull'
830 @wireprotocommand('debugwireargs', 'one two *')
856 @wireprotocommand('debugwireargs', 'one two *')
831 def debugwireargs(repo, proto, one, two, others):
857 def debugwireargs(repo, proto, one, two, others):
832 # only accept optional args from the known set
858 # only accept optional args from the known set
833 opts = options('debugwireargs', ['three', 'four'], others)
859 opts = options('debugwireargs', ['three', 'four'], others)
834 return repo.debugwireargs(one, two, **pycompat.strkwargs(opts))
860 return repo.debugwireargs(one, two, **pycompat.strkwargs(opts))
835
861
862 permissions['getbundle'] = 'pull'
836 @wireprotocommand('getbundle', '*')
863 @wireprotocommand('getbundle', '*')
837 def getbundle(repo, proto, others):
864 def getbundle(repo, proto, others):
838 opts = options('getbundle', gboptsmap.keys(), others)
865 opts = options('getbundle', gboptsmap.keys(), others)
839 for k, v in opts.iteritems():
866 for k, v in opts.iteritems():
840 keytype = gboptsmap[k]
867 keytype = gboptsmap[k]
841 if keytype == 'nodes':
868 if keytype == 'nodes':
842 opts[k] = decodelist(v)
869 opts[k] = decodelist(v)
843 elif keytype == 'csv':
870 elif keytype == 'csv':
844 opts[k] = list(v.split(','))
871 opts[k] = list(v.split(','))
845 elif keytype == 'scsv':
872 elif keytype == 'scsv':
846 opts[k] = set(v.split(','))
873 opts[k] = set(v.split(','))
847 elif keytype == 'boolean':
874 elif keytype == 'boolean':
848 # Client should serialize False as '0', which is a non-empty string
875 # Client should serialize False as '0', which is a non-empty string
849 # so it evaluates as a True bool.
876 # so it evaluates as a True bool.
850 if v == '0':
877 if v == '0':
851 opts[k] = False
878 opts[k] = False
852 else:
879 else:
853 opts[k] = bool(v)
880 opts[k] = bool(v)
854 elif keytype != 'plain':
881 elif keytype != 'plain':
855 raise KeyError('unknown getbundle option type %s'
882 raise KeyError('unknown getbundle option type %s'
856 % keytype)
883 % keytype)
857
884
858 if not bundle1allowed(repo, 'pull'):
885 if not bundle1allowed(repo, 'pull'):
859 if not exchange.bundle2requested(opts.get('bundlecaps')):
886 if not exchange.bundle2requested(opts.get('bundlecaps')):
860 if proto.name == 'http':
887 if proto.name == 'http':
861 return ooberror(bundle2required)
888 return ooberror(bundle2required)
862 raise error.Abort(bundle2requiredmain,
889 raise error.Abort(bundle2requiredmain,
863 hint=bundle2requiredhint)
890 hint=bundle2requiredhint)
864
891
865 prefercompressed = True
892 prefercompressed = True
866
893
867 try:
894 try:
868 if repo.ui.configbool('server', 'disablefullbundle'):
895 if repo.ui.configbool('server', 'disablefullbundle'):
869 # Check to see if this is a full clone.
896 # Check to see if this is a full clone.
870 clheads = set(repo.changelog.heads())
897 clheads = set(repo.changelog.heads())
871 changegroup = opts.get('cg', True)
898 changegroup = opts.get('cg', True)
872 heads = set(opts.get('heads', set()))
899 heads = set(opts.get('heads', set()))
873 common = set(opts.get('common', set()))
900 common = set(opts.get('common', set()))
874 common.discard(nullid)
901 common.discard(nullid)
875 if changegroup and not common and clheads == heads:
902 if changegroup and not common and clheads == heads:
876 raise error.Abort(
903 raise error.Abort(
877 _('server has pull-based clones disabled'),
904 _('server has pull-based clones disabled'),
878 hint=_('remove --pull if specified or upgrade Mercurial'))
905 hint=_('remove --pull if specified or upgrade Mercurial'))
879
906
880 info, chunks = exchange.getbundlechunks(repo, 'serve',
907 info, chunks = exchange.getbundlechunks(repo, 'serve',
881 **pycompat.strkwargs(opts))
908 **pycompat.strkwargs(opts))
882 prefercompressed = info.get('prefercompressed', True)
909 prefercompressed = info.get('prefercompressed', True)
883 except error.Abort as exc:
910 except error.Abort as exc:
884 # cleanly forward Abort error to the client
911 # cleanly forward Abort error to the client
885 if not exchange.bundle2requested(opts.get('bundlecaps')):
912 if not exchange.bundle2requested(opts.get('bundlecaps')):
886 if proto.name == 'http':
913 if proto.name == 'http':
887 return ooberror(str(exc) + '\n')
914 return ooberror(str(exc) + '\n')
888 raise # cannot do better for bundle1 + ssh
915 raise # cannot do better for bundle1 + ssh
889 # bundle2 request expect a bundle2 reply
916 # bundle2 request expect a bundle2 reply
890 bundler = bundle2.bundle20(repo.ui)
917 bundler = bundle2.bundle20(repo.ui)
891 manargs = [('message', str(exc))]
918 manargs = [('message', str(exc))]
892 advargs = []
919 advargs = []
893 if exc.hint is not None:
920 if exc.hint is not None:
894 advargs.append(('hint', exc.hint))
921 advargs.append(('hint', exc.hint))
895 bundler.addpart(bundle2.bundlepart('error:abort',
922 bundler.addpart(bundle2.bundlepart('error:abort',
896 manargs, advargs))
923 manargs, advargs))
897 chunks = bundler.getchunks()
924 chunks = bundler.getchunks()
898 prefercompressed = False
925 prefercompressed = False
899
926
900 return streamres(gen=chunks, prefer_uncompressed=not prefercompressed)
927 return streamres(gen=chunks, prefer_uncompressed=not prefercompressed)
901
928
929 permissions['heads'] = 'pull'
902 @wireprotocommand('heads')
930 @wireprotocommand('heads')
903 def heads(repo, proto):
931 def heads(repo, proto):
904 h = repo.heads()
932 h = repo.heads()
905 return encodelist(h) + "\n"
933 return encodelist(h) + "\n"
906
934
935 permissions['hello'] = 'pull'
907 @wireprotocommand('hello')
936 @wireprotocommand('hello')
908 def hello(repo, proto):
937 def hello(repo, proto):
909 '''the hello command returns a set of lines describing various
938 '''the hello command returns a set of lines describing various
910 interesting things about the server, in an RFC822-like format.
939 interesting things about the server, in an RFC822-like format.
911 Currently the only one defined is "capabilities", which
940 Currently the only one defined is "capabilities", which
912 consists of a line in the form:
941 consists of a line in the form:
913
942
914 capabilities: space separated list of tokens
943 capabilities: space separated list of tokens
915 '''
944 '''
916 return "capabilities: %s\n" % (capabilities(repo, proto))
945 return "capabilities: %s\n" % (capabilities(repo, proto))
917
946
947 permissions['listkeys'] = 'pull'
918 @wireprotocommand('listkeys', 'namespace')
948 @wireprotocommand('listkeys', 'namespace')
919 def listkeys(repo, proto, namespace):
949 def listkeys(repo, proto, namespace):
920 d = repo.listkeys(encoding.tolocal(namespace)).items()
950 d = repo.listkeys(encoding.tolocal(namespace)).items()
921 return pushkeymod.encodekeys(d)
951 return pushkeymod.encodekeys(d)
922
952
953 permissions['lookup'] = 'pull'
923 @wireprotocommand('lookup', 'key')
954 @wireprotocommand('lookup', 'key')
924 def lookup(repo, proto, key):
955 def lookup(repo, proto, key):
925 try:
956 try:
926 k = encoding.tolocal(key)
957 k = encoding.tolocal(key)
927 c = repo[k]
958 c = repo[k]
928 r = c.hex()
959 r = c.hex()
929 success = 1
960 success = 1
930 except Exception as inst:
961 except Exception as inst:
931 r = str(inst)
962 r = str(inst)
932 success = 0
963 success = 0
933 return "%d %s\n" % (success, r)
964 return "%d %s\n" % (success, r)
934
965
966 permissions['known'] = 'pull'
935 @wireprotocommand('known', 'nodes *')
967 @wireprotocommand('known', 'nodes *')
936 def known(repo, proto, nodes, others):
968 def known(repo, proto, nodes, others):
937 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
969 return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
938
970
971 permissions['pushkey'] = 'push'
939 @wireprotocommand('pushkey', 'namespace key old new')
972 @wireprotocommand('pushkey', 'namespace key old new')
940 def pushkey(repo, proto, namespace, key, old, new):
973 def pushkey(repo, proto, namespace, key, old, new):
941 # compatibility with pre-1.8 clients which were accidentally
974 # compatibility with pre-1.8 clients which were accidentally
942 # sending raw binary nodes rather than utf-8-encoded hex
975 # sending raw binary nodes rather than utf-8-encoded hex
943 if len(new) == 20 and util.escapestr(new) != new:
976 if len(new) == 20 and util.escapestr(new) != new:
944 # looks like it could be a binary node
977 # looks like it could be a binary node
945 try:
978 try:
946 new.decode('utf-8')
979 new.decode('utf-8')
947 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
980 new = encoding.tolocal(new) # but cleanly decodes as UTF-8
948 except UnicodeDecodeError:
981 except UnicodeDecodeError:
949 pass # binary, leave unmodified
982 pass # binary, leave unmodified
950 else:
983 else:
951 new = encoding.tolocal(new) # normal path
984 new = encoding.tolocal(new) # normal path
952
985
953 if util.safehasattr(proto, 'restore'):
986 if util.safehasattr(proto, 'restore'):
954
987
955 proto.redirect()
988 proto.redirect()
956
989
957 try:
990 try:
958 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
991 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
959 encoding.tolocal(old), new) or False
992 encoding.tolocal(old), new) or False
960 except error.Abort:
993 except error.Abort:
961 r = False
994 r = False
962
995
963 output = proto.restore()
996 output = proto.restore()
964
997
965 return '%s\n%s' % (int(r), output)
998 return '%s\n%s' % (int(r), output)
966
999
967 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
1000 r = repo.pushkey(encoding.tolocal(namespace), encoding.tolocal(key),
968 encoding.tolocal(old), new)
1001 encoding.tolocal(old), new)
969 return '%s\n' % int(r)
1002 return '%s\n' % int(r)
970
1003
1004 permissions['stream_out'] = 'pull'
971 @wireprotocommand('stream_out')
1005 @wireprotocommand('stream_out')
972 def stream(repo, proto):
1006 def stream(repo, proto):
973 '''If the server supports streaming clone, it advertises the "stream"
1007 '''If the server supports streaming clone, it advertises the "stream"
974 capability with a value representing the version and flags of the repo
1008 capability with a value representing the version and flags of the repo
975 it is serving. Client checks to see if it understands the format.
1009 it is serving. Client checks to see if it understands the format.
976 '''
1010 '''
977 return streamres_legacy(streamclone.generatev1wireproto(repo))
1011 return streamres_legacy(streamclone.generatev1wireproto(repo))
978
1012
1013 permissions['unbundle'] = 'push'
979 @wireprotocommand('unbundle', 'heads')
1014 @wireprotocommand('unbundle', 'heads')
980 def unbundle(repo, proto, heads):
1015 def unbundle(repo, proto, heads):
981 their_heads = decodelist(heads)
1016 their_heads = decodelist(heads)
982
1017
983 try:
1018 try:
984 proto.redirect()
1019 proto.redirect()
985
1020
986 exchange.check_heads(repo, their_heads, 'preparing changes')
1021 exchange.check_heads(repo, their_heads, 'preparing changes')
987
1022
988 # write bundle data to temporary file because it can be big
1023 # write bundle data to temporary file because it can be big
989 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
1024 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
990 fp = os.fdopen(fd, pycompat.sysstr('wb+'))
1025 fp = os.fdopen(fd, pycompat.sysstr('wb+'))
991 r = 0
1026 r = 0
992 try:
1027 try:
993 proto.getfile(fp)
1028 proto.getfile(fp)
994 fp.seek(0)
1029 fp.seek(0)
995 gen = exchange.readbundle(repo.ui, fp, None)
1030 gen = exchange.readbundle(repo.ui, fp, None)
996 if (isinstance(gen, changegroupmod.cg1unpacker)
1031 if (isinstance(gen, changegroupmod.cg1unpacker)
997 and not bundle1allowed(repo, 'push')):
1032 and not bundle1allowed(repo, 'push')):
998 if proto.name == 'http':
1033 if proto.name == 'http':
999 # need to special case http because stderr do not get to
1034 # need to special case http because stderr do not get to
1000 # the http client on failed push so we need to abuse some
1035 # the http client on failed push so we need to abuse some
1001 # other error type to make sure the message get to the
1036 # other error type to make sure the message get to the
1002 # user.
1037 # user.
1003 return ooberror(bundle2required)
1038 return ooberror(bundle2required)
1004 raise error.Abort(bundle2requiredmain,
1039 raise error.Abort(bundle2requiredmain,
1005 hint=bundle2requiredhint)
1040 hint=bundle2requiredhint)
1006
1041
1007 r = exchange.unbundle(repo, gen, their_heads, 'serve',
1042 r = exchange.unbundle(repo, gen, their_heads, 'serve',
1008 proto._client())
1043 proto._client())
1009 if util.safehasattr(r, 'addpart'):
1044 if util.safehasattr(r, 'addpart'):
1010 # The return looks streamable, we are in the bundle2 case and
1045 # The return looks streamable, we are in the bundle2 case and
1011 # should return a stream.
1046 # should return a stream.
1012 return streamres_legacy(gen=r.getchunks())
1047 return streamres_legacy(gen=r.getchunks())
1013 return pushres(r)
1048 return pushres(r)
1014
1049
1015 finally:
1050 finally:
1016 fp.close()
1051 fp.close()
1017 os.unlink(tempname)
1052 os.unlink(tempname)
1018
1053
1019 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
1054 except (error.BundleValueError, error.Abort, error.PushRaced) as exc:
1020 # handle non-bundle2 case first
1055 # handle non-bundle2 case first
1021 if not getattr(exc, 'duringunbundle2', False):
1056 if not getattr(exc, 'duringunbundle2', False):
1022 try:
1057 try:
1023 raise
1058 raise
1024 except error.Abort:
1059 except error.Abort:
1025 # The old code we moved used util.stderr directly.
1060 # The old code we moved used util.stderr directly.
1026 # We did not change it to minimise code change.
1061 # We did not change it to minimise code change.
1027 # This need to be moved to something proper.
1062 # This need to be moved to something proper.
1028 # Feel free to do it.
1063 # Feel free to do it.
1029 util.stderr.write("abort: %s\n" % exc)
1064 util.stderr.write("abort: %s\n" % exc)
1030 if exc.hint is not None:
1065 if exc.hint is not None:
1031 util.stderr.write("(%s)\n" % exc.hint)
1066 util.stderr.write("(%s)\n" % exc.hint)
1032 return pushres(0)
1067 return pushres(0)
1033 except error.PushRaced:
1068 except error.PushRaced:
1034 return pusherr(str(exc))
1069 return pusherr(str(exc))
1035
1070
1036 bundler = bundle2.bundle20(repo.ui)
1071 bundler = bundle2.bundle20(repo.ui)
1037 for out in getattr(exc, '_bundle2salvagedoutput', ()):
1072 for out in getattr(exc, '_bundle2salvagedoutput', ()):
1038 bundler.addpart(out)
1073 bundler.addpart(out)
1039 try:
1074 try:
1040 try:
1075 try:
1041 raise
1076 raise
1042 except error.PushkeyFailed as exc:
1077 except error.PushkeyFailed as exc:
1043 # check client caps
1078 # check client caps
1044 remotecaps = getattr(exc, '_replycaps', None)
1079 remotecaps = getattr(exc, '_replycaps', None)
1045 if (remotecaps is not None
1080 if (remotecaps is not None
1046 and 'pushkey' not in remotecaps.get('error', ())):
1081 and 'pushkey' not in remotecaps.get('error', ())):
1047 # no support remote side, fallback to Abort handler.
1082 # no support remote side, fallback to Abort handler.
1048 raise
1083 raise
1049 part = bundler.newpart('error:pushkey')
1084 part = bundler.newpart('error:pushkey')
1050 part.addparam('in-reply-to', exc.partid)
1085 part.addparam('in-reply-to', exc.partid)
1051 if exc.namespace is not None:
1086 if exc.namespace is not None:
1052 part.addparam('namespace', exc.namespace, mandatory=False)
1087 part.addparam('namespace', exc.namespace, mandatory=False)
1053 if exc.key is not None:
1088 if exc.key is not None:
1054 part.addparam('key', exc.key, mandatory=False)
1089 part.addparam('key', exc.key, mandatory=False)
1055 if exc.new is not None:
1090 if exc.new is not None:
1056 part.addparam('new', exc.new, mandatory=False)
1091 part.addparam('new', exc.new, mandatory=False)
1057 if exc.old is not None:
1092 if exc.old is not None:
1058 part.addparam('old', exc.old, mandatory=False)
1093 part.addparam('old', exc.old, mandatory=False)
1059 if exc.ret is not None:
1094 if exc.ret is not None:
1060 part.addparam('ret', exc.ret, mandatory=False)
1095 part.addparam('ret', exc.ret, mandatory=False)
1061 except error.BundleValueError as exc:
1096 except error.BundleValueError as exc:
1062 errpart = bundler.newpart('error:unsupportedcontent')
1097 errpart = bundler.newpart('error:unsupportedcontent')
1063 if exc.parttype is not None:
1098 if exc.parttype is not None:
1064 errpart.addparam('parttype', exc.parttype)
1099 errpart.addparam('parttype', exc.parttype)
1065 if exc.params:
1100 if exc.params:
1066 errpart.addparam('params', '\0'.join(exc.params))
1101 errpart.addparam('params', '\0'.join(exc.params))
1067 except error.Abort as exc:
1102 except error.Abort as exc:
1068 manargs = [('message', str(exc))]
1103 manargs = [('message', str(exc))]
1069 advargs = []
1104 advargs = []
1070 if exc.hint is not None:
1105 if exc.hint is not None:
1071 advargs.append(('hint', exc.hint))
1106 advargs.append(('hint', exc.hint))
1072 bundler.addpart(bundle2.bundlepart('error:abort',
1107 bundler.addpart(bundle2.bundlepart('error:abort',
1073 manargs, advargs))
1108 manargs, advargs))
1074 except error.PushRaced as exc:
1109 except error.PushRaced as exc:
1075 bundler.newpart('error:pushraced', [('message', str(exc))])
1110 bundler.newpart('error:pushraced', [('message', str(exc))])
1076 return streamres_legacy(gen=bundler.getchunks())
1111 return streamres_legacy(gen=bundler.getchunks())
@@ -1,411 +1,403 b''
1 #require serve
1 #require serve
2
2
3 This test is a duplicate of 'test-http.t', feel free to factor out
3 This test is a duplicate of 'test-http.t', feel free to factor out
4 parts that are not bundle1/bundle2 specific.
4 parts that are not bundle1/bundle2 specific.
5
5
6 $ cat << EOF >> $HGRCPATH
6 $ cat << EOF >> $HGRCPATH
7 > [devel]
7 > [devel]
8 > # This test is dedicated to interaction through old bundle
8 > # This test is dedicated to interaction through old bundle
9 > legacy.exchange = bundle1
9 > legacy.exchange = bundle1
10 > EOF
10 > EOF
11
11
12 $ hg init test
12 $ hg init test
13 $ cd test
13 $ cd test
14 $ echo foo>foo
14 $ echo foo>foo
15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
15 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
16 $ echo foo>foo.d/foo
16 $ echo foo>foo.d/foo
17 $ echo bar>foo.d/bAr.hg.d/BaR
17 $ echo bar>foo.d/bAr.hg.d/BaR
18 $ echo bar>foo.d/baR.d.hg/bAR
18 $ echo bar>foo.d/baR.d.hg/bAR
19 $ hg commit -A -m 1
19 $ hg commit -A -m 1
20 adding foo
20 adding foo
21 adding foo.d/bAr.hg.d/BaR
21 adding foo.d/bAr.hg.d/BaR
22 adding foo.d/baR.d.hg/bAR
22 adding foo.d/baR.d.hg/bAR
23 adding foo.d/foo
23 adding foo.d/foo
24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
24 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
25 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
26
26
27 Test server address cannot be reused
27 Test server address cannot be reused
28
28
29 $ hg serve -p $HGPORT1 2>&1
29 $ hg serve -p $HGPORT1 2>&1
30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
30 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
31 [255]
31 [255]
32
32
33 $ cd ..
33 $ cd ..
34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
34 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
35
35
36 clone via stream
36 clone via stream
37
37
38 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
38 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
39 streaming all changes
39 streaming all changes
40 6 files to transfer, 606 bytes of data
40 6 files to transfer, 606 bytes of data
41 transferred * bytes in * seconds (*/sec) (glob)
41 transferred * bytes in * seconds (*/sec) (glob)
42 searching for changes
42 searching for changes
43 no changes found
43 no changes found
44 updating to branch default
44 updating to branch default
45 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
45 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 $ hg verify -R copy
46 $ hg verify -R copy
47 checking changesets
47 checking changesets
48 checking manifests
48 checking manifests
49 crosschecking files in changesets and manifests
49 crosschecking files in changesets and manifests
50 checking files
50 checking files
51 4 files, 1 changesets, 4 total revisions
51 4 files, 1 changesets, 4 total revisions
52
52
53 try to clone via stream, should use pull instead
53 try to clone via stream, should use pull instead
54
54
55 $ hg clone --stream http://localhost:$HGPORT1/ copy2
55 $ hg clone --stream http://localhost:$HGPORT1/ copy2
56 warning: stream clone requested but server has them disabled
56 warning: stream clone requested but server has them disabled
57 requesting all changes
57 requesting all changes
58 adding changesets
58 adding changesets
59 adding manifests
59 adding manifests
60 adding file changes
60 adding file changes
61 added 1 changesets with 4 changes to 4 files
61 added 1 changesets with 4 changes to 4 files
62 new changesets 8b6053c928fe
62 new changesets 8b6053c928fe
63 updating to branch default
63 updating to branch default
64 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
64 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
65
65
66 try to clone via stream but missing requirements, so should use pull instead
66 try to clone via stream but missing requirements, so should use pull instead
67
67
68 $ cat > $TESTTMP/removesupportedformat.py << EOF
68 $ cat > $TESTTMP/removesupportedformat.py << EOF
69 > from mercurial import localrepo
69 > from mercurial import localrepo
70 > def extsetup(ui):
70 > def extsetup(ui):
71 > localrepo.localrepository.supportedformats.remove('generaldelta')
71 > localrepo.localrepository.supportedformats.remove('generaldelta')
72 > EOF
72 > EOF
73
73
74 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
74 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
75 warning: stream clone requested but client is missing requirements: generaldelta
75 warning: stream clone requested but client is missing requirements: generaldelta
76 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
76 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
77 requesting all changes
77 requesting all changes
78 adding changesets
78 adding changesets
79 adding manifests
79 adding manifests
80 adding file changes
80 adding file changes
81 added 1 changesets with 4 changes to 4 files
81 added 1 changesets with 4 changes to 4 files
82 new changesets 8b6053c928fe
82 new changesets 8b6053c928fe
83 updating to branch default
83 updating to branch default
84 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
84 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
85
85
86 clone via pull
86 clone via pull
87
87
88 $ hg clone http://localhost:$HGPORT1/ copy-pull
88 $ hg clone http://localhost:$HGPORT1/ copy-pull
89 requesting all changes
89 requesting all changes
90 adding changesets
90 adding changesets
91 adding manifests
91 adding manifests
92 adding file changes
92 adding file changes
93 added 1 changesets with 4 changes to 4 files
93 added 1 changesets with 4 changes to 4 files
94 new changesets 8b6053c928fe
94 new changesets 8b6053c928fe
95 updating to branch default
95 updating to branch default
96 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
96 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
97 $ hg verify -R copy-pull
97 $ hg verify -R copy-pull
98 checking changesets
98 checking changesets
99 checking manifests
99 checking manifests
100 crosschecking files in changesets and manifests
100 crosschecking files in changesets and manifests
101 checking files
101 checking files
102 4 files, 1 changesets, 4 total revisions
102 4 files, 1 changesets, 4 total revisions
103 $ cd test
103 $ cd test
104 $ echo bar > bar
104 $ echo bar > bar
105 $ hg commit -A -d '1 0' -m 2
105 $ hg commit -A -d '1 0' -m 2
106 adding bar
106 adding bar
107 $ cd ..
107 $ cd ..
108
108
109 clone over http with --update
109 clone over http with --update
110
110
111 $ hg clone http://localhost:$HGPORT1/ updated --update 0
111 $ hg clone http://localhost:$HGPORT1/ updated --update 0
112 requesting all changes
112 requesting all changes
113 adding changesets
113 adding changesets
114 adding manifests
114 adding manifests
115 adding file changes
115 adding file changes
116 added 2 changesets with 5 changes to 5 files
116 added 2 changesets with 5 changes to 5 files
117 new changesets 8b6053c928fe:5fed3813f7f5
117 new changesets 8b6053c928fe:5fed3813f7f5
118 updating to branch default
118 updating to branch default
119 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
119 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
120 $ hg log -r . -R updated
120 $ hg log -r . -R updated
121 changeset: 0:8b6053c928fe
121 changeset: 0:8b6053c928fe
122 user: test
122 user: test
123 date: Thu Jan 01 00:00:00 1970 +0000
123 date: Thu Jan 01 00:00:00 1970 +0000
124 summary: 1
124 summary: 1
125
125
126 $ rm -rf updated
126 $ rm -rf updated
127
127
128 incoming via HTTP
128 incoming via HTTP
129
129
130 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
130 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
131 adding changesets
131 adding changesets
132 adding manifests
132 adding manifests
133 adding file changes
133 adding file changes
134 added 1 changesets with 4 changes to 4 files
134 added 1 changesets with 4 changes to 4 files
135 new changesets 8b6053c928fe
135 new changesets 8b6053c928fe
136 updating to branch default
136 updating to branch default
137 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
137 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
138 $ cd partial
138 $ cd partial
139 $ touch LOCAL
139 $ touch LOCAL
140 $ hg ci -qAm LOCAL
140 $ hg ci -qAm LOCAL
141 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
141 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
142 comparing with http://localhost:$HGPORT1/
142 comparing with http://localhost:$HGPORT1/
143 searching for changes
143 searching for changes
144 2
144 2
145 $ cd ..
145 $ cd ..
146
146
147 pull
147 pull
148
148
149 $ cd copy-pull
149 $ cd copy-pull
150 $ cat >> .hg/hgrc <<EOF
150 $ cat >> .hg/hgrc <<EOF
151 > [hooks]
151 > [hooks]
152 > changegroup = sh -c "printenv.py changegroup"
152 > changegroup = sh -c "printenv.py changegroup"
153 > EOF
153 > EOF
154 $ hg pull
154 $ hg pull
155 pulling from http://localhost:$HGPORT1/
155 pulling from http://localhost:$HGPORT1/
156 searching for changes
156 searching for changes
157 adding changesets
157 adding changesets
158 adding manifests
158 adding manifests
159 adding file changes
159 adding file changes
160 added 1 changesets with 1 changes to 1 files
160 added 1 changesets with 1 changes to 1 files
161 new changesets 5fed3813f7f5
161 new changesets 5fed3813f7f5
162 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
162 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
163 (run 'hg update' to get a working copy)
163 (run 'hg update' to get a working copy)
164 $ cd ..
164 $ cd ..
165
165
166 clone from invalid URL
166 clone from invalid URL
167
167
168 $ hg clone http://localhost:$HGPORT/bad
168 $ hg clone http://localhost:$HGPORT/bad
169 abort: HTTP Error 404: Not Found
169 abort: HTTP Error 404: Not Found
170 [255]
170 [255]
171
171
172 test http authentication
172 test http authentication
173 + use the same server to test server side streaming preference
173 + use the same server to test server side streaming preference
174
174
175 $ cd test
175 $ cd test
176 $ cat << EOT > userpass.py
176 $ cat << EOT > userpass.py
177 > import base64
177 > import base64
178 > from mercurial.hgweb import common
178 > from mercurial.hgweb import common
179 > def perform_authentication(hgweb, req, op):
179 > def perform_authentication(hgweb, req, op):
180 > auth = req.env.get('HTTP_AUTHORIZATION')
180 > auth = req.env.get('HTTP_AUTHORIZATION')
181 > if not auth:
181 > if not auth:
182 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
182 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
183 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
183 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
184 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
184 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
185 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
185 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
186 > def extsetup():
186 > def extsetup():
187 > common.permhooks.insert(0, perform_authentication)
187 > common.permhooks.insert(0, perform_authentication)
188 > EOT
188 > EOT
189 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
189 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
190 > --config server.preferuncompressed=True \
190 > --config server.preferuncompressed=True \
191 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
191 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
192 $ cat pid >> $DAEMON_PIDS
192 $ cat pid >> $DAEMON_PIDS
193
193
194 $ cat << EOF > get_pass.py
194 $ cat << EOF > get_pass.py
195 > import getpass
195 > import getpass
196 > def newgetpass(arg):
196 > def newgetpass(arg):
197 > return "pass"
197 > return "pass"
198 > getpass.getpass = newgetpass
198 > getpass.getpass = newgetpass
199 > EOF
199 > EOF
200
200
201 $ hg id http://localhost:$HGPORT2/
201 $ hg id http://localhost:$HGPORT2/
202 abort: http authorization required for http://localhost:$HGPORT2/
202 abort: http authorization required for http://localhost:$HGPORT2/
203 [255]
203 [255]
204 $ hg id http://localhost:$HGPORT2/
204 $ hg id http://localhost:$HGPORT2/
205 abort: http authorization required for http://localhost:$HGPORT2/
205 abort: http authorization required for http://localhost:$HGPORT2/
206 [255]
206 [255]
207 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
207 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
208 http authorization required for http://localhost:$HGPORT2/
208 http authorization required for http://localhost:$HGPORT2/
209 realm: mercurial
209 realm: mercurial
210 user: user
210 user: user
211 password: 5fed3813f7f5
211 password: 5fed3813f7f5
212 $ hg id http://user:pass@localhost:$HGPORT2/
212 $ hg id http://user:pass@localhost:$HGPORT2/
213 5fed3813f7f5
213 5fed3813f7f5
214 $ echo '[auth]' >> .hg/hgrc
214 $ echo '[auth]' >> .hg/hgrc
215 $ echo 'l.schemes=http' >> .hg/hgrc
215 $ echo 'l.schemes=http' >> .hg/hgrc
216 $ echo 'l.prefix=lo' >> .hg/hgrc
216 $ echo 'l.prefix=lo' >> .hg/hgrc
217 $ echo 'l.username=user' >> .hg/hgrc
217 $ echo 'l.username=user' >> .hg/hgrc
218 $ echo 'l.password=pass' >> .hg/hgrc
218 $ echo 'l.password=pass' >> .hg/hgrc
219 $ hg id http://localhost:$HGPORT2/
219 $ hg id http://localhost:$HGPORT2/
220 5fed3813f7f5
220 5fed3813f7f5
221 $ hg id http://localhost:$HGPORT2/
221 $ hg id http://localhost:$HGPORT2/
222 5fed3813f7f5
222 5fed3813f7f5
223 $ hg id http://user@localhost:$HGPORT2/
223 $ hg id http://user@localhost:$HGPORT2/
224 5fed3813f7f5
224 5fed3813f7f5
225 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
225 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
226 streaming all changes
226 streaming all changes
227 7 files to transfer, 916 bytes of data
227 7 files to transfer, 916 bytes of data
228 transferred * bytes in * seconds (*/sec) (glob)
228 transferred * bytes in * seconds (*/sec) (glob)
229 searching for changes
229 searching for changes
230 no changes found
230 no changes found
231 updating to branch default
231 updating to branch default
232 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
232 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 --pull should override server's preferuncompressed
233 --pull should override server's preferuncompressed
234 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
234 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
235 requesting all changes
235 requesting all changes
236 adding changesets
236 adding changesets
237 adding manifests
237 adding manifests
238 adding file changes
238 adding file changes
239 added 2 changesets with 5 changes to 5 files
239 added 2 changesets with 5 changes to 5 files
240 new changesets 8b6053c928fe:5fed3813f7f5
240 new changesets 8b6053c928fe:5fed3813f7f5
241 updating to branch default
241 updating to branch default
242 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
243
243
244 $ hg id http://user2@localhost:$HGPORT2/
244 $ hg id http://user2@localhost:$HGPORT2/
245 abort: http authorization required for http://localhost:$HGPORT2/
245 abort: http authorization required for http://localhost:$HGPORT2/
246 [255]
246 [255]
247 $ hg id http://user:pass2@localhost:$HGPORT2/
247 $ hg id http://user:pass2@localhost:$HGPORT2/
248 abort: HTTP Error 403: no
248 abort: HTTP Error 403: no
249 [255]
249 [255]
250
250
251 $ hg -R dest tag -r tip top
251 $ hg -R dest tag -r tip top
252 $ hg -R dest push http://user:pass@localhost:$HGPORT2/
252 $ hg -R dest push http://user:pass@localhost:$HGPORT2/
253 pushing to http://user:***@localhost:$HGPORT2/
253 pushing to http://user:***@localhost:$HGPORT2/
254 searching for changes
254 searching for changes
255 remote: adding changesets
255 remote: adding changesets
256 remote: adding manifests
256 remote: adding manifests
257 remote: adding file changes
257 remote: adding file changes
258 remote: added 1 changesets with 1 changes to 1 files
258 remote: added 1 changesets with 1 changes to 1 files
259 $ hg rollback -q
259 $ hg rollback -q
260
260
261 $ sed 's/.*] "/"/' < ../access.log
261 $ sed 's/.*] "/"/' < ../access.log
262 "GET /?cmd=capabilities HTTP/1.1" 200 -
262 "GET /?cmd=capabilities HTTP/1.1" 401 -
263 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
263 "GET /?cmd=capabilities HTTP/1.1" 401 -
264 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
264 "GET /?cmd=capabilities HTTP/1.1" 401 -
265 "GET /?cmd=capabilities HTTP/1.1" 200 -
265 "GET /?cmd=capabilities HTTP/1.1" 200 -
266 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
266 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
267 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
267 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
268 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
269 "GET /?cmd=capabilities HTTP/1.1" 401 -
268 "GET /?cmd=capabilities HTTP/1.1" 200 -
270 "GET /?cmd=capabilities HTTP/1.1" 200 -
269 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
271 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
270 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
271 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
272 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
273 "GET /?cmd=capabilities HTTP/1.1" 200 -
274 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
275 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
276 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
272 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
277 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
273 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
274 "GET /?cmd=capabilities HTTP/1.1" 401 -
278 "GET /?cmd=capabilities HTTP/1.1" 200 -
275 "GET /?cmd=capabilities HTTP/1.1" 200 -
279 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
276 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
280 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
281 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
277 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
282 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
278 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
279 "GET /?cmd=capabilities HTTP/1.1" 401 -
283 "GET /?cmd=capabilities HTTP/1.1" 200 -
280 "GET /?cmd=capabilities HTTP/1.1" 200 -
284 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
281 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
285 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
286 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
282 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
287 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
283 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
284 "GET /?cmd=capabilities HTTP/1.1" 401 -
288 "GET /?cmd=capabilities HTTP/1.1" 200 -
285 "GET /?cmd=capabilities HTTP/1.1" 200 -
289 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
286 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
290 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
291 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
287 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
292 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
288 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
289 "GET /?cmd=capabilities HTTP/1.1" 401 -
293 "GET /?cmd=capabilities HTTP/1.1" 200 -
290 "GET /?cmd=capabilities HTTP/1.1" 200 -
294 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
291 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
295 "GET /?cmd=stream_out HTTP/1.1" 401 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
296 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
292 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
297 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
293 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
298 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
294 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
299 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
295 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
296 "GET /?cmd=capabilities HTTP/1.1" 401 -
300 "GET /?cmd=capabilities HTTP/1.1" 200 -
297 "GET /?cmd=capabilities HTTP/1.1" 200 -
301 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
302 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
298 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
303 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
299 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
304 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
300 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
305 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
301 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
306 "GET /?cmd=capabilities HTTP/1.1" 200 -
302 "GET /?cmd=capabilities HTTP/1.1" 401 -
307 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
303 "GET /?cmd=capabilities HTTP/1.1" 401 -
308 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
304 "GET /?cmd=capabilities HTTP/1.1" 403 -
309 "GET /?cmd=capabilities HTTP/1.1" 200 -
305 "GET /?cmd=capabilities HTTP/1.1" 401 -
310 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
311 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
312 "GET /?cmd=listkeys HTTP/1.1" 403 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
313 "GET /?cmd=capabilities HTTP/1.1" 200 -
306 "GET /?cmd=capabilities HTTP/1.1" 200 -
314 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
307 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
315 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
316 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
308 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
317 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
309 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
318 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
310 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
319 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
311 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
320 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
312 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
321 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
313 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524* (glob)
322 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
314 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
323
315
324 $ cd ..
316 $ cd ..
325
317
326 clone of serve with repo in root and unserved subrepo (issue2970)
318 clone of serve with repo in root and unserved subrepo (issue2970)
327
319
328 $ hg --cwd test init sub
320 $ hg --cwd test init sub
329 $ echo empty > test/sub/empty
321 $ echo empty > test/sub/empty
330 $ hg --cwd test/sub add empty
322 $ hg --cwd test/sub add empty
331 $ hg --cwd test/sub commit -qm 'add empty'
323 $ hg --cwd test/sub commit -qm 'add empty'
332 $ hg --cwd test/sub tag -r 0 something
324 $ hg --cwd test/sub tag -r 0 something
333 $ echo sub = sub > test/.hgsub
325 $ echo sub = sub > test/.hgsub
334 $ hg --cwd test add .hgsub
326 $ hg --cwd test add .hgsub
335 $ hg --cwd test commit -qm 'add subrepo'
327 $ hg --cwd test commit -qm 'add subrepo'
336 $ hg clone http://localhost:$HGPORT noslash-clone
328 $ hg clone http://localhost:$HGPORT noslash-clone
337 requesting all changes
329 requesting all changes
338 adding changesets
330 adding changesets
339 adding manifests
331 adding manifests
340 adding file changes
332 adding file changes
341 added 3 changesets with 7 changes to 7 files
333 added 3 changesets with 7 changes to 7 files
342 new changesets 8b6053c928fe:56f9bc90cce6
334 new changesets 8b6053c928fe:56f9bc90cce6
343 updating to branch default
335 updating to branch default
344 abort: HTTP Error 404: Not Found
336 abort: HTTP Error 404: Not Found
345 [255]
337 [255]
346 $ hg clone http://localhost:$HGPORT/ slash-clone
338 $ hg clone http://localhost:$HGPORT/ slash-clone
347 requesting all changes
339 requesting all changes
348 adding changesets
340 adding changesets
349 adding manifests
341 adding manifests
350 adding file changes
342 adding file changes
351 added 3 changesets with 7 changes to 7 files
343 added 3 changesets with 7 changes to 7 files
352 new changesets 8b6053c928fe:56f9bc90cce6
344 new changesets 8b6053c928fe:56f9bc90cce6
353 updating to branch default
345 updating to branch default
354 abort: HTTP Error 404: Not Found
346 abort: HTTP Error 404: Not Found
355 [255]
347 [255]
356
348
357 check error log
349 check error log
358
350
359 $ cat error.log
351 $ cat error.log
360
352
361 Check error reporting while pulling/cloning
353 Check error reporting while pulling/cloning
362
354
363 $ $RUNTESTDIR/killdaemons.py
355 $ $RUNTESTDIR/killdaemons.py
364 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
356 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
365 $ cat hg3.pid >> $DAEMON_PIDS
357 $ cat hg3.pid >> $DAEMON_PIDS
366 $ hg clone http://localhost:$HGPORT/ abort-clone
358 $ hg clone http://localhost:$HGPORT/ abort-clone
367 requesting all changes
359 requesting all changes
368 abort: remote error:
360 abort: remote error:
369 this is an exercise
361 this is an exercise
370 [255]
362 [255]
371 $ cat error.log
363 $ cat error.log
372
364
373 disable pull-based clones
365 disable pull-based clones
374
366
375 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
367 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
376 $ cat hg4.pid >> $DAEMON_PIDS
368 $ cat hg4.pid >> $DAEMON_PIDS
377 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
369 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
378 requesting all changes
370 requesting all changes
379 abort: remote error:
371 abort: remote error:
380 server has pull-based clones disabled
372 server has pull-based clones disabled
381 [255]
373 [255]
382
374
383 ... but keep stream clones working
375 ... but keep stream clones working
384
376
385 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
377 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
386 streaming all changes
378 streaming all changes
387 * files to transfer, * of data (glob)
379 * files to transfer, * of data (glob)
388 transferred * in * seconds (* KB/sec) (glob)
380 transferred * in * seconds (* KB/sec) (glob)
389 searching for changes
381 searching for changes
390 no changes found
382 no changes found
391
383
392 ... and also keep partial clones and pulls working
384 ... and also keep partial clones and pulls working
393 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
385 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
394 adding changesets
386 adding changesets
395 adding manifests
387 adding manifests
396 adding file changes
388 adding file changes
397 added 1 changesets with 4 changes to 4 files
389 added 1 changesets with 4 changes to 4 files
398 new changesets 8b6053c928fe
390 new changesets 8b6053c928fe
399 updating to branch default
391 updating to branch default
400 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
392 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
401 $ hg pull -R test-partial-clone
393 $ hg pull -R test-partial-clone
402 pulling from http://localhost:$HGPORT1/
394 pulling from http://localhost:$HGPORT1/
403 searching for changes
395 searching for changes
404 adding changesets
396 adding changesets
405 adding manifests
397 adding manifests
406 adding file changes
398 adding file changes
407 added 2 changesets with 3 changes to 3 files
399 added 2 changesets with 3 changes to 3 files
408 new changesets 5fed3813f7f5:56f9bc90cce6
400 new changesets 5fed3813f7f5:56f9bc90cce6
409 (run 'hg update' to get a working copy)
401 (run 'hg update' to get a working copy)
410
402
411 $ cat error.log
403 $ cat error.log
@@ -1,560 +1,552 b''
1 #require killdaemons serve
1 #require killdaemons serve
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo foo>foo
5 $ echo foo>foo
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
6 $ mkdir foo.d foo.d/bAr.hg.d foo.d/baR.d.hg
7 $ echo foo>foo.d/foo
7 $ echo foo>foo.d/foo
8 $ echo bar>foo.d/bAr.hg.d/BaR
8 $ echo bar>foo.d/bAr.hg.d/BaR
9 $ echo bar>foo.d/baR.d.hg/bAR
9 $ echo bar>foo.d/baR.d.hg/bAR
10 $ hg commit -A -m 1
10 $ hg commit -A -m 1
11 adding foo
11 adding foo
12 adding foo.d/bAr.hg.d/BaR
12 adding foo.d/bAr.hg.d/BaR
13 adding foo.d/baR.d.hg/bAR
13 adding foo.d/baR.d.hg/bAR
14 adding foo.d/foo
14 adding foo.d/foo
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
15 $ hg serve -p $HGPORT -d --pid-file=../hg1.pid -E ../error.log
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
16 $ hg serve --config server.uncompressed=False -p $HGPORT1 -d --pid-file=../hg2.pid
17
17
18 Test server address cannot be reused
18 Test server address cannot be reused
19
19
20 $ hg serve -p $HGPORT1 2>&1
20 $ hg serve -p $HGPORT1 2>&1
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
21 abort: cannot start server at 'localhost:$HGPORT1': $EADDRINUSE$
22 [255]
22 [255]
23
23
24 $ cd ..
24 $ cd ..
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
25 $ cat hg1.pid hg2.pid >> $DAEMON_PIDS
26
26
27 clone via stream
27 clone via stream
28
28
29 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
29 $ hg clone --stream http://localhost:$HGPORT/ copy 2>&1
30 streaming all changes
30 streaming all changes
31 6 files to transfer, 606 bytes of data
31 6 files to transfer, 606 bytes of data
32 transferred * bytes in * seconds (*/sec) (glob)
32 transferred * bytes in * seconds (*/sec) (glob)
33 searching for changes
33 searching for changes
34 no changes found
34 no changes found
35 updating to branch default
35 updating to branch default
36 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
36 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
37 $ hg verify -R copy
37 $ hg verify -R copy
38 checking changesets
38 checking changesets
39 checking manifests
39 checking manifests
40 crosschecking files in changesets and manifests
40 crosschecking files in changesets and manifests
41 checking files
41 checking files
42 4 files, 1 changesets, 4 total revisions
42 4 files, 1 changesets, 4 total revisions
43
43
44 try to clone via stream, should use pull instead
44 try to clone via stream, should use pull instead
45
45
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
46 $ hg clone --stream http://localhost:$HGPORT1/ copy2
47 warning: stream clone requested but server has them disabled
47 warning: stream clone requested but server has them disabled
48 requesting all changes
48 requesting all changes
49 adding changesets
49 adding changesets
50 adding manifests
50 adding manifests
51 adding file changes
51 adding file changes
52 added 1 changesets with 4 changes to 4 files
52 added 1 changesets with 4 changes to 4 files
53 new changesets 8b6053c928fe
53 new changesets 8b6053c928fe
54 updating to branch default
54 updating to branch default
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
56
56
57 try to clone via stream but missing requirements, so should use pull instead
57 try to clone via stream but missing requirements, so should use pull instead
58
58
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
59 $ cat > $TESTTMP/removesupportedformat.py << EOF
60 > from mercurial import localrepo
60 > from mercurial import localrepo
61 > def extsetup(ui):
61 > def extsetup(ui):
62 > localrepo.localrepository.supportedformats.remove('generaldelta')
62 > localrepo.localrepository.supportedformats.remove('generaldelta')
63 > EOF
63 > EOF
64
64
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
65 $ hg clone --config extensions.rsf=$TESTTMP/removesupportedformat.py --stream http://localhost:$HGPORT/ copy3
66 warning: stream clone requested but client is missing requirements: generaldelta
66 warning: stream clone requested but client is missing requirements: generaldelta
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
67 (see https://www.mercurial-scm.org/wiki/MissingRequirement for more information)
68 requesting all changes
68 requesting all changes
69 adding changesets
69 adding changesets
70 adding manifests
70 adding manifests
71 adding file changes
71 adding file changes
72 added 1 changesets with 4 changes to 4 files
72 added 1 changesets with 4 changes to 4 files
73 new changesets 8b6053c928fe
73 new changesets 8b6053c928fe
74 updating to branch default
74 updating to branch default
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
75 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
76
76
77 clone via pull
77 clone via pull
78
78
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
79 $ hg clone http://localhost:$HGPORT1/ copy-pull
80 requesting all changes
80 requesting all changes
81 adding changesets
81 adding changesets
82 adding manifests
82 adding manifests
83 adding file changes
83 adding file changes
84 added 1 changesets with 4 changes to 4 files
84 added 1 changesets with 4 changes to 4 files
85 new changesets 8b6053c928fe
85 new changesets 8b6053c928fe
86 updating to branch default
86 updating to branch default
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
87 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
88 $ hg verify -R copy-pull
88 $ hg verify -R copy-pull
89 checking changesets
89 checking changesets
90 checking manifests
90 checking manifests
91 crosschecking files in changesets and manifests
91 crosschecking files in changesets and manifests
92 checking files
92 checking files
93 4 files, 1 changesets, 4 total revisions
93 4 files, 1 changesets, 4 total revisions
94 $ cd test
94 $ cd test
95 $ echo bar > bar
95 $ echo bar > bar
96 $ hg commit -A -d '1 0' -m 2
96 $ hg commit -A -d '1 0' -m 2
97 adding bar
97 adding bar
98 $ cd ..
98 $ cd ..
99
99
100 clone over http with --update
100 clone over http with --update
101
101
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
102 $ hg clone http://localhost:$HGPORT1/ updated --update 0
103 requesting all changes
103 requesting all changes
104 adding changesets
104 adding changesets
105 adding manifests
105 adding manifests
106 adding file changes
106 adding file changes
107 added 2 changesets with 5 changes to 5 files
107 added 2 changesets with 5 changes to 5 files
108 new changesets 8b6053c928fe:5fed3813f7f5
108 new changesets 8b6053c928fe:5fed3813f7f5
109 updating to branch default
109 updating to branch default
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
110 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
111 $ hg log -r . -R updated
111 $ hg log -r . -R updated
112 changeset: 0:8b6053c928fe
112 changeset: 0:8b6053c928fe
113 user: test
113 user: test
114 date: Thu Jan 01 00:00:00 1970 +0000
114 date: Thu Jan 01 00:00:00 1970 +0000
115 summary: 1
115 summary: 1
116
116
117 $ rm -rf updated
117 $ rm -rf updated
118
118
119 incoming via HTTP
119 incoming via HTTP
120
120
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
121 $ hg clone http://localhost:$HGPORT1/ --rev 0 partial
122 adding changesets
122 adding changesets
123 adding manifests
123 adding manifests
124 adding file changes
124 adding file changes
125 added 1 changesets with 4 changes to 4 files
125 added 1 changesets with 4 changes to 4 files
126 new changesets 8b6053c928fe
126 new changesets 8b6053c928fe
127 updating to branch default
127 updating to branch default
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
128 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
129 $ cd partial
129 $ cd partial
130 $ touch LOCAL
130 $ touch LOCAL
131 $ hg ci -qAm LOCAL
131 $ hg ci -qAm LOCAL
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
132 $ hg incoming http://localhost:$HGPORT1/ --template '{desc}\n'
133 comparing with http://localhost:$HGPORT1/
133 comparing with http://localhost:$HGPORT1/
134 searching for changes
134 searching for changes
135 2
135 2
136 $ cd ..
136 $ cd ..
137
137
138 pull
138 pull
139
139
140 $ cd copy-pull
140 $ cd copy-pull
141 $ cat >> .hg/hgrc <<EOF
141 $ cat >> .hg/hgrc <<EOF
142 > [hooks]
142 > [hooks]
143 > changegroup = sh -c "printenv.py changegroup"
143 > changegroup = sh -c "printenv.py changegroup"
144 > EOF
144 > EOF
145 $ hg pull
145 $ hg pull
146 pulling from http://localhost:$HGPORT1/
146 pulling from http://localhost:$HGPORT1/
147 searching for changes
147 searching for changes
148 adding changesets
148 adding changesets
149 adding manifests
149 adding manifests
150 adding file changes
150 adding file changes
151 added 1 changesets with 1 changes to 1 files
151 added 1 changesets with 1 changes to 1 files
152 new changesets 5fed3813f7f5
152 new changesets 5fed3813f7f5
153 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
153 changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_NODE_LAST=5fed3813f7f5e1824344fdc9cf8f63bb662c292d HG_SOURCE=pull HG_TXNID=TXN:$ID$ HG_URL=http://localhost:$HGPORT1/
154 (run 'hg update' to get a working copy)
154 (run 'hg update' to get a working copy)
155 $ cd ..
155 $ cd ..
156
156
157 clone from invalid URL
157 clone from invalid URL
158
158
159 $ hg clone http://localhost:$HGPORT/bad
159 $ hg clone http://localhost:$HGPORT/bad
160 abort: HTTP Error 404: Not Found
160 abort: HTTP Error 404: Not Found
161 [255]
161 [255]
162
162
163 test http authentication
163 test http authentication
164 + use the same server to test server side streaming preference
164 + use the same server to test server side streaming preference
165
165
166 $ cd test
166 $ cd test
167 $ cat << EOT > userpass.py
167 $ cat << EOT > userpass.py
168 > import base64
168 > import base64
169 > from mercurial.hgweb import common
169 > from mercurial.hgweb import common
170 > def perform_authentication(hgweb, req, op):
170 > def perform_authentication(hgweb, req, op):
171 > auth = req.env.get('HTTP_AUTHORIZATION')
171 > auth = req.env.get('HTTP_AUTHORIZATION')
172 > if not auth:
172 > if not auth:
173 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
173 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
174 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
174 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
175 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
175 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
176 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
176 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
177 > def extsetup():
177 > def extsetup():
178 > common.permhooks.insert(0, perform_authentication)
178 > common.permhooks.insert(0, perform_authentication)
179 > EOT
179 > EOT
180 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
180 $ hg serve --config extensions.x=userpass.py -p $HGPORT2 -d --pid-file=pid \
181 > --config server.preferuncompressed=True \
181 > --config server.preferuncompressed=True \
182 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
182 > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
183 $ cat pid >> $DAEMON_PIDS
183 $ cat pid >> $DAEMON_PIDS
184
184
185 $ cat << EOF > get_pass.py
185 $ cat << EOF > get_pass.py
186 > import getpass
186 > import getpass
187 > def newgetpass(arg):
187 > def newgetpass(arg):
188 > return "pass"
188 > return "pass"
189 > getpass.getpass = newgetpass
189 > getpass.getpass = newgetpass
190 > EOF
190 > EOF
191
191
192 $ hg id http://localhost:$HGPORT2/
192 $ hg id http://localhost:$HGPORT2/
193 abort: http authorization required for http://localhost:$HGPORT2/
193 abort: http authorization required for http://localhost:$HGPORT2/
194 [255]
194 [255]
195 $ hg id http://localhost:$HGPORT2/
195 $ hg id http://localhost:$HGPORT2/
196 abort: http authorization required for http://localhost:$HGPORT2/
196 abort: http authorization required for http://localhost:$HGPORT2/
197 [255]
197 [255]
198 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
198 $ hg id --config ui.interactive=true --config extensions.getpass=get_pass.py http://user@localhost:$HGPORT2/
199 http authorization required for http://localhost:$HGPORT2/
199 http authorization required for http://localhost:$HGPORT2/
200 realm: mercurial
200 realm: mercurial
201 user: user
201 user: user
202 password: 5fed3813f7f5
202 password: 5fed3813f7f5
203 $ hg id http://user:pass@localhost:$HGPORT2/
203 $ hg id http://user:pass@localhost:$HGPORT2/
204 5fed3813f7f5
204 5fed3813f7f5
205 $ echo '[auth]' >> .hg/hgrc
205 $ echo '[auth]' >> .hg/hgrc
206 $ echo 'l.schemes=http' >> .hg/hgrc
206 $ echo 'l.schemes=http' >> .hg/hgrc
207 $ echo 'l.prefix=lo' >> .hg/hgrc
207 $ echo 'l.prefix=lo' >> .hg/hgrc
208 $ echo 'l.username=user' >> .hg/hgrc
208 $ echo 'l.username=user' >> .hg/hgrc
209 $ echo 'l.password=pass' >> .hg/hgrc
209 $ echo 'l.password=pass' >> .hg/hgrc
210 $ hg id http://localhost:$HGPORT2/
210 $ hg id http://localhost:$HGPORT2/
211 5fed3813f7f5
211 5fed3813f7f5
212 $ hg id http://localhost:$HGPORT2/
212 $ hg id http://localhost:$HGPORT2/
213 5fed3813f7f5
213 5fed3813f7f5
214 $ hg id http://user@localhost:$HGPORT2/
214 $ hg id http://user@localhost:$HGPORT2/
215 5fed3813f7f5
215 5fed3813f7f5
216 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
216 $ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
217 streaming all changes
217 streaming all changes
218 7 files to transfer, 916 bytes of data
218 7 files to transfer, 916 bytes of data
219 transferred * bytes in * seconds (*/sec) (glob)
219 transferred * bytes in * seconds (*/sec) (glob)
220 searching for changes
220 searching for changes
221 no changes found
221 no changes found
222 updating to branch default
222 updating to branch default
223 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
223 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
224 --pull should override server's preferuncompressed
224 --pull should override server's preferuncompressed
225 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
225 $ hg clone --pull http://user:pass@localhost:$HGPORT2/ dest-pull 2>&1
226 requesting all changes
226 requesting all changes
227 adding changesets
227 adding changesets
228 adding manifests
228 adding manifests
229 adding file changes
229 adding file changes
230 added 2 changesets with 5 changes to 5 files
230 added 2 changesets with 5 changes to 5 files
231 new changesets 8b6053c928fe:5fed3813f7f5
231 new changesets 8b6053c928fe:5fed3813f7f5
232 updating to branch default
232 updating to branch default
233 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
233 5 files updated, 0 files merged, 0 files removed, 0 files unresolved
234
234
235 $ hg id http://user2@localhost:$HGPORT2/
235 $ hg id http://user2@localhost:$HGPORT2/
236 abort: http authorization required for http://localhost:$HGPORT2/
236 abort: http authorization required for http://localhost:$HGPORT2/
237 [255]
237 [255]
238 $ hg id http://user:pass2@localhost:$HGPORT2/
238 $ hg id http://user:pass2@localhost:$HGPORT2/
239 abort: HTTP Error 403: no
239 abort: HTTP Error 403: no
240 [255]
240 [255]
241
241
242 $ hg -R dest tag -r tip top
242 $ hg -R dest tag -r tip top
243 $ hg -R dest push http://user:pass@localhost:$HGPORT2/
243 $ hg -R dest push http://user:pass@localhost:$HGPORT2/
244 pushing to http://user:***@localhost:$HGPORT2/
244 pushing to http://user:***@localhost:$HGPORT2/
245 searching for changes
245 searching for changes
246 remote: adding changesets
246 remote: adding changesets
247 remote: adding manifests
247 remote: adding manifests
248 remote: adding file changes
248 remote: adding file changes
249 remote: added 1 changesets with 1 changes to 1 files
249 remote: added 1 changesets with 1 changes to 1 files
250 $ hg rollback -q
250 $ hg rollback -q
251 $ hg -R dest push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
251 $ hg -R dest push http://user:pass@localhost:$HGPORT2/ --debug --config devel.debug.peer-request=yes
252 pushing to http://user:***@localhost:$HGPORT2/
252 pushing to http://user:***@localhost:$HGPORT2/
253 using http://localhost:$HGPORT2/
253 using http://localhost:$HGPORT2/
254 http auth: user user, password ****
254 http auth: user user, password ****
255 sending capabilities command
255 sending capabilities command
256 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
256 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=capabilities
257 http auth: user user, password ****
257 devel-peer-request: finished in *.???? seconds (200) (glob)
258 devel-peer-request: finished in *.???? seconds (200) (glob)
258 query 1; heads
259 query 1; heads
259 sending batch command
260 sending batch command
260 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
261 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=batch
261 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
262 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
262 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
263 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
263 devel-peer-request: 68 bytes of commands arguments in headers
264 devel-peer-request: 68 bytes of commands arguments in headers
264 devel-peer-request: finished in *.???? seconds (200) (glob)
265 devel-peer-request: finished in *.???? seconds (200) (glob)
265 searching for changes
266 searching for changes
266 all remote heads known locally
267 all remote heads known locally
267 preparing listkeys for "phases"
268 preparing listkeys for "phases"
268 sending listkeys command
269 sending listkeys command
269 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
270 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
270 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
271 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
271 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
272 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
272 devel-peer-request: 16 bytes of commands arguments in headers
273 devel-peer-request: 16 bytes of commands arguments in headers
273 http auth: user user, password ****
274 devel-peer-request: finished in *.???? seconds (200) (glob)
274 devel-peer-request: finished in *.???? seconds (200) (glob)
275 received listkey for "phases": 58 bytes
275 received listkey for "phases": 58 bytes
276 checking for updated bookmarks
276 checking for updated bookmarks
277 preparing listkeys for "bookmarks"
277 preparing listkeys for "bookmarks"
278 sending listkeys command
278 sending listkeys command
279 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
279 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
280 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
280 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
281 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
281 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
282 devel-peer-request: 19 bytes of commands arguments in headers
282 devel-peer-request: 19 bytes of commands arguments in headers
283 devel-peer-request: finished in *.???? seconds (200) (glob)
283 devel-peer-request: finished in *.???? seconds (200) (glob)
284 received listkey for "bookmarks": 0 bytes
284 received listkey for "bookmarks": 0 bytes
285 sending branchmap command
285 sending branchmap command
286 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
286 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
287 devel-peer-request: Vary X-HgProto-1
287 devel-peer-request: Vary X-HgProto-1
288 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
288 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
289 devel-peer-request: finished in *.???? seconds (200) (glob)
289 devel-peer-request: finished in *.???? seconds (200) (glob)
290 sending branchmap command
290 sending branchmap command
291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
291 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=branchmap
292 devel-peer-request: Vary X-HgProto-1
292 devel-peer-request: Vary X-HgProto-1
293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
293 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
294 devel-peer-request: finished in *.???? seconds (200) (glob)
294 devel-peer-request: finished in *.???? seconds (200) (glob)
295 preparing listkeys for "bookmarks"
295 preparing listkeys for "bookmarks"
296 sending listkeys command
296 sending listkeys command
297 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
297 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
298 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
298 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
299 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
299 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
300 devel-peer-request: 19 bytes of commands arguments in headers
300 devel-peer-request: 19 bytes of commands arguments in headers
301 devel-peer-request: finished in *.???? seconds (200) (glob)
301 devel-peer-request: finished in *.???? seconds (200) (glob)
302 received listkey for "bookmarks": 0 bytes
302 received listkey for "bookmarks": 0 bytes
303 1 changesets found
303 1 changesets found
304 list of changesets:
304 list of changesets:
305 7f4e523d01f2cc3765ac8934da3d14db775ff872
305 7f4e523d01f2cc3765ac8934da3d14db775ff872
306 bundle2-output-bundle: "HG20", 5 parts total
306 bundle2-output-bundle: "HG20", 5 parts total
307 bundle2-output-part: "replycaps" 188 bytes payload
307 bundle2-output-part: "replycaps" 188 bytes payload
308 bundle2-output-part: "check:phases" 24 bytes payload
308 bundle2-output-part: "check:phases" 24 bytes payload
309 bundle2-output-part: "check:heads" streamed payload
309 bundle2-output-part: "check:heads" streamed payload
310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
310 bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
311 bundle2-output-part: "phase-heads" 24 bytes payload
311 bundle2-output-part: "phase-heads" 24 bytes payload
312 sending unbundle command
312 sending unbundle command
313 sending 996 bytes
313 sending 996 bytes
314 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
314 devel-peer-request: POST http://localhost:$HGPORT2/?cmd=unbundle
315 devel-peer-request: Content-length 996
315 devel-peer-request: Content-length 996
316 devel-peer-request: Content-type application/mercurial-0.1
316 devel-peer-request: Content-type application/mercurial-0.1
317 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
317 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
318 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
318 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
319 devel-peer-request: 16 bytes of commands arguments in headers
319 devel-peer-request: 16 bytes of commands arguments in headers
320 devel-peer-request: 996 bytes of data
320 devel-peer-request: 996 bytes of data
321 devel-peer-request: finished in *.???? seconds (200) (glob)
321 devel-peer-request: finished in *.???? seconds (200) (glob)
322 bundle2-input-bundle: no-transaction
322 bundle2-input-bundle: no-transaction
323 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
323 bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) supported
324 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
324 bundle2-input-part: "output" (advisory) (params: 0 advisory) supported
325 bundle2-input-part: total payload size 100
325 bundle2-input-part: total payload size 100
326 remote: adding changesets
326 remote: adding changesets
327 remote: adding manifests
327 remote: adding manifests
328 remote: adding file changes
328 remote: adding file changes
329 remote: added 1 changesets with 1 changes to 1 files
329 remote: added 1 changesets with 1 changes to 1 files
330 bundle2-input-part: "output" (advisory) supported
330 bundle2-input-part: "output" (advisory) supported
331 bundle2-input-bundle: 2 parts total
331 bundle2-input-bundle: 2 parts total
332 preparing listkeys for "phases"
332 preparing listkeys for "phases"
333 sending listkeys command
333 sending listkeys command
334 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
334 devel-peer-request: GET http://localhost:$HGPORT2/?cmd=listkeys
335 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
335 devel-peer-request: Vary X-HgArg-1,X-HgProto-1
336 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
336 devel-peer-request: X-hgproto-1 0.1 0.2 comp=$USUAL_COMPRESSIONS$
337 devel-peer-request: 16 bytes of commands arguments in headers
337 devel-peer-request: 16 bytes of commands arguments in headers
338 devel-peer-request: finished in *.???? seconds (200) (glob)
338 devel-peer-request: finished in *.???? seconds (200) (glob)
339 received listkey for "phases": 15 bytes
339 received listkey for "phases": 15 bytes
340 $ hg rollback -q
340 $ hg rollback -q
341
341
342 $ sed 's/.*] "/"/' < ../access.log
342 $ sed 's/.*] "/"/' < ../access.log
343 "GET /?cmd=capabilities HTTP/1.1" 200 -
343 "GET /?cmd=capabilities HTTP/1.1" 401 -
344 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
344 "GET /?cmd=capabilities HTTP/1.1" 401 -
345 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
345 "GET /?cmd=capabilities HTTP/1.1" 401 -
346 "GET /?cmd=capabilities HTTP/1.1" 200 -
346 "GET /?cmd=capabilities HTTP/1.1" 200 -
347 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
347 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
348 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
348 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
349 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
350 "GET /?cmd=capabilities HTTP/1.1" 401 -
349 "GET /?cmd=capabilities HTTP/1.1" 200 -
351 "GET /?cmd=capabilities HTTP/1.1" 200 -
350 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
352 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
351 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
352 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
353 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
354 "GET /?cmd=capabilities HTTP/1.1" 200 -
355 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
356 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
357 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
353 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
358 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
354 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
355 "GET /?cmd=capabilities HTTP/1.1" 401 -
359 "GET /?cmd=capabilities HTTP/1.1" 200 -
356 "GET /?cmd=capabilities HTTP/1.1" 200 -
360 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
357 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
361 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
362 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
358 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
363 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
359 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
360 "GET /?cmd=capabilities HTTP/1.1" 401 -
364 "GET /?cmd=capabilities HTTP/1.1" 200 -
361 "GET /?cmd=capabilities HTTP/1.1" 200 -
365 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
362 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
366 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
367 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
363 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
368 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
364 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
365 "GET /?cmd=capabilities HTTP/1.1" 401 -
369 "GET /?cmd=capabilities HTTP/1.1" 200 -
366 "GET /?cmd=capabilities HTTP/1.1" 200 -
370 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
367 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
371 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
372 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
368 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
373 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
369 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
370 "GET /?cmd=capabilities HTTP/1.1" 401 -
374 "GET /?cmd=capabilities HTTP/1.1" 200 -
371 "GET /?cmd=capabilities HTTP/1.1" 200 -
375 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
372 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
376 "GET /?cmd=stream_out HTTP/1.1" 401 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
377 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
373 "GET /?cmd=stream_out HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
378 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
374 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D5fed3813f7f5e1824344fdc9cf8f63bb662c292d x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
379 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
375 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=0&common=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
376 "GET /?cmd=capabilities HTTP/1.1" 401 -
380 "GET /?cmd=capabilities HTTP/1.1" 200 -
377 "GET /?cmd=capabilities HTTP/1.1" 200 -
381 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
378 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
382 "GET /?cmd=getbundle HTTP/1.1" 401 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
383 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
379 "GET /?cmd=getbundle HTTP/1.1" 200 - x-hgarg-1:bookmarks=1&$USUAL_BUNDLE_CAPS$&cg=1&common=0000000000000000000000000000000000000000&heads=5fed3813f7f5e1824344fdc9cf8f63bb662c292d&listkeys=bookmarks&phases=1 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
384 "GET /?cmd=capabilities HTTP/1.1" 200 -
380 "GET /?cmd=capabilities HTTP/1.1" 401 -
385 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
381 "GET /?cmd=capabilities HTTP/1.1" 401 -
386 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
382 "GET /?cmd=capabilities HTTP/1.1" 403 -
387 "GET /?cmd=capabilities HTTP/1.1" 200 -
383 "GET /?cmd=capabilities HTTP/1.1" 401 -
388 "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
389 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
390 "GET /?cmd=listkeys HTTP/1.1" 403 - x-hgarg-1:namespace=namespaces x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
391 "GET /?cmd=capabilities HTTP/1.1" 200 -
384 "GET /?cmd=capabilities HTTP/1.1" 200 -
392 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
385 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
393 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
394 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
386 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
395 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
387 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
396 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
388 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
397 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
389 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
398 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
390 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
399 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
391 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365* (glob)
400 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
392 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
393 "GET /?cmd=capabilities HTTP/1.1" 401 -
401 "GET /?cmd=capabilities HTTP/1.1" 200 -
394 "GET /?cmd=capabilities HTTP/1.1" 200 -
402 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
395 "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
403 "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
404 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
396 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
405 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
397 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
406 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
398 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
407 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
399 "GET /?cmd=branchmap HTTP/1.1" 200 - x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
408 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
400 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
409 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
401 "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=666f726365 x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
410 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
402 "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$
411
403
412 $ cd ..
404 $ cd ..
413
405
414 clone of serve with repo in root and unserved subrepo (issue2970)
406 clone of serve with repo in root and unserved subrepo (issue2970)
415
407
416 $ hg --cwd test init sub
408 $ hg --cwd test init sub
417 $ echo empty > test/sub/empty
409 $ echo empty > test/sub/empty
418 $ hg --cwd test/sub add empty
410 $ hg --cwd test/sub add empty
419 $ hg --cwd test/sub commit -qm 'add empty'
411 $ hg --cwd test/sub commit -qm 'add empty'
420 $ hg --cwd test/sub tag -r 0 something
412 $ hg --cwd test/sub tag -r 0 something
421 $ echo sub = sub > test/.hgsub
413 $ echo sub = sub > test/.hgsub
422 $ hg --cwd test add .hgsub
414 $ hg --cwd test add .hgsub
423 $ hg --cwd test commit -qm 'add subrepo'
415 $ hg --cwd test commit -qm 'add subrepo'
424 $ hg clone http://localhost:$HGPORT noslash-clone
416 $ hg clone http://localhost:$HGPORT noslash-clone
425 requesting all changes
417 requesting all changes
426 adding changesets
418 adding changesets
427 adding manifests
419 adding manifests
428 adding file changes
420 adding file changes
429 added 3 changesets with 7 changes to 7 files
421 added 3 changesets with 7 changes to 7 files
430 new changesets 8b6053c928fe:56f9bc90cce6
422 new changesets 8b6053c928fe:56f9bc90cce6
431 updating to branch default
423 updating to branch default
432 abort: HTTP Error 404: Not Found
424 abort: HTTP Error 404: Not Found
433 [255]
425 [255]
434 $ hg clone http://localhost:$HGPORT/ slash-clone
426 $ hg clone http://localhost:$HGPORT/ slash-clone
435 requesting all changes
427 requesting all changes
436 adding changesets
428 adding changesets
437 adding manifests
429 adding manifests
438 adding file changes
430 adding file changes
439 added 3 changesets with 7 changes to 7 files
431 added 3 changesets with 7 changes to 7 files
440 new changesets 8b6053c928fe:56f9bc90cce6
432 new changesets 8b6053c928fe:56f9bc90cce6
441 updating to branch default
433 updating to branch default
442 abort: HTTP Error 404: Not Found
434 abort: HTTP Error 404: Not Found
443 [255]
435 [255]
444
436
445 check error log
437 check error log
446
438
447 $ cat error.log
439 $ cat error.log
448
440
449 check abort error reporting while pulling/cloning
441 check abort error reporting while pulling/cloning
450
442
451 $ $RUNTESTDIR/killdaemons.py
443 $ $RUNTESTDIR/killdaemons.py
452 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
444 $ hg serve -R test -p $HGPORT -d --pid-file=hg3.pid -E error.log --config extensions.crash=${TESTDIR}/crashgetbundler.py
453 $ cat hg3.pid >> $DAEMON_PIDS
445 $ cat hg3.pid >> $DAEMON_PIDS
454 $ hg clone http://localhost:$HGPORT/ abort-clone
446 $ hg clone http://localhost:$HGPORT/ abort-clone
455 requesting all changes
447 requesting all changes
456 remote: abort: this is an exercise
448 remote: abort: this is an exercise
457 abort: pull failed on remote
449 abort: pull failed on remote
458 [255]
450 [255]
459 $ cat error.log
451 $ cat error.log
460
452
461 disable pull-based clones
453 disable pull-based clones
462
454
463 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
455 $ hg serve -R test -p $HGPORT1 -d --pid-file=hg4.pid -E error.log --config server.disablefullbundle=True
464 $ cat hg4.pid >> $DAEMON_PIDS
456 $ cat hg4.pid >> $DAEMON_PIDS
465 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
457 $ hg clone http://localhost:$HGPORT1/ disable-pull-clone
466 requesting all changes
458 requesting all changes
467 remote: abort: server has pull-based clones disabled
459 remote: abort: server has pull-based clones disabled
468 abort: pull failed on remote
460 abort: pull failed on remote
469 (remove --pull if specified or upgrade Mercurial)
461 (remove --pull if specified or upgrade Mercurial)
470 [255]
462 [255]
471
463
472 ... but keep stream clones working
464 ... but keep stream clones working
473
465
474 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
466 $ hg clone --stream --noupdate http://localhost:$HGPORT1/ test-stream-clone
475 streaming all changes
467 streaming all changes
476 * files to transfer, * of data (glob)
468 * files to transfer, * of data (glob)
477 transferred * in * seconds (*/sec) (glob)
469 transferred * in * seconds (*/sec) (glob)
478 searching for changes
470 searching for changes
479 no changes found
471 no changes found
480 $ cat error.log
472 $ cat error.log
481
473
482 ... and also keep partial clones and pulls working
474 ... and also keep partial clones and pulls working
483 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
475 $ hg clone http://localhost:$HGPORT1 --rev 0 test-partial-clone
484 adding changesets
476 adding changesets
485 adding manifests
477 adding manifests
486 adding file changes
478 adding file changes
487 added 1 changesets with 4 changes to 4 files
479 added 1 changesets with 4 changes to 4 files
488 new changesets 8b6053c928fe
480 new changesets 8b6053c928fe
489 updating to branch default
481 updating to branch default
490 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
482 4 files updated, 0 files merged, 0 files removed, 0 files unresolved
491 $ hg pull -R test-partial-clone
483 $ hg pull -R test-partial-clone
492 pulling from http://localhost:$HGPORT1/
484 pulling from http://localhost:$HGPORT1/
493 searching for changes
485 searching for changes
494 adding changesets
486 adding changesets
495 adding manifests
487 adding manifests
496 adding file changes
488 adding file changes
497 added 2 changesets with 3 changes to 3 files
489 added 2 changesets with 3 changes to 3 files
498 new changesets 5fed3813f7f5:56f9bc90cce6
490 new changesets 5fed3813f7f5:56f9bc90cce6
499 (run 'hg update' to get a working copy)
491 (run 'hg update' to get a working copy)
500
492
501 corrupt cookies file should yield a warning
493 corrupt cookies file should yield a warning
502
494
503 $ cat > $TESTTMP/cookies.txt << EOF
495 $ cat > $TESTTMP/cookies.txt << EOF
504 > bad format
496 > bad format
505 > EOF
497 > EOF
506
498
507 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
499 $ hg --config auth.cookiefile=$TESTTMP/cookies.txt id http://localhost:$HGPORT/
508 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
500 (error loading cookie file $TESTTMP/cookies.txt: '*/cookies.txt' does not look like a Netscape format cookies file; continuing without cookies) (glob)
509 56f9bc90cce6
501 56f9bc90cce6
510
502
511 $ killdaemons.py
503 $ killdaemons.py
512
504
513 Create dummy authentication handler that looks for cookies. It doesn't do anything
505 Create dummy authentication handler that looks for cookies. It doesn't do anything
514 useful. It just raises an HTTP 500 with details about the Cookie request header.
506 useful. It just raises an HTTP 500 with details about the Cookie request header.
515 We raise HTTP 500 because its message is printed in the abort message.
507 We raise HTTP 500 because its message is printed in the abort message.
516
508
517 $ cat > cookieauth.py << EOF
509 $ cat > cookieauth.py << EOF
518 > from mercurial import util
510 > from mercurial import util
519 > from mercurial.hgweb import common
511 > from mercurial.hgweb import common
520 > def perform_authentication(hgweb, req, op):
512 > def perform_authentication(hgweb, req, op):
521 > cookie = req.env.get('HTTP_COOKIE')
513 > cookie = req.env.get('HTTP_COOKIE')
522 > if not cookie:
514 > if not cookie:
523 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'no-cookie')
515 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'no-cookie')
524 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'Cookie: %s' % cookie)
516 > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'Cookie: %s' % cookie)
525 > def extsetup():
517 > def extsetup():
526 > common.permhooks.insert(0, perform_authentication)
518 > common.permhooks.insert(0, perform_authentication)
527 > EOF
519 > EOF
528
520
529 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
521 $ hg serve --config extensions.cookieauth=cookieauth.py -R test -p $HGPORT -d --pid-file=pid
530 $ cat pid > $DAEMON_PIDS
522 $ cat pid > $DAEMON_PIDS
531
523
532 Request without cookie sent should fail due to lack of cookie
524 Request without cookie sent should fail due to lack of cookie
533
525
534 $ hg id http://localhost:$HGPORT
526 $ hg id http://localhost:$HGPORT
535 abort: HTTP Error 500: no-cookie
527 abort: HTTP Error 500: no-cookie
536 [255]
528 [255]
537
529
538 Populate a cookies file
530 Populate a cookies file
539
531
540 $ cat > cookies.txt << EOF
532 $ cat > cookies.txt << EOF
541 > # HTTP Cookie File
533 > # HTTP Cookie File
542 > # Expiration is 2030-01-01 at midnight
534 > # Expiration is 2030-01-01 at midnight
543 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
535 > .example.com TRUE / FALSE 1893456000 hgkey examplevalue
544 > EOF
536 > EOF
545
537
546 Should not send a cookie for another domain
538 Should not send a cookie for another domain
547
539
548 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
540 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
549 abort: HTTP Error 500: no-cookie
541 abort: HTTP Error 500: no-cookie
550 [255]
542 [255]
551
543
552 Add a cookie entry for our test server and verify it is sent
544 Add a cookie entry for our test server and verify it is sent
553
545
554 $ cat >> cookies.txt << EOF
546 $ cat >> cookies.txt << EOF
555 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
547 > localhost.local FALSE / FALSE 1893456000 hgkey localhostvalue
556 > EOF
548 > EOF
557
549
558 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
550 $ hg --config auth.cookiefile=cookies.txt id http://localhost:$HGPORT/
559 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
551 abort: HTTP Error 500: Cookie: hgkey=localhostvalue
560 [255]
552 [255]
@@ -1,454 +1,454 b''
1 This file contains testcases that tend to be related to the wire protocol part
1 This file contains testcases that tend to be related to the wire protocol part
2 of largefiles.
2 of largefiles.
3
3
4 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
4 $ USERCACHE="$TESTTMP/cache"; export USERCACHE
5 $ mkdir "${USERCACHE}"
5 $ mkdir "${USERCACHE}"
6 $ cat >> $HGRCPATH <<EOF
6 $ cat >> $HGRCPATH <<EOF
7 > [extensions]
7 > [extensions]
8 > largefiles=
8 > largefiles=
9 > purge=
9 > purge=
10 > rebase=
10 > rebase=
11 > transplant=
11 > transplant=
12 > [phases]
12 > [phases]
13 > publish=False
13 > publish=False
14 > [largefiles]
14 > [largefiles]
15 > minsize=2
15 > minsize=2
16 > patterns=glob:**.dat
16 > patterns=glob:**.dat
17 > usercache=${USERCACHE}
17 > usercache=${USERCACHE}
18 > [web]
18 > [web]
19 > allow_archive = zip
19 > allow_archive = zip
20 > [hooks]
20 > [hooks]
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
21 > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status"
22 > EOF
22 > EOF
23
23
24
24
25 #if serve
25 #if serve
26 vanilla clients not locked out from largefiles servers on vanilla repos
26 vanilla clients not locked out from largefiles servers on vanilla repos
27 $ mkdir r1
27 $ mkdir r1
28 $ cd r1
28 $ cd r1
29 $ hg init
29 $ hg init
30 $ echo c1 > f1
30 $ echo c1 > f1
31 $ hg add f1
31 $ hg add f1
32 $ hg commit -m "m1"
32 $ hg commit -m "m1"
33 Invoking status precommit hook
33 Invoking status precommit hook
34 A f1
34 A f1
35 $ cd ..
35 $ cd ..
36 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
36 $ hg serve -R r1 -d -p $HGPORT --pid-file hg.pid
37 $ cat hg.pid >> $DAEMON_PIDS
37 $ cat hg.pid >> $DAEMON_PIDS
38 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
38 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT r2
39 requesting all changes
39 requesting all changes
40 adding changesets
40 adding changesets
41 adding manifests
41 adding manifests
42 adding file changes
42 adding file changes
43 added 1 changesets with 1 changes to 1 files
43 added 1 changesets with 1 changes to 1 files
44 new changesets b6eb3a2e2efe
44 new changesets b6eb3a2e2efe
45 updating to branch default
45 updating to branch default
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
46 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
47
47
48 largefiles clients still work with vanilla servers
48 largefiles clients still work with vanilla servers
49 $ hg serve --config extensions.largefiles=! -R r1 -d -p $HGPORT1 --pid-file hg.pid
49 $ hg serve --config extensions.largefiles=! -R r1 -d -p $HGPORT1 --pid-file hg.pid
50 $ cat hg.pid >> $DAEMON_PIDS
50 $ cat hg.pid >> $DAEMON_PIDS
51 $ hg clone http://localhost:$HGPORT1 r3
51 $ hg clone http://localhost:$HGPORT1 r3
52 requesting all changes
52 requesting all changes
53 adding changesets
53 adding changesets
54 adding manifests
54 adding manifests
55 adding file changes
55 adding file changes
56 added 1 changesets with 1 changes to 1 files
56 added 1 changesets with 1 changes to 1 files
57 new changesets b6eb3a2e2efe
57 new changesets b6eb3a2e2efe
58 updating to branch default
58 updating to branch default
59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 #endif
60 #endif
61
61
62 vanilla clients locked out from largefiles http repos
62 vanilla clients locked out from largefiles http repos
63 $ mkdir r4
63 $ mkdir r4
64 $ cd r4
64 $ cd r4
65 $ hg init
65 $ hg init
66 $ echo c1 > f1
66 $ echo c1 > f1
67 $ hg add --large f1
67 $ hg add --large f1
68 $ hg commit -m "m1"
68 $ hg commit -m "m1"
69 Invoking status precommit hook
69 Invoking status precommit hook
70 A f1
70 A f1
71 $ cd ..
71 $ cd ..
72
72
73 largefiles can be pushed locally (issue3583)
73 largefiles can be pushed locally (issue3583)
74 $ hg init dest
74 $ hg init dest
75 $ cd r4
75 $ cd r4
76 $ hg outgoing ../dest
76 $ hg outgoing ../dest
77 comparing with ../dest
77 comparing with ../dest
78 searching for changes
78 searching for changes
79 changeset: 0:639881c12b4c
79 changeset: 0:639881c12b4c
80 tag: tip
80 tag: tip
81 user: test
81 user: test
82 date: Thu Jan 01 00:00:00 1970 +0000
82 date: Thu Jan 01 00:00:00 1970 +0000
83 summary: m1
83 summary: m1
84
84
85 $ hg push ../dest
85 $ hg push ../dest
86 pushing to ../dest
86 pushing to ../dest
87 searching for changes
87 searching for changes
88 adding changesets
88 adding changesets
89 adding manifests
89 adding manifests
90 adding file changes
90 adding file changes
91 added 1 changesets with 1 changes to 1 files
91 added 1 changesets with 1 changes to 1 files
92
92
93 exit code with nothing outgoing (issue3611)
93 exit code with nothing outgoing (issue3611)
94 $ hg outgoing ../dest
94 $ hg outgoing ../dest
95 comparing with ../dest
95 comparing with ../dest
96 searching for changes
96 searching for changes
97 no changes found
97 no changes found
98 [1]
98 [1]
99 $ cd ..
99 $ cd ..
100
100
101 #if serve
101 #if serve
102 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
102 $ hg serve -R r4 -d -p $HGPORT2 --pid-file hg.pid
103 $ cat hg.pid >> $DAEMON_PIDS
103 $ cat hg.pid >> $DAEMON_PIDS
104 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
104 $ hg --config extensions.largefiles=! clone http://localhost:$HGPORT2 r5
105 abort: remote error:
105 abort: remote error:
106
106
107 This repository uses the largefiles extension.
107 This repository uses the largefiles extension.
108
108
109 Please enable it in your Mercurial config file.
109 Please enable it in your Mercurial config file.
110 [255]
110 [255]
111
111
112 used all HGPORTs, kill all daemons
112 used all HGPORTs, kill all daemons
113 $ killdaemons.py
113 $ killdaemons.py
114 #endif
114 #endif
115
115
116 vanilla clients locked out from largefiles ssh repos
116 vanilla clients locked out from largefiles ssh repos
117 $ hg --config extensions.largefiles=! clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
117 $ hg --config extensions.largefiles=! clone -e "\"$PYTHON\" \"$TESTDIR/dummyssh\"" ssh://user@dummy/r4 r5
118 remote:
118 remote:
119 remote: This repository uses the largefiles extension.
119 remote: This repository uses the largefiles extension.
120 remote:
120 remote:
121 remote: Please enable it in your Mercurial config file.
121 remote: Please enable it in your Mercurial config file.
122 remote:
122 remote:
123 remote: -
123 remote: -
124 abort: remote error
124 abort: remote error
125 (check previous remote output)
125 (check previous remote output)
126 [255]
126 [255]
127
127
128 #if serve
128 #if serve
129
129
130 largefiles clients refuse to push largefiles repos to vanilla servers
130 largefiles clients refuse to push largefiles repos to vanilla servers
131 $ mkdir r6
131 $ mkdir r6
132 $ cd r6
132 $ cd r6
133 $ hg init
133 $ hg init
134 $ echo c1 > f1
134 $ echo c1 > f1
135 $ hg add f1
135 $ hg add f1
136 $ hg commit -m "m1"
136 $ hg commit -m "m1"
137 Invoking status precommit hook
137 Invoking status precommit hook
138 A f1
138 A f1
139 $ cat >> .hg/hgrc <<!
139 $ cat >> .hg/hgrc <<!
140 > [web]
140 > [web]
141 > push_ssl = false
141 > push_ssl = false
142 > allow_push = *
142 > allow_push = *
143 > !
143 > !
144 $ cd ..
144 $ cd ..
145 $ hg clone r6 r7
145 $ hg clone r6 r7
146 updating to branch default
146 updating to branch default
147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
147 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
148 $ cd r7
148 $ cd r7
149 $ echo c2 > f2
149 $ echo c2 > f2
150 $ hg add --large f2
150 $ hg add --large f2
151 $ hg commit -m "m2"
151 $ hg commit -m "m2"
152 Invoking status precommit hook
152 Invoking status precommit hook
153 A f2
153 A f2
154 $ hg verify --large
154 $ hg verify --large
155 checking changesets
155 checking changesets
156 checking manifests
156 checking manifests
157 crosschecking files in changesets and manifests
157 crosschecking files in changesets and manifests
158 checking files
158 checking files
159 2 files, 2 changesets, 2 total revisions
159 2 files, 2 changesets, 2 total revisions
160 searching 1 changesets for largefiles
160 searching 1 changesets for largefiles
161 verified existence of 1 revisions of 1 largefiles
161 verified existence of 1 revisions of 1 largefiles
162 $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid
162 $ hg serve --config extensions.largefiles=! -R ../r6 -d -p $HGPORT --pid-file ../hg.pid
163 $ cat ../hg.pid >> $DAEMON_PIDS
163 $ cat ../hg.pid >> $DAEMON_PIDS
164 $ hg push http://localhost:$HGPORT
164 $ hg push http://localhost:$HGPORT
165 pushing to http://localhost:$HGPORT/
165 pushing to http://localhost:$HGPORT/
166 searching for changes
166 searching for changes
167 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
167 abort: http://localhost:$HGPORT/ does not appear to be a largefile store
168 [255]
168 [255]
169 $ cd ..
169 $ cd ..
170
170
171 putlfile errors are shown (issue3123)
171 putlfile errors are shown (issue3123)
172 Corrupt the cached largefile in r7 and move it out of the servers usercache
172 Corrupt the cached largefile in r7 and move it out of the servers usercache
173 $ mv r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 .
173 $ mv r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 .
174 $ echo 'client side corruption' > r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
174 $ echo 'client side corruption' > r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
175 $ rm "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8"
175 $ rm "$USERCACHE/4cdac4d8b084d0b599525cf732437fb337d422a8"
176 $ hg init empty
176 $ hg init empty
177 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
177 $ hg serve -R empty -d -p $HGPORT1 --pid-file hg.pid \
178 > --config 'web.allow_push=*' --config web.push_ssl=False
178 > --config 'web.allow_push=*' --config web.push_ssl=False
179 $ cat hg.pid >> $DAEMON_PIDS
179 $ cat hg.pid >> $DAEMON_PIDS
180 $ hg push -R r7 http://localhost:$HGPORT1
180 $ hg push -R r7 http://localhost:$HGPORT1
181 pushing to http://localhost:$HGPORT1/
181 pushing to http://localhost:$HGPORT1/
182 searching for changes
182 searching for changes
183 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
183 remote: largefiles: failed to put 4cdac4d8b084d0b599525cf732437fb337d422a8 into store: largefile contents do not match hash
184 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
184 abort: remotestore: could not put $TESTTMP/r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8 to remote store http://localhost:$HGPORT1/
185 [255]
185 [255]
186 $ mv 4cdac4d8b084d0b599525cf732437fb337d422a8 r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
186 $ mv 4cdac4d8b084d0b599525cf732437fb337d422a8 r7/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
187 Push of file that exists on server but is corrupted - magic healing would be nice ... but too magic
187 Push of file that exists on server but is corrupted - magic healing would be nice ... but too magic
188 $ echo "server side corruption" > empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
188 $ echo "server side corruption" > empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
189 $ hg push -R r7 http://localhost:$HGPORT1
189 $ hg push -R r7 http://localhost:$HGPORT1
190 pushing to http://localhost:$HGPORT1/
190 pushing to http://localhost:$HGPORT1/
191 searching for changes
191 searching for changes
192 remote: adding changesets
192 remote: adding changesets
193 remote: adding manifests
193 remote: adding manifests
194 remote: adding file changes
194 remote: adding file changes
195 remote: added 2 changesets with 2 changes to 2 files
195 remote: added 2 changesets with 2 changes to 2 files
196 $ cat empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
196 $ cat empty/.hg/largefiles/4cdac4d8b084d0b599525cf732437fb337d422a8
197 server side corruption
197 server side corruption
198 $ rm -rf empty
198 $ rm -rf empty
199
199
200 Push a largefiles repository to a served empty repository
200 Push a largefiles repository to a served empty repository
201 $ hg init r8
201 $ hg init r8
202 $ echo c3 > r8/f1
202 $ echo c3 > r8/f1
203 $ hg add --large r8/f1 -R r8
203 $ hg add --large r8/f1 -R r8
204 $ hg commit -m "m1" -R r8
204 $ hg commit -m "m1" -R r8
205 Invoking status precommit hook
205 Invoking status precommit hook
206 A f1
206 A f1
207 $ hg init empty
207 $ hg init empty
208 $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \
208 $ hg serve -R empty -d -p $HGPORT2 --pid-file hg.pid \
209 > --config 'web.allow_push=*' --config web.push_ssl=False
209 > --config 'web.allow_push=*' --config web.push_ssl=False
210 $ cat hg.pid >> $DAEMON_PIDS
210 $ cat hg.pid >> $DAEMON_PIDS
211 $ rm "${USERCACHE}"/*
211 $ rm "${USERCACHE}"/*
212 $ hg push -R r8 http://localhost:$HGPORT2/#default
212 $ hg push -R r8 http://localhost:$HGPORT2/#default
213 pushing to http://localhost:$HGPORT2/
213 pushing to http://localhost:$HGPORT2/
214 searching for changes
214 searching for changes
215 remote: adding changesets
215 remote: adding changesets
216 remote: adding manifests
216 remote: adding manifests
217 remote: adding file changes
217 remote: adding file changes
218 remote: added 1 changesets with 1 changes to 1 files
218 remote: added 1 changesets with 1 changes to 1 files
219 $ [ -f "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
219 $ [ -f "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
220 $ [ -f empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
220 $ [ -f empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
221
221
222 Clone over http, no largefiles pulled on clone.
222 Clone over http, no largefiles pulled on clone.
223
223
224 $ hg clone http://localhost:$HGPORT2/#default http-clone -U
224 $ hg clone http://localhost:$HGPORT2/#default http-clone -U
225 adding changesets
225 adding changesets
226 adding manifests
226 adding manifests
227 adding file changes
227 adding file changes
228 added 1 changesets with 1 changes to 1 files
228 added 1 changesets with 1 changes to 1 files
229 new changesets cf03e5bb9936
229 new changesets cf03e5bb9936
230
230
231 Archive contains largefiles
231 Archive contains largefiles
232 >>> import os
232 >>> import os
233 >>> import urllib2
233 >>> import urllib2
234 >>> u = 'http://localhost:%s/archive/default.zip' % os.environ['HGPORT2']
234 >>> u = 'http://localhost:%s/archive/default.zip' % os.environ['HGPORT2']
235 >>> with open('archive.zip', 'w') as f:
235 >>> with open('archive.zip', 'w') as f:
236 ... f.write(urllib2.urlopen(u).read())
236 ... f.write(urllib2.urlopen(u).read())
237 $ unzip -t archive.zip
237 $ unzip -t archive.zip
238 Archive: archive.zip
238 Archive: archive.zip
239 testing: empty-default/.hg_archival.txt*OK (glob)
239 testing: empty-default/.hg_archival.txt*OK (glob)
240 testing: empty-default/f1*OK (glob)
240 testing: empty-default/f1*OK (glob)
241 No errors detected in compressed data of archive.zip.
241 No errors detected in compressed data of archive.zip.
242
242
243 test 'verify' with remotestore:
243 test 'verify' with remotestore:
244
244
245 $ rm "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90
245 $ rm "${USERCACHE}"/02a439e5c31c526465ab1a0ca1f431f76b827b90
246 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
246 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
247 $ hg -R http-clone verify --large --lfa
247 $ hg -R http-clone verify --large --lfa
248 checking changesets
248 checking changesets
249 checking manifests
249 checking manifests
250 crosschecking files in changesets and manifests
250 crosschecking files in changesets and manifests
251 checking files
251 checking files
252 1 files, 1 changesets, 1 total revisions
252 1 files, 1 changesets, 1 total revisions
253 searching 1 changesets for largefiles
253 searching 1 changesets for largefiles
254 changeset 0:cf03e5bb9936: f1 missing
254 changeset 0:cf03e5bb9936: f1 missing
255 verified existence of 1 revisions of 1 largefiles
255 verified existence of 1 revisions of 1 largefiles
256 [1]
256 [1]
257 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
257 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
258 $ hg -R http-clone -q verify --large --lfa
258 $ hg -R http-clone -q verify --large --lfa
259
259
260 largefiles pulled on update - a largefile missing on the server:
260 largefiles pulled on update - a largefile missing on the server:
261 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
261 $ mv empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 .
262 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
262 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
263 getting changed largefiles
263 getting changed largefiles
264 f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/
264 f1: largefile 02a439e5c31c526465ab1a0ca1f431f76b827b90 not available from http://localhost:$HGPORT2/
265 0 largefiles updated, 0 removed
265 0 largefiles updated, 0 removed
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
266 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
267 $ hg -R http-clone st
267 $ hg -R http-clone st
268 ! f1
268 ! f1
269 $ hg -R http-clone up -Cqr null
269 $ hg -R http-clone up -Cqr null
270
270
271 largefiles pulled on update - a largefile corrupted on the server:
271 largefiles pulled on update - a largefile corrupted on the server:
272 $ echo corruption > empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90
272 $ echo corruption > empty/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90
273 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
273 $ hg -R http-clone up --config largefiles.usercache=http-clone-usercache
274 getting changed largefiles
274 getting changed largefiles
275 f1: data corruption (expected 02a439e5c31c526465ab1a0ca1f431f76b827b90, got 6a7bb2556144babe3899b25e5428123735bb1e27)
275 f1: data corruption (expected 02a439e5c31c526465ab1a0ca1f431f76b827b90, got 6a7bb2556144babe3899b25e5428123735bb1e27)
276 0 largefiles updated, 0 removed
276 0 largefiles updated, 0 removed
277 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
277 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
278 $ hg -R http-clone st
278 $ hg -R http-clone st
279 ! f1
279 ! f1
280 $ [ ! -f http-clone/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
280 $ [ ! -f http-clone/.hg/largefiles/02a439e5c31c526465ab1a0ca1f431f76b827b90 ]
281 $ [ ! -f http-clone/f1 ]
281 $ [ ! -f http-clone/f1 ]
282 $ [ ! -f http-clone-usercache ]
282 $ [ ! -f http-clone-usercache ]
283 $ hg -R http-clone verify --large --lfc
283 $ hg -R http-clone verify --large --lfc
284 checking changesets
284 checking changesets
285 checking manifests
285 checking manifests
286 crosschecking files in changesets and manifests
286 crosschecking files in changesets and manifests
287 checking files
287 checking files
288 1 files, 1 changesets, 1 total revisions
288 1 files, 1 changesets, 1 total revisions
289 searching 1 changesets for largefiles
289 searching 1 changesets for largefiles
290 verified contents of 1 revisions of 1 largefiles
290 verified contents of 1 revisions of 1 largefiles
291 $ hg -R http-clone up -Cqr null
291 $ hg -R http-clone up -Cqr null
292
292
293 largefiles pulled on update - no server side problems:
293 largefiles pulled on update - no server side problems:
294 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
294 $ mv 02a439e5c31c526465ab1a0ca1f431f76b827b90 empty/.hg/largefiles/
295 $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true
295 $ hg -R http-clone --debug up --config largefiles.usercache=http-clone-usercache --config progress.debug=true
296 resolving manifests
296 resolving manifests
297 branchmerge: False, force: False, partial: False
297 branchmerge: False, force: False, partial: False
298 ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
298 ancestor: 000000000000, local: 000000000000+, remote: cf03e5bb9936
299 .hglf/f1: remote created -> g
299 .hglf/f1: remote created -> g
300 getting .hglf/f1
300 getting .hglf/f1
301 updating: .hglf/f1 1/1 files (100.00%)
301 updating: .hglf/f1 1/1 files (100.00%)
302 getting changed largefiles
302 getting changed largefiles
303 using http://localhost:$HGPORT2/
303 using http://localhost:$HGPORT2/
304 sending capabilities command
304 sending capabilities command
305 sending batch command
305 sending batch command
306 getting largefiles: 0/1 files (0.00%)
306 getting largefiles: 0/1 files (0.00%)
307 getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90
307 getting f1:02a439e5c31c526465ab1a0ca1f431f76b827b90
308 sending getlfile command
308 sending getlfile command
309 found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store
309 found 02a439e5c31c526465ab1a0ca1f431f76b827b90 in store
310 1 largefiles updated, 0 removed
310 1 largefiles updated, 0 removed
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
311 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
312
312
313 $ ls http-clone-usercache/*
313 $ ls http-clone-usercache/*
314 http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90
314 http-clone-usercache/02a439e5c31c526465ab1a0ca1f431f76b827b90
315
315
316 $ rm -rf empty http-clone*
316 $ rm -rf empty http-clone*
317
317
318 used all HGPORTs, kill all daemons
318 used all HGPORTs, kill all daemons
319 $ killdaemons.py
319 $ killdaemons.py
320
320
321 largefiles should batch verify remote calls
321 largefiles should batch verify remote calls
322
322
323 $ hg init batchverifymain
323 $ hg init batchverifymain
324 $ cd batchverifymain
324 $ cd batchverifymain
325 $ echo "aaa" >> a
325 $ echo "aaa" >> a
326 $ hg add --large a
326 $ hg add --large a
327 $ hg commit -m "a"
327 $ hg commit -m "a"
328 Invoking status precommit hook
328 Invoking status precommit hook
329 A a
329 A a
330 $ echo "bbb" >> b
330 $ echo "bbb" >> b
331 $ hg add --large b
331 $ hg add --large b
332 $ hg commit -m "b"
332 $ hg commit -m "b"
333 Invoking status precommit hook
333 Invoking status precommit hook
334 A b
334 A b
335 $ cd ..
335 $ cd ..
336 $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \
336 $ hg serve -R batchverifymain -d -p $HGPORT --pid-file hg.pid \
337 > -A access.log
337 > -A access.log
338 $ cat hg.pid >> $DAEMON_PIDS
338 $ cat hg.pid >> $DAEMON_PIDS
339 $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone
339 $ hg clone --noupdate http://localhost:$HGPORT batchverifyclone
340 requesting all changes
340 requesting all changes
341 adding changesets
341 adding changesets
342 adding manifests
342 adding manifests
343 adding file changes
343 adding file changes
344 added 2 changesets with 2 changes to 2 files
344 added 2 changesets with 2 changes to 2 files
345 new changesets 567253b0f523:04d19c27a332
345 new changesets 567253b0f523:04d19c27a332
346 $ hg -R batchverifyclone verify --large --lfa
346 $ hg -R batchverifyclone verify --large --lfa
347 checking changesets
347 checking changesets
348 checking manifests
348 checking manifests
349 crosschecking files in changesets and manifests
349 crosschecking files in changesets and manifests
350 checking files
350 checking files
351 2 files, 2 changesets, 2 total revisions
351 2 files, 2 changesets, 2 total revisions
352 searching 2 changesets for largefiles
352 searching 2 changesets for largefiles
353 verified existence of 2 revisions of 2 largefiles
353 verified existence of 2 revisions of 2 largefiles
354 $ tail -1 access.log
354 $ tail -1 access.log
355 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ (glob)
355 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3D972a1a11f19934401291cc99117ec614933374ce%3Bstatlfile+sha%3Dc801c9cfe94400963fcb683246217d5db77f9a9a x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ (glob)
356 $ hg -R batchverifyclone update
356 $ hg -R batchverifyclone update
357 getting changed largefiles
357 getting changed largefiles
358 2 largefiles updated, 0 removed
358 2 largefiles updated, 0 removed
359 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
359 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
360
360
361 Clear log file before next test
361 Clear log file before next test
362
362
363 $ printf "" > access.log
363 $ printf "" > access.log
364
364
365 Verify should check file on remote server only when file is not
365 Verify should check file on remote server only when file is not
366 available locally.
366 available locally.
367
367
368 $ echo "ccc" >> batchverifymain/c
368 $ echo "ccc" >> batchverifymain/c
369 $ hg -R batchverifymain status
369 $ hg -R batchverifymain status
370 ? c
370 ? c
371 $ hg -R batchverifymain add --large batchverifymain/c
371 $ hg -R batchverifymain add --large batchverifymain/c
372 $ hg -R batchverifymain commit -m "c"
372 $ hg -R batchverifymain commit -m "c"
373 Invoking status precommit hook
373 Invoking status precommit hook
374 A c
374 A c
375 $ hg -R batchverifyclone pull
375 $ hg -R batchverifyclone pull
376 pulling from http://localhost:$HGPORT/
376 pulling from http://localhost:$HGPORT/
377 searching for changes
377 searching for changes
378 adding changesets
378 adding changesets
379 adding manifests
379 adding manifests
380 adding file changes
380 adding file changes
381 added 1 changesets with 1 changes to 1 files
381 added 1 changesets with 1 changes to 1 files
382 new changesets 6bba8cb6935d
382 new changesets 6bba8cb6935d
383 (run 'hg update' to get a working copy)
383 (run 'hg update' to get a working copy)
384 $ hg -R batchverifyclone verify --lfa
384 $ hg -R batchverifyclone verify --lfa
385 checking changesets
385 checking changesets
386 checking manifests
386 checking manifests
387 crosschecking files in changesets and manifests
387 crosschecking files in changesets and manifests
388 checking files
388 checking files
389 3 files, 3 changesets, 3 total revisions
389 3 files, 3 changesets, 3 total revisions
390 searching 3 changesets for largefiles
390 searching 3 changesets for largefiles
391 verified existence of 3 revisions of 3 largefiles
391 verified existence of 3 revisions of 3 largefiles
392 $ tail -1 access.log
392 $ tail -1 access.log
393 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3Dc8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ (glob)
393 $LOCALIP - - [$LOGDATE$] "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=statlfile+sha%3Dc8559c3c9cfb42131794b7d8009230403b9b454c x-hgproto-1:0.1 0.2 comp=$USUAL_COMPRESSIONS$ (glob)
394
394
395 $ killdaemons.py
395 $ killdaemons.py
396
396
397 largefiles should not ask for password again after successful authorization
397 largefiles should not ask for password again after successful authorization
398
398
399 $ hg init credentialmain
399 $ hg init credentialmain
400 $ cd credentialmain
400 $ cd credentialmain
401 $ echo "aaa" >> a
401 $ echo "aaa" >> a
402 $ hg add --large a
402 $ hg add --large a
403 $ hg commit -m "a"
403 $ hg commit -m "a"
404 Invoking status precommit hook
404 Invoking status precommit hook
405 A a
405 A a
406
406
407 Before running server clear the user cache to force clone to download
407 Before running server clear the user cache to force clone to download
408 a large file from the server rather than to get it from the cache
408 a large file from the server rather than to get it from the cache
409
409
410 $ rm "${USERCACHE}"/*
410 $ rm "${USERCACHE}"/*
411
411
412 $ cd ..
412 $ cd ..
413 $ cat << EOT > userpass.py
413 $ cat << EOT > userpass.py
414 > import base64
414 > import base64
415 > from mercurial.hgweb import common
415 > from mercurial.hgweb import common
416 > def perform_authentication(hgweb, req, op):
416 > def perform_authentication(hgweb, req, op):
417 > auth = req.env.get('HTTP_AUTHORIZATION')
417 > auth = req.env.get('HTTP_AUTHORIZATION')
418 > if not auth:
418 > if not auth:
419 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
419 > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
420 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
420 > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
421 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
421 > if base64.b64decode(auth.split()[1]).split(':', 1) != ['user', 'pass']:
422 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
422 > raise common.ErrorResponse(common.HTTP_FORBIDDEN, 'no')
423 > def extsetup():
423 > def extsetup():
424 > common.permhooks.insert(0, perform_authentication)
424 > common.permhooks.insert(0, perform_authentication)
425 > EOT
425 > EOT
426 $ hg serve --config extensions.x=userpass.py -R credentialmain \
426 $ hg serve --config extensions.x=userpass.py -R credentialmain \
427 > -d -p $HGPORT --pid-file hg.pid -A access.log
427 > -d -p $HGPORT --pid-file hg.pid -A access.log
428 $ cat hg.pid >> $DAEMON_PIDS
428 $ cat hg.pid >> $DAEMON_PIDS
429 $ cat << EOF > get_pass.py
429 $ cat << EOF > get_pass.py
430 > import getpass
430 > import getpass
431 > def newgetpass(arg):
431 > def newgetpass(arg):
432 > return "pass"
432 > return "pass"
433 > getpass.getpass = newgetpass
433 > getpass.getpass = newgetpass
434 > EOF
434 > EOF
435 $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \
435 $ hg clone --config ui.interactive=true --config extensions.getpass=get_pass.py \
436 > http://user@localhost:$HGPORT credentialclone
436 > http://user@localhost:$HGPORT credentialclone
437 requesting all changes
438 http authorization required for http://localhost:$HGPORT/
437 http authorization required for http://localhost:$HGPORT/
439 realm: mercurial
438 realm: mercurial
440 user: user
439 user: user
441 password: adding changesets
440 password: requesting all changes
441 adding changesets
442 adding manifests
442 adding manifests
443 adding file changes
443 adding file changes
444 added 1 changesets with 1 changes to 1 files
444 added 1 changesets with 1 changes to 1 files
445 new changesets 567253b0f523
445 new changesets 567253b0f523
446 updating to branch default
446 updating to branch default
447 getting changed largefiles
447 getting changed largefiles
448 1 largefiles updated, 0 removed
448 1 largefiles updated, 0 removed
449 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
449 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
450
450
451 $ killdaemons.py
451 $ killdaemons.py
452 $ rm hg.pid access.log
452 $ rm hg.pid access.log
453
453
454 #endif
454 #endif
@@ -1,81 +1,79 b''
1 #require killdaemons
1 #require killdaemons
2
2
3 $ hg init test
3 $ hg init test
4 $ cd test
4 $ cd test
5 $ echo a > a
5 $ echo a > a
6 $ hg ci -Ama
6 $ hg ci -Ama
7 adding a
7 adding a
8 $ cd ..
8 $ cd ..
9 $ hg clone test test2
9 $ hg clone test test2
10 updating to branch default
10 updating to branch default
11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 $ cd test2
12 $ cd test2
13 $ echo a >> a
13 $ echo a >> a
14 $ hg ci -mb
14 $ hg ci -mb
15
15
16 Cloning with a password in the URL should not save the password in .hg/hgrc:
16 Cloning with a password in the URL should not save the password in .hg/hgrc:
17
17
18 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
18 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
19 $ cat hg.pid >> $DAEMON_PIDS
19 $ cat hg.pid >> $DAEMON_PIDS
20 $ hg clone http://foo:xyzzy@localhost:$HGPORT/ test3
20 $ hg clone http://foo:xyzzy@localhost:$HGPORT/ test3
21 requesting all changes
21 requesting all changes
22 adding changesets
22 adding changesets
23 adding manifests
23 adding manifests
24 adding file changes
24 adding file changes
25 added 2 changesets with 2 changes to 1 files
25 added 2 changesets with 2 changes to 1 files
26 new changesets cb9a9f314b8b:ba677d0156c1
26 new changesets cb9a9f314b8b:ba677d0156c1
27 updating to branch default
27 updating to branch default
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
28 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
29 $ cat test3/.hg/hgrc
29 $ cat test3/.hg/hgrc
30 # example repository config (see 'hg help config' for more info)
30 # example repository config (see 'hg help config' for more info)
31 [paths]
31 [paths]
32 default = http://foo@localhost:$HGPORT/
32 default = http://foo@localhost:$HGPORT/
33
33
34 # path aliases to other clones of this repo in URLs or filesystem paths
34 # path aliases to other clones of this repo in URLs or filesystem paths
35 # (see 'hg help config.paths' for more info)
35 # (see 'hg help config.paths' for more info)
36 #
36 #
37 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
37 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
39 # my-clone = /home/jdoe/jdoes-clone
39 # my-clone = /home/jdoe/jdoes-clone
40
40
41 [ui]
41 [ui]
42 # name and email (local to this repository, optional), e.g.
42 # name and email (local to this repository, optional), e.g.
43 # username = Jane Doe <jdoe@example.com>
43 # username = Jane Doe <jdoe@example.com>
44 $ killdaemons.py
44 $ killdaemons.py
45
45
46 expect error, cloning not allowed
46 expect error, cloning not allowed
47
47
48 $ echo '[web]' > .hg/hgrc
48 $ echo '[web]' > .hg/hgrc
49 $ echo 'allowpull = false' >> .hg/hgrc
49 $ echo 'allowpull = false' >> .hg/hgrc
50 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
50 $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
51 $ cat hg.pid >> $DAEMON_PIDS
51 $ cat hg.pid >> $DAEMON_PIDS
52 $ hg clone http://localhost:$HGPORT/ test4 # bundle2+
52 $ hg clone http://localhost:$HGPORT/ test4 # bundle2+
53 requesting all changes
54 abort: authorization failed
53 abort: authorization failed
55 [255]
54 [255]
56 $ hg clone http://localhost:$HGPORT/ test4 --config devel.legacy.exchange=bundle1
55 $ hg clone http://localhost:$HGPORT/ test4 --config devel.legacy.exchange=bundle1
57 abort: authorization failed
56 abort: authorization failed
58 [255]
57 [255]
59 $ killdaemons.py
58 $ killdaemons.py
60
59
61 serve errors
60 serve errors
62
61
63 $ cat errors.log
62 $ cat errors.log
64 $ req() {
63 $ req() {
65 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
64 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
66 > cat hg.pid >> $DAEMON_PIDS
65 > cat hg.pid >> $DAEMON_PIDS
67 > hg --cwd ../test pull http://localhost:$HGPORT/
66 > hg --cwd ../test pull http://localhost:$HGPORT/
68 > killdaemons.py hg.pid
67 > killdaemons.py hg.pid
69 > echo % serve errors
68 > echo % serve errors
70 > cat errors.log
69 > cat errors.log
71 > }
70 > }
72
71
73 expect error, pulling not allowed
72 expect error, pulling not allowed
74
73
75 $ req
74 $ req
76 pulling from http://localhost:$HGPORT/
75 pulling from http://localhost:$HGPORT/
77 searching for changes
78 abort: authorization failed
76 abort: authorization failed
79 % serve errors
77 % serve errors
80
78
81 $ cd ..
79 $ cd ..
@@ -1,352 +1,330 b''
1 #require killdaemons
1 #require killdaemons
2
2
3 #testcases bundle1 bundle2
3 #testcases bundle1 bundle2
4
4
5 #if bundle1
5 #if bundle1
6 $ cat << EOF >> $HGRCPATH
6 $ cat << EOF >> $HGRCPATH
7 > [devel]
7 > [devel]
8 > # This test is dedicated to interaction through old bundle
8 > # This test is dedicated to interaction through old bundle
9 > legacy.exchange = bundle1
9 > legacy.exchange = bundle1
10 > EOF
10 > EOF
11 #endif
11 #endif
12
12
13 $ hg init test
13 $ hg init test
14 $ cd test
14 $ cd test
15 $ echo a > a
15 $ echo a > a
16 $ hg ci -Ama
16 $ hg ci -Ama
17 adding a
17 adding a
18 $ cd ..
18 $ cd ..
19 $ hg clone test test2
19 $ hg clone test test2
20 updating to branch default
20 updating to branch default
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
21 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 $ cd test2
22 $ cd test2
23 $ echo a >> a
23 $ echo a >> a
24 $ hg ci -mb
24 $ hg ci -mb
25 $ req() {
25 $ req() {
26 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
26 > hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log
27 > cat hg.pid >> $DAEMON_PIDS
27 > cat hg.pid >> $DAEMON_PIDS
28 > hg --cwd ../test2 push http://localhost:$HGPORT/
28 > hg --cwd ../test2 push http://localhost:$HGPORT/
29 > exitstatus=$?
29 > exitstatus=$?
30 > killdaemons.py
30 > killdaemons.py
31 > echo % serve errors
31 > echo % serve errors
32 > cat errors.log
32 > cat errors.log
33 > return $exitstatus
33 > return $exitstatus
34 > }
34 > }
35 $ cd ../test
35 $ cd ../test
36
36
37 expect ssl error
37 expect ssl error
38
38
39 $ req
39 $ req
40 pushing to http://localhost:$HGPORT/
40 pushing to http://localhost:$HGPORT/
41 searching for changes
41 searching for changes
42 abort: HTTP Error 403: ssl required
42 abort: HTTP Error 403: ssl required
43 % serve errors
43 % serve errors
44 [255]
44 [255]
45
45
46 expect authorization error
46 expect authorization error
47
47
48 $ echo '[web]' > .hg/hgrc
48 $ echo '[web]' > .hg/hgrc
49 $ echo 'push_ssl = false' >> .hg/hgrc
49 $ echo 'push_ssl = false' >> .hg/hgrc
50 $ req
50 $ req
51 pushing to http://localhost:$HGPORT/
51 pushing to http://localhost:$HGPORT/
52 searching for changes
52 searching for changes
53 abort: authorization failed
53 abort: authorization failed
54 % serve errors
54 % serve errors
55 [255]
55 [255]
56
56
57 expect authorization error: must have authorized user
57 expect authorization error: must have authorized user
58
58
59 $ echo 'allow_push = unperson' >> .hg/hgrc
59 $ echo 'allow_push = unperson' >> .hg/hgrc
60 $ req
60 $ req
61 pushing to http://localhost:$HGPORT/
61 pushing to http://localhost:$HGPORT/
62 searching for changes
62 searching for changes
63 abort: authorization failed
63 abort: authorization failed
64 % serve errors
64 % serve errors
65 [255]
65 [255]
66
66
67 expect success
67 expect success
68
68
69 $ cat > $TESTTMP/hook.sh <<'EOF'
69 $ cat > $TESTTMP/hook.sh <<'EOF'
70 > echo "phase-move: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
70 > echo "phase-move: $HG_NODE: $HG_OLDPHASE -> $HG_PHASE"
71 > EOF
71 > EOF
72
72
73 $ cat >> .hg/hgrc <<EOF
73 $ cat >> .hg/hgrc <<EOF
74 > allow_push = *
74 > allow_push = *
75 > [hooks]
75 > [hooks]
76 > changegroup = sh -c "printenv.py changegroup 0"
76 > changegroup = sh -c "printenv.py changegroup 0"
77 > pushkey = sh -c "printenv.py pushkey 0"
77 > pushkey = sh -c "printenv.py pushkey 0"
78 > txnclose-phase.test = sh $TESTTMP/hook.sh
78 > txnclose-phase.test = sh $TESTTMP/hook.sh
79 > EOF
79 > EOF
80 $ req
80 $ req
81 pushing to http://localhost:$HGPORT/
81 pushing to http://localhost:$HGPORT/
82 searching for changes
82 searching for changes
83 remote: adding changesets
83 remote: adding changesets
84 remote: adding manifests
84 remote: adding manifests
85 remote: adding file changes
85 remote: adding file changes
86 remote: added 1 changesets with 1 changes to 1 files
86 remote: added 1 changesets with 1 changes to 1 files
87 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
87 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
88 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
88 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
89 remote: changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle1 !)
89 remote: changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle1 !)
90 remote: changegroup hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle2 !)
90 remote: changegroup hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle2 !)
91 % serve errors
91 % serve errors
92 $ hg rollback
92 $ hg rollback
93 repository tip rolled back to revision 0 (undo serve)
93 repository tip rolled back to revision 0 (undo serve)
94
94
95 expect success, server lacks the httpheader capability
95 expect success, server lacks the httpheader capability
96
96
97 $ CAP=httpheader
97 $ CAP=httpheader
98 $ . "$TESTDIR/notcapable"
98 $ . "$TESTDIR/notcapable"
99 $ req
99 $ req
100 pushing to http://localhost:$HGPORT/
100 pushing to http://localhost:$HGPORT/
101 searching for changes
101 searching for changes
102 remote: adding changesets
102 remote: adding changesets
103 remote: adding manifests
103 remote: adding manifests
104 remote: adding file changes
104 remote: adding file changes
105 remote: added 1 changesets with 1 changes to 1 files
105 remote: added 1 changesets with 1 changes to 1 files
106 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
106 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
107 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
107 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
108 remote: changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle1 !)
108 remote: changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle1 !)
109 remote: changegroup hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle2 !)
109 remote: changegroup hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle2 !)
110 % serve errors
110 % serve errors
111 $ hg rollback
111 $ hg rollback
112 repository tip rolled back to revision 0 (undo serve)
112 repository tip rolled back to revision 0 (undo serve)
113
113
114 expect success, server lacks the unbundlehash capability
114 expect success, server lacks the unbundlehash capability
115
115
116 $ CAP=unbundlehash
116 $ CAP=unbundlehash
117 $ . "$TESTDIR/notcapable"
117 $ . "$TESTDIR/notcapable"
118 $ req
118 $ req
119 pushing to http://localhost:$HGPORT/
119 pushing to http://localhost:$HGPORT/
120 searching for changes
120 searching for changes
121 remote: adding changesets
121 remote: adding changesets
122 remote: adding manifests
122 remote: adding manifests
123 remote: adding file changes
123 remote: adding file changes
124 remote: added 1 changesets with 1 changes to 1 files
124 remote: added 1 changesets with 1 changes to 1 files
125 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
125 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
126 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
126 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
127 remote: changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle1 !)
127 remote: changegroup hook: HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle1 !)
128 remote: changegroup hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle2 !)
128 remote: changegroup hook: HG_BUNDLE2=1 HG_HOOKNAME=changegroup HG_HOOKTYPE=changegroup HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob) (bundle2 !)
129 % serve errors
129 % serve errors
130 $ hg rollback
130 $ hg rollback
131 repository tip rolled back to revision 0 (undo serve)
131 repository tip rolled back to revision 0 (undo serve)
132
132
133 expect success, pre-d1b16a746db6 server supports the unbundle capability, but
133 expect success, pre-d1b16a746db6 server supports the unbundle capability, but
134 has no parameter
134 has no parameter
135
135
136 $ cat <<EOF > notcapable-unbundleparam.py
136 $ cat <<EOF > notcapable-unbundleparam.py
137 > from mercurial import extensions, httppeer
137 > from mercurial import extensions, httppeer
138 > def capable(orig, self, name):
138 > def capable(orig, self, name):
139 > if name == 'unbundle':
139 > if name == 'unbundle':
140 > return True
140 > return True
141 > return orig(self, name)
141 > return orig(self, name)
142 > def uisetup(ui):
142 > def uisetup(ui):
143 > extensions.wrapfunction(httppeer.httppeer, 'capable', capable)
143 > extensions.wrapfunction(httppeer.httppeer, 'capable', capable)
144 > EOF
144 > EOF
145 $ cp $HGRCPATH $HGRCPATH.orig
145 $ cp $HGRCPATH $HGRCPATH.orig
146 $ cat <<EOF >> $HGRCPATH
146 $ cat <<EOF >> $HGRCPATH
147 > [extensions]
147 > [extensions]
148 > notcapable-unbundleparam = `pwd`/notcapable-unbundleparam.py
148 > notcapable-unbundleparam = `pwd`/notcapable-unbundleparam.py
149 > EOF
149 > EOF
150 $ req
150 $ req
151 pushing to http://localhost:$HGPORT/
151 pushing to http://localhost:$HGPORT/
152 searching for changes
152 searching for changes
153 remote: adding changesets
153 remote: adding changesets
154 remote: adding manifests
154 remote: adding manifests
155 remote: adding file changes
155 remote: adding file changes
156 remote: added 1 changesets with 1 changes to 1 files
156 remote: added 1 changesets with 1 changes to 1 files
157 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
157 remote: phase-move: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b: draft -> public
158 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
158 remote: phase-move: ba677d0156c1196c1a699fa53f390dcfc3ce3872: -> public
159 remote: changegroup hook: * (glob)
159 remote: changegroup hook: * (glob)
160 % serve errors
160 % serve errors
161 $ hg rollback
161 $ hg rollback
162 repository tip rolled back to revision 0 (undo serve)
162 repository tip rolled back to revision 0 (undo serve)
163 $ mv $HGRCPATH.orig $HGRCPATH
163 $ mv $HGRCPATH.orig $HGRCPATH
164
164
165 Test pushing to a publishing repository with a failing prepushkey hook
165 Test pushing to a publishing repository with a failing prepushkey hook
166
166
167 $ cat > .hg/hgrc <<EOF
167 $ cat > .hg/hgrc <<EOF
168 > [web]
168 > [web]
169 > push_ssl = false
169 > push_ssl = false
170 > allow_push = *
170 > allow_push = *
171 > [hooks]
171 > [hooks]
172 > prepushkey = sh -c "printenv.py prepushkey 1"
172 > prepushkey = sh -c "printenv.py prepushkey 1"
173 > [devel]
173 > [devel]
174 > legacy.exchange=phases
174 > legacy.exchange=phases
175 > EOF
175 > EOF
176
176
177 #if bundle1
177 #if bundle1
178 Bundle1 works because a) phases are updated as part of changegroup application
178 Bundle1 works because a) phases are updated as part of changegroup application
179 and b) client checks phases after the "unbundle" command. Since it sees no
179 and b) client checks phases after the "unbundle" command. Since it sees no
180 phase changes are necessary, it doesn't send the "pushkey" command and the
180 phase changes are necessary, it doesn't send the "pushkey" command and the
181 prepushkey hook never has to fire.
181 prepushkey hook never has to fire.
182
182
183 $ req
183 $ req
184 pushing to http://localhost:$HGPORT/
184 pushing to http://localhost:$HGPORT/
185 searching for changes
185 searching for changes
186 remote: adding changesets
186 remote: adding changesets
187 remote: adding manifests
187 remote: adding manifests
188 remote: adding file changes
188 remote: adding file changes
189 remote: added 1 changesets with 1 changes to 1 files
189 remote: added 1 changesets with 1 changes to 1 files
190 % serve errors
190 % serve errors
191
191
192 #endif
192 #endif
193
193
194 #if bundle2
194 #if bundle2
195 Bundle2 sends a "pushkey" bundle2 part. This runs as part of the transaction
195 Bundle2 sends a "pushkey" bundle2 part. This runs as part of the transaction
196 and fails the entire push.
196 and fails the entire push.
197 $ req
197 $ req
198 pushing to http://localhost:$HGPORT/
198 pushing to http://localhost:$HGPORT/
199 searching for changes
199 searching for changes
200 remote: adding changesets
200 remote: adding changesets
201 remote: adding manifests
201 remote: adding manifests
202 remote: adding file changes
202 remote: adding file changes
203 remote: added 1 changesets with 1 changes to 1 files
203 remote: added 1 changesets with 1 changes to 1 files
204 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
204 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
205 remote: pushkey-abort: prepushkey hook exited with status 1
205 remote: pushkey-abort: prepushkey hook exited with status 1
206 remote: transaction abort!
206 remote: transaction abort!
207 remote: rollback completed
207 remote: rollback completed
208 abort: updating ba677d0156c1 to public failed
208 abort: updating ba677d0156c1 to public failed
209 % serve errors
209 % serve errors
210 [255]
210 [255]
211
211
212 #endif
212 #endif
213
213
214 Now remove the failing prepushkey hook.
214 Now remove the failing prepushkey hook.
215
215
216 $ cat >> .hg/hgrc <<EOF
216 $ cat >> .hg/hgrc <<EOF
217 > [hooks]
217 > [hooks]
218 > prepushkey = sh -c "printenv.py prepushkey 0"
218 > prepushkey = sh -c "printenv.py prepushkey 0"
219 > EOF
219 > EOF
220
220
221 We don't need to test bundle1 because it succeeded above.
221 We don't need to test bundle1 because it succeeded above.
222
222
223 #if bundle2
223 #if bundle2
224 $ req
224 $ req
225 pushing to http://localhost:$HGPORT/
225 pushing to http://localhost:$HGPORT/
226 searching for changes
226 searching for changes
227 remote: adding changesets
227 remote: adding changesets
228 remote: adding manifests
228 remote: adding manifests
229 remote: adding file changes
229 remote: adding file changes
230 remote: added 1 changesets with 1 changes to 1 files
230 remote: added 1 changesets with 1 changes to 1 files
231 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
231 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
232 % serve errors
232 % serve errors
233 #endif
233 #endif
234
234
235 $ hg --config extensions.strip= strip -r 1:
235 $ hg --config extensions.strip= strip -r 1:
236 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
236 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
237
237
238 Now do a variant of the above, except on a non-publishing repository
238 Now do a variant of the above, except on a non-publishing repository
239
239
240 $ cat >> .hg/hgrc <<EOF
240 $ cat >> .hg/hgrc <<EOF
241 > [phases]
241 > [phases]
242 > publish = false
242 > publish = false
243 > [hooks]
243 > [hooks]
244 > prepushkey = sh -c "printenv.py prepushkey 1"
244 > prepushkey = sh -c "printenv.py prepushkey 1"
245 > EOF
245 > EOF
246
246
247 #if bundle1
247 #if bundle1
248 $ req
248 $ req
249 pushing to http://localhost:$HGPORT/
249 pushing to http://localhost:$HGPORT/
250 searching for changes
250 searching for changes
251 remote: adding changesets
251 remote: adding changesets
252 remote: adding manifests
252 remote: adding manifests
253 remote: adding file changes
253 remote: adding file changes
254 remote: added 1 changesets with 1 changes to 1 files
254 remote: added 1 changesets with 1 changes to 1 files
255 remote: prepushkey hook: HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_OLD=1
255 remote: prepushkey hook: HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_OLD=1
256 remote: pushkey-abort: prepushkey hook exited with status 1
256 remote: pushkey-abort: prepushkey hook exited with status 1
257 updating ba677d0156c1 to public failed!
257 updating ba677d0156c1 to public failed!
258 % serve errors
258 % serve errors
259 #endif
259 #endif
260
260
261 #if bundle2
261 #if bundle2
262 $ req
262 $ req
263 pushing to http://localhost:$HGPORT/
263 pushing to http://localhost:$HGPORT/
264 searching for changes
264 searching for changes
265 remote: adding changesets
265 remote: adding changesets
266 remote: adding manifests
266 remote: adding manifests
267 remote: adding file changes
267 remote: adding file changes
268 remote: added 1 changesets with 1 changes to 1 files
268 remote: added 1 changesets with 1 changes to 1 files
269 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
269 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
270 remote: pushkey-abort: prepushkey hook exited with status 1
270 remote: pushkey-abort: prepushkey hook exited with status 1
271 remote: transaction abort!
271 remote: transaction abort!
272 remote: rollback completed
272 remote: rollback completed
273 abort: updating ba677d0156c1 to public failed
273 abort: updating ba677d0156c1 to public failed
274 % serve errors
274 % serve errors
275 [255]
275 [255]
276 #endif
276 #endif
277
277
278 Make phases updates work
278 Make phases updates work
279
279
280 $ cat >> .hg/hgrc <<EOF
280 $ cat >> .hg/hgrc <<EOF
281 > [hooks]
281 > [hooks]
282 > prepushkey = sh -c "printenv.py prepushkey 0"
282 > prepushkey = sh -c "printenv.py prepushkey 0"
283 > EOF
283 > EOF
284
284
285 #if bundle1
285 #if bundle1
286 $ req
286 $ req
287 pushing to http://localhost:$HGPORT/
287 pushing to http://localhost:$HGPORT/
288 searching for changes
288 searching for changes
289 no changes found
289 no changes found
290 remote: prepushkey hook: HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_OLD=1
290 remote: prepushkey hook: HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_OLD=1
291 % serve errors
291 % serve errors
292 [1]
292 [1]
293 #endif
293 #endif
294
294
295 #if bundle2
295 #if bundle2
296 $ req
296 $ req
297 pushing to http://localhost:$HGPORT/
297 pushing to http://localhost:$HGPORT/
298 searching for changes
298 searching for changes
299 remote: adding changesets
299 remote: adding changesets
300 remote: adding manifests
300 remote: adding manifests
301 remote: adding file changes
301 remote: adding file changes
302 remote: added 1 changesets with 1 changes to 1 files
302 remote: added 1 changesets with 1 changes to 1 files
303 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
303 remote: prepushkey hook: HG_BUNDLE2=1 HG_HOOKNAME=prepushkey HG_HOOKTYPE=prepushkey HG_KEY=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NAMESPACE=phases HG_NEW=0 HG_NODE=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_NODE_LAST=ba677d0156c1196c1a699fa53f390dcfc3ce3872 HG_OLD=1 HG_PENDING=$TESTTMP/test HG_PHASES_MOVED=1 HG_SOURCE=serve HG_TXNID=TXN:$ID$ HG_URL=remote:http:$LOCALIP: (glob)
304 % serve errors
304 % serve errors
305 #endif
305 #endif
306
306
307 $ hg --config extensions.strip= strip -r 1:
307 $ hg --config extensions.strip= strip -r 1:
308 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
308 saved backup bundle to $TESTTMP/test/.hg/strip-backup/ba677d0156c1-eea704d7-backup.hg
309
309
310 expect authorization error: all users denied
311
312 $ echo '[web]' > .hg/hgrc
313 $ echo 'push_ssl = false' >> .hg/hgrc
314 $ echo 'deny_push = *' >> .hg/hgrc
315 $ req
316 pushing to http://localhost:$HGPORT/
317 searching for changes
318 abort: authorization failed
319 % serve errors
320 [255]
321
322 expect authorization error: some users denied, users must be authenticated
323
324 $ echo 'deny_push = unperson' >> .hg/hgrc
325 $ req
326 pushing to http://localhost:$HGPORT/
327 searching for changes
328 abort: authorization failed
329 % serve errors
330 [255]
331
332 #if bundle2
310 #if bundle2
333
311
334 $ cat > .hg/hgrc <<EOF
312 $ cat > .hg/hgrc <<EOF
335 > [web]
313 > [web]
336 > push_ssl = false
314 > push_ssl = false
337 > allow_push = *
315 > allow_push = *
338 > [experimental]
316 > [experimental]
339 > httppostargs=true
317 > httppostargs=true
340 > EOF
318 > EOF
341 $ req
319 $ req
342 pushing to http://localhost:$HGPORT/
320 pushing to http://localhost:$HGPORT/
343 searching for changes
321 searching for changes
344 remote: adding changesets
322 remote: adding changesets
345 remote: adding manifests
323 remote: adding manifests
346 remote: adding file changes
324 remote: adding file changes
347 remote: added 1 changesets with 1 changes to 1 files
325 remote: added 1 changesets with 1 changes to 1 files
348 % serve errors
326 % serve errors
349
327
350 #endif
328 #endif
351
329
352 $ cd ..
330 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now