D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg added a comment.


  I'm sorry, but we cannot ship this as is.
  
  The reason is mtime based build systems, like GNU make.
  
  We can't have version control modifying files without bumping their mtime 
because this invalidates the target freshness checks of mtime-based build 
systems.
  
  If we want to preserve mtime on file copy or move, I believe it is safe to do 
that if and only if the destination file didn't already exist. But if the 
destination exists, we need to ensure the mtime of the new file is greater than 
the mtime of the old file.
  
  Reading this patch, I /think/ the previous behavior was buggy in edge cases 
because we never ensured the mtime of the replacement was newer than the 
existing file. In 99.99% of cases, it will be because the existing file was 
created sometime in the past. But if bad clocks or other wonky things are in 
play, there's no guarantee that *wall clock now* is greater than the mtime of 
the existing destination file. The correct thing to do in this situation is 
read the mtime of the existing file and ensure the mtime of the new file is at 
least 1s greater than the previous mtime (1s because not all filesystems 
preserve microsecond or millisecond mtime granularity).

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

To: spectral, #hg-reviewers, durin42
Cc: indygreg, durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH 2 of 3] test-lfs: dial up the debugging on commands that interact with the server

2018-03-12 Thread Matt Harbison
# HG changeset patch
# User Matt Harbison 
# Date 1520908201 14400
#  Mon Mar 12 22:30:01 2018 -0400
# Node ID f28c282005085edb77f64612be503a7cd43ab994
# Parent  dade0442450d1f813ff5c90025603363210aa0f1
test-lfs: dial up the debugging on commands that interact with the server

This will be useful to let the client print out the HTTP headers and JSON in a
future patch, so we can compare native LFS serving against test-lfs-server
behavior.  There tends to be a lot of debug stuff that we don't care about here
in a push, so I was tempted to print this output with a [devel] config.  But
this will be useful for field debugging too, so just put up with the extra
output here.

It would have been nice to be able to set ui.debug once, but issue5815 prevents
that.

diff --git a/tests/test-lfs-test-server.t b/tests/test-lfs-test-server.t
--- a/tests/test-lfs-test-server.t
+++ b/tests/test-lfs-test-server.t
@@ -43,32 +43,66 @@ store.
 
   $ hg init ../repo2
   $ mv .hg/store/lfs .hg/store/lfs_
-  $ hg push ../repo2 -v
+  $ hg push ../repo2 --debug
+  http auth: user foo, password ***
   pushing to ../repo2
+  http auth: user foo, password ***
+  query 1; heads
   searching for changes
+  1 total queries in *s (glob)
+  listing keys for "phases"
+  checking for updated bookmarks
+  listing keys for "bookmarks"
+  lfs: computing set of blobs to upload
   lfs: uploading 
31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
   lfs: processed: 
31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
   lfs: uploaded 1 files (12 bytes)
   1 changesets found
-  uncompressed size of bundle content:
-   * (changelog) (glob)
-   * (manifests) (glob)
-   *  a (glob)
+  list of changesets:
+  99a7098854a3984a5c9eab0fc7a2906697b7cb5c
+  bundle2-output-bundle: "HG20", 4 parts total
+  bundle2-output-part: "replycaps" 191 bytes payload
+  bundle2-output-part: "check:heads" streamed payload
+  bundle2-output-part: "changegroup" (params: 1 mandatory) streamed payload
+  bundle2-output-part: "phase-heads" 24 bytes payload
+  bundle2-input-bundle: with-transaction
+  bundle2-input-part: "replycaps" supported
+  bundle2-input-part: total payload size 191
+  bundle2-input-part: "check:heads" supported
+  bundle2-input-part: total payload size 20
+  bundle2-input-part: "changegroup" (params: 1 mandatory) supported
   adding changesets
+  add changeset 99a7098854a3
   adding manifests
   adding file changes
+  adding a revisions
   added 1 changesets with 1 changes to 1 files
   calling hook pretxnchangegroup.lfs: hgext.lfs.checkrequireslfs
+  bundle2-input-part: total payload size 617
+  bundle2-input-part: "phase-heads" supported
+  bundle2-input-part: total payload size 24
+  bundle2-input-bundle: 3 parts total
+  updating the branch cache
+  bundle2-output-bundle: "HG20", 1 parts total
+  bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) 
empty payload
+  bundle2-input-bundle: no-transaction
+  bundle2-input-part: "reply:changegroup" (advisory) (params: 0 advisory) 
supported
+  bundle2-input-bundle: 0 parts total
+  listing keys for "phases"
   $ mv .hg/store/lfs_ .hg/store/lfs
 
 Clear the cache to force a download
   $ rm -rf `hg config lfs.usercache`
   $ cd ../repo2
-  $ hg update tip -v
+  $ hg update tip --debug
+  http auth: user foo, password ***
   resolving manifests
+   branchmerge: False, force: False, partial: False
+   ancestor: , local: +, remote: 99a7098854a3
   lfs: downloading 
31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
   lfs: adding 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b 
to the usercache
   lfs: processed: 
31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
+   a: remote created -> g
   getting a
   lfs: found 31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b 
in the local lfs store
   1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -79,9 +113,18 @@ When the server has some blobs already
   $ echo ANOTHER-LARGE-FILE > c
   $ echo ANOTHER-LARGE-FILE2 > d
   $ hg commit -m b-and-c -A b c d
-  $ hg push ../repo1 -v | grep -v '^  '
+  $ hg push ../repo1 --debug
+  http auth: user foo, password ***
   pushing to ../repo1
+  http auth: user foo, password ***
+  query 1; heads
   searching for changes
+  all remote heads known locally
+  listing keys for "phases"
+  checking for updated bookmarks
+  listing keys for "bookmarks"
+  listing keys for "bookmarks"
+  lfs: computing set of blobs to upload
   lfs: need to transfer 2 objects (39 bytes)
   lfs: uploading 
37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19 (20 bytes)
   lfs: processed: 
37a65ab78d5ecda767e8622c248b5dbff1e68b1678ab0e730d5eb8601ec8ad19
@@ -89,16 +132,49 @@ When the server has some blobs already
   lfs: processed: 
d11e1a642b60813aee592094109b406089b8dff4cb157157f753418ec7857998
   lfs: uploaded 2 files (39 

[PATCH 3 of 3] lfs: debug print HTTP headers and JSON payload received from the server

2018-03-12 Thread Matt Harbison
# HG changeset patch
# User Matt Harbison 
# Date 1520910527 14400
#  Mon Mar 12 23:08:47 2018 -0400
# Node ID d0b66408b224022fd96cb347ff3439e807812b21
# Parent  f28c282005085edb77f64612be503a7cd43ab994
lfs: debug print HTTP headers and JSON payload received from the server

This has been extremely valuable to show divergences between `hg serve` and
`lfs-test-server`.  Once the `hg serve` code lands, there will be a certain
amount of conditionalizing that needs to be done, because `lfs-test-server`
doesn't always follow its spec.

The $ISO_8601_DATE_TIME$ pattern masks the fact that `lfs-test-serve` is sending
out an expires_at value of "0001-01-01T00:00:00Z".  `hg serve` will (probably)
use current time + 10 minutes or similar.  The $HTTP_DATE$ is the current time.

diff --git a/hgext/lfs/blobstore.py b/hgext/lfs/blobstore.py
--- a/hgext/lfs/blobstore.py
+++ b/hgext/lfs/blobstore.py
@@ -217,7 +217,8 @@ class _gitlfsremote(object):
 batchreq.add_header('Accept', 'application/vnd.git-lfs+json')
 batchreq.add_header('Content-Type', 'application/vnd.git-lfs+json')
 try:
-rawjson = self.urlopener.open(batchreq).read()
+rsp = self.urlopener.open(batchreq)
+rawjson = rsp.read()
 except util.urlerr.httperror as ex:
 raise LfsRemoteError(_('LFS HTTP error: %s (action=%s)')
  % (ex, action))
@@ -226,6 +227,19 @@ class _gitlfsremote(object):
 except ValueError:
 raise LfsRemoteError(_('LFS server returns invalid JSON: %s')
  % rawjson)
+
+if self.ui.debugflag:
+self.ui.debug('Status: %d\n' % rsp.status)
+# lfs-test-server and hg serve return headers in different order
+self.ui.debug('%s\n'
+  % '\n'.join(sorted(str(rsp.info()).splitlines(
+
+if 'objects' in response:
+response['objects'] = sorted(response['objects'],
+ key=lambda p: p['oid'])
+self.ui.debug('%s\n'
+  % json.dumps(response, indent=2, sort_keys=True))
+
 return response
 
 def _checkforservererror(self, pointers, responses, action):
@@ -301,6 +315,13 @@ class _gitlfsremote(object):
 response = b''
 try:
 req = self.urlopener.open(request)
+
+if self.ui.debugflag:
+self.ui.debug('Status: %d\n' % req.status)
+# lfs-test-server and hg serve return headers in different 
order
+self.ui.debug('%s\n'
+  % 
'\n'.join(sorted(str(req.info()).splitlines(
+
 if action == 'download':
 # If downloading blobs, store downloaded data to local 
blobstore
 localstore.download(oid, req)
diff --git a/tests/test-lfs-test-server.t b/tests/test-lfs-test-server.t
--- a/tests/test-lfs-test-server.t
+++ b/tests/test-lfs-test-server.t
@@ -54,7 +54,32 @@ store.
   checking for updated bookmarks
   listing keys for "bookmarks"
   lfs: computing set of blobs to upload
+  Status: 200
+  Content-Length: 309
+  Content-Type: application/vnd.git-lfs+json
+  Date: $HTTP_DATE$
+  {
+"objects": [
+  {
+"actions": {
+  "upload": {
+"expires_at": "$ISO_8601_DATE_TIME$", 
+"header": {
+  "Accept": "application/vnd.git-lfs"
+}, 
+"href": 
"http://localhost:$HGPORT/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b;
+  }
+}, 
+"oid": 
"31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b", 
+"size": 12
+  }
+]
+  }
   lfs: uploading 
31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b (12 bytes)
+  Status: 200
+  Content-Length: 0
+  Content-Type: text/plain; charset=utf-8
+  Date: $HTTP_DATE$
   lfs: processed: 
31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b
   lfs: uploaded 1 files (12 bytes)
   1 changesets found
@@ -99,7 +124,32 @@ Clear the cache to force a download
   resolving manifests
branchmerge: False, force: False, partial: False
ancestor: , local: +, remote: 99a7098854a3
+  Status: 200
+  Content-Length: 311
+  Content-Type: application/vnd.git-lfs+json
+  Date: $HTTP_DATE$
+  {
+"objects": [
+  {
+"actions": {
+  "download": {
+"expires_at": "$ISO_8601_DATE_TIME$", 
+"header": {
+  "Accept": "application/vnd.git-lfs"
+}, 
+"href": 
"http://localhost:$HGPORT/objects/31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b;
+  }
+}, 
+"oid": 
"31cf46fbc4ecd458a0943c5b4881f1f5a6dd36c53d6167d5b69ac45149b38e5b", 
+"size": 12
+  }
+]
+  }
   lfs: downloading 

[PATCH 1 of 3] tests: add a substitution pattern for dates in HTTP headers and LFS payload

2018-03-12 Thread Matt Harbison
# HG changeset patch
# User Matt Harbison 
# Date 1519503121 18000
#  Sat Feb 24 15:12:01 2018 -0500
# Node ID dade0442450d1f813ff5c90025603363210aa0f1
# Parent  6aeb076b1321c540db6419fc48c8a3a552fb596b
tests: add a substitution pattern for dates in HTTP headers and LFS payload

This will be useful when printing HTTP headers and JSON payload received from an
LFS server.  The RFC 1123 date masking has uses elsewhere too.

diff --git a/tests/common-pattern.py b/tests/common-pattern.py
--- a/tests/common-pattern.py
+++ b/tests/common-pattern.py
@@ -72,6 +72,14 @@ substitutions = [
 (br' - - \[\d\d/.../2\d\d\d \d\d:\d\d:\d\d] "(GET|PUT|POST)',
  lambda m: br' - - [$LOGDATE$] "' + m.group(1)
 ),
+# HTTP header dates- RFC 1123
+(br'Date: [A-Za-z]{3}, \d\d [A-Za-z]{3} \d{4} \d\d:\d\d:\d\d GMT',
+ br'Date: $HTTP_DATE$'
+),
+# LFS expiration value
+(br'"expires_at": "\d{4}-\d\d-\d\dT\d\d:\d\d:\d\dZ"',
+ br'"expires_at": "$ISO_8601_DATE_TIME$"'
+),
 # Windows has an extra '/' in the following lines that get globbed away:
 #   pushing to file:/*/$TESTTMP/r2 (glob)
 #   comparing with file:/*/$TESTTMP/r2 (glob)
diff --git a/tests/test-http-bad-server.t b/tests/test-http-bad-server.t
--- a/tests/test-http-bad-server.t
+++ b/tests/test-http-bad-server.t
@@ -116,7 +116,7 @@ Failure on subsequent HTTP request on th
   readline(4? from -1) -> (2) \r\n (glob)
   write(36) -> HTTP/1.1 200 Script output follows\r\n
   write(23) -> Server: badhttpserver\r\n
-  write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37) -> Date: $HTTP_DATE$\r\n
   write(41) -> Content-Type: application/mercurial-0.1\r\n
   write(21) -> Content-Length: 417\r\n
   write(2) -> \r\n
@@ -157,7 +157,7 @@ Failure to read getbundle HTTP request
   readline(13? from -1) -> (2) \r\n (glob)
   write(36) -> HTTP/1.1 200 Script output follows\r\n
   write(23) -> Server: badhttpserver\r\n
-  write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37) -> Date: $HTTP_DATE$\r\n
   write(41) -> Content-Type: application/mercurial-0.1\r\n
   write(21) -> Content-Length: 417\r\n
   write(2) -> \r\n
@@ -179,7 +179,7 @@ Failure to read getbundle HTTP request
   readline(2? from -1) -> (2) \r\n (glob)
   write(36) -> HTTP/1.1 200 Script output follows\r\n
   write(23) -> Server: badhttpserver\r\n
-  write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37) -> Date: $HTTP_DATE$\r\n
   write(41) -> Content-Type: application/mercurial-0.1\r\n
   write(20) -> Content-Length: 42\r\n
   write(2) -> \r\n
@@ -214,7 +214,7 @@ Now do a variation using POST to send ar
   readline(14? from -1) -> (2) \r\n (glob)
   write(36) -> HTTP/1.1 200 Script output follows\r\n
   write(23) -> Server: badhttpserver\r\n
-  write(37) -> Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37) -> Date: $HTTP_DATE$\r\n
   write(41) -> Content-Type: application/mercurial-0.1\r\n
   write(21) -> Content-Length: 430\r\n
   write(2) -> \r\n
@@ -290,7 +290,7 @@ Server sends an incomplete capabilities 
   readline(-1) -> (2) \r\n
   write(36 from 36) -> (144) HTTP/1.1 200 Script output follows\r\n
   write(23 from 23) -> (121) Server: badhttpserver\r\n
-  write(37 from 37) -> (84) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37 from 37) -> (84) Date: $HTTP_DATE$\r\n
   write(41 from 41) -> (43) Content-Type: application/mercurial-0.1\r\n
   write(21 from 21) -> (22) Content-Length: 417\r\n
   write(2 from 2) -> (20) \r\n
@@ -325,7 +325,7 @@ TODO this output is horrible
   readline(-1) -> (2) \r\n
   write(36 from 36) -> (659) HTTP/1.1 200 Script output follows\r\n
   write(23 from 23) -> (636) Server: badhttpserver\r\n
-  write(37 from 37) -> (599) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37 from 37) -> (599) Date: $HTTP_DATE$\r\n
   write(41 from 41) -> (558) Content-Type: application/mercurial-0.1\r\n
   write(21 from 21) -> (537) Content-Length: 417\r\n
   write(2 from 2) -> (535) \r\n
@@ -341,7 +341,7 @@ TODO this output is horrible
   readline(-1) -> (2) \r\n
   write(36 from 36) -> (82) HTTP/1.1 200 Script output follows\r\n
   write(23 from 23) -> (59) Server: badhttpserver\r\n
-  write(37 from 37) -> (22) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37 from 37) -> (22) Date: $HTTP_DATE$\r\n
   write(22 from 41) -> (0) Content-Type: applicat
   write limit reached; closing socket
   write(36) -> HTTP/1.1 500 Internal Server Error\r\n
@@ -373,7 +373,7 @@ TODO client spews a stack due to uncaugh
   readline(-1) -> (2) \r\n
   write(36 from 36) -> (724) HTTP/1.1 200 Script output follows\r\n
   write(23 from 23) -> (701) Server: badhttpserver\r\n
-  write(37 from 37) -> (664) Date: Fri, 14 Apr 2017 00:00:00 GMT\r\n
+  write(37 from 37) -> (664) Date: $HTTP_DATE$\r\n
   write(41 from 41) -> (623) Content-Type: application/mercurial-0.1\r\n
   write(21 from 21) -> (602) Content-Length: 417\r\n
   write(2 from 2) -> (600) \r\n
@@ -389,7 +389,7 @@ TODO client spews a stack due to 

D2836: wireproto: define permissions-based routing of HTTPv2 wire protocol

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Now that we have a scaffolding for serving version 2 of the HTTP
  protocol, let's start implementing it.
  
  A good place to start is URL routing and basic request processing
  semantics. We can focus on content types, capabilities detect, etc
  later.
  
  Version 2 of the HTTP wire protocol encodes the needed permissions
  of the request in the URL path. The reasons for this are documented
  in the added documentation. In short, a) it makes it really easy and
  fail proof for server administrators to implement path-based
  authentication and b) it will enable clients to realize very early in
  a server exchange that authentication will be required to complete
  the operation. This latter point avoids all kinds of complexity and
  problems, like dealing with Expect: 100-continue and clients finding
  out later during `hg push` that they need to provide authentication.
  This will avoid the current badness where clients send a full bundle,
  get an HTTP 403, provide authentication, then retransmit the bundle.
  
  In order to implement command checking, we needed to implement a
  protocol handler for the new wire protocol. Our handler is just
  small enough to run the code we've implemented.
  
  Tests for the defined functionality have been added.
  
  I very much want to refactor the permissions checking code and define
  a better response format. But this can be done later. Nothing is
  covered by backwards compatibility at this point.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2836

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -1,3 +1,5 @@
+  $ HTTPV2=exp-http-v2-0001
+
   $ hg init server
   $ cat > server/.hg/hgrc << EOF
   > [experimental]
@@ -8,7 +10,7 @@
 
 HTTP v2 protocol not enabled by default
 
-  $ get-with-headers.py $LOCALIP:$HGPORT api/exp-http-v2-0001 -
+  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2 -
   404 Not Found
   content-length: 33
   content-type: text/plain
@@ -28,11 +30,100 @@
   $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
   $ cat hg.pid > $DAEMON_PIDS
 
-Requests simply echo their path (for now)
+Request to read-only command works out of the box
 
-  $ get-with-headers.py $LOCALIP:$HGPORT api/exp-http-v2-0001/path1/path2 -
+  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/ro/known -
   200 OK
-  content-length: 12
+  content-length: 9
+  content-type: text/plain
+  
+  ro/known/ (no-eol)
+
+Request to unknown command yields 404
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/ro/badcommand -
+  404 Not Found
+  content-length: 42
   content-type: text/plain
   
-  path1/path2/ (no-eol)
+  unknown wire protocol command: badcommand
+  [1]
+
+Request to read-write command fails because server is read-only by default
+
+GET request not allowed
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/rw/known -
+  405 push requires POST request
+  content-length: 17
+  
+  permission denied (no-eol)
+  [1]
+
+Even for unknown commands
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/rw/badcommand -
+  405 push requires POST request
+  content-length: 17
+  
+  permission denied (no-eol)
+  [1]
+
+
+SSL required by default
+
+  $ get-with-headers.py --method POST $LOCALIP:$HGPORT api/$HTTPV2/rw/known -
+  403 ssl required
+  content-length: 17
+  
+  permission denied (no-eol)
+  [1]
+
+Restart server to allow non-ssl read-write operations
+
+  $ killdaemons.py
+  $ cat > server/.hg/hgrc << EOF
+  > [experimental]
+  > web.apiserver = true
+  > web.api.http-v2 = true
+  > [web]
+  > push_ssl = false
+  > EOF
+
+  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+  $ get-with-headers.py --method POST $LOCALIP:$HGPORT api/$HTTPV2/rw/known -
+  401 push not authorized
+  content-length: 17
+  
+  permission denied (no-eol)
+  [1]
+
+  $ killdaemons.py
+  $ cat > server/.hg/hgrc << EOF
+  > [experimental]
+  > web.apiserver = true
+  > web.api.http-v2 = true
+  > [web]
+  > push_ssl = false
+  > allow-push = *
+  > EOF
+
+  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+  $ get-with-headers.py --method POST $LOCALIP:$HGPORT api/$HTTPV2/rw/known -
+  200 OK
+  content-length: 9
+  content-type: text/plain
+  
+  rw/known/ (no-eol)
+
+  $ get-with-headers.py --method POST $LOCALIP:$HGPORT 
api/$HTTPV2/rw/badcommand -
+  404 Not Found
+  content-length: 42
+  content-type: text/plain
+  
+  unknown wire protocol command: badcommand
+  [1]
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ 

D2837: wireproto: require POST for all HTTPv2 requests

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Wire protocol version 1 transfers argument data via request
  headers by default. This has historically caused problems because
  servers institute limits on the length of individual HTTP headers
  as well as the total size of all request headers. Mercurial servers
  can advertise the maximum length of an individual header. But
  there's no guarantee any intermediate HTTP agents will accept
  headers up to that length.
  
  In the existing wire protocol, server operators typically also
  key off the HTTP request method to implement authentication.
  For example, GET requests translate to read-only requests and
  can be allowed. But read-write commands must use POST and require
  authentication. This has typically worked because the only wire
  protocol commands that use POST modify the repo (e.g. the
  "unbundle" command).
  
  There is an experimental feature to enable clients to transmit
  argument data via POST request bodies. This is technically a
  better and more robust solution. But we can't enable it by default
  because of servers assuming POST means write access.
  
  In version 2 of the wire protocol, the permissions of a request
  are encoded in the URL. And with it being a new protocol in a new
  URL space, we're not constrained by backwards compatibility
  requirements.
  
  This commit adopts the technically superior mechanism of using
  HTTP request bodies to send argument data by requiring POST for
  all commands. Strictly speaking, it may be possible to send
  request bodies on GET requests. But my experience is that not all
  HTTP stacks support this. POST pretty much always works. Using POST
  for read-only operations does sacrifice some RESTful design
  purity. But this API cares about practicality, not about being
  in Roy T. Fielding's REST ivory tower.
  
  There's a chance we may relax this restriction in the future. But
  for now, I want to see how far we can get with a POST only API.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2837

AFFECTED FILES
  mercurial/help/internals/wireprotocol.txt
  mercurial/wireprotoserver.py
  tests/test-http-api-httpv2.t

CHANGE DETAILS

diff --git a/tests/test-http-api-httpv2.t b/tests/test-http-api-httpv2.t
--- a/tests/test-http-api-httpv2.t
+++ b/tests/test-http-api-httpv2.t
@@ -32,41 +32,53 @@
 
 Request to read-only command works out of the box
 
-  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/ro/known -
+  $ get-with-headers.py --method POST $LOCALIP:$HGPORT api/$HTTPV2/ro/known -
   200 OK
   content-length: 9
   content-type: text/plain
   
   ro/known/ (no-eol)
 
 Request to unknown command yields 404
 
-  $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/ro/badcommand -
+  $ get-with-headers.py --method POST $LOCALIP:$HGPORT 
api/$HTTPV2/ro/badcommand -
   404 Not Found
   content-length: 42
   content-type: text/plain
   
   unknown wire protocol command: badcommand
   [1]
 
+Only POST is allowed
+
+  $ get-with-headers.py --method GET $LOCALIP:$HGPORT api/$HTTPV2/ro/known -
+  405 Method Not Allowed
+  allow: POST
+  content-length: 30
+  
+  commands require POST requests (no-eol)
+  [1]
+
 Request to read-write command fails because server is read-only by default
 
 GET request not allowed
 
   $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/rw/known -
-  405 push requires POST request
-  content-length: 17
+  405 Method Not Allowed
+  allow: POST
+  content-length: 30
   
-  permission denied (no-eol)
+  commands require POST requests (no-eol)
   [1]
 
 Even for unknown commands
 
   $ get-with-headers.py $LOCALIP:$HGPORT api/$HTTPV2/rw/badcommand -
-  405 push requires POST request
-  content-length: 17
+  405 Method Not Allowed
+  allow: POST
+  content-length: 30
   
-  permission denied (no-eol)
+  commands require POST requests (no-eol)
   [1]
 
 
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -299,6 +299,12 @@
 res.setbodybytes(_('unknown permission: %s') % permission)
 return
 
+if req.method != 'POST':
+res.status = b'405 Method Not Allowed'
+res.headers[b'Allow'] = b'POST'
+res.setbodybytes(_('commands require POST requests'))
+return
+
 # At some point we'll want to use our own API instead of recycling the
 # behavior of version 1 of the wire protocol...
 # TODO return reasonable responses - not responses that overload the
diff --git a/mercurial/help/internals/wireprotocol.txt 
b/mercurial/help/internals/wireprotocol.txt
--- a/mercurial/help/internals/wireprotocol.txt
+++ b/mercurial/help/internals/wireprotocol.txt
@@ -152,11 +152,14 @@
 Version 2 of the HTTP protocol is exposed under the ``/api/*`` URL space.
 It's final API name is not yet formalized.
 
-Commands are triggered by 

D2835: tests: teach get-with-headers to send other request methods

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The "get" in "get-with-headers" now makes this a poorly named command.
  But renaming it would be rather invasive. It's tempting to invent
  a new script to make HTTP requests, since the API of get-with-headers
  leaves a lot to be desired. But let's not cross that bridge until
  we have a reason to.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2835

AFFECTED FILES
  tests/get-with-headers.py

CHANGE DETAILS

diff --git a/tests/get-with-headers.py b/tests/get-with-headers.py
--- a/tests/get-with-headers.py
+++ b/tests/get-with-headers.py
@@ -37,6 +37,8 @@
 help='Write HTTP response body to a file')
 parser.add_argument('--showdynamicheaders', action='store_true',
 help='Show dynamic headers that are hidden by default')
+parser.add_argument('--method', default='GET',
+help='HTTP request method')
 parser.add_argument('host')
 parser.add_argument('path')
 parser.add_argument('show', nargs='*')
@@ -64,7 +66,7 @@
 headers[key] = value
 
 conn = httplib.HTTPConnection(host)
-conn.request("GET", '/' + path, None, headers)
+conn.request(args.method.encode('ascii'), '/' + path, None, headers)
 response = conn.getresponse()
 stdout.write(b'%d %s\n' % (response.status,
response.reason.encode('ascii')))



To: indygreg, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH] xdiff: fix a hard crash on Windows

2018-03-12 Thread Matt Harbison

On Mon, 12 Mar 2018 21:55:26 -0400, Jun Wu  wrote:


Looks good. Thanks for fixing this!


Do you think it's worth changing the few other instances of long over to  
uint64?  If nothing else, it will be unconditionally 64 bit ops.  But I'm  
not sure what else you have pending.  (I think I saw you mention the  
hashing specifically, which looks like a lot of the rest of these.)



Excerpts from Matt Harbison's message of 2018-03-12 21:53:12 -0400:

# HG changeset patch
# User Matt Harbison 
# Date 1520905818 14400
#  Mon Mar 12 21:50:18 2018 -0400
# Node ID 60bb2f7dd9ba313f96374470e8419bf1a20454a1
# Parent  aed445748c7885482cd90e56e81f57a13d4ac95c
xdiff: fix a hard crash on Windows

The xdiff case of test-diff-antipatience.t started crashing in the C  
extension
with 882657a9f768 (with 6a71a5ba666b backported so it compiles).  There  
are a

few more instances of 'long', but this resolves the crashing.

diff --git a/mercurial/thirdparty/xdiff/xdiffi.c  
b/mercurial/thirdparty/xdiff/xdiffi.c

--- a/mercurial/thirdparty/xdiff/xdiffi.c
+++ b/mercurial/thirdparty/xdiff/xdiffi.c
@@ -342,7 +342,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t
  * One is to store the forward path and one to store the backward  
path.

  */
 ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
-if (!(kvd = (int64_t *) xdl_malloc((2 * ndiags + 2) *  
sizeof(long {
+if (!(kvd = (int64_t *) xdl_malloc((2 * ndiags + 2) *  
sizeof(int64_t {


 xdl_free_env(xe);
 return -1;
diff --git a/mercurial/thirdparty/xdiff/xprepare.c  
b/mercurial/thirdparty/xdiff/xprepare.c

--- a/mercurial/thirdparty/xdiff/xprepare.c
+++ b/mercurial/thirdparty/xdiff/xprepare.c
@@ -296,9 +296,9 @@ static int xdl_prepare_ctx(unsigned int
 goto abort;
 memset(rchg, 0, (nrec + 2) * sizeof(char));

-if (!(rindex = (int64_t *) xdl_malloc((nrec + 1) * sizeof(long
+if (!(rindex = (int64_t *) xdl_malloc((nrec + 1) *  
sizeof(int64_t

 goto abort;
-if (!(ha = (uint64_t *) xdl_malloc((nrec + 1) * sizeof(unsigned  
long

+if (!(ha = (uint64_t *) xdl_malloc((nrec + 1) * sizeof(uint64_t
 goto abort;

 xdf->nrec = nrec;

___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH] xdiff: fix a hard crash on Windows

2018-03-12 Thread Jun Wu
Looks good. Thanks for fixing this!

Excerpts from Matt Harbison's message of 2018-03-12 21:53:12 -0400:
> # HG changeset patch
> # User Matt Harbison 
> # Date 1520905818 14400
> #  Mon Mar 12 21:50:18 2018 -0400
> # Node ID 60bb2f7dd9ba313f96374470e8419bf1a20454a1
> # Parent  aed445748c7885482cd90e56e81f57a13d4ac95c
> xdiff: fix a hard crash on Windows
> 
> The xdiff case of test-diff-antipatience.t started crashing in the C extension
> with 882657a9f768 (with 6a71a5ba666b backported so it compiles).  There are a
> few more instances of 'long', but this resolves the crashing.
> 
> diff --git a/mercurial/thirdparty/xdiff/xdiffi.c 
> b/mercurial/thirdparty/xdiff/xdiffi.c
> --- a/mercurial/thirdparty/xdiff/xdiffi.c
> +++ b/mercurial/thirdparty/xdiff/xdiffi.c
> @@ -342,7 +342,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t 
>   * One is to store the forward path and one to store the backward path.
>   */
>  ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
> -if (!(kvd = (int64_t *) xdl_malloc((2 * ndiags + 2) * sizeof(long {
> +if (!(kvd = (int64_t *) xdl_malloc((2 * ndiags + 2) * sizeof(int64_t 
> {
>  
>  xdl_free_env(xe);
>  return -1;
> diff --git a/mercurial/thirdparty/xdiff/xprepare.c 
> b/mercurial/thirdparty/xdiff/xprepare.c
> --- a/mercurial/thirdparty/xdiff/xprepare.c
> +++ b/mercurial/thirdparty/xdiff/xprepare.c
> @@ -296,9 +296,9 @@ static int xdl_prepare_ctx(unsigned int 
>  goto abort;
>  memset(rchg, 0, (nrec + 2) * sizeof(char));
>  
> -if (!(rindex = (int64_t *) xdl_malloc((nrec + 1) * sizeof(long
> +if (!(rindex = (int64_t *) xdl_malloc((nrec + 1) * sizeof(int64_t
>  goto abort;
> -if (!(ha = (uint64_t *) xdl_malloc((nrec + 1) * sizeof(unsigned long
> +if (!(ha = (uint64_t *) xdl_malloc((nrec + 1) * sizeof(uint64_t
>  goto abort;
>  
>  xdf->nrec = nrec;
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[PATCH] xdiff: fix a hard crash on Windows

2018-03-12 Thread Matt Harbison
# HG changeset patch
# User Matt Harbison 
# Date 1520905818 14400
#  Mon Mar 12 21:50:18 2018 -0400
# Node ID 60bb2f7dd9ba313f96374470e8419bf1a20454a1
# Parent  aed445748c7885482cd90e56e81f57a13d4ac95c
xdiff: fix a hard crash on Windows

The xdiff case of test-diff-antipatience.t started crashing in the C extension
with 882657a9f768 (with 6a71a5ba666b backported so it compiles).  There are a
few more instances of 'long', but this resolves the crashing.

diff --git a/mercurial/thirdparty/xdiff/xdiffi.c 
b/mercurial/thirdparty/xdiff/xdiffi.c
--- a/mercurial/thirdparty/xdiff/xdiffi.c
+++ b/mercurial/thirdparty/xdiff/xdiffi.c
@@ -342,7 +342,7 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t 
 * One is to store the forward path and one to store the backward path.
 */
ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
-   if (!(kvd = (int64_t *) xdl_malloc((2 * ndiags + 2) * sizeof(long {
+   if (!(kvd = (int64_t *) xdl_malloc((2 * ndiags + 2) * 
sizeof(int64_t {
 
xdl_free_env(xe);
return -1;
diff --git a/mercurial/thirdparty/xdiff/xprepare.c 
b/mercurial/thirdparty/xdiff/xprepare.c
--- a/mercurial/thirdparty/xdiff/xprepare.c
+++ b/mercurial/thirdparty/xdiff/xprepare.c
@@ -296,9 +296,9 @@ static int xdl_prepare_ctx(unsigned int 
goto abort;
memset(rchg, 0, (nrec + 2) * sizeof(char));
 
-   if (!(rindex = (int64_t *) xdl_malloc((nrec + 1) * sizeof(long
+   if (!(rindex = (int64_t *) xdl_malloc((nrec + 1) * sizeof(int64_t
goto abort;
-   if (!(ha = (uint64_t *) xdl_malloc((nrec + 1) * sizeof(unsigned long
+   if (!(ha = (uint64_t *) xdl_malloc((nrec + 1) * sizeof(uint64_t
goto abort;
 
xdf->nrec = nrec;
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2678: help: supporting both help and doc for aliases

2018-03-12 Thread spectral (Kyle Lippincott)
spectral added a comment.


  In https://phab.mercurial-scm.org/D2678#45552, @durin42 wrote:
  
  > Ugh:
  >
  >   --- /home/augie/hg/tests/test-alias.t
  >   +++ /home/augie/hg/tests/test-alias.t.err
  >   @@ -357,15 +357,8 @@
  >properly recursive
  >  
  >  $ hg dln
  >   -  changeset:   -1:
  >   -  phase:   public
  >   -  parent:  -1:
  >   -  parent:  -1:
  >   -  manifest:-1:
  >   -  user:
  >   -  date:Thu Jan 01 00:00:00 1970 +
  >   -  extra:   branch=default
  >   -
  >   +  abort: alias 'dln' resolves to unknown command 'lognull'
  >   +  [255]
  
  
  I wasn't able to reproduce this error when based off of 
https://phab.mercurial-scm.org/rHG31581528f2421dc5d8a567125b8ecc0367b2b906
  
  > 
  > 
  >   path expanding
  >
  > 
  > ERROR: test-alias.t output changed
  > 
  > - /home/augie/hg/tests/test-show.t +++ /home/augie/hg/tests/test-show.t.err 
@@ -135,19 +135,37 @@ commands.show.aliasprefix aliases values to `show `
  > 
  >   $ hg --config commands.show.aliasprefix=s sbookmarks +  devel-warn: 
config item requires an explicit default value: 'alias.sstack' at: 
/tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 (extsetup) +  
devel-warn: config item requires an explicit default value: 'alias.swork' at: 
/tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 (extsetup) +  
devel-warn: config item requires an explicit default value: 'alias.sbookmarks' 
at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 (extsetup) (no 
bookmarks set)
  
  ... etc 
  
  I fixed this one. PTAL (added third argument of None to call in 
hgext/show.py).

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2678

To: rdamazio, #hg-reviewers, durin42
Cc: mharbison72, spectral, pulkit, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2678: help: supporting both help and doc for aliases

2018-03-12 Thread spectral (Kyle Lippincott)
spectral updated this revision to Diff 6980.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2678?vs=6635=6980

REVISION DETAIL
  https://phab.mercurial-scm.org/D2678

AFFECTED FILES
  hgext/show.py
  mercurial/configitems.py
  mercurial/dispatch.py
  mercurial/help.py
  tests/test-alias.t
  tests/test-help.t

CHANGE DETAILS

diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -769,9 +769,9 @@
   $ hg help shellalias
   hg shellalias
   
-  shell alias for:
-  
-echo hi
+  shell alias for: echo hi
+  
+  (no help text available)
   
   defined by: helpext
   
diff --git a/tests/test-alias.t b/tests/test-alias.t
--- a/tests/test-alias.t
+++ b/tests/test-alias.t
@@ -4,9 +4,13 @@
   > # should clobber ci but not commit (issue2993)
   > ci = version
   > myinit = init
+  > myinit:doc = This is my documented alias for init.
+  > myinit:help = [OPTIONS] [BLA] [BLE]
   > mycommit = commit
+  > mycommit:doc = This is my alias with only doc.
   > optionalrepo = showconfig alias.myinit
   > cleanstatus = status -c
+  > cleanstatus:help = [ONLYHELPHERE]
   > unknown = bargle
   > ambiguous = s
   > recursive = recursive
@@ -53,11 +57,135 @@
   > log = -v
   > EOF
 
-
 basic
 
   $ hg myinit alias
 
+help
+
+  $ hg help -c | grep myinit
+   myinit This is my documented alias for init.
+  $ hg help -c | grep mycommit
+   mycommit   This is my alias with only doc.
+  $ hg help -c | grep cleanstatus
+   cleanstatusshow changed files in the working directory
+  $ hg help myinit
+  hg myinit [OPTIONS] [BLA] [BLE]
+  
+  alias for: hg init
+  
+  This is my documented alias for init.
+  
+  defined by: * (glob)
+  */* (glob) (?)
+  */* (glob) (?)
+  */* (glob) (?)
+  
+  options:
+  
+   -e --ssh CMD   specify ssh command to use
+  --remotecmd CMD specify hg command to run on the remote side
+  --insecure  do not verify server certificate (ignoring web.cacerts
+  config)
+  
+  (some details hidden, use --verbose to show complete help)
+
+  $ hg help mycommit
+  hg mycommit [OPTION]... [FILE]...
+  
+  alias for: hg commit
+  
+  This is my alias with only doc.
+  
+  defined by: * (glob)
+  */* (glob) (?)
+  */* (glob) (?)
+  */* (glob) (?)
+  
+  options ([+] can be repeated):
+  
+   -A --addremove   mark new/missing files as added/removed before
+committing
+  --close-branchmark a branch head as closed
+  --amend   amend the parent of the working directory
+   -s --secret  use the secret phase for committing
+   -e --editinvoke editor on commit messages
+   -i --interactive use interactive mode
+   -I --include PATTERN [+] include names matching the given patterns
+   -X --exclude PATTERN [+] exclude names matching the given patterns
+   -m --message TEXTuse text as commit message
+   -l --logfile FILEread commit message from file
+   -d --date DATE   record the specified date as commit date
+   -u --user USER   record the specified user as committer
+   -S --subreposrecurse into subrepositories
+  
+  (some details hidden, use --verbose to show complete help)
+
+  $ hg help cleanstatus
+  hg cleanstatus [ONLYHELPHERE]
+  
+  alias for: hg status -c
+  
+  show changed files in the working directory
+  
+  Show status of files in the repository. If names are given, only files
+  that match are shown. Files that are clean or ignored or the source of a
+  copy/move operation, are not listed unless -c/--clean, -i/--ignored,
+  -C/--copies or -A/--all are given. Unless options described with "show
+  only ..." are given, the options -mardu are used.
+  
+  Option -q/--quiet hides untracked (unknown and ignored) files unless
+  explicitly requested with -u/--unknown or -i/--ignored.
+  
+  Note:
+ 'hg status' may appear to disagree with diff if permissions have
+ changed or a merge has occurred. The standard diff format does not
+ report permission changes and diff only reports changes relative to 
one
+ merge parent.
+  
+  If one revision is given, it is used as the base revision. If two
+  revisions are given, the differences between them are shown. The --change
+  option can also be used as a shortcut to list the changed files of a
+  revision from its first parent.
+  
+  The codes used to show the status of files are:
+  
+M = modified
+A = added
+R = removed
+C = clean
+! = missing (deleted by non-hg command, but still tracked)
+? = not tracked
+I = ignored
+  = origin of the previous file (with --copies)
+  
+  Returns 0 on success.
+  
+  defined by: * (glob)
+  */* (glob) (?)
+  */* (glob) (?)
+  */* (glob) (?)
+  
+  options ([+] can be repeated):
+  
+   -A --all

D2833: tests: teach get-with-headers.py to ignore dynamic headers

2018-03-12 Thread mharbison72 (Matt Harbison)
mharbison72 added a comment.


  I’ve got a patch locally that will pattern substitute away the date.  It’s 
been useful for LFS, because I’ve been debug dumping the headers and JSON on 
the client side.  I’ll try to submit that tonight.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2833

To: indygreg, #hg-reviewers
Cc: mharbison72, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2720: debugcommands: introduce actions to perform deterministic reads

2018-03-12 Thread mharbison72 (Matt Harbison)
mharbison72 added a comment.


  Sounds good to me.  I think the only thing I have inflight is the patch with 
explicit flushes in the observer.  I haven't tried Yuya's series yet, because 
I'm not sure where it fits into the other stuff inflight.  I don't mind mopping 
this up after it lands.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2720

To: indygreg, #hg-reviewers
Cc: yuja, mharbison72, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2720: debugcommands: introduce actions to perform deterministic reads

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg added a subscriber: yuja.
indygreg added a comment.


  I removed the test changes from this. So we should be able to land it without 
causing chaos to Windows tests. It may conflict with other patches that @yuja 
and @mharbison72 have in flight though.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2720

To: indygreg, #hg-reviewers
Cc: yuja, mharbison72, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2725: httppeer: refactor how httppeer is created (API)

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6975.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2725?vs=6720=6975

REVISION DETAIL
  https://phab.mercurial-scm.org/D2725

AFFECTED FILES
  mercurial/httppeer.py
  tests/test-check-interfaces.py

CHANGE DETAILS

diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py
--- a/tests/test-check-interfaces.py
+++ b/tests/test-check-interfaces.py
@@ -50,10 +50,13 @@
 def _restrictcapabilities(self, caps):
 pass
 
+class dummyopener(object):
+handlers = []
+
 # Facilitates testing sshpeer without requiring an SSH server.
 class badpeer(httppeer.httppeer):
 def __init__(self):
-super(badpeer, self).__init__(uimod.ui(), 'http://localhost')
+super(badpeer, self).__init__(None, None, None, dummyopener())
 self.badattribute = True
 
 def badmethod(self):
@@ -67,7 +70,7 @@
 ui = uimod.ui()
 
 checkobject(badpeer())
-checkobject(httppeer.httppeer(ui, 'http://localhost'))
+checkobject(httppeer.httppeer(None, None, None, dummyopener()))
 checkobject(localrepo.localpeer(dummyrepo()))
 checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(),
   dummypipe(), None, None))
diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -134,29 +134,17 @@
 self._index = 0
 
 class httppeer(wireproto.wirepeer):
-def __init__(self, ui, path):
+def __init__(self, ui, path, url, opener):
+self._ui = ui
 self._path = path
+self._url = url
 self._caps = None
-self._urlopener = None
-u = util.url(path)
-if u.query or u.fragment:
-raise error.Abort(_('unsupported URL component: "%s"') %
- (u.query or u.fragment))
-
-# urllib cannot handle URLs with embedded user or passwd
-self._url, authinfo = u.authinfo()
-
-self._ui = ui
-ui.debug('using %s\n' % self._url)
-
-self._urlopener = urlmod.opener(ui, authinfo)
+self._urlopener = opener
 
 def __del__(self):
-urlopener = getattr(self, '_urlopener', None)
-if urlopener:
-for h in urlopener.handlers:
-h.close()
-getattr(h, "close_all", lambda: None)()
+for h in self._urlopener.handlers:
+h.close()
+getattr(h, "close_all", lambda: None)()
 
 def _openurl(self, req):
 if (self._ui.debugflag
@@ -480,15 +468,29 @@
 def _abort(self, exception):
 raise exception
 
+def makepeer(ui, path):
+u = util.url(path)
+if u.query or u.fragment:
+raise error.Abort(_('unsupported URL component: "%s"') %
+  (u.query or u.fragment))
+
+# urllib cannot handle URLs with embedded user or passwd.
+url, authinfo = u.authinfo()
+ui.debug('using %s\n' % url)
+
+opener = urlmod.opener(ui, authinfo)
+
+return httppeer(ui, path, url, opener)
+
 def instance(ui, path, create):
 if create:
 raise error.Abort(_('cannot create new http repository'))
 try:
 if path.startswith('https:') and not urlmod.has_https:
 raise error.Abort(_('Python support for SSL and HTTPS '
 'is not installed'))
 
-inst = httppeer(ui, path)
+inst = makepeer(ui, path)
 inst._fetchcaps()
 
 return inst



To: indygreg, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2726: debugcommands: support connecting to HTTP peers

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6977.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2726?vs=6721=6977

REVISION DETAIL
  https://phab.mercurial-scm.org/D2726

AFFECTED FILES
  mercurial/debugcommands.py
  tests/test-http-protocol.t

CHANGE DETAILS

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -161,3 +161,68 @@
   : 32 30 30 20 53 63 72 69 70 74 20 6f 75 74 70 75 |200 Script outpu|
   0010: 74 20 66 6f 6c 6c 6f 77 73 0a 0a 04 7a 6c 69 62 |t follows...zlib|
   0020: 78  |x|
+
+  $ killdaemons.py
+  $ cd ..
+
+Test listkeys for listing namespaces
+
+  $ hg init empty
+  $ hg -R empty serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+  $ hg --verbose debugwireproto http://$LOCALIP:$HGPORT << EOF
+  > command listkeys
+  > namespace namespaces
+  > EOF
+  s> sendall(*, 0): (glob)
+  s> GET /?cmd=capabilities HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> accept: application/mercurial-0.1\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> user-agent: mercurial/proto-1.0 (Mercurial *)\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> *: (glob)
+  s> Server: *\r\n (glob)
+  s> readline() -> *: (glob)
+  s> Date: *\r\n (glob)
+  s> readline() -> 41:
+  s> Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 21:
+  s> Content-Length: *\r\n (glob)
+  s> readline() -> 2:
+  s> \r\n
+  s> read(*) -> *: lookup branchmap pushkey known getbundle unbundlehash batch 
changegroupsubset streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ 
unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx 
compression=$BUNDLE2_COMPRESSIONS$ (glob)
+  sending listkeys command
+  s> sendall(*, 0): (glob)
+  s> GET /?cmd=listkeys HTTP/1.1\r\n
+  s> Accept-Encoding: identity\r\n
+  s> vary: X-HgArg-1,X-HgProto-1\r\n
+  s> x-hgarg-1: namespace=namespaces\r\n
+  s> x-hgproto-1: 0.1 0.2 comp=$USUAL_COMPRESSIONS$\r\n
+  s> accept: application/mercurial-0.1\r\n
+  s> host: $LOCALIP:$HGPORT\r\n (glob)
+  s> user-agent: mercurial/proto-1.0 (Mercurial *)\r\n (glob)
+  s> \r\n
+  s> makefile('rb', None)
+  s> readline() -> 36:
+  s> HTTP/1.1 200 Script output follows\r\n
+  s> readline() -> 36:
+  s> Server: *\r\n (glob)
+  s> readline() -> *: (glob)
+  s> Date: *\r\n (glob)
+  s> readline() -> 41:
+  s> Content-Type: application/mercurial-0.1\r\n
+  s> readline() -> 20:
+  s> Content-Length: 30\r\n
+  s> readline() -> 2:
+  s> \r\n
+  s> read(30) -> 30:
+  s> bookmarks \n
+  s> namespaces\n
+  s> phases
+  response: bookmarks  \nnamespaces\nphases
+
+  $ killdaemons.py
diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -48,6 +48,7 @@
 fileset,
 formatter,
 hg,
+httppeer,
 localrepo,
 lock as lockmod,
 logcmdutil,
@@ -2588,9 +2589,9 @@
 ('', 'peer', '', _('construct a specific version of the peer')),
 ('', 'noreadstderr', False, _('do not read from stderr of the 
remote')),
 ] + cmdutil.remoteopts,
-_('[REPO]'),
+_('[PATH]'),
 optionalrepo=True)
-def debugwireproto(ui, repo, **opts):
+def debugwireproto(ui, repo, path=None, **opts):
 """send wire protocol commands to a server
 
 This command can be used to issue wire protocol commands to remote
@@ -2726,12 +2727,19 @@
 raise error.Abort(_('invalid value for --peer'),
   hint=_('valid values are "raw", "ssh1", and "ssh2"'))
 
+if path and opts['localssh']:
+raise error.Abort(_('cannot specify --localssh with an explicit '
+'path'))
+
 if ui.interactive():
 ui.write(_('(waiting for commands on stdin)\n'))
 
 blocks = list(_parsewirelangblocks(ui.fin))
 
 proc = None
+stdin = None
+stdout = None
+stderr = None
 
 if opts['localssh']:
 # We start the SSH server in its own process so there is process
@@ -2779,22 +2787,55 @@
 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr,
 autoreadstderr=autoreadstderr)
 
+elif path:
+# We bypass hg.peer() so we can proxy the sockets.
+# TODO consider not doing this because we skip
+# ``hg.wirepeersetupfuncs`` and potentially other useful functionality.
+u = util.url(path)
+if u.scheme != 'http':
+raise error.Abort(_('only http:// paths are currently supported'))
+
+if opts['peer']:
+raise error.Abort(_('--peer is not supported with HTTP peers'))
+
+url, authinfo = 

D2834: wireproto: support /api/* URL space for exposing APIs

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  I will soon be introducing a new version of the HTTP wire protocol.
  One of the things I want to change with it is the URL routing.
  I want to rely on URL paths to define endpoints rather than the
  "cmd" query string argument. That should be pretty straightforward.
  
  I was thinking about what URL space to reserve for the new protocol.
  We /could/ put everything at a top-level path. e.g.
  /wireproto/* or /http-v2-wireproto/*. However, these constrain us
  a bit because they assume there will only be 1 API: version 2 of
  the HTTP wire protocol. I think there is room to grow multiple
  APIs. For example, there may someday be a proper JSON API to query
  or even manipulate the repository. And I don't think we should have
  to create a new top-level URL space for each API nor should we
  attempt to shoehorn each future API into the same shared URL space:
  that would just be too chaotic.
  
  This commits reserves the /api/* URL space for all our future API
  needs. Essentially, all requests to /api/* get routed to a new WSGI
  handler. By default, it 404's the entire URL space unless the
  "api server" feature is enabled. When enabled, requests to "/api"
  list available APIs. URLs of the form /api//* are reserved for
  a particular named API. Behavior within each API is left up to that
  API. So, we can grow new APIs easily without worrying about URL
  space conflicts.
  
  APIs can be registered by adding entries to a global dict. This allows
  extensions to provide their own APIs should they choose to do so.
  This is probably a premature feature. But IMO the code is easier
  to read if we're not dealing with API-specific behavior like config
  option querying inline.
  
  To prove it works, we implement a very basic API for version 2
  of the HTTP wire protocol. It does nothing of value except
  facilitate testing of the /api/* URL space.
  
  We currently emit plain text responses for all /api/* endpoints.
  There's definitely room to look at Accept and other request headers
  to vary the response format. But we have to start somewhere.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2834

AFFECTED FILES
  mercurial/configitems.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/wireprotoserver.py
  mercurial/wireprototypes.py
  tests/test-http-api-httpv2.t
  tests/test-http-api.t

CHANGE DETAILS

diff --git a/tests/test-http-api.t b/tests/test-http-api.t
new file mode 100644
--- /dev/null
+++ b/tests/test-http-api.t
@@ -0,0 +1,105 @@
+  $ hg init server
+  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+Request to /api fails unless web.apiserver is enabled
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api -
+  404 Not Found
+  content-length: 44
+  content-type: text/plain
+  
+  Experimental API server endpoint not enabled (no-eol)
+  [1]
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/ -
+  404 Not Found
+  content-length: 44
+  content-type: text/plain
+  
+  Experimental API server endpoint not enabled (no-eol)
+  [1]
+
+Restart server with support for API server
+
+  $ killdaemons.py
+  $ cat > server/.hg/hgrc << EOF
+  > [experimental]
+  > web.apiserver = true
+  > EOF
+
+  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+/api lists available APIs (empty since none are available by default)
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api -
+  200 OK
+  content-length: 100
+  content-type: text/plain
+  
+  APIs can be accessed at /api/, where  can be one of the 
following:
+  
+  (no available APIs)
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/ -
+  200 OK
+  content-length: 100
+  content-type: text/plain
+  
+  APIs can be accessed at /api/, where  can be one of the 
following:
+  
+  (no available APIs)
+
+Accessing an unknown API yields a 404
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/unknown -
+  404 Not Found
+  content-length: 33
+  content-type: text/plain
+  
+  Unknown API: unknown
+  Known APIs:  (no-eol)
+  [1]
+
+Accessing a known but not enabled API yields a different error
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/exp-http-v2-0001 -
+  404 Not Found
+  content-length: 33
+  content-type: text/plain
+  
+  API exp-http-v2-0001 not enabled
+  [1]
+
+Restart server with support for HTTP v2 API
+
+  $ killdaemons.py
+  $ cat > server/.hg/hgrc << EOF
+  > [experimental]
+  > web.apiserver = true
+  > web.api.http-v2 = true
+  > EOF
+
+  $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
+  $ cat hg.pid > $DAEMON_PIDS
+
+/api lists the HTTP v2 protocol as available
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api -
+  200 OK
+  content-length: 96
+  content-type: text/plain
+  
+  APIs can be accessed at /api/, where  can be one of the 
following:
+  
+  exp-http-v2-0001 (no-eol)
+
+  $ get-with-headers.py $LOCALIP:$HGPORT api/ -
+  

D2721: util: observable proxy objects for sockets

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6972.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2721?vs=6716=6972

REVISION DETAIL
  https://phab.mercurial-scm.org/D2721

AFFECTED FILES
  mercurial/util.py

CHANGE DETAILS

diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -687,6 +687,120 @@
 
 return res
 
+PROXIED_SOCKET_METHODS = {
+r'makefile',
+r'recv',
+r'recvfrom',
+r'recvfrom_into',
+r'recv_into',
+r'send',
+r'sendall',
+r'sendto',
+r'setblocking',
+r'settimeout',
+r'gettimeout',
+r'setsockopt',
+}
+
+class socketproxy(object):
+"""A proxy around a socket that tells a watcher when events occur.
+
+This is like ``fileobjectproxy`` except for sockets.
+
+This type is intended to only be used for testing purposes. Think hard
+before using it in important code.
+"""
+__slots__ = (
+r'_orig',
+r'_observer',
+)
+
+def __init__(self, sock, observer):
+object.__setattr__(self, r'_orig', sock)
+object.__setattr__(self, r'_observer', observer)
+
+def __getattribute__(self, name):
+if name in PROXIED_SOCKET_METHODS:
+return object.__getattribute__(self, name)
+
+return getattr(object.__getattribute__(self, r'_orig'), name)
+
+def __delattr__(self, name):
+return delattr(object.__getattribute__(self, r'_orig'), name)
+
+def __setattr__(self, name, value):
+return setattr(object.__getattribute__(self, r'_orig'), name, value)
+
+def _observedcall(self, name, *args, **kwargs):
+# Call the original object.
+orig = object.__getattribute__(self, r'_orig')
+res = getattr(orig, name)(*args, **kwargs)
+
+# Call a method on the observer of the same name with arguments
+# so it can react, log, etc.
+observer = object.__getattribute__(self, r'_observer')
+fn = getattr(observer, name, None)
+if fn:
+fn(res, *args, **kwargs)
+
+return res
+
+def makefile(self, *args, **kwargs):
+res = object.__getattribute__(self, r'_observedcall')(
+r'makefile', *args, **kwargs)
+
+# The file object may be used for I/O. So we turn it into a
+# proxy using our observer.
+observer = object.__getattribute__(self, r'_observer')
+return makeloggingfileobject(observer.fh, res, observer.name,
+ reads=observer.reads,
+ writes=observer.writes,
+ logdata=observer.logdata)
+
+def recv(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recv', *args, **kwargs)
+
+def recvfrom(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recvfrom', *args, **kwargs)
+
+def recvfrom_into(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recvfrom_into', *args, **kwargs)
+
+def recv_into(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'recv_info', *args, **kwargs)
+
+def send(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'send', *args, **kwargs)
+
+def sendall(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'sendall', *args, **kwargs)
+
+def sendto(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'sendto', *args, **kwargs)
+
+def setblocking(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'setblocking', *args, **kwargs)
+
+def settimeout(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'settimeout', *args, **kwargs)
+
+def gettimeout(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'gettimeout', *args, **kwargs)
+
+def setsockopt(self, *args, **kwargs):
+return object.__getattribute__(self, r'_observedcall')(
+r'setsockopt', *args, **kwargs)
+
 DATA_ESCAPE_MAP = {pycompat.bytechr(i): br'\x%02x' % i for i in range(256)}
 DATA_ESCAPE_MAP.update({
 b'\\': b'',
@@ -701,15 +815,7 @@
 
 return DATA_ESCAPE_RE.sub(lambda m: DATA_ESCAPE_MAP[m.group(0)], s)
 
-class fileobjectobserver(object):
-"""Logs file object activity."""
-def __init__(self, fh, name, reads=True, writes=True, logdata=False):
-self.fh = fh
-self.name = name
-self.logdata = logdata
-self.reads = reads
-self.writes = writes
-
+class baseproxyobserver(object):
 def _writedata(self, data):
 if not self.logdata:
 

D2833: tests: teach get-with-headers.py to ignore dynamic headers

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  The Date and Server HTTP headers are dynamic by nature. They are
  also present on every response and are mostly advisory.
  
  It isn't important for us to test for the presence of these
  headers. In fact, all tests currently glob over the entirety
  of their values.
  
  Having to constantly glob these headers feels like more trouble
  than it is worth. Let's teach get-with-headers.py to hide them
  by default.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2833

AFFECTED FILES
  tests/get-with-headers.py
  tests/test-archive.t
  tests/test-hgweb-commands.t
  tests/test-http-protocol.t

CHANGE DETAILS

diff --git a/tests/test-http-protocol.t b/tests/test-http-protocol.t
--- a/tests/test-http-protocol.t
+++ b/tests/test-http-protocol.t
@@ -49,42 +49,32 @@
   $ get-with-headers.py --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
-  date: * (glob)
-  server: * (glob)
   transfer-encoding: chunked
 
 Server should send application/mercurial-0.1 when client says it wants it
 
   $ get-with-headers.py --hgproto '0.1' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
-  date: * (glob)
-  server: * (glob)
   transfer-encoding: chunked
 
 Server should send application/mercurial-0.2 when client says it wants it
 
   $ get-with-headers.py --hgproto '0.2' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.2
-  date: * (glob)
-  server: * (glob)
   transfer-encoding: chunked
 
   $ get-with-headers.py --hgproto '0.1 0.2' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.2
-  date: * (glob)
-  server: * (glob)
   transfer-encoding: chunked
 
 Requesting a compression format that server doesn't support results will fall 
back to 0.1
 
   $ get-with-headers.py --hgproto '0.2 comp=aa' --headeronly $LOCALIP:$HGPORT 
'?cmd=getbundle=e93700bd72895c5addab234c56d4024b487a362f='
 -
   200 Script output follows
   content-type: application/mercurial-0.1
-  date: * (glob)
-  server: * (glob)
   transfer-encoding: chunked
 
 #if zstd
@@ -105,8 +95,6 @@
   200 Script output follows
   content-length: 41
   content-type: application/mercurial-0.1
-  date: * (glob)
-  server: * (glob)
   
   e93700bd72895c5addab234c56d4024b487a362f
 
diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t
+++ b/tests/test-hgweb-commands.t
@@ -1922,8 +1922,6 @@
   404 Not Found
   content-length: 12
   content-type: application/mercurial-0.1
-  date: * (glob)
-  server: * (glob)
   
   0
   Not Found
diff --git a/tests/test-archive.t b/tests/test-archive.t
--- a/tests/test-archive.t
+++ b/tests/test-archive.t
@@ -126,82 +126,64 @@
   200 Script output follows
   content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
   content-type: application/x-gzip
-  date: * (glob)
   etag: W/"*" (glob)
-  server: * (glob)
   transfer-encoding: chunked
   
   body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
   % tar.bz2 and zip disallowed should both give 403
   403 Archive type not allowed: bz2
   content-type: text/html; charset=ascii
-  date: * (glob)
   etag: W/"*" (glob)
-  server: * (glob)
   transfer-encoding: chunked
   
   body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
   403 Archive type not allowed: zip
   content-type: text/html; charset=ascii
-  date: * (glob)
   etag: W/"*" (glob)
-  server: * (glob)
   transfer-encoding: chunked
   
   body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
   $ test_archtype bz2 tar.bz2 zip tar.gz
   % bz2 allowed should give 200
   200 Script output follows
   content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
   content-type: application/x-bzip2
-  date: * (glob)
   etag: W/"*" (glob)
-  server: * (glob)
   transfer-encoding: chunked
   
   body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
   % zip and tar.gz disallowed should both give 403
   403 Archive type not allowed: zip
   content-type: text/html; charset=ascii
-  date: * (glob)
   etag: W/"*" (glob)
-  server: * (glob)
   transfer-encoding: chunked
   
   body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
   403 Archive type not allowed: gz
   content-type: text/html; charset=ascii
-  date: * 

D2723: httppeer: remove _requestbuilder attribute

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6973.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2723?vs=6718=6973

REVISION DETAIL
  https://phab.mercurial-scm.org/D2723

AFFECTED FILES
  mercurial/httppeer.py

CHANGE DETAILS

diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -138,7 +138,6 @@
 self._path = path
 self._caps = None
 self._urlopener = None
-self._requestbuilder = None
 u = util.url(path)
 if u.query or u.fragment:
 raise error.Abort(_('unsupported URL component: "%s"') %
@@ -151,7 +150,6 @@
 ui.debug('using %s\n' % self._url)
 
 self._urlopener = url.opener(ui, authinfo)
-self._requestbuilder = urlreq.request
 
 def __del__(self):
 urlopener = getattr(self, '_urlopener', None)
@@ -328,7 +326,7 @@
 if varyheaders:
 headers[r'Vary'] = r','.join(varyheaders)
 
-req = self._requestbuilder(pycompat.strurl(cu), data, headers)
+req = urlreq.request(pycompat.strurl(cu), data, headers)
 
 if data is not None:
 self.ui.debug("sending %d bytes\n" % size)



To: indygreg, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2724: httppeer: alias url as urlmod

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6974.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2724?vs=6719=6974

REVISION DETAIL
  https://phab.mercurial-scm.org/D2724

AFFECTED FILES
  mercurial/httppeer.py

CHANGE DETAILS

diff --git a/mercurial/httppeer.py b/mercurial/httppeer.py
--- a/mercurial/httppeer.py
+++ b/mercurial/httppeer.py
@@ -22,7 +22,7 @@
 httpconnection,
 pycompat,
 statichttprepo,
-url,
+url as urlmod,
 util,
 wireproto,
 )
@@ -149,7 +149,7 @@
 self._ui = ui
 ui.debug('using %s\n' % self._url)
 
-self._urlopener = url.opener(ui, authinfo)
+self._urlopener = urlmod.opener(ui, authinfo)
 
 def __del__(self):
 urlopener = getattr(self, '_urlopener', None)
@@ -484,7 +484,7 @@
 if create:
 raise error.Abort(_('cannot create new http repository'))
 try:
-if path.startswith('https:') and not url.has_https:
+if path.startswith('https:') and not urlmod.has_https:
 raise error.Abort(_('Python support for SSL and HTTPS '
 'is not installed'))
 



To: indygreg, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2720: debugcommands: introduce actions to perform deterministic reads

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6976.
indygreg edited the summary of this revision.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2720?vs=6714=6976

REVISION DETAIL
  https://phab.mercurial-scm.org/D2720

AFFECTED FILES
  mercurial/debugcommands.py

CHANGE DETAILS

diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -2701,6 +2701,21 @@
 
 Read a line of output from the server. If there are multiple output
 pipes, reads only the main pipe.
+
+ereadline
+-
+
+Like ``readline``, but read from the stderr pipe, if available.
+
+read 
+
+
+``read()`` N bytes from the server's main output pipe.
+
+eread 
+-
+
+``read()`` N bytes from the server's stderr pipe, if available.
 """
 opts = pycompat.byteskwargs(opts)
 
@@ -2841,6 +2856,14 @@
 stderr.read()
 elif action == 'readline':
 stdout.readline()
+elif action == 'ereadline':
+stderr.readline()
+elif action.startswith('read '):
+count = int(action.split(' ', 1)[1])
+stdout.read(count)
+elif action.startswith('eread '):
+count = int(action.split(' ', 1)[1])
+stderr.read(count)
 else:
 raise error.Abort(_('unknown action: %s') % action)
 



To: indygreg, #hg-reviewers
Cc: mharbison72, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


[Bug 5817] New: fold should respect experimental.evolve.allowdivergence

2018-03-12 Thread mercurial-bugs
https://bz.mercurial-scm.org/show_bug.cgi?id=5817

Bug ID: 5817
   Summary: fold should respect
experimental.evolve.allowdivergence
   Product: Mercurial
   Version: unspecified
  Hardware: PC
OS: Linux
Status: UNCONFIRMED
  Severity: feature
  Priority: wish
 Component: evolution
  Assignee: bugzi...@mercurial-scm.org
  Reporter: martinv...@google.com
CC: mercurial-devel@mercurial-scm.org,
pierre-yves.da...@ens-lyon.org

-- 
You are receiving this mail because:
You are on the CC list for the bug.
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


mercurial@36843: 55 new changesets

2018-03-12 Thread Mercurial Commits
55 new changesets in mercurial:

https://www.mercurial-scm.org/repo/hg/rev/09b58af83d44
changeset:   36789:09b58af83d44
user:Augie Fackler 
date:Mon Aug 29 10:42:58 2016 -0400
summary: bookmarks: test for exchanging long bookmark names (issue5165)

https://www.mercurial-scm.org/repo/hg/rev/cb0afaf112e8
changeset:   36790:cb0afaf112e8
user:Yuya Nishihara 
date:Tue Mar 06 02:05:25 2018 -0600
summary: hgk: stop using util.bytesinput() to read a single line from stdin

https://www.mercurial-scm.org/repo/hg/rev/30742c216abb
changeset:   36791:30742c216abb
user:Yuya Nishihara 
date:Tue Mar 06 02:14:11 2018 -0600
summary: ui: inline util.bytesinput() into ui._readline()

https://www.mercurial-scm.org/repo/hg/rev/15c050b5d599
changeset:   36792:15c050b5d599
user:Yuya Nishihara 
date:Tue Mar 06 03:05:49 2018 -0600
summary: ui: add debug commands to test interactive prompt

https://www.mercurial-scm.org/repo/hg/rev/eca1051e6c22
changeset:   36793:eca1051e6c22
user:Yuya Nishihara 
date:Tue Mar 06 02:28:59 2018 -0600
summary: util: add public isstdin/isstdout() functions

https://www.mercurial-scm.org/repo/hg/rev/fa53a1d1f16e
changeset:   36794:fa53a1d1f16e
user:Yuya Nishihara 
date:Tue Mar 06 02:32:26 2018 -0600
summary: ui: do not try readline support if fin/fout aren't standard streams

https://www.mercurial-scm.org/repo/hg/rev/9b513888ea23
changeset:   36795:9b513888ea23
user:Yuya Nishihara 
date:Tue Mar 06 02:38:53 2018 -0600
summary: ui: do not use rawinput() when we have to replace sys.stdin/stdout

https://www.mercurial-scm.org/repo/hg/rev/aa0fc12743c7
changeset:   36796:aa0fc12743c7
user:Yuya Nishihara 
date:Tue Mar 06 02:42:37 2018 -0600
summary: ui: adjust Windows workaround to new _readline() code

https://www.mercurial-scm.org/repo/hg/rev/d4c760c997cd
changeset:   36797:d4c760c997cd
user:Yuya Nishihara 
date:Tue Mar 06 02:43:17 2018 -0600
summary: py3: drop encoding.strio()

https://www.mercurial-scm.org/repo/hg/rev/7574c8173d5e
changeset:   36798:7574c8173d5e
user:Gregory Szorc 
date:Tue Mar 06 15:02:53 2018 -0800
summary: wireprotoserver: check if command available before calling it

https://www.mercurial-scm.org/repo/hg/rev/c638a13093cf
changeset:   36799:c638a13093cf
user:Gregory Szorc 
date:Tue Mar 06 15:08:33 2018 -0800
summary: wireprotoserver: check permissions in main dispatch function

https://www.mercurial-scm.org/repo/hg/rev/0b18604db95e
changeset:   36800:0b18604db95e
user:Gregory Szorc 
date:Wed Mar 07 16:02:24 2018 -0800
summary: wireproto: declare permissions requirements in @wireprotocommand 
(API)

https://www.mercurial-scm.org/repo/hg/rev/66de4555cefd
changeset:   36801:66de4555cefd
user:Gregory Szorc 
date:Wed Mar 07 16:18:52 2018 -0800
summary: wireproto: formalize permissions checking as part of protocol 
interface

https://www.mercurial-scm.org/repo/hg/rev/7fc80c982656
changeset:   36802:7fc80c982656
user:Gregory Szorc 
date:Thu Mar 08 09:26:51 2018 -0800
summary: hgweb: ensure all wsgi environment values are str

https://www.mercurial-scm.org/repo/hg/rev/8e1556ac01bb
changeset:   36803:8e1556ac01bb
user:Gregory Szorc 
date:Thu Mar 08 09:44:27 2018 -0800
summary: hgweb: validate WSGI environment dict

https://www.mercurial-scm.org/repo/hg/rev/b9b968e21f78
changeset:   36804:b9b968e21f78
user:Gregory Szorc 
date:Thu Mar 08 15:15:59 2018 -0800
summary: hgweb: rename req to wsgireq

https://www.mercurial-scm.org/repo/hg/rev/ec46415ed826
changeset:   36805:ec46415ed826
user:Gregory Szorc 
date:Thu Mar 08 15:14:32 2018 -0800
summary: hgweb: always use "?" when writing session vars

https://www.mercurial-scm.org/repo/hg/rev/69b2d0900cd7
changeset:   36806:69b2d0900cd7
user:Gregory Szorc 
date:Sat Mar 10 10:20:51 2018 -0800
summary: hgweb: parse WSGI request into a data structure

https://www.mercurial-scm.org/repo/hg/rev/1e2194e0ef62
changeset:   36807:1e2194e0ef62
user:Gregory Szorc 
date:Thu Mar 08 12:59:25 2018 -0800
summary: hgweb: use computed base URL from parsed request

https://www.mercurial-scm.org/repo/hg/rev/0031e972ded2
changeset:   36808:0031e972ded2
user:Gregory Szorc 
date:Thu Mar 08 15:08:20 2018 -0800
summary: hgweb: use the parsed application path directly


D2691: commands: don't check for merge.update() truthiness

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6968.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2691?vs=6651=6968

REVISION DETAIL
  https://phab.mercurial-scm.org/D2691

AFFECTED FILES
  hgext/rebase.py
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -2301,7 +2301,7 @@
 finally:
 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
 # report any conflicts
-if stats and stats[3] > 0:
+if stats[3] > 0:
 # write out state for --continue
 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
 repo.vfs.write('graftstate', ''.join(nodelines))
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -486,7 +486,7 @@
  'rebase')
 stats = rebasenode(repo, rev, p1, base, self.collapsef,
dest, wctx=self.wctx)
-if stats and stats[3] > 0:
+if stats[3] > 0:
 if self.wctx.isinmemory():
 raise error.InMemoryMergeConflictsError()
 else:



To: indygreg, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2694: merge: deprecate accessing update results by index

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6970.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2694?vs=6654=6970

REVISION DETAIL
  https://phab.mercurial-scm.org/D2694

AFFECTED FILES
  hgext/histedit.py
  hgext/rebase.py
  mercurial/commands.py
  mercurial/hg.py
  mercurial/merge.py

CHANGE DETAILS

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -1407,9 +1407,15 @@
 removedcount = attr.ib()
 unresolvedcount = attr.ib()
 
+def isempty(self):
+return (not self.updatedcount and not self.mergedcount
+and not self.removedcount and not self.unresolvedcount)
+
 # TODO remove container emulation once consumers switch to new API.
 
 def __getitem__(self, x):
+util.nouideprecwarn('access merge.update() results by name instead of '
+'index', '4.6', 2)
 if x == 0:
 return self.updatedcount
 elif x == 1:
@@ -1422,6 +1428,8 @@
 raise IndexError('can only access items 0-3')
 
 def __len__(self):
+util.nouideprecwarn('access merge.update() results by name instead of '
+'index', '4.6', 2)
 return 4
 
 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
@@ -2069,7 +2077,8 @@
 sparse.prunetemporaryincludes(repo)
 
 if not partial:
-repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
+repo.hook('update', parent1=xp1, parent2=xp2,
+  error=stats.unresolvedcount)
 return stats
 
 def graft(repo, ctx, pctx, labels, keepparent=False):
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -745,7 +745,7 @@
 return srcpeer, destpeer
 
 def _showstats(repo, stats, quietempty=False):
-if quietempty and not any(stats):
+if quietempty and stats.isempty():
 return
 repo.ui.status(_("%d files updated, %d files merged, "
  "%d files removed, %d files unresolved\n") % (
@@ -766,9 +766,9 @@
 """update the working directory to node"""
 stats = updaterepo(repo, node, False, updatecheck=updatecheck)
 _showstats(repo, stats, quietempty)
-if stats[3]:
+if stats.unresolvedcount:
 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges\n"))
-return stats[3] > 0
+return stats.unresolvedcount > 0
 
 # naming conflict in clone()
 _update = update
@@ -779,7 +779,7 @@
 repo.vfs.unlinkpath('graftstate', ignoremissing=True)
 if show_stats:
 _showstats(repo, stats, quietempty)
-return stats[3] > 0
+return stats.unresolvedcount > 0
 
 # naming conflict in updatetotally()
 _clean = clean
@@ -878,12 +878,12 @@
 labels=labels)
 
 _showstats(repo, stats)
-if stats[3]:
+if stats.unresolvedcount:
 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
  "or 'hg merge --abort' to abandon\n"))
 elif remind and not abort:
 repo.ui.status(_("(branch merge, don't forget to commit)\n"))
-return stats[3] > 0
+return stats.unresolvedcount > 0
 
 def _incoming(displaychlist, subreporecurse, ui, repo, source,
 opts, buffered=False):
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -624,7 +624,7 @@
 repo.setparents(op1, op2)
 dsguard.close()
 hg._showstats(repo, stats)
-if stats[3]:
+if stats.unresolvedcount:
 repo.ui.status(_("use 'hg resolve' to retry unresolved "
  "file merges\n"))
 return 1
@@ -2301,7 +2301,7 @@
 finally:
 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
 # report any conflicts
-if stats[3] > 0:
+if stats.unresolvedcount > 0:
 # write out state for --continue
 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
 repo.vfs.write('graftstate', ''.join(nodelines))
diff --git a/hgext/rebase.py b/hgext/rebase.py
--- a/hgext/rebase.py
+++ b/hgext/rebase.py
@@ -486,7 +486,7 @@
  'rebase')
 stats = rebasenode(repo, rev, p1, base, self.collapsef,
dest, wctx=self.wctx)
-if stats[3] > 0:
+if stats.unresolvedcount > 0:
 if self.wctx.isinmemory():
 raise error.InMemoryMergeConflictsError()
 else:
diff --git a/hgext/histedit.py b/hgext/histedit.py
--- a/hgext/histedit.py
+++ b/hgext/histedit.py
@@ -489,7 +489,7 @@
 hg.update(repo, self.state.parentctxnode, quietempty=True)
 stats = applychanges(repo.ui, 

D2702: commands: use constants for merge things

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6971.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2702?vs=6663=6971

REVISION DETAIL
  https://phab.mercurial-scm.org/D2702

AFFECTED FILES
  mercurial/commands.py

CHANGE DETAILS

diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -4351,11 +4351,12 @@
 # as 'P'.  Resolved path conflicts show as 'R', the same as normal
 # resolved conflicts.
 mergestateinfo = {
-'u': ('resolve.unresolved', 'U'),
-'r': ('resolve.resolved', 'R'),
-'pu': ('resolve.unresolved', 'P'),
-'pr': ('resolve.resolved', 'R'),
-'d': ('resolve.driverresolved', 'D'),
+mergemod.MERGE_RECORD_UNRESOLVED: ('resolve.unresolved', 'U'),
+mergemod.MERGE_RECORD_RESOLVED: ('resolve.resolved', 'R'),
+mergemod.MERGE_RECORD_UNRESOLVED_PATH: ('resolve.unresolved', 'P'),
+mergemod.MERGE_RECORD_RESOLVED_PATH: ('resolve.resolved', 'R'),
+mergemod.MERGE_RECORD_DRIVER_RESOLVED: ('resolve.driverresolved',
+'D'),
 }
 
 for f in ms:
@@ -4378,7 +4379,8 @@
 
 wctx = repo[None]
 
-if ms.mergedriver and ms.mdstate() == 'u':
+if (ms.mergedriver
+and ms.mdstate() == mergemod.MERGE_DRIVER_STATE_UNMARKED):
 proceed = mergemod.driverpreprocess(repo, ms, wctx)
 ms.commit()
 # allow mark and unmark to go through
@@ -4399,7 +4401,7 @@
 
 # don't let driver-resolved files be marked, and run the conclude
 # step if asked to resolve
-if ms[f] == "d":
+if ms[f] == mergemod.MERGE_RECORD_DRIVER_RESOLVED:
 exact = m.exact(f)
 if mark:
 if exact:
@@ -4414,20 +4416,21 @@
 continue
 
 # path conflicts must be resolved manually
-if ms[f] in ("pu", "pr"):
+if ms[f] in (mergemod.MERGE_RECORD_UNRESOLVED_PATH,
+ mergemod.MERGE_RECORD_RESOLVED_PATH):
 if mark:
-ms.mark(f, "pr")
+ms.mark(f, mergemod.MERGE_RECORD_RESOLVED_PATH)
 elif unmark:
-ms.mark(f, "pu")
-elif ms[f] == "pu":
+ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED_PATH)
+elif ms[f] == mergemod.MERGE_RECORD_UNRESOLVED_PATH:
 ui.warn(_('%s: path conflict must be resolved manually\n')
 % f)
 continue
 
 if mark:
-ms.mark(f, "r")
+ms.mark(f, mergemod.MERGE_RECORD_RESOLVED)
 elif unmark:
-ms.mark(f, "u")
+ms.mark(f, mergemod.MERGE_RECORD_UNRESOLVED)
 else:
 # backup pre-resolve (merge uses .orig for its own purposes)
 a = repo.wjoin(f)



To: indygreg, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2692: merge: return an attrs class from update() and applyupdates()

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg updated this revision to Diff 6969.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2692?vs=6652=6969

REVISION DETAIL
  https://phab.mercurial-scm.org/D2692

AFFECTED FILES
  mercurial/hg.py
  mercurial/merge.py

CHANGE DETAILS

diff --git a/mercurial/merge.py b/mercurial/merge.py
--- a/mercurial/merge.py
+++ b/mercurial/merge.py
@@ -22,6 +22,9 @@
 nullid,
 nullrev,
 )
+from .thirdparty import (
+attr,
+)
 from . import (
 copies,
 error,
@@ -1397,6 +1400,30 @@
 prefetch = scmutil.fileprefetchhooks
 prefetch(repo, ctx, [f for sublist in oplist for f, args, msg in sublist])
 
+@attr.s(frozen=True)
+class updateresult(object):
+updatedcount = attr.ib()
+mergedcount = attr.ib()
+removedcount = attr.ib()
+unresolvedcount = attr.ib()
+
+# TODO remove container emulation once consumers switch to new API.
+
+def __getitem__(self, x):
+if x == 0:
+return self.updatedcount
+elif x == 1:
+return self.mergedcount
+elif x == 2:
+return self.removedcount
+elif x == 3:
+return self.unresolvedcount
+else:
+raise IndexError('can only access items 0-3')
+
+def __len__(self):
+return 4
+
 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
 """apply the merge action list to the working directory
 
@@ -1580,7 +1607,8 @@
 if not proceed:
 # XXX setting unresolved to at least 1 is a hack to make sure we
 # error out
-return updated, merged, removed, max(len(unresolvedf), 1)
+return updateresult(updated, merged, removed,
+max(len(unresolvedf), 1))
 newactions = []
 for f, args, msg in mergeactions:
 if f in unresolvedf:
@@ -1655,8 +1683,7 @@
 actions['m'] = [a for a in actions['m'] if a[0] in mfiles]
 
 progress(_updating, None, total=numupdates, unit=_files)
-
-return updated, merged, removed, unresolved
+return updateresult(updated, merged, removed, unresolved)
 
 def recordupdates(repo, actions, branchmerge):
 "record merge actions to the dirstate"
@@ -1877,7 +1904,7 @@
 # call the hooks and exit early
 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
 repo.hook('update', parent1=xp2, parent2='', error=0)
-return 0, 0, 0, 0
+return updateresult(0, 0, 0, 0)
 
 if (updatecheck == 'linear' and
 pas not in ([p1], [p2])):  # nonlinear
diff --git a/mercurial/hg.py b/mercurial/hg.py
--- a/mercurial/hg.py
+++ b/mercurial/hg.py
@@ -748,7 +748,9 @@
 if quietempty and not any(stats):
 return
 repo.ui.status(_("%d files updated, %d files merged, "
- "%d files removed, %d files unresolved\n") % stats)
+ "%d files removed, %d files unresolved\n") % (
+   stats.updatedcount, stats.mergedcount,
+   stats.removedcount, stats.unresolvedcount))
 
 def updaterepo(repo, node, overwrite, updatecheck=None):
 """Update the working directory to node.



To: indygreg, #hg-reviewers
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH V2] forget: add --dry-run mode

2018-03-12 Thread Matt Harbison
On Sun, 11 Mar 2018 05:49:26 -0400, Sushil khanchi  
 wrote:



# HG changeset patch
# User Sushil khanchi 
# Date 1520665399 -19800
#  Sat Mar 10 12:33:19 2018 +0530
# Node ID a1be8989c0158abc69ebd97ca8a0cc7dc3801be9
# Parent  4c71a26a4009d88590c9ae3d64a5912fd556d82e
forget: add --dry-run mode

diff -r 4c71a26a4009 -r a1be8989c015 mercurial/cmdutil.py
--- a/mercurial/cmdutil.py  Sun Mar 04 21:16:36 2018 -0500
+++ b/mercurial/cmdutil.py  Sat Mar 10 12:33:19 2018 +0530
@@ -1996,7 +1996,7 @@
 for subpath in ctx.substate:
 ctx.sub(subpath).addwebdirpath(serverpath, webconf)
-def forget(ui, repo, match, prefix, explicitonly):
+def forget(ui, repo, match, prefix, explicitonly, **opts):
 join = lambda f: os.path.join(prefix, f)
 bad = []
 badfn = lambda x, y: bad.append(x) or match.bad(x, y)
@@ -2039,9 +2039,10 @@
 if ui.verbose or not match.exact(f):
 ui.status(_('removing %s\n') % match.rel(f))
-rejected = wctx.forget(forget, prefix)


Shouldn't --dry-run be passed into wctx.forget() too?  Then the warning  
about bad paths there will be emitted, and you won't have to  
conditionalize the following lines here.  That in turn won't affect the  
exit code.



-bad.extend(f for f in rejected if f in match.files())
-forgot.extend(f for f in forget if f not in rejected)
+if not opts.get(r'dry_run'):
+rejected = wctx.forget(forget, prefix)
+bad.extend(f for f in rejected if f in match.files())
+forgot.extend(f for f in forget if f not in rejected)
 return bad, forgot
def files(ui, ctx, m, fm, fmt, subrepos):
diff -r 4c71a26a4009 -r a1be8989c015 mercurial/commands.py
--- a/mercurial/commands.py Sun Mar 04 21:16:36 2018 -0500
+++ b/mercurial/commands.py Sat Mar 10 12:33:19 2018 +0530
@@ -2036,7 +2036,11 @@
 with ui.formatter('files', opts) as fm:
 return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos'))
-@command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
+@command(
+'^forget',
+[('', 'dry-run', None, _('only print output'))]
++ walkopts,
+_('[OPTION]... FILE...'), inferrepo=True)
 def forget(ui, repo, *pats, **opts):
 """forget the specified files on the next commit
@@ -2071,7 +2075,7 @@
 raise error.Abort(_('no files specified'))
m = scmutil.match(repo[None], pats, opts)
-rejected = cmdutil.forget(ui, repo, m, prefix="",  
explicitonly=False)[0]
+rejected = cmdutil.forget(ui, repo, m, prefix="",  
explicitonly=False, **opts)[0]

 return rejected and 1 or 0
@command(
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel

___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread spectral (Kyle Lippincott)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG9e460318ca4b: copyfile: preserve stat info (mtime, etc.) 
when doing copies/renames (authored by spectral, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2729?vs=6965=6967

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

AFFECTED FILES
  hgext/largefiles/overrides.py
  mercurial/cmdutil.py
  tests/test-rename.t

CHANGE DETAILS

diff --git a/tests/test-rename.t b/tests/test-rename.t
--- a/tests/test-rename.t
+++ b/tests/test-rename.t
@@ -657,3 +657,24 @@
   [255]
   $ hg status -C
 
+check that stat information such as mtime is preserved - it's unclear whether
+the `touch` and `stat` commands are portable, so we mimic them using python.
+Not all platforms support precision of even one-second granularity, so we allow
+a rather generous fudge factor here; 1234567890 is 2009, and the primary thing
+we care about is that it's not the machine's current time; hopefully it's 
really
+unlikely for a machine to have such a broken clock that this test fails. :)
+
+  $ mkdir mtime
+Create the file (as empty), then update its mtime and atime to be 1234567890.
+  >>> import os
+  >>> filename = "mtime/f"
+  >>> mtime = 1234567890
+  >>> open(filename, "w").close()
+  >>> os.utime(filename, (mtime, mtime))
+  $ hg ci -qAm 'add mtime dir'
+  $ hg mv -q mtime mtime2
+  >>> from __future__ import print_function
+  >>> import os
+  >>> filename = "mtime2/f"
+  >>> print(os.stat(filename).st_mtime < 1234567999)
+  True
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1186,7 +1186,7 @@
 os.rename(src, tmp)
 os.rename(tmp, target)
 else:
-util.copyfile(src, target)
+util.copyfile(src, target, copystat=True)
 srcexists = True
 except IOError as inst:
 if inst.errno == errno.ENOENT:
diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py
--- a/hgext/largefiles/overrides.py
+++ b/hgext/largefiles/overrides.py
@@ -667,15 +667,15 @@
 try:
 origcopyfile = util.copyfile
 copiedfiles = []
-def overridecopyfile(src, dest):
+def overridecopyfile(src, dest, *args, **kwargs):
 if (lfutil.shortname in src and
 dest.startswith(repo.wjoin(lfutil.shortname))):
 destlfile = dest.replace(lfutil.shortname, '')
 if not opts['force'] and os.path.exists(destlfile):
 raise IOError('',
 _('destination largefile already exists'))
 copiedfiles.append((src, dest))
-origcopyfile(src, dest)
+origcopyfile(src, dest, *args, **kwargs)
 
 util.copyfile = overridecopyfile
 result += orig(ui, repo, listpats, opts, rename)



To: spectral, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2832: hgweb: remove wsgirequest (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf0a851542a05: hgweb: remove wsgirequest (API) (authored by 
indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2832?vs=6958=6966

REVISION DETAIL
  https://phab.mercurial-scm.org/D2832

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/request.py
  tests/test-wsgirequest.py

CHANGE DETAILS

diff --git a/tests/test-wsgirequest.py b/tests/test-wsgirequest.py
--- a/tests/test-wsgirequest.py
+++ b/tests/test-wsgirequest.py
@@ -23,11 +23,11 @@
 r'wsgi.run_once': False,
 }
 
-def parse(env, bodyfh=None, reponame=None, altbaseurl=None, extra=None):
+def parse(env, reponame=None, altbaseurl=None, extra=None):
 env = dict(env)
 env.update(extra or {})
 
-return requestmod.parserequestfromenv(env, bodyfh, reponame=reponame,
+return requestmod.parserequestfromenv(env, reponame=reponame,
   altbaseurl=altbaseurl)
 
 class ParseRequestTests(unittest.TestCase):
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -152,7 +152,7 @@
 # WSGI environment dict, unmodified.
 rawenv = attr.ib()
 
-def parserequestfromenv(env, bodyfh, reponame=None, altbaseurl=None):
+def parserequestfromenv(env, reponame=None, altbaseurl=None):
 """Parse URL components from environment variables.
 
 WSGI defines request attributes via environment variables. This function
@@ -325,11 +325,9 @@
 if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
 headers['Content-Length'] = env['CONTENT_LENGTH']
 
-# TODO do this once we remove wsgirequest.inp, otherwise we could have
-# multiple readers from the underlying input stream.
-#bodyfh = env['wsgi.input']
-#if 'Content-Length' in headers:
-#bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))
+bodyfh = env['wsgi.input']
+if 'Content-Length' in headers:
+bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))
 
 return parsedrequest(method=env['REQUEST_METHOD'],
  url=fullurl, baseurl=baseurl,
@@ -578,34 +576,6 @@
 assert self._bodywritefn
 return offsettrackingwriter(self._bodywritefn)
 
-class wsgirequest(object):
-"""Higher-level API for a WSGI request.
-
-WSGI applications are invoked with 2 arguments. They are used to
-instantiate instances of this class, which provides higher-level APIs
-for obtaining request parameters, writing HTTP output, etc.
-"""
-def __init__(self, wsgienv, start_response, altbaseurl=None):
-version = wsgienv[r'wsgi.version']
-if (version < (1, 0)) or (version >= (2, 0)):
-raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
-   % version)
-
-inp = wsgienv[r'wsgi.input']
-
-if r'HTTP_CONTENT_LENGTH' in wsgienv:
-inp = util.cappedreader(inp, int(wsgienv[r'HTTP_CONTENT_LENGTH']))
-elif r'CONTENT_LENGTH' in wsgienv:
-inp = util.cappedreader(inp, int(wsgienv[r'CONTENT_LENGTH']))
-
-self.err = wsgienv[r'wsgi.errors']
-self.threaded = wsgienv[r'wsgi.multithread']
-self.multiprocess = wsgienv[r'wsgi.multiprocess']
-self.run_once = wsgienv[r'wsgi.run_once']
-self.env = wsgienv
-self.req = parserequestfromenv(wsgienv, inp, altbaseurl=altbaseurl)
-self.res = wsgiresponse(self.req, start_response)
-
 def wsgiapplication(app_maker):
 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
 can and should now be used as a WSGI application.'''
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -348,19 +348,18 @@
 
 def __call__(self, env, respond):
 baseurl = self.ui.config('web', 'baseurl')
-wsgireq = requestmod.wsgirequest(env, respond, altbaseurl=baseurl)
-return self.run_wsgi(wsgireq)
+req = requestmod.parserequestfromenv(env, altbaseurl=baseurl)
+res = requestmod.wsgiresponse(req, respond)
 
-def run_wsgi(self, wsgireq):
+return self.run_wsgi(req, res)
+
+def run_wsgi(self, req, res):
 profile = self.ui.configbool('profiling', 'enabled')
 with profiling.profile(self.ui, enabled=profile):
-for r in self._runwsgi(wsgireq):
+for r in self._runwsgi(req, res):
 yield r
 
-def _runwsgi(self, wsgireq):
-req = wsgireq.req
-res = wsgireq.res
-
+def _runwsgi(self, req, res):
 try:
 self.refresh()
 
@@ -423,13 +422,13 @@
 if real:
 # Re-parse the WSGI environment to take into account our
  

Re: [PATCH 3 of 3] dagop: move lines() out of annotate()

2018-03-12 Thread Augie Fackler
On Mon, Mar 12, 2018 at 12:00:41AM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara 
> # Date 1519849241 18000
> #  Wed Feb 28 15:20:41 2018 -0500
> # Node ID ba4ae5454659806d03b7e4ebceb7238bfe46443e
> # Parent  f634f309bed610e84b4ff151044f320461509e78
> dagop: move lines() out of annotate()

queued, thanks
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH 3 of 3] bdiff: convert more longs to int64_t

2018-03-12 Thread Augie Fackler
On Fri, Mar 09, 2018 at 10:07:03PM -0500, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison 
> # Date 1520650747 18000
> #  Fri Mar 09 21:59:07 2018 -0500
> # Node ID 09be2aeb8f5a364fab574ed3cf00bddbb7b9728a
> # Parent  1f313a913f4356f272ef275061d5d169d9c1690e
> bdiff: convert more longs to int64_t

queued, thanks
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH 5 of 5] templater: split template functions to new module

2018-03-12 Thread Augie Fackler
On Fri, Mar 09, 2018 at 09:45:34PM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara 
> # Date 1520515382 -32400
> #  Thu Mar 08 22:23:02 2018 +0900
> # Node ID b3f764c8098d6de1dca102f8b5b5d05721a6341f
> # Parent  c22e2d75938bc761554c8da98a49b85aa6bff0de
> templater: split template functions to new module

queued, thanks
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


Re: [PATCH STABLE] amend: abort if unresolved merge conflicts found (issue5805)

2018-03-12 Thread Augie Fackler
On Sun, Mar 11, 2018 at 08:16:19PM +0900, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara 
> # Date 1520766638 -32400
> #  Sun Mar 11 20:10:38 2018 +0900
> # Branch stable
> # Node ID eeb87b24aea7f547f6d95b812dd080dc6e9ab194
> # Parent  9639c433be54191b4136b48fe70fc8344d2b5db2
> amend: abort if unresolved merge conflicts found (issue5805)

queued, thanks
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread spectral (Kyle Lippincott)
spectral updated this revision to Diff 6965.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2729?vs=6964=6965

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

AFFECTED FILES
  hgext/largefiles/overrides.py
  mercurial/cmdutil.py
  tests/test-rename.t

CHANGE DETAILS

diff --git a/tests/test-rename.t b/tests/test-rename.t
--- a/tests/test-rename.t
+++ b/tests/test-rename.t
@@ -657,3 +657,24 @@
   [255]
   $ hg status -C
 
+check that stat information such as mtime is preserved - it's unclear whether
+the `touch` and `stat` commands are portable, so we mimic them using python.
+Not all platforms support precision of even one-second granularity, so we allow
+a rather generous fudge factor here; 1234567890 is 2009, and the primary thing
+we care about is that it's not the machine's current time; hopefully it's 
really
+unlikely for a machine to have such a broken clock that this test fails. :)
+
+  $ mkdir mtime
+Create the file (as empty), then update its mtime and atime to be 1234567890.
+  >>> import os
+  >>> filename = "mtime/f"
+  >>> mtime = 1234567890
+  >>> open(filename, "w").close()
+  >>> os.utime(filename, (mtime, mtime))
+  $ hg ci -qAm 'add mtime dir'
+  $ hg mv -q mtime mtime2
+  >>> from __future__ import print_function
+  >>> import os
+  >>> filename = "mtime2/f"
+  >>> print(os.stat(filename).st_mtime < 1234567999)
+  True
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1186,7 +1186,7 @@
 os.rename(src, tmp)
 os.rename(tmp, target)
 else:
-util.copyfile(src, target)
+util.copyfile(src, target, copystat=True)
 srcexists = True
 except IOError as inst:
 if inst.errno == errno.ENOENT:
diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py
--- a/hgext/largefiles/overrides.py
+++ b/hgext/largefiles/overrides.py
@@ -667,15 +667,15 @@
 try:
 origcopyfile = util.copyfile
 copiedfiles = []
-def overridecopyfile(src, dest):
+def overridecopyfile(src, dest, *args, **kwargs):
 if (lfutil.shortname in src and
 dest.startswith(repo.wjoin(lfutil.shortname))):
 destlfile = dest.replace(lfutil.shortname, '')
 if not opts['force'] and os.path.exists(destlfile):
 raise IOError('',
 _('destination largefile already exists'))
 copiedfiles.append((src, dest))
-origcopyfile(src, dest)
+origcopyfile(src, dest, *args, **kwargs)
 
 util.copyfile = overridecopyfile
 result += orig(ui, repo, listpats, opts, rename)



To: spectral, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread durin42 (Augie Fackler)
durin42 requested changes to this revision.
durin42 added inline comments.
This revision now requires changes to proceed.

INLINE COMMENTS

> test-rename.t:681
> +  >>> filename = "mtime2/f"
> +  >>> sys.exit(int(not (os.stat(filename).st_mtime < 1234567999)))

Oh, probably don't do sys.exit here. Instead just always print the boolean 
result.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

To: spectral, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2691: commands: don't check for merge.update() truthiness

2018-03-12 Thread durin42 (Augie Fackler)
durin42 requested changes to this revision.
durin42 added a comment.
This revision now requires changes to proceed.


  Needs rebased, and the parent listed in the patch header isn't available for 
me. Can you give a rebase a shot?

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2691

To: indygreg, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread spectral (Kyle Lippincott)
spectral marked 2 inline comments as done.
spectral added a comment.


  Neat, didn't know about the inline python stuff.  That's much nicer.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

To: spectral, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread spectral (Kyle Lippincott)
spectral updated this revision to Diff 6964.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2729?vs=6735=6964

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

AFFECTED FILES
  hgext/largefiles/overrides.py
  mercurial/cmdutil.py
  tests/test-rename.t

CHANGE DETAILS

diff --git a/tests/test-rename.t b/tests/test-rename.t
--- a/tests/test-rename.t
+++ b/tests/test-rename.t
@@ -657,3 +657,25 @@
   [255]
   $ hg status -C
 
+check that stat information such as mtime is preserved - it's unclear whether
+the `touch` and `stat` commands are portable, so we mimic them using python.
+Not all platforms support precision of even one-second granularity, so we allow
+a rather generous fudge factor here; 1234567890 is 2009, and the primary thing
+we care about is that it's not the machine's current time; hopefully it's 
really
+unlikely for a machine to have such a broken clock that this test fails. :)
+
+  $ mkdir mtime
+Create the file (as empty), then update its mtime and atime to be 1234567890.
+  >>> import os
+  >>> filename = "mtime/f"
+  >>> mtime = 1234567890
+  >>> open(filename, "w").close()
+  >>> os.utime(filename, (mtime, mtime))
+  $ hg ci -qAm 'add mtime dir'
+  $ hg mv -q mtime mtime2
+Check the actual mtime on the file. Will return non-zero if the mtime is too
+new.
+  >>> import os
+  >>> import sys
+  >>> filename = "mtime2/f"
+  >>> sys.exit(int(not (os.stat(filename).st_mtime < 1234567999)))
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -1186,7 +1186,7 @@
 os.rename(src, tmp)
 os.rename(tmp, target)
 else:
-util.copyfile(src, target)
+util.copyfile(src, target, copystat=True)
 srcexists = True
 except IOError as inst:
 if inst.errno == errno.ENOENT:
diff --git a/hgext/largefiles/overrides.py b/hgext/largefiles/overrides.py
--- a/hgext/largefiles/overrides.py
+++ b/hgext/largefiles/overrides.py
@@ -667,15 +667,15 @@
 try:
 origcopyfile = util.copyfile
 copiedfiles = []
-def overridecopyfile(src, dest):
+def overridecopyfile(src, dest, *args, **kwargs):
 if (lfutil.shortname in src and
 dest.startswith(repo.wjoin(lfutil.shortname))):
 destlfile = dest.replace(lfutil.shortname, '')
 if not opts['force'] and os.path.exists(destlfile):
 raise IOError('',
 _('destination largefile already exists'))
 copiedfiles.append((src, dest))
-origcopyfile(src, dest)
+origcopyfile(src, dest, *args, **kwargs)
 
 util.copyfile = overridecopyfile
 result += orig(ui, repo, listpats, opts, rename)



To: spectral, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2678: help: supporting both help and doc for aliases

2018-03-12 Thread durin42 (Augie Fackler)
durin42 added a comment.


  Ugh:
  
--- /home/augie/hg/tests/test-alias.t
+++ /home/augie/hg/tests/test-alias.t.err
@@ -357,15 +357,8 @@
 properly recursive

   $ hg dln
-  changeset:   -1:
-  phase:   public
-  parent:  -1:
-  parent:  -1:
-  manifest:-1:
-  user:
-  date:Thu Jan 01 00:00:00 1970 +
-  extra:   branch=default
-
+  abort: alias 'dln' resolves to unknown command 'lognull'
+  [255]


 path expanding

ERROR: test-alias.t output changed
--- /home/augie/hg/tests/test-show.t
+++ /home/augie/hg/tests/test-show.t.err
@@ -135,19 +135,37 @@
 commands.show.aliasprefix aliases values to `show `

   $ hg --config commands.show.aliasprefix=s sbookmarks
+  devel-warn: config item requires an explicit default value: 
'alias.sstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.swork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.sbookmarks' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
   (no bookmarks set)

   $ hg --config commands.show.aliasprefix=sh shwork
+  devel-warn: config item requires an explicit default value: 
'alias.shstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shwork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shbookmarks' at: 
/tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 (extsetup)
   @  7b57 commit for book2
   o  b757 commit for book1
   o  ba59 initial

   $ hg --config commands.show.aliasprefix='s sh' swork
+  devel-warn: config item requires an explicit default value: 
'alias.sstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.swork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.sbookmarks' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shwork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shbookmarks' at: 
/tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 (extsetup)
   @  7b57 commit for book2
   o  b757 commit for book1
   o  ba59 initial

   $ hg --config commands.show.aliasprefix='s sh' shwork
+  devel-warn: config item requires an explicit default value: 
'alias.sstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.swork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.sbookmarks' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shwork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.shbookmarks' at: 
/tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 (extsetup)
   @  7b57 commit for book2
   o  b757 commit for book1
   o  ba59 initial
@@ -155,11 +173,17 @@
 The aliases don't appear in `hg config`

   $ hg --config commands.show.aliasprefix=s config alias
+  devel-warn: config item requires an explicit default value: 
'alias.sstack' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.swork' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
+  devel-warn: config item requires an explicit default value: 
'alias.sbookmarks' at: /tmp/hgtests.iyj57T/install/lib/python/hgext/show.py:430 
(extsetup)
   [1]

 Doesn't overwrite existing alias

   $ hg --config alias.swork='log -r .' 

D2806: tweakdefaults: add commands.status.verbose to tweakefaults

2018-03-12 Thread pulkit (Pulkit Goyal)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG98487ad0cf8b: tweakdefaults: add commands.status.verbose to 
tweakefaults (authored by pulkit, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2806?vs=6868=6963

REVISION DETAIL
  https://phab.mercurial-scm.org/D2806

AFFECTED FILES
  mercurial/ui.py

CHANGE DETAILS

diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -60,6 +60,10 @@
 status.relative = yes
 # Refuse to perform an `hg update` that would cause a file content merge
 update.check = noconflict
+# Show conflicts information in `hg status`
+status.verbose = True
+# Skip the bisect state in conflicts information in `hg status`
+status.skipstates = bisect
 
 [diff]
 git = 1



To: pulkit, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2831: hgweb: store the raw WSGI environment dict

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG84110a1d0f7d: hgweb: store the raw WSGI environment dict 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2831?vs=6897=6962

REVISION DETAIL
  https://phab.mercurial-scm.org/D2831

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -149,6 +149,8 @@
 headers = attr.ib()
 # Request body input stream.
 bodyfh = attr.ib()
+# WSGI environment dict, unmodified.
+rawenv = attr.ib()
 
 def parserequestfromenv(env, bodyfh, reponame=None, altbaseurl=None):
 """Parse URL components from environment variables.
@@ -342,7 +344,8 @@
  querystring=querystring,
  qsparams=qsparams,
  headers=headers,
- bodyfh=bodyfh)
+ bodyfh=bodyfh,
+ rawenv=env)
 
 class offsettrackingwriter(object):
 """A file object like object that is append only and tracks write count.
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -312,7 +312,7 @@
 
 # This state is global across all threads.
 encoding.encoding = rctx.config('web', 'encoding')
-rctx.repo.ui.environ = wsgireq.env
+rctx.repo.ui.environ = req.rawenv
 
 if rctx.csp:
 # hgwebdir may have added CSP header. Since we generate our own,



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2830: hgweb: remove dead wsgirequest code

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGcd6ae9ab7bd8: hgweb: remove dead wsgirequest code (authored 
by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2830?vs=6896=6961

REVISION DETAIL
  https://phab.mercurial-scm.org/D2830

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -8,16 +8,9 @@
 
 from __future__ import absolute_import
 
-import errno
-import socket
 import wsgiref.headers as wsgiheaders
 #import wsgiref.validate
 
-from .common import (
-ErrorResponse,
-statusmessage,
-)
-
 from ..thirdparty import (
 attr,
 )
@@ -609,100 +602,6 @@
 self.env = wsgienv
 self.req = parserequestfromenv(wsgienv, inp, altbaseurl=altbaseurl)
 self.res = wsgiresponse(self.req, start_response)
-self._start_response = start_response
-self.server_write = None
-self.headers = []
-
-def respond(self, status, type, filename=None, body=None):
-if not isinstance(type, str):
-type = pycompat.sysstr(type)
-if self._start_response is not None:
-self.headers.append((r'Content-Type', type))
-if filename:
-filename = (filename.rpartition('/')[-1]
-.replace('\\', '').replace('"', '\\"'))
-self.headers.append(('Content-Disposition',
- 'inline; filename="%s"' % filename))
-if body is not None:
-self.headers.append((r'Content-Length', str(len(body
-
-for k, v in self.headers:
-if not isinstance(v, str):
-raise TypeError('header value must be string: %r' % (v,))
-
-if isinstance(status, ErrorResponse):
-self.headers.extend(status.headers)
-status = statusmessage(status.code, pycompat.bytestr(status))
-elif status == 200:
-status = '200 Script output follows'
-elif isinstance(status, int):
-status = statusmessage(status)
-
-# Various HTTP clients (notably httplib) won't read the HTTP
-# response until the HTTP request has been sent in full. If servers
-# (us) send a response before the HTTP request has been fully sent,
-# the connection may deadlock because neither end is reading.
-#
-# We work around this by "draining" the request data before
-# sending any response in some conditions.
-drain = False
-close = False
-
-# If the client sent Expect: 100-continue, we assume it is smart
-# enough to deal with the server sending a response before reading
-# the request. (httplib doesn't do this.)
-if self.env.get(r'HTTP_EXPECT', r'').lower() == r'100-continue':
-pass
-# Only tend to request methods that have bodies. Strictly speaking,
-# we should sniff for a body. But this is fine for our existing
-# WSGI applications.
-elif self.env[r'REQUEST_METHOD'] not in (r'POST', r'PUT'):
-pass
-else:
-# If we don't know how much data to read, there's no guarantee
-# that we can drain the request responsibly. The WSGI
-# specification only says that servers *should* ensure the
-# input stream doesn't overrun the actual request. So there's
-# no guarantee that reading until EOF won't corrupt the stream
-# state.
-if not isinstance(self.req.bodyfh, util.cappedreader):
-close = True
-else:
-# We /could/ only drain certain HTTP response codes. But 
200
-# and non-200 wire protocol responses both require 
draining.
-# Since we have a capped reader in place for all situations
-# where we drain, it is safe to read from that stream. 
We'll
-# either do a drain or no-op if we're already at EOF.
-drain = True
-
-if close:
-self.headers.append((r'Connection', r'Close'))
-
-if drain:
-assert isinstance(self.req.bodyfh, util.cappedreader)
-while True:
-chunk = self.req.bodyfh.read(32768)
-if not chunk:
-break
-
-self.server_write = self._start_response(
-pycompat.sysstr(status), self.headers)
-self._start_response = None
-self.headers = []
-if body is not None:
-

D2828: hgweb: pass modern request type into templater()

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG6a0e4efbc61e: hgweb: pass modern request type into 
templater() (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2828?vs=6894=6959

REVISION DETAIL
  https://phab.mercurial-scm.org/D2828

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -369,7 +369,7 @@
 wsgireq.headers.append(('Content-Security-Policy', csp))
 
 virtual = req.dispatchpath.strip('/')
-tmpl = self.templater(wsgireq, nonce)
+tmpl = self.templater(req, nonce)
 ctype = tmpl('mimetype', encoding=encoding.encoding)
 ctype = templater.stringify(ctype)
 
@@ -485,7 +485,7 @@
 
 return res.sendresponse()
 
-def templater(self, wsgireq, nonce):
+def templater(self, req, nonce):
 
 def motd(**map):
 if self.motd is not None:
@@ -497,23 +497,23 @@
 return self.ui.config(section, name, default, untrusted)
 
 vars = {}
-styles, (style, mapfile) = hgweb_mod.getstyle(wsgireq.req, config,
+styles, (style, mapfile) = hgweb_mod.getstyle(req, config,
   self.templatepath)
 if style == styles[0]:
 vars['style'] = style
 
 sessionvars = webutil.sessionvars(vars, r'?')
 logourl = config('web', 'logourl')
 logoimg = config('web', 'logoimg')
 staticurl = (config('web', 'staticurl')
- or wsgireq.req.apppath + '/static/')
+ or req.apppath + '/static/')
 if not staticurl.endswith('/'):
 staticurl += '/'
 
 defaults = {
 "encoding": encoding.encoding,
 "motd": motd,
-"url": wsgireq.req.apppath + '/',
+"url": req.apppath + '/',
 "logourl": logourl,
 "logoimg": logoimg,
 "staticurl": staticurl,



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2829: hgweb: port to new response API

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGc1de7efca574: hgweb: port to new response API (authored by 
indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2829?vs=6895=6960

REVISION DETAIL
  https://phab.mercurial-scm.org/D2829

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -15,22 +15,23 @@
 
 from .common import (
 ErrorResponse,
-HTTP_NOT_FOUND,
 HTTP_SERVER_ERROR,
 cspvalues,
 get_contact,
 get_mtime,
 ismember,
 paritygen,
 staticfile,
+statusmessage,
 )
 
 from .. import (
 configitems,
 encoding,
 error,
 hg,
 profiling,
+pycompat,
 scmutil,
 templater,
 ui as uimod,
@@ -442,12 +443,14 @@
 return self.makeindex(req, res, tmpl, subdir)
 
 # prefixes not found
-wsgireq.respond(HTTP_NOT_FOUND, ctype)
-return tmpl("notfound", repo=virtual)
+res.status = '404 Not Found'
+res.setbodygen(tmpl('notfound', repo=virtual))
+return res.sendresponse()
 
-except ErrorResponse as err:
-wsgireq.respond(err, ctype)
-return tmpl('error', error=err.message or '')
+except ErrorResponse as e:
+res.status = statusmessage(e.code, pycompat.bytestr(e))
+res.setbodygen(tmpl('error', error=e.message or ''))
+return res.sendresponse()
 finally:
 tmpl = None
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2808: remotenames: show remote bookmarks in `hg bookmarks`

2018-03-12 Thread durin42 (Augie Fackler)
durin42 accepted this revision as: durin42.
durin42 added a comment.


  Sounds nice.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2808

To: pulkit, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2832: hgweb: remove wsgirequest (API)

2018-03-12 Thread indygreg (Gregory Szorc)
indygreg created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.

REVISION SUMMARY
  Good riddance.
  
  .. api::
  
The old ``wsgirequest`` class for handling everything WSGI in hgweb
has been replaced by separate request and response types. Various
high-level functions in the hgweb WSGI applications now receive
these new types as arguments instead of the old ``wsgirequest``
type.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2832

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/request.py
  tests/test-wsgirequest.py

CHANGE DETAILS

diff --git a/tests/test-wsgirequest.py b/tests/test-wsgirequest.py
--- a/tests/test-wsgirequest.py
+++ b/tests/test-wsgirequest.py
@@ -23,11 +23,11 @@
 r'wsgi.run_once': False,
 }
 
-def parse(env, bodyfh=None, reponame=None, altbaseurl=None, extra=None):
+def parse(env, reponame=None, altbaseurl=None, extra=None):
 env = dict(env)
 env.update(extra or {})
 
-return requestmod.parserequestfromenv(env, bodyfh, reponame=reponame,
+return requestmod.parserequestfromenv(env, reponame=reponame,
   altbaseurl=altbaseurl)
 
 class ParseRequestTests(unittest.TestCase):
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -152,7 +152,7 @@
 # WSGI environment dict, unmodified.
 rawenv = attr.ib()
 
-def parserequestfromenv(env, bodyfh, reponame=None, altbaseurl=None):
+def parserequestfromenv(env, reponame=None, altbaseurl=None):
 """Parse URL components from environment variables.
 
 WSGI defines request attributes via environment variables. This function
@@ -325,11 +325,9 @@
 if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
 headers['Content-Length'] = env['CONTENT_LENGTH']
 
-# TODO do this once we remove wsgirequest.inp, otherwise we could have
-# multiple readers from the underlying input stream.
-#bodyfh = env['wsgi.input']
-#if 'Content-Length' in headers:
-#bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))
+bodyfh = env['wsgi.input']
+if 'Content-Length' in headers:
+bodyfh = util.cappedreader(bodyfh, int(headers['Content-Length']))
 
 return parsedrequest(method=env['REQUEST_METHOD'],
  url=fullurl, baseurl=baseurl,
@@ -578,34 +576,6 @@
 assert self._bodywritefn
 return offsettrackingwriter(self._bodywritefn)
 
-class wsgirequest(object):
-"""Higher-level API for a WSGI request.
-
-WSGI applications are invoked with 2 arguments. They are used to
-instantiate instances of this class, which provides higher-level APIs
-for obtaining request parameters, writing HTTP output, etc.
-"""
-def __init__(self, wsgienv, start_response, altbaseurl=None):
-version = wsgienv[r'wsgi.version']
-if (version < (1, 0)) or (version >= (2, 0)):
-raise RuntimeError("Unknown and unsupported WSGI version %d.%d"
-   % version)
-
-inp = wsgienv[r'wsgi.input']
-
-if r'HTTP_CONTENT_LENGTH' in wsgienv:
-inp = util.cappedreader(inp, int(wsgienv[r'HTTP_CONTENT_LENGTH']))
-elif r'CONTENT_LENGTH' in wsgienv:
-inp = util.cappedreader(inp, int(wsgienv[r'CONTENT_LENGTH']))
-
-self.err = wsgienv[r'wsgi.errors']
-self.threaded = wsgienv[r'wsgi.multithread']
-self.multiprocess = wsgienv[r'wsgi.multiprocess']
-self.run_once = wsgienv[r'wsgi.run_once']
-self.env = wsgienv
-self.req = parserequestfromenv(wsgienv, inp, altbaseurl=altbaseurl)
-self.res = wsgiresponse(self.req, start_response)
-
 def wsgiapplication(app_maker):
 '''For compatibility with old CGI scripts. A plain hgweb() or hgwebdir()
 can and should now be used as a WSGI application.'''
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -348,19 +348,18 @@
 
 def __call__(self, env, respond):
 baseurl = self.ui.config('web', 'baseurl')
-wsgireq = requestmod.wsgirequest(env, respond, altbaseurl=baseurl)
-return self.run_wsgi(wsgireq)
+req = requestmod.parserequestfromenv(env, altbaseurl=baseurl)
+res = requestmod.wsgiresponse(req, respond)
 
-def run_wsgi(self, wsgireq):
+return self.run_wsgi(req, res)
+
+def run_wsgi(self, req, res):
 profile = self.ui.configbool('profiling', 'enabled')
 with profiling.profile(self.ui, enabled=profile):
-for r in self._runwsgi(wsgireq):
+for r in self._runwsgi(req, res):
 yield r
 
-def _runwsgi(self, wsgireq):
-req = wsgireq.req
-res = wsgireq.res
-
+def 

D2807: remotenames: add functionality to hoist remotebookmarks

2018-03-12 Thread durin42 (Augie Fackler)
durin42 accepted this revision as: durin42.
durin42 added a comment.


  Seems fine, I'd like to hear what others think?
  
  I'm not sure I'm crazy about "hoistedname" I guess.

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2807

To: pulkit, #hg-reviewers, durin42
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2823: hgweb: construct {url} with req.apppath

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG006165d4d7e2: hgweb: construct {url} with req.apppath 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2823?vs=6889=6954

REVISION DETAIL
  https://phab.mercurial-scm.org/D2823

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -496,10 +496,6 @@
 def config(section, name, default=uimod._unset, untrusted=True):
 return self.ui.config(section, name, default, untrusted)
 
-url = wsgireq.env.get('SCRIPT_NAME', '')
-if not url.endswith('/'):
-url += '/'
-
 vars = {}
 styles, (style, mapfile) = hgweb_mod.getstyle(wsgireq.req, config,
   self.templatepath)
@@ -517,7 +513,7 @@
 defaults = {
 "encoding": encoding.encoding,
 "motd": motd,
-"url": url,
+"url": wsgireq.req.apppath + '/',
 "logourl": logourl,
 "logoimg": logoimg,
 "staticurl": staticurl,



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2824: hgweb: rewrite path generation for index entries

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGe473a032f38a: hgweb: rewrite path generation for index 
entries (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2824?vs=6890=6953

REVISION DETAIL
  https://phab.mercurial-scm.org/D2824

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -9,7 +9,6 @@
 from __future__ import absolute_import
 
 import os
-import re
 import time
 
 from ..i18n import _
@@ -161,11 +160,12 @@
 except (IOError, error.RepoError):
 pass
 
-parts = [name]
-parts.insert(0, '/' + subdir.rstrip('/'))
-if wsgireq.env['SCRIPT_NAME']:
-parts.insert(0, wsgireq.env['SCRIPT_NAME'])
-url = re.sub(r'/+', '/', '/'.join(parts) + '/')
+parts = [
+wsgireq.req.apppath.strip('/'),
+subdir.strip('/'),
+name.strip('/'),
+]
+url = '/' + '/'.join(p for p in parts if p) + '/'
 
 # show either a directory entry or a repository
 if directory:



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2822: hgweb: support constructing URLs from an alternate base URL

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG219b23359f4c: hgweb: support constructing URLs from an 
alternate base URL (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2822?vs=6888=6952

REVISION DETAIL
  https://phab.mercurial-scm.org/D2822

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/request.py
  tests/test-wsgirequest.py

CHANGE DETAILS

diff --git a/tests/test-wsgirequest.py b/tests/test-wsgirequest.py
--- a/tests/test-wsgirequest.py
+++ b/tests/test-wsgirequest.py
@@ -23,11 +23,12 @@
 r'wsgi.run_once': False,
 }
 
-def parse(env, bodyfh=None, reponame=None, extra=None):
+def parse(env, bodyfh=None, reponame=None, altbaseurl=None, extra=None):
 env = dict(env)
 env.update(extra or {})
 
-return requestmod.parserequestfromenv(env, bodyfh, reponame=reponame)
+return requestmod.parserequestfromenv(env, bodyfh, reponame=reponame,
+  altbaseurl=altbaseurl)
 
 class ParseRequestTests(unittest.TestCase):
 def testdefault(self):
@@ -242,6 +243,174 @@
 self.assertEqual(r.dispatchpath, b'path1/path2')
 self.assertEqual(r.reponame, b'prefix/repo')
 
+def testaltbaseurl(self):
+# Simple hostname remap.
+r = parse(DEFAULT_ENV, altbaseurl='http://altserver')
+
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://altserver')
+self.assertEqual(r.advertisedbaseurl, b'http://altserver')
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [])
+self.assertIsNone(r.dispatchpath)
+self.assertIsNone(r.reponame)
+
+# With a custom port.
+r = parse(DEFAULT_ENV, altbaseurl='http://altserver:8000')
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://altserver:8000')
+self.assertEqual(r.advertisedbaseurl, b'http://altserver:8000')
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [])
+self.assertIsNone(r.dispatchpath)
+self.assertIsNone(r.reponame)
+
+# With a changed protocol.
+r = parse(DEFAULT_ENV, altbaseurl='https://altserver')
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'https://altserver')
+self.assertEqual(r.advertisedbaseurl, b'https://altserver')
+# URL scheme is defined as the actual scheme, not advertised.
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [])
+self.assertIsNone(r.dispatchpath)
+self.assertIsNone(r.reponame)
+
+# Need to specify explicit port number for proper https:// alt URLs.
+r = parse(DEFAULT_ENV, altbaseurl='https://altserver:443')
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'https://altserver')
+self.assertEqual(r.advertisedbaseurl, b'https://altserver')
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [])
+self.assertIsNone(r.dispatchpath)
+self.assertIsNone(r.reponame)
+
+# With only PATH_INFO defined.
+r = parse(DEFAULT_ENV, altbaseurl='http://altserver', extra={
+r'PATH_INFO': r'/path1/path2',
+})
+self.assertEqual(r.url, b'http://testserver/path1/path2')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://altserver/path1/path2')
+self.assertEqual(r.advertisedbaseurl, b'http://altserver')
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [b'path1', b'path2'])
+self.assertEqual(r.dispatchpath, b'path1/path2')
+self.assertIsNone(r.reponame)
+
+# Path on alt URL.
+r = parse(DEFAULT_ENV, altbaseurl='http://altserver/altpath')
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://altserver/altpath')
+self.assertEqual(r.advertisedbaseurl, b'http://altserver')
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.apppath, b'/altpath')
+self.assertEqual(r.dispatchparts, [])
+self.assertIsNone(r.dispatchpath)
+self.assertIsNone(r.reponame)
+
+  

D2827: hgweb: use modern response type for index generation

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG93717f082af9: hgweb: use modern response type for index 
generation (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2827?vs=6893=6957

REVISION DETAIL
  https://phab.mercurial-scm.org/D2827

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -16,7 +16,6 @@
 from .common import (
 ErrorResponse,
 HTTP_NOT_FOUND,
-HTTP_OK,
 HTTP_SERVER_ERROR,
 cspvalues,
 get_contact,
@@ -400,16 +399,14 @@
 repos = dict(self.repos)
 
 if (not virtual or virtual == 'index') and virtual not in repos:
-wsgireq.respond(HTTP_OK, ctype)
-return self.makeindex(req, tmpl)
+return self.makeindex(req, res, tmpl)
 
 # nested indexes and hgwebs
 
 if virtual.endswith('/index') and virtual not in repos:
 subdir = virtual[:-len('index')]
 if any(r.startswith(subdir) for r in repos):
-wsgireq.respond(HTTP_OK, ctype)
-return self.makeindex(req, tmpl, subdir)
+return self.makeindex(req, res, tmpl, subdir)
 
 def _virtualdirs():
 # Check the full virtual path, each parent, and the root ('')
@@ -442,8 +439,7 @@
 # browse subdirectories
 subdir = virtual + '/'
 if [r for r in repos if r.startswith(subdir)]:
-wsgireq.respond(HTTP_OK, ctype)
-return self.makeindex(req, tmpl, subdir)
+return self.makeindex(req, res, tmpl, subdir)
 
 # prefixes not found
 wsgireq.respond(HTTP_NOT_FOUND, ctype)
@@ -455,7 +451,7 @@
 finally:
 tmpl = None
 
-def makeindex(self, req, tmpl, subdir=""):
+def makeindex(self, req, res, tmpl, subdir=""):
 self.refresh()
 sortable = ["name", "description", "contact", "lastchange"]
 sortcolumn, descending = None, False
@@ -478,10 +474,16 @@
self.stripecount, sortcolumn=sortcolumn,
descending=descending, subdir=subdir)
 
-return tmpl("index", entries=entries, subdir=subdir,
-pathdef=hgweb_mod.makebreadcrumb('/' + subdir, 
self.prefix),
-sortcolumn=sortcolumn, descending=descending,
-**dict(sort))
+res.setbodygen(tmpl(
+'index',
+entries=entries,
+subdir=subdir,
+pathdef=hgweb_mod.makebreadcrumb('/' + subdir, self.prefix),
+sortcolumn=sortcolumn,
+descending=descending,
+**dict(sort)))
+
+return res.sendresponse()
 
 def templater(self, wsgireq, nonce):
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2826: hgweb: don't pass wsgireq to makeindex and other functions

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG092ab4ba7ee5: hgweb: dont pass wsgireq to makeindex 
and other functions (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2826?vs=6892=6956

REVISION DETAIL
  https://phab.mercurial-scm.org/D2826

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -119,7 +119,7 @@
 
 return archives
 
-def rawindexentries(ui, repos, wsgireq, req, subdir=''):
+def rawindexentries(ui, repos, req, subdir=''):
 descend = ui.configbool('web', 'descend')
 collapse = ui.configbool('web', 'collapse')
 seenrepos = set()
@@ -161,7 +161,7 @@
 pass
 
 parts = [
-wsgireq.req.apppath.strip('/'),
+req.apppath.strip('/'),
 subdir.strip('/'),
 name.strip('/'),
 ]
@@ -245,10 +245,10 @@
 
 yield row
 
-def indexentries(ui, repos, wsgireq, req, stripecount, sortcolumn='',
+def indexentries(ui, repos, req, stripecount, sortcolumn='',
  descending=False, subdir=''):
 
-rows = rawindexentries(ui, repos, wsgireq, req, subdir=subdir)
+rows = rawindexentries(ui, repos, req, subdir=subdir)
 
 sortdefault = None, False
 
@@ -401,15 +401,15 @@
 
 if (not virtual or virtual == 'index') and virtual not in repos:
 wsgireq.respond(HTTP_OK, ctype)
-return self.makeindex(wsgireq, tmpl)
+return self.makeindex(req, tmpl)
 
 # nested indexes and hgwebs
 
 if virtual.endswith('/index') and virtual not in repos:
 subdir = virtual[:-len('index')]
 if any(r.startswith(subdir) for r in repos):
 wsgireq.respond(HTTP_OK, ctype)
-return self.makeindex(wsgireq, tmpl, subdir)
+return self.makeindex(req, tmpl, subdir)
 
 def _virtualdirs():
 # Check the full virtual path, each parent, and the root ('')
@@ -443,7 +443,7 @@
 subdir = virtual + '/'
 if [r for r in repos if r.startswith(subdir)]:
 wsgireq.respond(HTTP_OK, ctype)
-return self.makeindex(wsgireq, tmpl, subdir)
+return self.makeindex(req, tmpl, subdir)
 
 # prefixes not found
 wsgireq.respond(HTTP_NOT_FOUND, ctype)
@@ -455,9 +455,7 @@
 finally:
 tmpl = None
 
-def makeindex(self, wsgireq, tmpl, subdir=""):
-req = wsgireq.req
-
+def makeindex(self, req, tmpl, subdir=""):
 self.refresh()
 sortable = ["name", "description", "contact", "lastchange"]
 sortcolumn, descending = None, False
@@ -476,7 +474,7 @@
 
 self.refresh()
 
-entries = indexentries(self.ui, self.repos, wsgireq, req,
+entries = indexentries(self.ui, self.repos, req,
self.stripecount, sortcolumn=sortcolumn,
descending=descending, subdir=subdir)
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2825: hgweb: replace PATH_INFO with dispatchpath

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG803e0fc0cc9a: hgweb: replace PATH_INFO with dispatchpath 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2825?vs=6891=6955

REVISION DETAIL
  https://phab.mercurial-scm.org/D2825

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -369,7 +369,7 @@
 res.headers['Content-Security-Policy'] = csp
 wsgireq.headers.append(('Content-Security-Policy', csp))
 
-virtual = wsgireq.env.get("PATH_INFO", "").strip('/')
+virtual = req.dispatchpath.strip('/')
 tmpl = self.templater(wsgireq, nonce)
 ctype = tmpl('mimetype', encoding=encoding.encoding)
 ctype = templater.stringify(ctype)



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2821: hgweb: clarify that apppath begins with a forward slash

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGe67a2e05fa8a: hgweb: clarify that apppath begins with a 
forward slash (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2821?vs=6887=6951

REVISION DETAIL
  https://phab.mercurial-scm.org/D2821

AFFECTED FILES
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -134,7 +134,8 @@
 remoteuser = attr.ib()
 # Value of REMOTE_HOST, if set, or None.
 remotehost = attr.ib()
-# WSGI application path.
+# Relative WSGI application path. If defined, will begin with a
+# ``/``.
 apppath = attr.ib()
 # List of path parts to be used for dispatch.
 dispatchparts = attr.ib()



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2820: hgweb: change how dispatch path is reported

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGd0b0fedbfb53: hgweb: change how dispatch path is reported 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2820?vs=6886=6949

REVISION DETAIL
  https://phab.mercurial-scm.org/D2820

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py
  tests/test-wsgirequest.py

CHANGE DETAILS

diff --git a/tests/test-wsgirequest.py b/tests/test-wsgirequest.py
--- a/tests/test-wsgirequest.py
+++ b/tests/test-wsgirequest.py
@@ -42,8 +42,7 @@
 self.assertIsNone(r.remotehost)
 self.assertEqual(r.apppath, b'')
 self.assertEqual(r.dispatchparts, [])
-self.assertEqual(r.dispatchpath, b'')
-self.assertFalse(r.havepathinfo)
+self.assertIsNone(r.dispatchpath)
 self.assertIsNone(r.reponame)
 self.assertEqual(r.querystring, b'')
 self.assertEqual(len(r.qsparams), 0)
@@ -90,8 +89,7 @@
 self.assertEqual(r.advertisedbaseurl, b'http://testserver')
 self.assertEqual(r.apppath, b'')
 self.assertEqual(r.dispatchparts, [])
-self.assertEqual(r.dispatchpath, b'')
-self.assertFalse(r.havepathinfo)
+self.assertIsNone(r.dispatchpath)
 
 r = parse(DEFAULT_ENV, extra={
 r'SCRIPT_NAME': r'/script',
@@ -103,8 +101,7 @@
 self.assertEqual(r.advertisedbaseurl, b'http://testserver')
 self.assertEqual(r.apppath, b'/script')
 self.assertEqual(r.dispatchparts, [])
-self.assertEqual(r.dispatchpath, b'')
-self.assertFalse(r.havepathinfo)
+self.assertIsNone(r.dispatchpath)
 
 r = parse(DEFAULT_ENV, extra={
 r'SCRIPT_NAME': r'/multiple words',
@@ -116,8 +113,7 @@
 self.assertEqual(r.advertisedbaseurl, b'http://testserver')
 self.assertEqual(r.apppath, b'/multiple words')
 self.assertEqual(r.dispatchparts, [])
-self.assertEqual(r.dispatchpath, b'')
-self.assertFalse(r.havepathinfo)
+self.assertIsNone(r.dispatchpath)
 
 def testpathinfo(self):
 r = parse(DEFAULT_ENV, extra={
@@ -131,7 +127,6 @@
 self.assertEqual(r.apppath, b'')
 self.assertEqual(r.dispatchparts, [])
 self.assertEqual(r.dispatchpath, b'')
-self.assertTrue(r.havepathinfo)
 
 r = parse(DEFAULT_ENV, extra={
 r'PATH_INFO': r'/pathinfo',
@@ -144,7 +139,6 @@
 self.assertEqual(r.apppath, b'')
 self.assertEqual(r.dispatchparts, [b'pathinfo'])
 self.assertEqual(r.dispatchpath, b'pathinfo')
-self.assertTrue(r.havepathinfo)
 
 r = parse(DEFAULT_ENV, extra={
 r'PATH_INFO': r'/one/two/',
@@ -157,7 +151,6 @@
 self.assertEqual(r.apppath, b'')
 self.assertEqual(r.dispatchparts, [b'one', b'two'])
 self.assertEqual(r.dispatchpath, b'one/two')
-self.assertTrue(r.havepathinfo)
 
 def testscriptandpathinfo(self):
 r = parse(DEFAULT_ENV, extra={
@@ -172,7 +165,6 @@
 self.assertEqual(r.apppath, b'/script')
 self.assertEqual(r.dispatchparts, [b'pathinfo'])
 self.assertEqual(r.dispatchpath, b'pathinfo')
-self.assertTrue(r.havepathinfo)
 
 r = parse(DEFAULT_ENV, extra={
 r'SCRIPT_NAME': r'/script1/script2',
@@ -188,7 +180,6 @@
 self.assertEqual(r.apppath, b'/script1/script2')
 self.assertEqual(r.dispatchparts, [b'path1', b'path2'])
 self.assertEqual(r.dispatchpath, b'path1/path2')
-self.assertTrue(r.havepathinfo)
 
 r = parse(DEFAULT_ENV, extra={
 r'HTTP_HOST': r'hostserver',
@@ -203,7 +194,6 @@
 self.assertEqual(r.apppath, b'/script')
 self.assertEqual(r.dispatchparts, [b'pathinfo'])
 self.assertEqual(r.dispatchpath, b'pathinfo')
-self.assertTrue(r.havepathinfo)
 
 def testreponame(self):
 """repository path components get stripped from URL."""
@@ -236,7 +226,6 @@
 self.assertEqual(r.apppath, b'/repo')
 self.assertEqual(r.dispatchparts, [b'path1', b'path2'])
 self.assertEqual(r.dispatchpath, b'path1/path2')
-self.assertTrue(r.havepathinfo)
 self.assertEqual(r.reponame, b'repo')
 
 r = parse(DEFAULT_ENV, reponame=b'prefix/repo', extra={
@@ -251,7 +240,6 @@
 self.assertEqual(r.apppath, b'/prefix/repo')
 self.assertEqual(r.dispatchparts, [b'path1', b'path2'])
 self.assertEqual(r.dispatchpath, b'path1/path2')
-self.assertTrue(r.havepathinfo)
 self.assertEqual(r.reponame, b'prefix/repo')
 
 if __name__ == '__main__':
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -138,11 +138,12 @@
 apppath = attr.ib()
 # List of path parts to be used for dispatch.
 dispatchparts = attr.ib()
-# URL path 

D2814: hgweb: move rawentries() to a standalone function

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf370f1b4f12c: hgweb: move rawentries() to a standalone 
function (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2814?vs=6880=6944

REVISION DETAIL
  https://phab.mercurial-scm.org/D2814

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -148,6 +148,131 @@
 
 return archives
 
+def rawindexentries(ui, repos, wsgireq, req, subdir='', **map):
+descend = ui.configbool('web', 'descend')
+collapse = ui.configbool('web', 'collapse')
+seenrepos = set()
+seendirs = set()
+for name, path in repos:
+
+if not name.startswith(subdir):
+continue
+name = name[len(subdir):]
+directory = False
+
+if '/' in name:
+if not descend:
+continue
+
+nameparts = name.split('/')
+rootname = nameparts[0]
+
+if not collapse:
+pass
+elif rootname in seendirs:
+continue
+elif rootname in seenrepos:
+pass
+else:
+directory = True
+name = rootname
+
+# redefine the path to refer to the directory
+discarded = '/'.join(nameparts[1:])
+
+# remove name parts plus accompanying slash
+path = path[:-len(discarded) - 1]
+
+try:
+r = hg.repository(ui, path)
+directory = False
+except (IOError, error.RepoError):
+pass
+
+parts = [name]
+parts.insert(0, '/' + subdir.rstrip('/'))
+if wsgireq.env['SCRIPT_NAME']:
+parts.insert(0, wsgireq.env['SCRIPT_NAME'])
+url = re.sub(r'/+', '/', '/'.join(parts) + '/')
+
+# show either a directory entry or a repository
+if directory:
+# get the directory's time information
+try:
+d = (get_mtime(path), dateutil.makedate()[1])
+except OSError:
+continue
+
+# add '/' to the name to make it obvious that
+# the entry is a directory, not a regular repository
+row = {'contact': "",
+   'contact_sort': "",
+   'name': name + '/',
+   'name_sort': name,
+   'url': url,
+   'description': "",
+   'description_sort': "",
+   'lastchange': d,
+   'lastchange_sort': d[1] - d[0],
+   'archives': [],
+   'isdirectory': True,
+   'labels': [],
+   }
+
+seendirs.add(name)
+yield row
+continue
+
+u = ui.copy()
+try:
+u.readconfig(os.path.join(path, '.hg', 'hgrc'))
+except Exception as e:
+u.warn(_('error reading %s/.hg/hgrc: %s\n') % (path, e))
+continue
+
+def get(section, name, default=uimod._unset):
+return u.config(section, name, default, untrusted=True)
+
+if u.configbool("web", "hidden", untrusted=True):
+continue
+
+if not readallowed(u, req):
+continue
+
+# update time with local timezone
+try:
+r = hg.repository(ui, path)
+except IOError:
+u.warn(_('error accessing repository at %s\n') % path)
+continue
+except error.RepoError:
+u.warn(_('error accessing repository at %s\n') % path)
+continue
+try:
+d = (get_mtime(r.spath), dateutil.makedate()[1])
+except OSError:
+continue
+
+contact = get_contact(get)
+description = get("web", "description")
+seenrepos.add(name)
+name = get("web", "name", name)
+row = {'contact': contact or "unknown",
+   'contact_sort': contact.upper() or "unknown",
+   'name': name,
+   'name_sort': name,
+   'url': url,
+   'description': description or "unknown",
+   'description_sort': description.upper() or "unknown",
+   'lastchange': d,
+   'lastchange_sort': d[1] - d[0],
+   'archives': archivelist(u, "tip", url),
+   'isdirectory': None,
+   'labels': u.configlist('web', 'labels', untrusted=True),
+   }
+
+yield row
+
 class hgwebdir(object):
 """HTTP server for multiple repositories.
 
@@ -347,134 +472,10 @@
 def makeindex(self, wsgireq, tmpl, subdir=""):
 req = wsgireq.req
 
-def rawentries(subdir="", **map):
-

D2815: hgweb: extract entries() to standalone function

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG455918512ed2: hgweb: extract entries() to standalone 
function (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2815?vs=6881=6945

REVISION DETAIL
  https://phab.mercurial-scm.org/D2815

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -273,6 +273,22 @@
 
 yield row
 
+def indexentries(ui, repos, wsgireq, req, stripecount, sortcolumn='',
+ descending=False, subdir='', **map):
+
+rows = rawindexentries(ui, repos, wsgireq, req, subdir=subdir, **map)
+
+sortdefault = None, False
+
+if sortcolumn and sortdefault != (sortcolumn, descending):
+sortkey = '%s_sort' % sortcolumn
+rows = sorted(rows, key=lambda x: x[sortkey],
+  reverse=descending)
+
+for row, parity in zip(rows, paritygen(stripecount)):
+row['parity'] = parity
+yield row
+
 class hgwebdir(object):
 """HTTP server for multiple repositories.
 
@@ -472,22 +488,9 @@
 def makeindex(self, wsgireq, tmpl, subdir=""):
 req = wsgireq.req
 
-sortdefault = None, False
-def entries(sortcolumn="", descending=False, subdir="", **map):
-rows = rawindexentries(self.ui, self.repos, wsgireq, req,
-   subdir=subdir, **map)
-
-if sortcolumn and sortdefault != (sortcolumn, descending):
-sortkey = '%s_sort' % sortcolumn
-rows = sorted(rows, key=lambda x: x[sortkey],
-  reverse=descending)
-for row, parity in zip(rows, paritygen(self.stripecount)):
-row['parity'] = parity
-yield row
-
 self.refresh()
 sortable = ["name", "description", "contact", "lastchange"]
-sortcolumn, descending = sortdefault
+sortcolumn, descending = None, False
 if 'sort' in req.qsparams:
 sortcolumn = req.qsparams['sort']
 descending = sortcolumn.startswith('-')
@@ -504,6 +507,10 @@
 self.refresh()
 self.updatereqenv(wsgireq.env)
 
+entries = indexentries(self.ui, self.repos, wsgireq, req,
+   self.stripecount, sortcolumn=sortcolumn,
+   descending=descending, subdir=subdir)
+
 return tmpl("index", entries=entries, subdir=subdir,
 pathdef=hgweb_mod.makebreadcrumb('/' + subdir, 
self.prefix),
 sortcolumn=sortcolumn, descending=descending,



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2818: tests: add test coverage for parsing WSGI requests

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGb2a3308d6a21: tests: add test coverage for parsing WSGI 
requests (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2818?vs=6884=6948

REVISION DETAIL
  https://phab.mercurial-scm.org/D2818

AFFECTED FILES
  mercurial/hgweb/request.py
  tests/test-wsgirequest.py

CHANGE DETAILS

diff --git a/tests/test-wsgirequest.py b/tests/test-wsgirequest.py
new file mode 100644
--- /dev/null
+++ b/tests/test-wsgirequest.py
@@ -0,0 +1,255 @@
+from __future__ import absolute_import, print_function
+
+import unittest
+
+from mercurial.hgweb import (
+request as requestmod,
+)
+
+DEFAULT_ENV = {
+r'REQUEST_METHOD': r'GET',
+r'SERVER_NAME': r'testserver',
+r'SERVER_PORT': r'80',
+r'SERVER_PROTOCOL': r'http',
+r'wsgi.version': (1, 0),
+r'wsgi.url_scheme': r'http',
+r'wsgi.input': None,
+r'wsgi.errors': None,
+r'wsgi.multithread': False,
+r'wsgi.multiprocess': True,
+r'wsgi.run_once': False,
+}
+
+def parse(env, bodyfh=None, extra=None):
+env = dict(env)
+env.update(extra or {})
+
+return requestmod.parserequestfromenv(env, bodyfh)
+
+class ParseRequestTests(unittest.TestCase):
+def testdefault(self):
+r = parse(DEFAULT_ENV)
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://testserver')
+self.assertEqual(r.advertisedbaseurl, b'http://testserver')
+self.assertEqual(r.urlscheme, b'http')
+self.assertEqual(r.method, b'GET')
+self.assertIsNone(r.remoteuser)
+self.assertIsNone(r.remotehost)
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [])
+self.assertEqual(r.dispatchpath, b'')
+self.assertFalse(r.havepathinfo)
+self.assertIsNone(r.reponame)
+self.assertEqual(r.querystring, b'')
+self.assertEqual(len(r.qsparams), 0)
+self.assertEqual(len(r.headers), 0)
+
+def testcustomport(self):
+r = parse(DEFAULT_ENV, extra={
+r'SERVER_PORT': r'8000',
+})
+
+self.assertEqual(r.url, b'http://testserver:8000')
+self.assertEqual(r.baseurl, b'http://testserver:8000')
+self.assertEqual(r.advertisedurl, b'http://testserver:8000')
+self.assertEqual(r.advertisedbaseurl, b'http://testserver:8000')
+
+r = parse(DEFAULT_ENV, extra={
+r'SERVER_PORT': r'4000',
+r'wsgi.url_scheme': r'https',
+})
+
+self.assertEqual(r.url, b'https://testserver:4000')
+self.assertEqual(r.baseurl, b'https://testserver:4000')
+self.assertEqual(r.advertisedurl, b'https://testserver:4000')
+self.assertEqual(r.advertisedbaseurl, b'https://testserver:4000')
+
+def testhttphost(self):
+r = parse(DEFAULT_ENV, extra={
+r'HTTP_HOST': r'altserver',
+})
+
+self.assertEqual(r.url, b'http://altserver')
+self.assertEqual(r.baseurl, b'http://altserver')
+self.assertEqual(r.advertisedurl, b'http://testserver')
+self.assertEqual(r.advertisedbaseurl, b'http://testserver')
+
+def testscriptname(self):
+r = parse(DEFAULT_ENV, extra={
+r'SCRIPT_NAME': r'',
+})
+
+self.assertEqual(r.url, b'http://testserver')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://testserver')
+self.assertEqual(r.advertisedbaseurl, b'http://testserver')
+self.assertEqual(r.apppath, b'')
+self.assertEqual(r.dispatchparts, [])
+self.assertEqual(r.dispatchpath, b'')
+self.assertFalse(r.havepathinfo)
+
+r = parse(DEFAULT_ENV, extra={
+r'SCRIPT_NAME': r'/script',
+})
+
+self.assertEqual(r.url, b'http://testserver/script')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, b'http://testserver/script')
+self.assertEqual(r.advertisedbaseurl, b'http://testserver')
+self.assertEqual(r.apppath, b'/script')
+self.assertEqual(r.dispatchparts, [])
+self.assertEqual(r.dispatchpath, b'')
+self.assertFalse(r.havepathinfo)
+
+r = parse(DEFAULT_ENV, extra={
+r'SCRIPT_NAME': r'/multiple words',
+})
+
+self.assertEqual(r.url, b'http://testserver/multiple%20words')
+self.assertEqual(r.baseurl, b'http://testserver')
+self.assertEqual(r.advertisedurl, 
b'http://testserver/multiple%20words')
+self.assertEqual(r.advertisedbaseurl, b'http://testserver')
+self.assertEqual(r.apppath, b'/multiple words')
+self.assertEqual(r.dispatchparts, [])
+self.assertEqual(r.dispatchpath, b'')
+self.assertFalse(r.havepathinfo)
+
+

D2817: hgweb: construct static URL like hgweb does

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGa5c478843c82: hgweb: construct static URL like hgweb does 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2817?vs=6883=6947

REVISION DETAIL
  https://phab.mercurial-scm.org/D2817

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -542,7 +542,8 @@
 sessionvars = webutil.sessionvars(vars, r'?')
 logourl = config('web', 'logourl')
 logoimg = config('web', 'logoimg')
-staticurl = config('web', 'staticurl') or url + 'static/'
+staticurl = (config('web', 'staticurl')
+ or wsgireq.req.apppath + '/static/')
 if not staticurl.endswith('/'):
 staticurl += '/'
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2816: hgweb: remove unused **map argument

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGee395147bb28: hgweb: remove unused **map argument (authored 
by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2816?vs=6882=6946

REVISION DETAIL
  https://phab.mercurial-scm.org/D2816

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -148,7 +148,7 @@
 
 return archives
 
-def rawindexentries(ui, repos, wsgireq, req, subdir='', **map):
+def rawindexentries(ui, repos, wsgireq, req, subdir=''):
 descend = ui.configbool('web', 'descend')
 collapse = ui.configbool('web', 'collapse')
 seenrepos = set()
@@ -274,9 +274,9 @@
 yield row
 
 def indexentries(ui, repos, wsgireq, req, stripecount, sortcolumn='',
- descending=False, subdir='', **map):
+ descending=False, subdir=''):
 
-rows = rawindexentries(ui, repos, wsgireq, req, subdir=subdir, **map)
+rows = rawindexentries(ui, repos, wsgireq, req, subdir=subdir)
 
 sortdefault = None, False
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames

2018-03-12 Thread durin42 (Augie Fackler)
durin42 added inline comments.

INLINE COMMENTS

> test-rename.t:668
> +  $ mkdir mtime
> +  $ python -c 'import os; p="mtime/f"; t=1234567890; open(p, "w").close(); 
> os.utime(p, (t, t))'
> +  $ hg ci -qAm 'add mtime dir'

You either need to do $PYTHON or do inline python, eg

  >>> import os
  >>> p="mtime/f"
  >>> t=1234567890
  >>> open(p, "w").close()
  >>> os.utime(p, (t, t))

> test-rename.t:671
> +  $ hg mv -q mtime mtime2
> +  $ python -c 'import os, sys; p="mtime2/f"; sys.exit(int(not 
> (os.stat(p).st_mtime < 1234567999)))'

here too

REPOSITORY
  rHG Mercurial

REVISION DETAIL
  https://phab.mercurial-scm.org/D2729

To: spectral, #hg-reviewers
Cc: durin42, mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2805: hgweb: remove some use of wsgireq in hgwebdir

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGfc4e31297ffb: hgweb: remove some use of wsgireq in hgwebdir 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2805?vs=6877=6941

REVISION DETAIL
  https://phab.mercurial-scm.org/D2805

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -200,16 +200,16 @@
 wsgireq = requestmod.wsgirequest(env, respond)
 return self.run_wsgi(wsgireq)
 
-def read_allowed(self, ui, wsgireq):
+def readallowed(self, ui, req):
 """Check allow_read and deny_read config options of a repo's ui object
 to determine user permissions.  By default, with neither option set (or
 both empty), allow all users to read the repo.  There are two ways a
 user can be denied read access:  (1) deny_read is not empty, and the
 user is unauthenticated or deny_read contains user (or *), and (2)
 allow_read is not empty and the user is not in allow_read.  Return True
 if user is allowed to read the repo, else return False."""
 
-user = wsgireq.env.get('REMOTE_USER')
+user = req.remoteuser
 
 deny_read = ui.configlist('web', 'deny_read', untrusted=True)
 if deny_read and (not user or ismember(ui, user, deny_read)):
@@ -329,6 +329,7 @@
 tmpl = None
 
 def makeindex(self, wsgireq, tmpl, subdir=""):
+req = wsgireq.req
 
 def archivelist(ui, nodeid, url):
 allowed = ui.configlist("web", "allow_archive", untrusted=True)
@@ -428,7 +429,7 @@
 if u.configbool("web", "hidden", untrusted=True):
 continue
 
-if not self.read_allowed(u, wsgireq):
+if not self.readallowed(u, req):
 continue
 
 # update time with local timezone
@@ -480,8 +481,8 @@
 self.refresh()
 sortable = ["name", "description", "contact", "lastchange"]
 sortcolumn, descending = sortdefault
-if 'sort' in wsgireq.req.qsparams:
-sortcolumn = wsgireq.req.qsparams['sort']
+if 'sort' in req.qsparams:
+sortcolumn = req.qsparams['sort']
 descending = sortcolumn.startswith('-')
 if descending:
 sortcolumn = sortcolumn[1:]



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2812: hgweb: move readallowed to a standalone function

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGf8d6d9b29b39: hgweb: move readallowed to a standalone 
function (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2812?vs=6878=6942

REVISION DETAIL
  https://phab.mercurial-scm.org/D2812

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -110,6 +110,28 @@
 
 return name, pycompat.bytestr(port), path
 
+def readallowed(ui, req):
+"""Check allow_read and deny_read config options of a repo's ui object
+to determine user permissions.  By default, with neither option set (or
+both empty), allow all users to read the repo.  There are two ways a
+user can be denied read access:  (1) deny_read is not empty, and the
+user is unauthenticated or deny_read contains user (or *), and (2)
+allow_read is not empty and the user is not in allow_read.  Return True
+if user is allowed to read the repo, else return False."""
+
+user = req.remoteuser
+
+deny_read = ui.configlist('web', 'deny_read', untrusted=True)
+if deny_read and (not user or ismember(ui, user, deny_read)):
+return False
+
+allow_read = ui.configlist('web', 'allow_read', untrusted=True)
+# by default, allow reading if no allow_read option has been set
+if not allow_read or ismember(ui, user, allow_read):
+return True
+
+return False
+
 class hgwebdir(object):
 """HTTP server for multiple repositories.
 
@@ -200,28 +222,6 @@
 wsgireq = requestmod.wsgirequest(env, respond)
 return self.run_wsgi(wsgireq)
 
-def readallowed(self, ui, req):
-"""Check allow_read and deny_read config options of a repo's ui object
-to determine user permissions.  By default, with neither option set (or
-both empty), allow all users to read the repo.  There are two ways a
-user can be denied read access:  (1) deny_read is not empty, and the
-user is unauthenticated or deny_read contains user (or *), and (2)
-allow_read is not empty and the user is not in allow_read.  Return True
-if user is allowed to read the repo, else return False."""
-
-user = req.remoteuser
-
-deny_read = ui.configlist('web', 'deny_read', untrusted=True)
-if deny_read and (not user or ismember(ui, user, deny_read)):
-return False
-
-allow_read = ui.configlist('web', 'allow_read', untrusted=True)
-# by default, allow reading if no allow_read option has been set
-if (not allow_read) or ismember(ui, user, allow_read):
-return True
-
-return False
-
 def run_wsgi(self, wsgireq):
 profile = self.ui.configbool('profiling', 'enabled')
 with profiling.profile(self.ui, enabled=profile):
@@ -429,7 +429,7 @@
 if u.configbool("web", "hidden", untrusted=True):
 continue
 
-if not self.readallowed(u, req):
+if not readallowed(u, req):
 continue
 
 # update time with local timezone



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2804: hgweb: fix a bug due to variable name typo

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG97e1dda94af8: hgweb: fix a bug due to variable name typo 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2804?vs=6865=6940

REVISION DETAIL
  https://phab.mercurial-scm.org/D2804

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -481,7 +481,7 @@
 sortable = ["name", "description", "contact", "lastchange"]
 sortcolumn, descending = sortdefault
 if 'sort' in wsgireq.req.qsparams:
-sortcolum = wsgireq.req.qsparams['sort']
+sortcolumn = wsgireq.req.qsparams['sort']
 descending = sortcolumn.startswith('-')
 if descending:
 sortcolumn = sortcolumn[1:]



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2801: hgweb: don't redundantly pass templater with requestcontext (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGc68e79dcf21c: hgweb: dont redundantly pass templater 
with requestcontext (API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2801?vs=6862=6938

REVISION DETAIL
  https://phab.mercurial-scm.org/D2801

AFFECTED FILES
  mercurial/hgweb/webcommands.py
  mercurial/hgweb/webutil.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ b/mercurial/hgweb/webutil.py
@@ -389,17 +389,17 @@
 'child': lambda **x: children(ctx),
 }
 
-def changelistentry(web, ctx, tmpl):
+def changelistentry(web, ctx):
 '''Obtain a dictionary to be used for entries in a changelist.
 
 This function is called when producing items for the "entries" list passed
 to the "shortlog" and "changelog" templates.
 '''
 repo = web.repo
 rev = ctx.rev()
 n = ctx.node()
-showtags = showtag(repo, tmpl, 'changelogtag', n)
-files = listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
+showtags = showtag(repo, web.tmpl, 'changelogtag', n)
+files = listfilediffs(web.tmpl, ctx.files(), n, web.maxfiles)
 
 entry = commonentry(repo, ctx)
 entry.update(
@@ -417,21 +417,21 @@
 else:
 return short(ctx.node())
 
-def changesetentry(web, req, tmpl, ctx):
+def changesetentry(web, req, ctx):
 '''Obtain a dictionary to be used to render the "changeset" template.'''
 
-showtags = showtag(web.repo, tmpl, 'changesettag', ctx.node())
-showbookmarks = showbookmark(web.repo, tmpl, 'changesetbookmark',
+showtags = showtag(web.repo, web.tmpl, 'changesettag', ctx.node())
+showbookmarks = showbookmark(web.repo, web.tmpl, 'changesetbookmark',
  ctx.node())
 showbranch = nodebranchnodefault(ctx)
 
 files = []
 parity = paritygen(web.stripecount)
 for blockno, f in enumerate(ctx.files()):
 template = 'filenodelink' if f in ctx else 'filenolink'
-files.append(tmpl(template,
-  node=ctx.hex(), file=f, blockno=blockno + 1,
-  parity=next(parity)))
+files.append(web.tmpl(template,
+  node=ctx.hex(), file=f, blockno=blockno + 1,
+  parity=next(parity)))
 
 basectx = basechangectx(web.repo, req)
 if basectx is None:
@@ -441,11 +441,11 @@
 if 'style' in req.req.qsparams:
 style = req.req.qsparams['style']
 
-diff = diffs(web, tmpl, ctx, basectx, None, style)
+diff = diffs(web, ctx, basectx, None, style)
 
 parity = paritygen(web.stripecount)
 diffstatsgen = diffstatgen(ctx, basectx)
-diffstats = diffstat(tmpl, ctx, diffstatsgen, parity)
+diffstats = diffstat(web.tmpl, ctx, diffstatsgen, parity)
 
 return dict(
 diff=diff,
@@ -466,7 +466,7 @@
 if len(files) > max:
 yield tmpl('fileellipses')
 
-def diffs(web, tmpl, ctx, basectx, files, style, linerange=None,
+def diffs(web, ctx, basectx, files, style, linerange=None,
   lineidprefix=''):
 
 def prettyprintlines(lines, blockno):
@@ -480,11 +480,12 @@
 ltype = "difflineat"
 else:
 ltype = "diffline"
-yield tmpl(ltype,
-   line=l,
-   lineno=lineno,
-   lineid=lineidprefix + "l%s" % difflineno,
-   linenumber="% 8s" % difflineno)
+yield web.tmpl(
+ltype,
+line=l,
+lineno=lineno,
+lineid=lineidprefix + "l%s" % difflineno,
+linenumber="% 8s" % difflineno)
 
 repo = web.repo
 if files:
@@ -509,8 +510,8 @@
 continue
 lines.extend(hunklines)
 if lines:
-yield tmpl('diffblock', parity=next(parity), blockno=blockno,
-   lines=prettyprintlines(lines, blockno))
+yield web.tmpl('diffblock', parity=next(parity), blockno=blockno,
+   lines=prettyprintlines(lines, blockno))
 
 def compare(tmpl, context, leftlines, rightlines):
 '''Generator function that provides side-by-side comparison data.'''
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -399,7 +399,7 @@
 if curcount > revcount + 1:
 break
 
-entry = webutil.changelistentry(web, web.repo[rev], web.tmpl)
+entry = webutil.changelistentry(web, web.repo[rev])
 entry['parity'] = next(parity)
 yield entry
 
@@ -485,7 +485,7 @@
 
 return web.sendtemplate(
 'changeset',
-**webutil.changesetentry(web, req, web.tmpl, ctx))
+**webutil.changesetentry(web, req, ctx))
 
 rev = 

D2802: hgweb: pass modern request type into various webutil functions (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG563fd95a6efb: hgweb: pass modern request type into various 
webutil functions (API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2802?vs=6863=6937

REVISION DETAIL
  https://phab.mercurial-scm.org/D2802

AFFECTED FILES
  hgext/highlight/__init__.py
  mercurial/hgweb/webcommands.py
  mercurial/hgweb/webutil.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ b/mercurial/hgweb/webutil.py
@@ -177,7 +177,7 @@
  section=section, whitespace=True)
 
 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
-v = req.req.qsparams.get(k)
+v = req.qsparams.get(k)
 if v is not None:
 v = util.parsebool(v)
 setattr(diffopts, k, v if v is not None else True)
@@ -295,34 +295,34 @@
 
 def changectx(repo, req):
 changeid = "tip"
-if 'node' in req.req.qsparams:
-changeid = req.req.qsparams['node']
+if 'node' in req.qsparams:
+changeid = req.qsparams['node']
 ipos = changeid.find(':')
 if ipos != -1:
 changeid = changeid[(ipos + 1):]
-elif 'manifest' in req.req.qsparams:
-changeid = req.req.qsparams['manifest']
+elif 'manifest' in req.qsparams:
+changeid = req.qsparams['manifest']
 
 return changeidctx(repo, changeid)
 
 def basechangectx(repo, req):
-if 'node' in req.req.qsparams:
-changeid = req.req.qsparams['node']
+if 'node' in req.qsparams:
+changeid = req.qsparams['node']
 ipos = changeid.find(':')
 if ipos != -1:
 changeid = changeid[:ipos]
 return changeidctx(repo, changeid)
 
 return None
 
 def filectx(repo, req):
-if 'file' not in req.req.qsparams:
+if 'file' not in req.qsparams:
 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
-path = cleanpath(repo, req.req.qsparams['file'])
-if 'node' in req.req.qsparams:
-changeid = req.req.qsparams['node']
-elif 'filenode' in req.req.qsparams:
-changeid = req.req.qsparams['filenode']
+path = cleanpath(repo, req.qsparams['file'])
+if 'node' in req.qsparams:
+changeid = req.qsparams['node']
+elif 'filenode' in req.qsparams:
+changeid = req.qsparams['filenode']
 else:
 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
 try:
@@ -333,7 +333,7 @@
 return fctx
 
 def linerange(req):
-linerange = req.req.qsparams.getall('linerange')
+linerange = req.qsparams.getall('linerange')
 if not linerange:
 return None
 if len(linerange) > 1:
@@ -412,12 +412,12 @@
 return entry
 
 def symrevorshortnode(req, ctx):
-if 'node' in req.req.qsparams:
-return templatefilters.revescape(req.req.qsparams['node'])
+if 'node' in req.qsparams:
+return templatefilters.revescape(req.qsparams['node'])
 else:
 return short(ctx.node())
 
-def changesetentry(web, req, ctx):
+def changesetentry(web, ctx):
 '''Obtain a dictionary to be used to render the "changeset" template.'''
 
 showtags = showtag(web.repo, web.tmpl, 'changesettag', ctx.node())
@@ -433,13 +433,13 @@
   node=ctx.hex(), file=f, blockno=blockno + 1,
   parity=next(parity)))
 
-basectx = basechangectx(web.repo, req)
+basectx = basechangectx(web.repo, web.req)
 if basectx is None:
 basectx = ctx.p1()
 
 style = web.config('web', 'style')
-if 'style' in req.req.qsparams:
-style = req.req.qsparams['style']
+if 'style' in web.req.qsparams:
+style = web.req.qsparams['style']
 
 diff = diffs(web, ctx, basectx, None, style)
 
@@ -449,7 +449,7 @@
 
 return dict(
 diff=diff,
-symrev=symrevorshortnode(req, ctx),
+symrev=symrevorshortnode(web.req, ctx),
 basenode=basectx.hex(),
 changesettag=showtags,
 changesetbookmark=showbookmarks,
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -108,7 +108,7 @@
 return manifest(web, req, None)
 
 try:
-fctx = webutil.filectx(web.repo, req)
+fctx = webutil.filectx(web.repo, web.req)
 except error.LookupError as inst:
 try:
 return manifest(web, req, None)
@@ -157,7 +157,7 @@
 file=f,
 path=webutil.up(f),
 text=lines(),
-symrev=webutil.symrevorshortnode(req, fctx),
+symrev=webutil.symrevorshortnode(web.req, fctx),
 rename=webutil.renamelink(fctx),
 permissions=fctx.manifest().flags(f),
 ishead=int(ishead),
@@ -190,7 +190,7 @@
 if not path:
 return 

D2813: hgweb: move archivelist to standalone function

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG04af43e0a997: hgweb: move archivelist to standalone 
function (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2813?vs=6879=6943

REVISION DETAIL
  https://phab.mercurial-scm.org/D2813

AFFECTED FILES
  mercurial/hgweb/hgwebdir_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -132,6 +132,22 @@
 
 return False
 
+def archivelist(ui, nodeid, url):
+allowed = ui.configlist('web', 'allow_archive', untrusted=True)
+archives = []
+
+for typ, spec in hgweb_mod.archivespecs.iteritems():
+if typ in allowed or ui.configbool('web', 'allow' + typ,
+   untrusted=True):
+archives.append({
+'type': typ,
+'extension': spec[2],
+'node': nodeid,
+'url': url,
+})
+
+return archives
+
 class hgwebdir(object):
 """HTTP server for multiple repositories.
 
@@ -331,16 +347,6 @@
 def makeindex(self, wsgireq, tmpl, subdir=""):
 req = wsgireq.req
 
-def archivelist(ui, nodeid, url):
-allowed = ui.configlist("web", "allow_archive", untrusted=True)
-archives = []
-for typ, spec in hgweb_mod.archivespecs.iteritems():
-if typ in allowed or ui.configbool("web", "allow" + typ,
-untrusted=True):
-archives.append({"type": typ, "extension": spec[2],
- "node": nodeid, "url": url})
-return archives
-
 def rawentries(subdir="", **map):
 
 descend = self.ui.configbool('web', 'descend')



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2803: hgweb: stop passing req and tmpl into @webcommand functions (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG4daa22071d5d: hgweb: stop passing req and tmpl into 
@webcommand functions (API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2803?vs=6864=6939

REVISION DETAIL
  https://phab.mercurial-scm.org/D2803

AFFECTED FILES
  hgext/highlight/__init__.py
  hgext/keyword.py
  hgext/largefiles/overrides.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/webcommands.py
  tests/hgweberror.py

CHANGE DETAILS

diff --git a/tests/hgweberror.py b/tests/hgweberror.py
--- a/tests/hgweberror.py
+++ b/tests/hgweberror.py
@@ -6,7 +6,7 @@
 webcommands,
 )
 
-def raiseerror(web, req, tmpl):
+def raiseerror(web):
 '''Dummy web command that raises an uncaught Exception.'''
 
 # Simulate an error after partial response.
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -65,7 +65,7 @@
 Usage:
 
 @webcommand('mycommand')
-def mycommand(web, req, tmpl):
+def mycommand(web):
 pass
 """
 
@@ -78,7 +78,7 @@
 return func
 
 @webcommand('log')
-def log(web, req, tmpl):
+def log(web):
 """
 /log[/{revision}[/{path}]]
 --
@@ -95,23 +95,23 @@
 """
 
 if web.req.qsparams.get('file'):
-return filelog(web, req, None)
+return filelog(web)
 else:
-return changelog(web, req, None)
+return changelog(web)
 
 @webcommand('rawfile')
-def rawfile(web, req, tmpl):
+def rawfile(web):
 guessmime = web.configbool('web', 'guessmime')
 
 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 if not path:
-return manifest(web, req, None)
+return manifest(web)
 
 try:
 fctx = webutil.filectx(web.repo, web.req)
 except error.LookupError as inst:
 try:
-return manifest(web, req, None)
+return manifest(web)
 except ErrorResponse:
 raise inst
 
@@ -135,7 +135,7 @@
 web.res.setbodybytes(text)
 return web.res.sendresponse()
 
-def _filerevision(web, req, fctx):
+def _filerevision(web, fctx):
 f = fctx.path()
 text = fctx.data()
 parity = paritygen(web.stripecount)
@@ -164,7 +164,7 @@
 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
 
 @webcommand('file')
-def file(web, req, tmpl):
+def file(web):
 """
 /file/{revision}[/{path}]
 -
@@ -184,16 +184,16 @@
 be rendered.
 """
 if web.req.qsparams.get('style') == 'raw':
-return rawfile(web, req, None)
+return rawfile(web)
 
 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 if not path:
-return manifest(web, req, None)
+return manifest(web)
 try:
-return _filerevision(web, req, webutil.filectx(web.repo, web.req))
+return _filerevision(web, webutil.filectx(web.repo, web.req))
 except error.LookupError as inst:
 try:
-return manifest(web, req, None)
+return manifest(web)
 except ErrorResponse:
 raise inst
 
@@ -354,7 +354,7 @@
 showunforcekw=showunforcekw)
 
 @webcommand('changelog')
-def changelog(web, req, tmpl, shortlog=False):
+def changelog(web, shortlog=False):
 """
 /changelog[/{revision}]
 ---
@@ -452,7 +452,7 @@
 query=query)
 
 @webcommand('shortlog')
-def shortlog(web, req, tmpl):
+def shortlog(web):
 """
 /shortlog
 -
@@ -463,10 +463,10 @@
 difference is the ``shortlog`` template will be rendered instead of the
 ``changelog`` template.
 """
-return changelog(web, req, None, shortlog=True)
+return changelog(web, shortlog=True)
 
 @webcommand('changeset')
-def changeset(web, req, tmpl):
+def changeset(web):
 """
 /changeset[/{revision}]
 ---
@@ -498,7 +498,7 @@
 return path
 
 @webcommand('manifest')
-def manifest(web, req, tmpl):
+def manifest(web):
 """
 /manifest[/{revision}[/{path}]]
 ---
@@ -598,7 +598,7 @@
 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
 
 @webcommand('tags')
-def tags(web, req, tmpl):
+def tags(web):
 """
 /tags
 -
@@ -632,7 +632,7 @@
 latestentry=lambda **x: entries(True, True, **x))
 
 @webcommand('bookmarks')
-def bookmarks(web, req, tmpl):
+def bookmarks(web):
 """
 /bookmarks
 --
@@ -671,7 +671,7 @@
 latestentry=lambda **x: entries(latestonly=True, **x))
 
 @webcommand('branches')
-def branches(web, req, tmpl):
+def branches(web):
 """
 /branches
 -
@@ -694,7 +694,7 @@
 latestentry=latestentry)
 
 @webcommand('summary')
-def summary(web, req, tmpl):
+def summary(web):
 """
 /summary
 

D2796: hgweb: always return iterable from @webcommand functions (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG67fb0dca29bc: hgweb: always return iterable from 
@webcommand functions (API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2796?vs=6857=6932

REVISION DETAIL
  https://phab.mercurial-scm.org/D2796

AFFECTED FILES
  hgext/highlight/__init__.py
  hgext/keyword.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -57,12 +57,9 @@
 The functions should populate the ``rctx.res`` object with details
 about the HTTP response.
 
-The function can return the ``requestcontext.res`` instance to signal
-that it wants to use this object to generate the response. If an iterable
-is returned, the ``wsgirequest`` instance will be used and the returned
-content will constitute the response body. ``True`` can be returned to
-indicate that the function already sent output and the caller doesn't
-need to do anything more to send the response.
+The function returns a generator to be consumed by the WSGI application.
+For most commands, this should be the result from
+``web.res.sendresponse()``.
 
 Usage:
 
@@ -135,7 +132,7 @@
 .replace('\\', '').replace('"', '\\"'))
 web.res.headers['Content-Disposition'] = 'inline; filename="%s"' % filename
 web.res.setbodybytes(text)
-return web.res
+return web.res.sendresponse()
 
 def _filerevision(web, req, tmpl, fctx):
 f = fctx.path()
@@ -165,7 +162,7 @@
 ishead=int(ishead),
 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('file')
 def file(web, req, tmpl):
@@ -355,7 +352,7 @@
 showforcekw=showforcekw,
 showunforcekw=showunforcekw))
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('changelog')
 def changelog(web, req, tmpl, shortlog=False):
@@ -455,7 +452,7 @@
 lessvars=lessvars,
 query=query))
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('shortlog')
 def shortlog(web, req, tmpl):
@@ -490,7 +487,7 @@
 ctx = webutil.changectx(web.repo, req)
 web.res.setbodygen(tmpl('changeset',
 **webutil.changesetentry(web, req, tmpl, ctx)))
-return web.res
+return web.res.sendresponse()
 
 rev = webcommand('rev')(changeset)
 
@@ -602,7 +599,7 @@
 archives=web.archivelist(hex(node)),
 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('tags')
 def tags(web, req, tmpl):
@@ -638,7 +635,7 @@
 entriesnotip=lambda **x: entries(True, False, **x),
 latestentry=lambda **x: entries(True, True, **x)))
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('bookmarks')
 def bookmarks(web, req, tmpl):
@@ -679,7 +676,7 @@
 entries=lambda **x: entries(latestonly=False, **x),
 latestentry=lambda **x: entries(latestonly=True, **x)))
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('branches')
 def branches(web, req, tmpl):
@@ -704,7 +701,7 @@
 entries=entries,
 latestentry=latestentry))
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('summary')
 def summary(web, req, tmpl):
@@ -789,7 +786,7 @@
 archives=web.archivelist('tip'),
 labels=web.configlist('web', 'labels')))
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('filediff')
 def filediff(web, req, tmpl):
@@ -838,7 +835,7 @@
 diff=diffs,
 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx
 
-return web.res
+return web.res.sendresponse()
 
 diff = webcommand('diff')(filediff)
 
@@ -917,7 +914,7 @@
 comparison=comparison,
 **pycompat.strkwargs(webutil.commonentry(web.repo, ctx
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('annotate')
 def annotate(web, req, tmpl):
@@ -1011,7 +1008,7 @@
 diffopts=diffopts,
 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('filelog')
 def filelog(web, req, tmpl):
@@ -1151,7 +1148,7 @@
 lessvars=lessvars,
 **pycompat.strkwargs(webutil.commonentry(web.repo, fctx
 
-return web.res
+return web.res.sendresponse()
 
 @webcommand('archive')
 def archive(web, req, tmpl):
@@ -1225,7 +1222,7 @@
  matchfn=match,
  subrepos=web.configbool("web", "archivesubrepos"))
 
-return True
+return []
 
 @webcommand('static')
 def static(web, req, tmpl):
@@ -1240,7 +1237,7 @@
 static = 

D2798: hgweb: use web.req instead of req.req

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG66f62d120ba2: hgweb: use web.req instead of req.req 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2798?vs=6859=6934

REVISION DETAIL
  https://phab.mercurial-scm.org/D2798

AFFECTED FILES
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -93,16 +93,16 @@
 file will be shown. This form is equivalent to the ``filelog`` handler.
 """
 
-if req.req.qsparams.get('file'):
+if web.req.qsparams.get('file'):
 return filelog(web, req, tmpl)
 else:
 return changelog(web, req, tmpl)
 
 @webcommand('rawfile')
 def rawfile(web, req, tmpl):
 guessmime = web.configbool('web', 'guessmime')
 
-path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
+path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 if not path:
 return manifest(web, req, tmpl)
 
@@ -187,7 +187,7 @@
 if web.req.qsparams.get('style') == 'raw':
 return rawfile(web, req, tmpl)
 
-path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
+path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 if not path:
 return manifest(web, req, tmpl)
 try:
@@ -198,7 +198,7 @@
 except ErrorResponse:
 raise inst
 
-def _search(web, req, tmpl):
+def _search(web, tmpl):
 MODE_REVISION = 'rev'
 MODE_KEYWORD = 'keyword'
 MODE_REVSET = 'revset'
@@ -303,11 +303,11 @@
 if count >= revcount:
 break
 
-query = req.req.qsparams['rev']
+query = web.req.qsparams['rev']
 revcount = web.maxchanges
-if 'revcount' in req.req.qsparams:
+if 'revcount' in web.req.qsparams:
 try:
-revcount = int(req.req.qsparams.get('revcount', revcount))
+revcount = int(web.req.qsparams.get('revcount', revcount))
 revcount = max(revcount, 1)
 tmpl.defaults['sessionvars']['revcount'] = revcount
 except ValueError:
@@ -322,7 +322,7 @@
 
 mode, funcarg = getsearchmode(query)
 
-if 'forcekw' in req.req.qsparams:
+if 'forcekw' in web.req.qsparams:
 showforcekw = ''
 showunforcekw = searchfuncs[mode][1]
 mode = MODE_KEYWORD
@@ -381,11 +381,11 @@
 """
 
 query = ''
-if 'node' in req.req.qsparams:
+if 'node' in web.req.qsparams:
 ctx = webutil.changectx(web.repo, req)
 symrev = webutil.symrevorshortnode(req, ctx)
-elif 'rev' in req.req.qsparams:
-return _search(web, req, tmpl)
+elif 'rev' in web.req.qsparams:
+return _search(web, tmpl)
 else:
 ctx = web.repo['tip']
 symrev = 'tip'
@@ -409,9 +409,9 @@
 else:
 revcount = web.maxchanges
 
-if 'revcount' in req.req.qsparams:
+if 'revcount' in web.req.qsparams:
 try:
-revcount = int(req.req.qsparams.get('revcount', revcount))
+revcount = int(web.req.qsparams.get('revcount', revcount))
 revcount = max(revcount, 1)
 tmpl.defaults['sessionvars']['revcount'] = revcount
 except ValueError:
@@ -516,13 +516,13 @@
 
 The ``manifest`` template will be rendered for this handler.
 """
-if 'node' in req.req.qsparams:
+if 'node' in web.req.qsparams:
 ctx = webutil.changectx(web.repo, req)
 symrev = webutil.symrevorshortnode(req, ctx)
 else:
 ctx = web.repo['tip']
 symrev = 'tip'
-path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
+path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 mf = ctx.manifest()
 node = ctx.node()
 
@@ -806,7 +806,7 @@
 fctx = webutil.filectx(web.repo, req)
 except LookupError:
 ctx = webutil.changectx(web.repo, req)
-path = webutil.cleanpath(web.repo, req.req.qsparams['file'])
+path = webutil.cleanpath(web.repo, web.req.qsparams['file'])
 if path not in ctx.files():
 raise
 
@@ -816,8 +816,8 @@
 basectx = ctx.p1()
 
 style = web.config('web', 'style')
-if 'style' in req.req.qsparams:
-style = req.req.qsparams['style']
+if 'style' in web.req.qsparams:
+style = web.req.qsparams['style']
 
 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
 if fctx is not None:
@@ -857,13 +857,13 @@
 The ``filecomparison`` template is rendered.
 """
 ctx = webutil.changectx(web.repo, req)
-if 'file' not in req.req.qsparams:
+if 'file' not in web.req.qsparams:
 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
-path = webutil.cleanpath(web.repo, req.req.qsparams['file'])
+path = webutil.cleanpath(web.repo, 

D2799: hgweb: add a sendtemplate() helper function

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG061635d4221c: hgweb: add a sendtemplate() helper function 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2799?vs=6860=6935

REVISION DETAIL
  https://phab.mercurial-scm.org/D2799

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -59,7 +59,8 @@
 
 The function returns a generator to be consumed by the WSGI application.
 For most commands, this should be the result from
-``web.res.sendresponse()``.
+``web.res.sendresponse()``. Many commands will call ``web.sendtemplate()``
+to render a template.
 
 Usage:
 
@@ -151,18 +152,16 @@
"linenumber": "% 6d" % (lineno + 1),
"parity": next(parity)}
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'filerevision',
 file=f,
 path=webutil.up(f),
 text=lines(),
 symrev=webutil.symrevorshortnode(req, fctx),
 rename=webutil.renamelink(fctx),
 permissions=fctx.manifest().flags(f),
 ishead=int(ishead),
-**pycompat.strkwargs(webutil.commonentry(web.repo, fctx
-
-return web.res.sendresponse()
+**pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
 
 @webcommand('file')
 def file(web, req, tmpl):
@@ -339,7 +338,7 @@
 tip = web.repo['tip']
 parity = paritygen(web.stripecount)
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'search',
 query=query,
 node=tip.hex(),
@@ -350,9 +349,7 @@
 lessvars=lessvars,
 modedesc=searchfunc[1],
 showforcekw=showforcekw,
-showunforcekw=showunforcekw))
-
-return web.res.sendresponse()
+showunforcekw=showunforcekw)
 
 @webcommand('changelog')
 def changelog(web, req, tmpl, shortlog=False):
@@ -436,7 +433,7 @@
 else:
 nextentry = []
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'shortlog' if shortlog else 'changelog',
 changenav=changenav,
 node=ctx.hex(),
@@ -450,9 +447,7 @@
 revcount=revcount,
 morevars=morevars,
 lessvars=lessvars,
-query=query))
-
-return web.res.sendresponse()
+query=query)
 
 @webcommand('shortlog')
 def shortlog(web, req, tmpl):
@@ -485,9 +480,10 @@
 templates related to diffs may all be used to produce the output.
 """
 ctx = webutil.changectx(web.repo, req)
-web.res.setbodygen(tmpl('changeset',
-**webutil.changesetentry(web, req, tmpl, ctx)))
-return web.res.sendresponse()
+
+return web.sendtemplate(
+'changeset',
+**webutil.changesetentry(web, req, tmpl, ctx))
 
 rev = webcommand('rev')(changeset)
 
@@ -588,18 +584,16 @@
"emptydirs": "/".join(emptydirs),
"basename": d}
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'manifest',
 symrev=symrev,
 path=abspath,
 up=webutil.up(abspath),
 upparity=next(parity),
 fentries=filelist,
 dentries=dirlist,
 archives=web.archivelist(hex(node)),
-**pycompat.strkwargs(webutil.commonentry(web.repo, ctx
-
-return web.res.sendresponse()
+**pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
 
 @webcommand('tags')
 def tags(web, req, tmpl):
@@ -628,14 +622,12 @@
"date": web.repo[n].date(),
"node": hex(n)}
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'tags',
 node=hex(web.repo.changelog.tip()),
 entries=lambda **x: entries(False, False, **x),
 entriesnotip=lambda **x: entries(True, False, **x),
-latestentry=lambda **x: entries(True, True, **x)))
-
-return web.res.sendresponse()
+latestentry=lambda **x: entries(True, True, **x))
 
 @webcommand('bookmarks')
 def bookmarks(web, req, tmpl):
@@ -669,14 +661,12 @@
 else:
 latestrev = -1
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'bookmarks',
 node=hex(web.repo.changelog.tip()),
 lastchange=[{'date': web.repo[latestrev].date()}],
 entries=lambda **x: entries(latestonly=False, **x),
-latestentry=lambda **x: entries(latestonly=True, **x)))
-
-return web.res.sendresponse()
+latestentry=lambda **x: entries(latestonly=True, **x))
 
 @webcommand('branches')
 def branches(web, req, tmpl):
@@ -695,13 +685,11 @@
 entries = webutil.branchentries(web.repo, web.stripecount)
 latestentry = webutil.branchentries(web.repo, web.stripecount, 1)
 
-web.res.setbodygen(tmpl(
+return web.sendtemplate(
 'branches',
 

D2800: hgweb: use templater on requestcontext instance

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGece242db5000: hgweb: use templater on requestcontext 
instance (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2800?vs=6861=6936

REVISION DETAIL
  https://phab.mercurial-scm.org/D2800

AFFECTED FILES
  hgext/highlight/__init__.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -95,23 +95,23 @@
 """
 
 if web.req.qsparams.get('file'):
-return filelog(web, req, tmpl)
+return filelog(web, req, None)
 else:
-return changelog(web, req, tmpl)
+return changelog(web, req, None)
 
 @webcommand('rawfile')
 def rawfile(web, req, tmpl):
 guessmime = web.configbool('web', 'guessmime')
 
 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 if not path:
-return manifest(web, req, tmpl)
+return manifest(web, req, None)
 
 try:
 fctx = webutil.filectx(web.repo, req)
 except error.LookupError as inst:
 try:
-return manifest(web, req, tmpl)
+return manifest(web, req, None)
 except ErrorResponse:
 raise inst
 
@@ -135,7 +135,7 @@
 web.res.setbodybytes(text)
 return web.res.sendresponse()
 
-def _filerevision(web, req, tmpl, fctx):
+def _filerevision(web, req, fctx):
 f = fctx.path()
 text = fctx.data()
 parity = paritygen(web.stripecount)
@@ -184,20 +184,20 @@
 be rendered.
 """
 if web.req.qsparams.get('style') == 'raw':
-return rawfile(web, req, tmpl)
+return rawfile(web, req, None)
 
 path = webutil.cleanpath(web.repo, web.req.qsparams.get('file', ''))
 if not path:
-return manifest(web, req, tmpl)
+return manifest(web, req, None)
 try:
-return _filerevision(web, req, tmpl, webutil.filectx(web.repo, req))
+return _filerevision(web, req, webutil.filectx(web.repo, req))
 except error.LookupError as inst:
 try:
-return manifest(web, req, tmpl)
+return manifest(web, req, None)
 except ErrorResponse:
 raise inst
 
-def _search(web, tmpl):
+def _search(web):
 MODE_REVISION = 'rev'
 MODE_KEYWORD = 'keyword'
 MODE_REVSET = 'revset'
@@ -290,14 +290,16 @@
 for ctx in searchfunc[0](funcarg):
 count += 1
 n = ctx.node()
-showtags = webutil.showtag(web.repo, tmpl, 'changelogtag', n)
-files = webutil.listfilediffs(tmpl, ctx.files(), n, web.maxfiles)
+showtags = webutil.showtag(web.repo, web.tmpl, 'changelogtag', n)
+files = webutil.listfilediffs(web.tmpl, ctx.files(), n,
+  web.maxfiles)
 
-yield tmpl('searchentry',
-   parity=next(parity),
-   changelogtag=showtags,
-   files=files,
-   **pycompat.strkwargs(webutil.commonentry(web.repo, 
ctx)))
+yield web.tmpl(
+'searchentry',
+parity=next(parity),
+changelogtag=showtags,
+files=files,
+**pycompat.strkwargs(webutil.commonentry(web.repo, ctx)))
 
 if count >= revcount:
 break
@@ -308,14 +310,14 @@
 try:
 revcount = int(web.req.qsparams.get('revcount', revcount))
 revcount = max(revcount, 1)
-tmpl.defaults['sessionvars']['revcount'] = revcount
+web.tmpl.defaults['sessionvars']['revcount'] = revcount
 except ValueError:
 pass
 
-lessvars = copy.copy(tmpl.defaults['sessionvars'])
+lessvars = copy.copy(web.tmpl.defaults['sessionvars'])
 lessvars['revcount'] = max(revcount // 2, 1)
 lessvars['rev'] = query
-morevars = copy.copy(tmpl.defaults['sessionvars'])
+morevars = copy.copy(web.tmpl.defaults['sessionvars'])
 morevars['revcount'] = revcount * 2
 morevars['rev'] = query
 
@@ -382,7 +384,7 @@
 ctx = webutil.changectx(web.repo, req)
 symrev = webutil.symrevorshortnode(req, ctx)
 elif 'rev' in web.req.qsparams:
-return _search(web, tmpl)
+return _search(web)
 else:
 ctx = web.repo['tip']
 symrev = 'tip'
@@ -397,7 +399,7 @@
 if curcount > revcount + 1:
 break
 
-entry = webutil.changelistentry(web, web.repo[rev], tmpl)
+entry = webutil.changelistentry(web, web.repo[rev], web.tmpl)
 entry['parity'] = next(parity)
 yield entry
 
@@ -410,13 +412,13 @@
 try:
 revcount = int(web.req.qsparams.get('revcount', revcount))
 revcount = max(revcount, 1)
-

D2792: hgweb: port archive command to modern response API

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG97f44b0720e2: hgweb: port archive command to modern 
response API (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2792?vs=6853=6928

REVISION DETAIL
  https://phab.mercurial-scm.org/D2792

AFFECTED FILES
  hgext/keyword.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py
  mercurial/hgweb/webcommands.py
  tests/hgweberror.py

CHANGE DETAILS

diff --git a/tests/hgweberror.py b/tests/hgweberror.py
--- a/tests/hgweberror.py
+++ b/tests/hgweberror.py
@@ -10,9 +10,12 @@
 '''Dummy web command that raises an uncaught Exception.'''
 
 # Simulate an error after partial response.
-if 'partialresponse' in req.req.qsparams:
-req.respond(200, 'text/plain')
-req.write('partial content\n')
+if 'partialresponse' in web.req.qsparams:
+web.res.status = b'200 Script output follows'
+web.res.headers[b'Content-Type'] = b'text/plain'
+web.res.setbodywillwrite()
+list(web.res.sendresponse())
+web.res.getbodyfile().write(b'partial content\n')
 
 raise AttributeError('I am an uncaught error!')
 
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -19,14 +19,10 @@
 ErrorResponse,
 HTTP_FORBIDDEN,
 HTTP_NOT_FOUND,
-HTTP_OK,
 get_contact,
 paritygen,
 staticfile,
 )
-from . import (
-request as requestmod,
-)
 
 from .. import (
 archival,
@@ -64,7 +60,9 @@
 The function can return the ``requestcontext.res`` instance to signal
 that it wants to use this object to generate the response. If an iterable
 is returned, the ``wsgirequest`` instance will be used and the returned
-content will constitute the response body.
+content will constitute the response body. ``True`` can be returned to
+indicate that the function already sent output and the caller doesn't
+need to do anything more to send the response.
 
 Usage:
 
@@ -1210,21 +1208,24 @@
 'file(s) not found: %s' % file)
 
 mimetype, artype, extension, encoding = web.archivespecs[type_]
-headers = [
-('Content-Disposition', 'attachment; filename=%s%s' % (name, 
extension))
-]
+
+web.res.headers['Content-Type'] = mimetype
+web.res.headers['Content-Disposition'] = 'attachment; filename=%s%s' % (
+name, extension)
+
 if encoding:
-headers.append(('Content-Encoding', encoding))
-req.headers.extend(headers)
-req.respond(HTTP_OK, mimetype)
+web.res.headers['Content-Encoding'] = encoding
 
-bodyfh = requestmod.offsettrackingwriter(req.write)
+web.res.setbodywillwrite()
+assert list(web.res.sendresponse()) == []
+
+bodyfh = web.res.getbodyfile()
 
 archival.archive(web.repo, bodyfh, cnode, artype, prefix=name,
  matchfn=match,
  subrepos=web.configbool("web", "archivesubrepos"))
-return []
 
+return True
 
 @webcommand('static')
 def static(web, req, tmpl):
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -351,22 +351,42 @@
 
 self._bodybytes = None
 self._bodygen = None
+self._bodywillwrite = False
 self._started = False
+self._bodywritefn = None
+
+def _verifybody(self):
+if (self._bodybytes is not None or self._bodygen is not None
+or self._bodywillwrite):
+raise error.ProgrammingError('cannot define body multiple times')
 
 def setbodybytes(self, b):
 """Define the response body as static bytes."""
-if self._bodybytes is not None or self._bodygen is not None:
-raise error.ProgrammingError('cannot define body multiple times')
-
+self._verifybody()
 self._bodybytes = b
 self.headers['Content-Length'] = '%d' % len(b)
 
 def setbodygen(self, gen):
 """Define the response body as a generator of bytes."""
-if self._bodybytes is not None or self._bodygen is not None:
-raise error.ProgrammingError('cannot define body multiple times')
+self._verifybody()
+self._bodygen = gen
+
+def setbodywillwrite(self):
+"""Signal an intent to use write() to emit the response body.
+
+**This is the least preferred way to send a body.**
 
-self._bodygen = gen
+It is preferred for WSGI applications to emit a generator of chunks
+constituting the response body. However, some consumers can't emit
+data this way. So, WSGI provides a way to obtain a ``write(data)``
+function that can be used to synchronously perform an unbuffered
+write.
+
+Calling this function signals an intent to produce the body in 

D2797: hgweb: stop setting headers on wsgirequest

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG96a93625a824: hgweb: stop setting headers on wsgirequest 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2797?vs=6858=6933

REVISION DETAIL
  https://phab.mercurial-scm.org/D2797

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -313,9 +313,6 @@
 if rctx.csp:
 # hgwebdir may have added CSP header. Since we generate our own,
 # replace it.
-wsgireq.headers = [h for h in wsgireq.headers
-   if h[0] != 'Content-Security-Policy']
-wsgireq.headers.append(('Content-Security-Policy', rctx.csp))
 res.headers['Content-Security-Policy'] = rctx.csp
 
 handled = wireprotoserver.handlewsgirequest(
@@ -393,7 +390,6 @@
 res.setbodybytes('')
 return res.sendresponse()
 
-wsgireq.headers.append((r'ETag', pycompat.sysstr(tag)))
 res.headers['ETag'] = tag
 
 if cmd not in webcommands.__all__:



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2793: hgweb: transition permissions hooks to modern request type (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG02bea04b4c54: hgweb: transition permissions hooks to modern 
request type (API) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2793?vs=6854=6929

REVISION DETAIL
  https://phab.mercurial-scm.org/D2793

AFFECTED FILES
  mercurial/hgweb/common.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/wireprotoserver.py
  tests/test-http-bundle1.t
  tests/test-http.t
  tests/test-largefiles-wireproto.t

CHANGE DETAILS

diff --git a/tests/test-largefiles-wireproto.t 
b/tests/test-largefiles-wireproto.t
--- a/tests/test-largefiles-wireproto.t
+++ b/tests/test-largefiles-wireproto.t
@@ -424,7 +424,7 @@
   > import base64
   > from mercurial.hgweb import common
   > def perform_authentication(hgweb, req, op):
-  > auth = req.env.get('HTTP_AUTHORIZATION')
+  > auth = req.headers.get('Authorization')
   > if not auth:
   > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
   > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
diff --git a/tests/test-http.t b/tests/test-http.t
--- a/tests/test-http.t
+++ b/tests/test-http.t
@@ -168,7 +168,7 @@
   > import base64
   > from mercurial.hgweb import common
   > def perform_authentication(hgweb, req, op):
-  > auth = req.env.get('HTTP_AUTHORIZATION')
+  > auth = req.headers.get('Authorization')
   > if not auth:
   > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
   > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
@@ -510,7 +510,7 @@
   > from mercurial import util
   > from mercurial.hgweb import common
   > def perform_authentication(hgweb, req, op):
-  > cookie = req.env.get('HTTP_COOKIE')
+  > cookie = req.headers.get('Cookie')
   > if not cookie:
   > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'no-cookie')
   > raise common.ErrorResponse(common.HTTP_SERVER_ERROR, 'Cookie: %s' % 
cookie)
diff --git a/tests/test-http-bundle1.t b/tests/test-http-bundle1.t
--- a/tests/test-http-bundle1.t
+++ b/tests/test-http-bundle1.t
@@ -177,7 +177,7 @@
   > import base64
   > from mercurial.hgweb import common
   > def perform_authentication(hgweb, req, op):
-  > auth = req.env.get('HTTP_AUTHORIZATION')
+  > auth = req.headers.get('Authorization')
   > if not auth:
   > raise common.ErrorResponse(common.HTTP_UNAUTHORIZED, 'who',
   > [('WWW-Authenticate', 'Basic Realm="mercurial"')])
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -148,13 +148,12 @@
 def iscmd(cmd):
 return cmd in wireproto.commands
 
-def handlewsgirequest(rctx, wsgireq, req, res, checkperm):
+def handlewsgirequest(rctx, req, res, checkperm):
 """Possibly process a wire protocol request.
 
 If the current request is a wire protocol request, the request is
 processed by this function.
 
-``wsgireq`` is a ``wsgirequest`` instance.
 ``req`` is a ``parsedrequest`` instance.
 ``res`` is a ``wsgiresponse`` instance.
 
@@ -197,7 +196,7 @@
 return True
 
 proto = httpv1protocolhandler(req, repo.ui,
-  lambda perm: checkperm(rctx, wsgireq, perm))
+  lambda perm: checkperm(rctx, req, perm))
 
 # The permissions checker should be the only thing that can raise an
 # ErrorResponse. It is kind of a layer violation to catch an hgweb
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -322,7 +322,7 @@
 res.headers['Content-Security-Policy'] = rctx.csp
 
 handled = wireprotoserver.handlewsgirequest(
-rctx, wsgireq, req, res, self.check_perm)
+rctx, req, res, self.check_perm)
 if handled:
 return res.sendresponse()
 
@@ -380,7 +380,7 @@
 
 # check read permissions non-static content
 if cmd != 'static':
-self.check_perm(rctx, wsgireq, None)
+self.check_perm(rctx, req, None)
 
 if cmd == '':
 req.qsparams['cmd'] = tmpl.cache['default']
diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -46,7 +46,7 @@
 authentication info). Return if op allowed, else raise an ErrorResponse
 exception.'''
 
-user = req.env.get(r'REMOTE_USER')
+user = req.remoteuser
 
 deny_read = hgweb.configlist('web', 'deny_read')
 if deny_read and (not user or ismember(hgweb.repo.ui, user, deny_read)):
@@ -62,14 +62,13 @@
 return
 
 # enforce that you can only push using POST requests
-if req.env[r'REQUEST_METHOD'] != r'POST':
+if req.method != 'POST':
 msg = 'push 

D2794: hgweb: refactor 304 handling code

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGccb70a77f746: hgweb: refactor 304 handling code (authored 
by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2794?vs=6855=6930

REVISION DETAIL
  https://phab.mercurial-scm.org/D2794

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -15,7 +15,6 @@
 
 from .common import (
 ErrorResponse,
-HTTP_NOT_MODIFIED,
 statusmessage,
 )
 
@@ -361,7 +360,10 @@
 raise error.ProgrammingError('cannot define body multiple times')
 
 def setbodybytes(self, b):
-"""Define the response body as static bytes."""
+"""Define the response body as static bytes.
+
+The empty string signals that there is no response body.
+"""
 self._verifybody()
 self._bodybytes = b
 self.headers['Content-Length'] = '%d' % len(b)
@@ -408,6 +410,35 @@
 and not self._bodywillwrite):
 raise error.ProgrammingError('response body not defined')
 
+# RFC 7232 Section 4.1 states that a 304 MUST generate one of
+# {Cache-Control, Content-Location, Date, ETag, Expires, Vary}
+# and SHOULD NOT generate other headers unless they could be used
+# to guide cache updates. Furthermore, RFC 7230 Section 3.3.2
+# states that no response body can be issued. Content-Length can
+# be sent. But if it is present, it should be the size of the response
+# that wasn't transferred.
+if self.status.startswith('304 '):
+# setbodybytes('') will set C-L to 0. This doesn't conform with the
+# spec. So remove it.
+if self.headers.get('Content-Length') == '0':
+del self.headers['Content-Length']
+
+# Strictly speaking, this is too strict. But until it causes
+# problems, let's be strict.
+badheaders = {k for k in self.headers.keys()
+  if k.lower() not in ('date', 'etag', 'expires',
+   'cache-control',
+   'content-location',
+   'vary')}
+if badheaders:
+raise error.ProgrammingError(
+'illegal header on 304 response: %s' %
+', '.join(sorted(badheaders)))
+
+if self._bodygen is not None or self._bodywillwrite:
+raise error.ProgrammingError("must use setbodybytes('') with "
+ "304 responses")
+
 # Various HTTP clients (notably httplib) won't read the HTTP response
 # until the HTTP request has been sent in full. If servers (us) send a
 # response before the HTTP request has been fully sent, the connection
@@ -539,13 +570,6 @@
 
 if isinstance(status, ErrorResponse):
 self.headers.extend(status.headers)
-if status.code == HTTP_NOT_MODIFIED:
-# RFC 2616 Section 10.3.5: 304 Not Modified has cases where
-# it MUST NOT include any headers other than these and no
-# body
-self.headers = [(k, v) for (k, v) in self.headers if
-k in ('Date', 'ETag', 'Expires',
-  'Cache-Control', 'Vary')]
 status = statusmessage(status.code, pycompat.bytestr(status))
 elif status == 200:
 status = '200 Script output follows'
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -15,7 +15,6 @@
 ErrorResponse,
 HTTP_BAD_REQUEST,
 HTTP_NOT_FOUND,
-HTTP_NOT_MODIFIED,
 HTTP_OK,
 HTTP_SERVER_ERROR,
 cspvalues,
@@ -391,7 +390,10 @@
 if rctx.configbool('web', 'cache') and not rctx.nonce:
 tag = 'W/"%d"' % self.mtime
 if req.headers.get('If-None-Match') == tag:
-raise ErrorResponse(HTTP_NOT_MODIFIED)
+res.status = '304 Not Modified'
+# Response body not allowed on 304.
+res.setbodybytes('')
+return res.sendresponse()
 
 wsgireq.headers.append((r'ETag', pycompat.sysstr(tag)))
 res.headers['ETag'] = tag
@@ -426,9 +428,6 @@
 return tmpl('error', error=pycompat.bytestr(inst))
 except ErrorResponse as inst:
 wsgireq.respond(inst, ctype)
-if inst.code == HTTP_NOT_MODIFIED:
-# Not allowed to return a body on a 304
-  

D2795: hgweb: send errors using new response API

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG9675147aec06: hgweb: send errors using new response API 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2795?vs=6856=6931

REVISION DETAIL
  https://phab.mercurial-scm.org/D2795

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -14,11 +14,10 @@
 from .common import (
 ErrorResponse,
 HTTP_BAD_REQUEST,
-HTTP_NOT_FOUND,
 HTTP_OK,
-HTTP_SERVER_ERROR,
 cspvalues,
 permhooks,
+statusmessage,
 )
 
 from .. import (
@@ -417,18 +416,25 @@
 return content
 
 except (error.LookupError, error.RepoLookupError) as err:
-wsgireq.respond(HTTP_NOT_FOUND, ctype)
 msg = pycompat.bytestr(err)
 if (util.safehasattr(err, 'name') and
 not isinstance(err,  error.ManifestLookupError)):
 msg = 'revision not found: %s' % err.name
-return tmpl('error', error=msg)
-except (error.RepoError, error.RevlogError) as inst:
-wsgireq.respond(HTTP_SERVER_ERROR, ctype)
-return tmpl('error', error=pycompat.bytestr(inst))
-except ErrorResponse as inst:
-wsgireq.respond(inst, ctype)
-return tmpl('error', error=pycompat.bytestr(inst))
+
+res.status = '404 Not Found'
+res.headers['Content-Type'] = ctype
+res.setbodygen(tmpl('error', error=msg))
+return res.sendresponse()
+except (error.RepoError, error.RevlogError) as e:
+res.status = '500 Internal Server Error'
+res.headers['Content-Type'] = ctype
+res.setbodygen(tmpl('error', error=pycompat.bytestr(e)))
+return res.sendresponse()
+except ErrorResponse as e:
+res.status = statusmessage(e.code, pycompat.bytestr(e))
+res.headers['Content-Type'] = ctype
+res.setbodygen(tmpl('error', error=pycompat.bytestr(e)))
+return res.sendresponse()
 
 def check_perm(self, rctx, req, op):
 for permhook in permhooks:



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2791: hgweb: refactor fake file object proxy for archiving

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG16499427f6de: hgweb: refactor fake file object proxy for 
archiving (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2791?vs=6852=6927

REVISION DETAIL
  https://phab.mercurial-scm.org/D2791

AFFECTED FILES
  mercurial/archival.py
  mercurial/hgweb/request.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -24,6 +24,9 @@
 paritygen,
 staticfile,
 )
+from . import (
+request as requestmod,
+)
 
 from .. import (
 archival,
@@ -1215,7 +1218,9 @@
 req.headers.extend(headers)
 req.respond(HTTP_OK, mimetype)
 
-archival.archive(web.repo, req, cnode, artype, prefix=name,
+bodyfh = requestmod.offsettrackingwriter(req.write)
+
+archival.archive(web.repo, bodyfh, cnode, artype, prefix=name,
  matchfn=match,
  subrepos=web.configbool("web", "archivesubrepos"))
 return []
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -290,6 +290,37 @@
  headers=headers,
  bodyfh=bodyfh)
 
+class offsettrackingwriter(object):
+"""A file object like object that is append only and tracks write count.
+
+Instances are bound to a callable. This callable is called with data
+whenever a ``write()`` is attempted.
+
+Instances track the amount of written data so they can answer ``tell()``
+requests.
+
+The intent of this class is to wrap the ``write()`` function returned by
+a WSGI ``start_response()`` function. Since ``write()`` is a callable and
+not a file object, it doesn't implement other file object methods.
+"""
+def __init__(self, writefn):
+self._write = writefn
+self._offset = 0
+
+def write(self, s):
+res = self._write(s)
+# Some Python objects don't report the number of bytes written.
+if res is None:
+self._offset += len(s)
+else:
+self._offset += res
+
+def flush(self):
+pass
+
+def tell(self):
+return self._offset
+
 class wsgiresponse(object):
 """Represents a response to a WSGI request.
 
diff --git a/mercurial/archival.py b/mercurial/archival.py
--- a/mercurial/archival.py
+++ b/mercurial/archival.py
@@ -195,34 +195,11 @@
 if self.fileobj:
 self.fileobj.close()
 
-class tellable(object):
-'''provide tell method for zipfile.ZipFile when writing to http
-response file object.'''
-
-def __init__(self, fp):
-self.fp = fp
-self.offset = 0
-
-def __getattr__(self, key):
-return getattr(self.fp, key)
-
-def write(self, s):
-self.fp.write(s)
-self.offset += len(s)
-
-def tell(self):
-return self.offset
-
 class zipit(object):
 '''write archive to zip file or stream.  can write uncompressed,
 or compressed with deflate.'''
 
 def __init__(self, dest, mtime, compress=True):
-if not isinstance(dest, bytes):
-try:
-dest.tell()
-except (AttributeError, IOError):
-dest = tellable(dest)
 self.z = zipfile.ZipFile(pycompat.fsdecode(dest), r'w',
  compress and zipfile.ZIP_DEFLATED or
  zipfile.ZIP_STORED)



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2786: hgweb: support using new response object for web commands

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG1f42d621f090: hgweb: support using new response object for 
web commands (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2786?vs=6847=6922

REVISION DETAIL
  https://phab.mercurial-scm.org/D2786

AFFECTED FILES
  hgext/keyword.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -53,6 +53,16 @@
 The decorator takes as its positional arguments the name/path the
 command should be accessible under.
 
+When called, functions receive as arguments a ``requestcontext``,
+``wsgirequest``, and a templater instance for generatoring output.
+The functions should populate the ``rctx.res`` object with details
+about the HTTP response.
+
+The function can return the ``requestcontext.res`` instance to signal
+that it wants to use this object to generate the response. If an iterable
+is returned, the ``wsgirequest`` instance will be used and the returned
+content will constitute the response body.
+
 Usage:
 
 @webcommand('mycommand')
@@ -1068,19 +1078,22 @@
 
 latestentry = entries[:1]
 
-return tmpl("filelog",
-file=f,
-nav=nav,
-symrev=webutil.symrevorshortnode(req, fctx),
-entries=entries,
-descend=descend,
-patch=patch,
-latestentry=latestentry,
-linerange=linerange,
-revcount=revcount,
-morevars=morevars,
-lessvars=lessvars,
-**pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
+web.res.setbodygen(tmpl(
+'filelog',
+file=f,
+nav=nav,
+symrev=webutil.symrevorshortnode(req, fctx),
+entries=entries,
+descend=descend,
+patch=patch,
+latestentry=latestentry,
+linerange=linerange,
+revcount=revcount,
+morevars=morevars,
+lessvars=lessvars,
+**pycompat.strkwargs(webutil.commonentry(web.repo, fctx
+
+return web.res
 
 @webcommand('archive')
 def archive(web, req, tmpl):
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -91,9 +91,11 @@
 is prone to race conditions. Instances of this class exist to hold
 mutable and race-free state for requests.
 """
-def __init__(self, app, repo):
+def __init__(self, app, repo, req, res):
 self.repo = repo
 self.reponame = app.reponame
+self.req = req
+self.res = res
 
 self.archivespecs = archivespecs
 
@@ -305,7 +307,7 @@
 def _runwsgi(self, wsgireq, repo):
 req = wsgireq.req
 res = wsgireq.res
-rctx = requestcontext(self, repo)
+rctx = requestcontext(self, repo, req, res)
 
 # This state is global across all threads.
 encoding.encoding = rctx.config('web', 'encoding')
@@ -401,7 +403,15 @@
 rctx.ctype = ctype
 content = webcommands.rawfile(rctx, wsgireq, tmpl)
 else:
+# Set some globals appropriate for web handlers. Commands can
+# override easily enough.
+res.status = '200 Script output follows'
+res.headers['Content-Type'] = ctype
 content = getattr(webcommands, cmd)(rctx, wsgireq, tmpl)
+
+if content is res:
+return res.sendresponse()
+
 wsgireq.respond(HTTP_OK, ctype)
 
 return content
diff --git a/hgext/keyword.py b/hgext/keyword.py
--- a/hgext/keyword.py
+++ b/hgext/keyword.py
@@ -621,7 +621,10 @@
 origmatch = kwt.match
 kwt.match = util.never
 try:
-for chunk in orig(web, req, tmpl):
+res = orig(web, req, tmpl)
+if res is web.res:
+res = res.sendresponse()
+for chunk in res:
 yield chunk
 finally:
 if kwt:



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2788: hgweb: remove one-off routing for file?style=raw

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG89002d07a114: hgweb: remove one-off routing for 
file?style=raw (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2788?vs=6849=6924

REVISION DETAIL
  https://phab.mercurial-scm.org/D2788

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -186,6 +186,9 @@
 If ``path`` is not defined, information about the root directory will
 be rendered.
 """
+if web.req.qsparams.get('style') == 'raw':
+return rawfile(web, req, tmpl)
+
 path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
 if not path:
 return manifest(web, req, tmpl)
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -399,12 +399,6 @@
 if cmd not in webcommands.__all__:
 msg = 'no such method: %s' % cmd
 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
-elif cmd == 'file' and req.qsparams.get('style') == 'raw':
-res.status = '200 Script output follows'
-res.headers['Content-Type'] = ctype
-content = webcommands.rawfile(rctx, wsgireq, tmpl)
-assert content is res
-return res.sendresponse()
 else:
 # Set some globals appropriate for web handlers. Commands can
 # override easily enough.



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2789: hgweb: port static file handling to new response API

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG98baf8dea553: hgweb: port static file handling to new 
response API (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2789?vs=6850=6925

REVISION DETAIL
  https://phab.mercurial-scm.org/D2789

AFFECTED FILES
  mercurial/hgweb/common.py
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -1232,8 +1232,9 @@
 if isinstance(tp, str):
 tp = [tp]
 static = [os.path.join(p, 'static') for p in tp]
-staticfile(static, fname, req)
-return []
+
+staticfile(static, fname, web.res)
+return web.res
 
 @webcommand('graph')
 def graph(web, req, tmpl):
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -230,19 +230,25 @@
 
 def _runwsgi(self, wsgireq):
 req = wsgireq.req
+res = wsgireq.res
 
 try:
 self.refresh()
 
 csp, nonce = cspvalues(self.ui)
 if csp:
+res.headers['Content-Security-Policy'] = csp
 wsgireq.headers.append(('Content-Security-Policy', csp))
 
 virtual = wsgireq.env.get("PATH_INFO", "").strip('/')
 tmpl = self.templater(wsgireq, nonce)
 ctype = tmpl('mimetype', encoding=encoding.encoding)
 ctype = templater.stringify(ctype)
 
+# Global defaults. These can be overridden by any handler.
+res.status = '200 Script output follows'
+res.headers['Content-Type'] = ctype
+
 # a static file
 if virtual.startswith('static/') or 'static' in req.qsparams:
 if virtual.startswith('static/'):
@@ -256,8 +262,9 @@
 if isinstance(tp, str):
 tp = [tp]
 static = [os.path.join(p, 'static') for p in tp]
-staticfile(static, fname, wsgireq)
-return []
+
+staticfile(static, fname, res)
+return res.sendresponse()
 
 # top-level index
 
diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -153,7 +153,7 @@
 
 return True
 
-def staticfile(directory, fname, req):
+def staticfile(directory, fname, res):
 """return a file inside directory with guessed Content-Type header
 
 fname always uses '/' as directory separator and isn't allowed to
@@ -178,7 +178,9 @@
 with open(path, 'rb') as fh:
 data = fh.read()
 
-req.respond(HTTP_OK, ct, body=data)
+res.headers['Content-Type'] = ct
+res.setbodybytes(data)
+return res
 except TypeError:
 raise ErrorResponse(HTTP_SERVER_ERROR, 'illegal filename')
 except OSError as err:



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2790: tests: additional test coverage of archive web command

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG40193f977a8b: tests: additional test coverage of archive 
web command (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2790?vs=6851=6926

REVISION DETAIL
  https://phab.mercurial-scm.org/D2790

AFFECTED FILES
  tests/test-archive.t

CHANGE DETAILS

diff --git a/tests/test-archive.t b/tests/test-archive.t
--- a/tests/test-archive.t
+++ b/tests/test-archive.t
@@ -106,10 +106,13 @@
   > --config extensions.blackbox= --config blackbox.track=develwarn
   > cat hg.pid >> $DAEMON_PIDS
   > echo % $1 allowed should give 200
-  > get-with-headers.py localhost:$HGPORT "archive/tip.$2" | head -n 1
+  > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$2" 
-
+  > f --size --sha1 body
   > echo % $3 and $4 disallowed should both give 403
-  > get-with-headers.py localhost:$HGPORT "archive/tip.$3" | head -n 1
-  > get-with-headers.py localhost:$HGPORT "archive/tip.$4" | head -n 1
+  > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$3" 
-
+  > f --size --sha1 body
+  > get-with-headers.py --bodyfile body localhost:$HGPORT "archive/tip.$4" 
-
+  > f --size --sha1 body
   > killdaemons.py
   > cat errors.log
   > hg blackbox --config extensions.blackbox= --config blackbox.track=
@@ -121,42 +124,174 @@
   $ test_archtype gz tar.gz tar.bz2 zip
   % gz allowed should give 200
   200 Script output follows
+  content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
+  content-type: application/x-gzip
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
   % tar.bz2 and zip disallowed should both give 403
   403 Archive type not allowed: bz2
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
   403 Archive type not allowed: zip
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
   $ test_archtype bz2 tar.bz2 zip tar.gz
   % bz2 allowed should give 200
   200 Script output follows
+  content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.bz2
+  content-type: application/x-bzip2
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=426, sha1=8d87f5aba6e14f1bfea6c232985982c278b2fb0b
   % zip and tar.gz disallowed should both give 403
   403 Archive type not allowed: zip
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1451, sha1=cbfa5574b337348bfd0564cc534474d002e7d6c7
   403 Archive type not allowed: gz
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
   $ test_archtype zip zip tar.gz tar.bz2
   % zip allowed should give 200
   200 Script output follows
+  content-disposition: attachment; filename=test-archive-1701ef1f1510.zip
+  content-type: application/zip
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1377, sha1=677b14d3d048778d5eb5552c14a67e6192068650
   % tar.gz and tar.bz2 disallowed should both give 403
   403 Archive type not allowed: gz
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1450, sha1=71f0b12d59f85fdcfe8ff493e2dc66863f2f7734
   403 Archive type not allowed: bz2
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
 
 check http return codes (with deprecated option)
 
   $ test_archtype_deprecated gz tar.gz tar.bz2 zip
   % gz allowed should give 200
   200 Script output follows
+  content-disposition: attachment; filename=test-archive-1701ef1f1510.tar.gz
+  content-type: application/x-gzip
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=408, sha1=8fa06531bddecc365a9f5edb0f88b65974bfe505
   % tar.bz2 and zip disallowed should both give 403
   403 Archive type not allowed: bz2
+  content-type: text/html; charset=ascii
+  date: * (glob)
+  etag: W/"*" (glob)
+  server: * (glob)
+  transfer-encoding: chunked
+  
+  body: size=1451, sha1=4c5cf0f574446c44feb7f88f4e0e2a56bd92c352
   403 Archive type not allowed: zip
+  content-type: text/html; 

D2787: hgweb: port most @webcommand to use modern response type

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG9fc3d814646e: hgweb: port most @webcommand to use modern 
response type (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2787?vs=6848=6923

REVISION DETAIL
  https://phab.mercurial-scm.org/D2787

AFFECTED FILES
  hgext/highlight/__init__.py
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/webcommands.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -106,17 +106,13 @@
 
 path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
 if not path:
-content = manifest(web, req, tmpl)
-req.respond(HTTP_OK, web.ctype)
-return content
+return manifest(web, req, tmpl)
 
 try:
 fctx = webutil.filectx(web.repo, req)
 except error.LookupError as inst:
 try:
-content = manifest(web, req, tmpl)
-req.respond(HTTP_OK, web.ctype)
-return content
+return manifest(web, req, tmpl)
 except ErrorResponse:
 raise inst
 
@@ -133,8 +129,12 @@
 if mt.startswith('text/'):
 mt += '; charset="%s"' % encoding.encoding
 
-req.respond(HTTP_OK, mt, path, body=text)
-return []
+web.res.headers['Content-Type'] = mt
+filename = (path.rpartition('/')[-1]
+.replace('\\', '').replace('"', '\\"'))
+web.res.headers['Content-Disposition'] = 'inline; filename="%s"' % filename
+web.res.setbodybytes(text)
+return web.res
 
 def _filerevision(web, req, tmpl, fctx):
 f = fctx.path()
@@ -153,15 +153,18 @@
"linenumber": "% 6d" % (lineno + 1),
"parity": next(parity)}
 
-return tmpl("filerevision",
-file=f,
-path=webutil.up(f),
-text=lines(),
-symrev=webutil.symrevorshortnode(req, fctx),
-rename=webutil.renamelink(fctx),
-permissions=fctx.manifest().flags(f),
-ishead=int(ishead),
-**pycompat.strkwargs(webutil.commonentry(web.repo, fctx)))
+web.res.setbodygen(tmpl(
+'filerevision',
+file=f,
+path=webutil.up(f),
+text=lines(),
+symrev=webutil.symrevorshortnode(req, fctx),
+rename=webutil.renamelink(fctx),
+permissions=fctx.manifest().flags(f),
+ishead=int(ishead),
+**pycompat.strkwargs(webutil.commonentry(web.repo, fctx
+
+return web.res
 
 @webcommand('file')
 def file(web, req, tmpl):
@@ -335,11 +338,20 @@
 tip = web.repo['tip']
 parity = paritygen(web.stripecount)
 
-return tmpl('search', query=query, node=tip.hex(), symrev='tip',
-entries=changelist, archives=web.archivelist("tip"),
-morevars=morevars, lessvars=lessvars,
-modedesc=searchfunc[1],
-showforcekw=showforcekw, showunforcekw=showunforcekw)
+web.res.setbodygen(tmpl(
+'search',
+query=query,
+node=tip.hex(),
+symrev='tip',
+entries=changelist,
+archives=web.archivelist('tip'),
+morevars=morevars,
+lessvars=lessvars,
+modedesc=searchfunc[1],
+showforcekw=showforcekw,
+showunforcekw=showunforcekw))
+
+return web.res
 
 @webcommand('changelog')
 def changelog(web, req, tmpl, shortlog=False):
@@ -423,12 +435,23 @@
 else:
 nextentry = []
 
-return tmpl('shortlog' if shortlog else 'changelog', changenav=changenav,
-node=ctx.hex(), rev=pos, symrev=symrev, changesets=count,
-entries=entries,
-latestentry=latestentry, nextentry=nextentry,
-archives=web.archivelist("tip"), revcount=revcount,
-morevars=morevars, lessvars=lessvars, query=query)
+web.res.setbodygen(tmpl(
+'shortlog' if shortlog else 'changelog',
+changenav=changenav,
+node=ctx.hex(),
+rev=pos,
+symrev=symrev,
+changesets=count,
+entries=entries,
+latestentry=latestentry,
+nextentry=nextentry,
+archives=web.archivelist('tip'),
+revcount=revcount,
+morevars=morevars,
+lessvars=lessvars,
+query=query))
+
+return web.res
 
 @webcommand('shortlog')
 def shortlog(web, req, tmpl):
@@ -461,8 +484,9 @@
 templates related to diffs may all be used to produce the output.
 """
 ctx = webutil.changectx(web.repo, req)
-
-return tmpl('changeset', **webutil.changesetentry(web, req, tmpl, ctx))
+web.res.setbodygen(tmpl('changeset',
+**webutil.changesetentry(web, req, tmpl, ctx)))
+return web.res
 
 rev = webcommand('rev')(changeset)
 
@@ -563,15 +587,18 @@

D2781: hgweb: perform all parameter lookup via qsparams

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG3d60a22e27f5: hgweb: perform all parameter lookup via 
qsparams (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2781?vs=6842=6917

REVISION DETAIL
  https://phab.mercurial-scm.org/D2781

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/webcommands.py
  mercurial/hgweb/webutil.py
  tests/hgweberror.py

CHANGE DETAILS

diff --git a/tests/hgweberror.py b/tests/hgweberror.py
--- a/tests/hgweberror.py
+++ b/tests/hgweberror.py
@@ -10,7 +10,7 @@
 '''Dummy web command that raises an uncaught Exception.'''
 
 # Simulate an error after partial response.
-if 'partialresponse' in req.form:
+if 'partialresponse' in req.req.qsparams:
 req.respond(200, 'text/plain')
 req.write('partial content\n')
 
diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ b/mercurial/hgweb/webutil.py
@@ -177,7 +177,7 @@
  section=section, whitespace=True)
 
 for k in ('ignorews', 'ignorewsamount', 'ignorewseol', 'ignoreblanklines'):
-v = req.form.get(k, [None])[0]
+v = req.req.qsparams.get(k)
 if v is not None:
 v = util.parsebool(v)
 setattr(diffopts, k, v if v is not None else True)
@@ -295,34 +295,34 @@
 
 def changectx(repo, req):
 changeid = "tip"
-if 'node' in req.form:
-changeid = req.form['node'][0]
+if 'node' in req.req.qsparams:
+changeid = req.req.qsparams['node']
 ipos = changeid.find(':')
 if ipos != -1:
 changeid = changeid[(ipos + 1):]
-elif 'manifest' in req.form:
-changeid = req.form['manifest'][0]
+elif 'manifest' in req.req.qsparams:
+changeid = req.req.qsparams['manifest']
 
 return changeidctx(repo, changeid)
 
 def basechangectx(repo, req):
-if 'node' in req.form:
-changeid = req.form['node'][0]
+if 'node' in req.req.qsparams:
+changeid = req.req.qsparams['node']
 ipos = changeid.find(':')
 if ipos != -1:
 changeid = changeid[:ipos]
 return changeidctx(repo, changeid)
 
 return None
 
 def filectx(repo, req):
-if 'file' not in req.form:
+if 'file' not in req.req.qsparams:
 raise ErrorResponse(HTTP_NOT_FOUND, 'file not given')
-path = cleanpath(repo, req.form['file'][0])
-if 'node' in req.form:
-changeid = req.form['node'][0]
-elif 'filenode' in req.form:
-changeid = req.form['filenode'][0]
+path = cleanpath(repo, req.req.qsparams['file'])
+if 'node' in req.req.qsparams:
+changeid = req.req.qsparams['node']
+elif 'filenode' in req.req.qsparams:
+changeid = req.req.qsparams['filenode']
 else:
 raise ErrorResponse(HTTP_NOT_FOUND, 'node or filenode not given')
 try:
@@ -333,8 +333,8 @@
 return fctx
 
 def linerange(req):
-linerange = req.form.get('linerange')
-if linerange is None:
+linerange = req.req.qsparams.getall('linerange')
+if not linerange:
 return None
 if len(linerange) > 1:
 raise ErrorResponse(HTTP_BAD_REQUEST,
@@ -412,8 +412,8 @@
 return entry
 
 def symrevorshortnode(req, ctx):
-if 'node' in req.form:
-return templatefilters.revescape(req.form['node'][0])
+if 'node' in req.req.qsparams:
+return templatefilters.revescape(req.req.qsparams['node'])
 else:
 return short(ctx.node())
 
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -85,16 +85,16 @@
 file will be shown. This form is equivalent to the ``filelog`` handler.
 """
 
-if 'file' in req.form and req.form['file'][0]:
+if req.req.qsparams.get('file'):
 return filelog(web, req, tmpl)
 else:
 return changelog(web, req, tmpl)
 
 @webcommand('rawfile')
 def rawfile(web, req, tmpl):
 guessmime = web.configbool('web', 'guessmime')
 
-path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
+path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
 if not path:
 content = manifest(web, req, tmpl)
 req.respond(HTTP_OK, web.ctype)
@@ -173,7 +173,7 @@
 If ``path`` is not defined, information about the root directory will
 be rendered.
 """
-path = webutil.cleanpath(web.repo, req.form.get('file', [''])[0])
+path = webutil.cleanpath(web.repo, req.req.qsparams.get('file', ''))
 if not path:
 return manifest(web, req, tmpl)
 try:
@@ -289,11 +289,11 @@
 if count >= revcount:
 break
 
-query = req.form['rev'][0]
+query = req.req.qsparams['rev']
 revcount = web.maxchanges
-if 'revcount' in req.form:
+ 

D2784: hgweb: expose repo name on parsedrequest

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG8ddb5c354906: hgweb: expose repo name on parsedrequest 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2784?vs=6845=6920

REVISION DETAIL
  https://phab.mercurial-scm.org/D2784

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -144,6 +144,8 @@
 # Whether there is a path component to this request. This can be true
 # when ``dispatchpath`` is empty due to REPO_NAME muckery.
 havepathinfo = attr.ib()
+# The name of the repository being accessed.
+reponame = attr.ib()
 # Raw query string (part after "?" in URL).
 querystring = attr.ib()
 # multidict of query string parameters.
@@ -282,6 +284,7 @@
  apppath=apppath,
  dispatchparts=dispatchparts, 
dispatchpath=dispatchpath,
  havepathinfo='PATH_INFO' in env,
+ reponame=env.get('REPO_NAME'),
  querystring=querystring,
  qsparams=qsparams,
  headers=headers,
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -141,7 +141,7 @@
 if typ in allowed or self.configbool('web', 'allow%s' % typ):
 yield {'type': typ, 'extension': spec[2], 'node': nodeid}
 
-def templater(self, wsgireq, req):
+def templater(self, req):
 # determine scheme, port and server name
 # this is needed to create absolute urls
 logourl = self.config('web', 'logourl')
@@ -159,17 +159,18 @@
 # figure out which style to use
 
 vars = {}
-styles, (style, mapfile) = getstyle(wsgireq.req, self.config,
+styles, (style, mapfile) = getstyle(req, self.config,
 self.templatepath)
 if style == styles[0]:
 vars['style'] = style
 
 sessionvars = webutil.sessionvars(vars, '?')
 
 if not self.reponame:
 self.reponame = (self.config('web', 'name', '')
- or wsgireq.env.get('REPO_NAME')
- or req.apppath or self.repo.root)
+ or req.reponame
+ or req.apppath
+ or self.repo.root)
 
 def websubfilter(text):
 return templatefilters.websub(text, self.websubtable)
@@ -372,7 +373,7 @@
 # process the web interface request
 
 try:
-tmpl = rctx.templater(wsgireq, req)
+tmpl = rctx.templater(req)
 ctype = tmpl('mimetype', encoding=encoding.encoding)
 ctype = templater.stringify(ctype)
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2785: hgweb: inline caching() and port to modern mechanisms

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG7ad6a275316f: hgweb: inline caching() and port to modern 
mechanisms (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2785?vs=6846=6921

REVISION DETAIL
  https://phab.mercurial-scm.org/D2785

AFFECTED FILES
  mercurial/hgweb/common.py
  mercurial/hgweb/hgweb_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -18,7 +18,6 @@
 HTTP_NOT_MODIFIED,
 HTTP_OK,
 HTTP_SERVER_ERROR,
-caching,
 cspvalues,
 permhooks,
 )
@@ -388,7 +387,13 @@
 # Don't enable caching if using a CSP nonce because then it 
wouldn't
 # be a nonce.
 if rctx.configbool('web', 'cache') and not rctx.nonce:
-caching(self, wsgireq) # sets ETag header or raises 
NOT_MODIFIED
+tag = 'W/"%d"' % self.mtime
+if req.headers.get('If-None-Match') == tag:
+raise ErrorResponse(HTTP_NOT_MODIFIED)
+
+wsgireq.headers.append((r'ETag', pycompat.sysstr(tag)))
+res.headers['ETag'] = tag
+
 if cmd not in webcommands.__all__:
 msg = 'no such method: %s' % cmd
 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
diff --git a/mercurial/hgweb/common.py b/mercurial/hgweb/common.py
--- a/mercurial/hgweb/common.py
+++ b/mercurial/hgweb/common.py
@@ -214,12 +214,6 @@
 config("ui", "username") or
 encoding.environ.get("EMAIL") or "")
 
-def caching(web, req):
-tag = r'W/"%d"' % web.mtime
-if req.env.get('HTTP_IF_NONE_MATCH') == tag:
-raise ErrorResponse(HTTP_NOT_MODIFIED)
-req.headers.append(('ETag', tag))
-
 def cspvalues(ui):
 """Obtain the Content-Security-Policy header and nonce value.
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2780: hgweb: set variables in qsparams

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG4e06e8336634: hgweb: set variables in qsparams (authored by 
indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2780?vs=6841=6916

REVISION DETAIL
  https://phab.mercurial-scm.org/D2780

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -342,15 +342,22 @@
 # avoid accepting e.g. style parameter as command
 if util.safehasattr(webcommands, cmd):
 wsgireq.form['cmd'] = [cmd]
+req.qsparams['cmd'] = cmd
 
 if cmd == 'static':
 wsgireq.form['file'] = ['/'.join(args)]
+req.qsparams['file'] = '/'.join(args)
 else:
 if args and args[0]:
 node = args.pop(0).replace('%2F', '/')
 wsgireq.form['node'] = [node]
+req.qsparams['node'] = node
 if args:
 wsgireq.form['file'] = args
+if 'file' in req.qsparams:
+del req.qsparams['file']
+for a in args:
+req.qsparams.add('file', a)
 
 ua = req.headers.get('User-Agent', '')
 if cmd == 'rev' and 'mercurial' in ua:
@@ -362,7 +369,9 @@
 ext = spec[2]
 if fn.endswith(ext):
 wsgireq.form['node'] = [fn[:-len(ext)]]
+req.qsparams['node'] = fn[:-len(next)]
 wsgireq.form['type'] = [type_]
+req.qsparams['type'] = type_
 else:
 cmd = wsgireq.form.get('cmd', [''])[0]
 
@@ -379,6 +388,7 @@
 
 if cmd == '':
 wsgireq.form['cmd'] = [tmpl.cache['default']]
+req.qsparams['cmd'] = tmpl.cache['default']
 cmd = wsgireq.form['cmd'][0]
 
 # Don't enable caching if using a CSP nonce because then it 
wouldn't



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2779: hgweb: use our new request object for "style" parameter

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG1a1972b1a1ff: hgweb: use our new request object for 
style parameter (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2779?vs=6839=6915

REVISION DETAIL
  https://phab.mercurial-scm.org/D2779

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/hgwebdir_mod.py
  mercurial/hgweb/webcommands.py
  mercurial/hgweb/webutil.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/webutil.py b/mercurial/hgweb/webutil.py
--- a/mercurial/hgweb/webutil.py
+++ b/mercurial/hgweb/webutil.py
@@ -438,8 +438,8 @@
 basectx = ctx.p1()
 
 style = web.config('web', 'style')
-if 'style' in req.form:
-style = req.form['style'][0]
+if 'style' in req.req.qsparams:
+style = req.req.qsparams['style']
 
 diff = diffs(web, tmpl, ctx, basectx, None, style)
 
diff --git a/mercurial/hgweb/webcommands.py b/mercurial/hgweb/webcommands.py
--- a/mercurial/hgweb/webcommands.py
+++ b/mercurial/hgweb/webcommands.py
@@ -762,8 +762,8 @@
 basectx = ctx.p1()
 
 style = web.config('web', 'style')
-if 'style' in req.form:
-style = req.form['style'][0]
+if 'style' in req.req.qsparams:
+style = req.req.qsparams['style']
 
 diffs = webutil.diffs(web, tmpl, ctx, basectx, [path], style)
 if fctx is not None:
@@ -1011,8 +1011,8 @@
 entries = []
 
 diffstyle = web.config('web', 'style')
-if 'style' in req.form:
-diffstyle = req.form['style'][0]
+if 'style' in req.req.qsparams:
+diffstyle = req.req.qsparams['style']
 
 def diff(fctx, linerange=None):
 ctx = fctx.changectx()
diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -510,7 +510,7 @@
 url += '/'
 
 vars = {}
-styles, (style, mapfile) = hgweb_mod.getstyle(wsgireq, config,
+styles, (style, mapfile) = hgweb_mod.getstyle(wsgireq.req, config,
   self.templatepath)
 if style == styles[0]:
 vars['style'] = style
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -53,9 +53,8 @@
 ))
 
 def getstyle(req, configfn, templatepath):
-fromreq = req.form.get('style', [None])[0]
 styles = (
-fromreq,
+req.qsparams.get('style', None),
 configfn('web', 'style'),
 'paper',
 )
@@ -160,7 +159,7 @@
 # figure out which style to use
 
 vars = {}
-styles, (style, mapfile) = getstyle(wsgireq, self.config,
+styles, (style, mapfile) = getstyle(wsgireq.req, self.config,
 self.templatepath)
 if style == styles[0]:
 vars['style'] = style
@@ -337,7 +336,7 @@
 cmd = args.pop(0)
 style = cmd.rfind('-')
 if style != -1:
-wsgireq.form['style'] = [cmd[:style]]
+req.qsparams['style'] = cmd[:style]
 cmd = cmd[style + 1:]
 
 # avoid accepting e.g. style parameter as command
@@ -355,7 +354,7 @@
 
 ua = req.headers.get('User-Agent', '')
 if cmd == 'rev' and 'mercurial' in ua:
-wsgireq.form['style'] = ['raw']
+req.qsparams['style'] = 'raw'
 
 if cmd == 'archive':
 fn = wsgireq.form['node'][0]
@@ -389,7 +388,7 @@
 if cmd not in webcommands.__all__:
 msg = 'no such method: %s' % cmd
 raise ErrorResponse(HTTP_BAD_REQUEST, msg)
-elif cmd == 'file' and 'raw' in wsgireq.form.get('style', []):
+elif cmd == 'file' and req.qsparams.get('style') == 'raw':
 rctx.ctype = ctype
 content = webcommands.rawfile(rctx, wsgireq, tmpl)
 else:



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2775: hgweb: create dedicated type for WSGI responses

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGa88d68dc3ee8: hgweb: create dedicated type for WSGI 
responses (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2775?vs=6837=6914

REVISION DETAIL
  https://phab.mercurial-scm.org/D2775

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py
  mercurial/wireprotoserver.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -149,18 +149,18 @@
 def iscmd(cmd):
 return cmd in wireproto.commands
 
-def handlewsgirequest(rctx, wsgireq, req, checkperm):
+def handlewsgirequest(rctx, wsgireq, req, res, checkperm):
 """Possibly process a wire protocol request.
 
 If the current request is a wire protocol request, the request is
 processed by this function.
 
 ``wsgireq`` is a ``wsgirequest`` instance.
 ``req`` is a ``parsedrequest`` instance.
+``res`` is a ``wsgiresponse`` instance.
 
-Returns a 2-tuple of (bool, response) where the 1st element indicates
-whether the request was handled and the 2nd element is a return
-value for a WSGI application (often a generator of bytes).
+Returns a bool indicating if the request was serviced. If set, the caller
+should stop processing the request, as a response has already been issued.
 """
 # Avoid cycle involving hg module.
 from .hgweb import common as hgwebcommon
@@ -171,7 +171,7 @@
 # string parameter. If it isn't present, this isn't a wire protocol
 # request.
 if 'cmd' not in req.querystringdict:
-return False, None
+return False
 
 cmd = req.querystringdict['cmd'][0]
 
@@ -183,18 +183,19 @@
 # known wire protocol commands and it is less confusing for machine
 # clients.
 if not iscmd(cmd):
-return False, None
+return False
 
 # The "cmd" query string argument is only valid on the root path of the
 # repo. e.g. ``/?cmd=foo``, ``/repo?cmd=foo``. URL paths within the repo
 # like ``/blah?cmd=foo`` are not allowed. So don't recognize the request
 # in this case. We send an HTTP 404 for backwards compatibility reasons.
 if req.dispatchpath:
-res = _handlehttperror(
-hgwebcommon.ErrorResponse(hgwebcommon.HTTP_NOT_FOUND), wsgireq,
-req)
-
-return True, res
+res.status = hgwebcommon.statusmessage(404)
+res.headers['Content-Type'] = HGTYPE
+# TODO This is not a good response to issue for this request. This
+# is mostly for BC for now.
+res.setbodybytes('0\n%s\n' % b'Not Found')
+return True
 
 proto = httpv1protocolhandler(wsgireq, req, repo.ui,
   lambda perm: checkperm(rctx, wsgireq, perm))
@@ -204,11 +205,16 @@
 # exception here. So consider refactoring into a exception type that
 # is associated with the wire protocol.
 try:
-res = _callhttp(repo, wsgireq, req, proto, cmd)
+_callhttp(repo, wsgireq, req, res, proto, cmd)
 except hgwebcommon.ErrorResponse as e:
-res = _handlehttperror(e, wsgireq, req)
+for k, v in e.headers:
+res.headers[k] = v
+res.status = hgwebcommon.statusmessage(e.code, pycompat.bytestr(e))
+# TODO This response body assumes the failed command was
+# "unbundle." That assumption is not always valid.
+res.setbodybytes('0\n%s\n' % pycompat.bytestr(e))
 
-return True, res
+return True
 
 def _httpresponsetype(ui, req, prefer_uncompressed):
 """Determine the appropriate response type and compression settings.
@@ -250,7 +256,10 @@
 opts = {'level': ui.configint('server', 'zliblevel')}
 return HGTYPE, util.compengines['zlib'], opts
 
-def _callhttp(repo, wsgireq, req, proto, cmd):
+def _callhttp(repo, wsgireq, req, res, proto, cmd):
+# Avoid cycle involving hg module.
+from .hgweb import common as hgwebcommon
+
 def genversion2(gen, engine, engineopts):
 # application/mercurial-0.2 always sends a payload header
 # identifying the compression engine.
@@ -262,26 +271,35 @@
 for chunk in gen:
 yield chunk
 
+def setresponse(code, contenttype, bodybytes=None, bodygen=None):
+if code == HTTP_OK:
+res.status = '200 Script output follows'
+else:
+res.status = hgwebcommon.statusmessage(code)
+
+res.headers['Content-Type'] = contenttype
+
+if bodybytes is not None:
+res.setbodybytes(bodybytes)
+if bodygen is not None:
+res.setbodygen(bodygen)
+
 if not wireproto.commands.commandavailable(cmd, proto):
-wsgireq.respond(HTTP_OK, HGERRTYPE,
-body=_('requested wire protocol command is not '
-   'available 

D2782: hgweb: remove wsgirequest.form (API)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGcf69df7ea385: hgweb: remove wsgirequest.form (API) 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2782?vs=6843=6918

REVISION DETAIL
  https://phab.mercurial-scm.org/D2782

AFFECTED FILES
  mercurial/hgweb/hgweb_mod.py
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -426,7 +426,6 @@
 self.run_once = wsgienv[r'wsgi.run_once']
 self.env = wsgienv
 self.req = parserequestfromenv(wsgienv, inp)
-self.form = self.req.qsparams.asdictoflists()
 self.res = wsgiresponse(self.req, start_response)
 self._start_response = start_response
 self.server_write = None
diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
--- a/mercurial/hgweb/hgweb_mod.py
+++ b/mercurial/hgweb/hgweb_mod.py
@@ -341,19 +341,15 @@
 
 # avoid accepting e.g. style parameter as command
 if util.safehasattr(webcommands, cmd):
-wsgireq.form['cmd'] = [cmd]
 req.qsparams['cmd'] = cmd
 
 if cmd == 'static':
-wsgireq.form['file'] = ['/'.join(args)]
 req.qsparams['file'] = '/'.join(args)
 else:
 if args and args[0]:
 node = args.pop(0).replace('%2F', '/')
-wsgireq.form['node'] = [node]
 req.qsparams['node'] = node
 if args:
-wsgireq.form['file'] = args
 if 'file' in req.qsparams:
 del req.qsparams['file']
 for a in args:
@@ -368,9 +364,7 @@
 for type_, spec in rctx.archivespecs.iteritems():
 ext = spec[2]
 if fn.endswith(ext):
-wsgireq.form['node'] = [fn[:-len(ext)]]
 req.qsparams['node'] = fn[:-len(ext)]
-wsgireq.form['type'] = [type_]
 req.qsparams['type'] = type_
 else:
 cmd = req.qsparams.get('cmd', '')
@@ -387,7 +381,6 @@
 self.check_perm(rctx, wsgireq, None)
 
 if cmd == '':
-wsgireq.form['cmd'] = [tmpl.cache['default']]
 req.qsparams['cmd'] = tmpl.cache['default']
 cmd = req.qsparams['cmd']
 



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2783: hgweb: expose URL scheme and REMOTE_* attributes

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGa755fd3b7146: hgweb: expose URL scheme and REMOTE_* 
attributes (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2783?vs=6844=6919

REVISION DETAIL
  https://phab.mercurial-scm.org/D2783

AFFECTED FILES
  mercurial/hgweb/request.py
  mercurial/wireprotoserver.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -53,8 +53,7 @@
 return ''.join(chunks)
 
 class httpv1protocolhandler(wireprototypes.baseprotocolhandler):
-def __init__(self, wsgireq, req, ui, checkperm):
-self._wsgireq = wsgireq
+def __init__(self, req, ui, checkperm):
 self._req = req
 self._ui = ui
 self._checkperm = checkperm
@@ -117,9 +116,9 @@
 
 def client(self):
 return 'remote:%s:%s:%s' % (
-self._wsgireq.env.get('wsgi.url_scheme') or 'http',
-urlreq.quote(self._wsgireq.env.get('REMOTE_HOST', '')),
-urlreq.quote(self._wsgireq.env.get('REMOTE_USER', '')))
+self._req.urlscheme,
+urlreq.quote(self._req.remotehost or ''),
+urlreq.quote(self._req.remoteuser or ''))
 
 def addcapabilities(self, repo, caps):
 caps.append('httpheader=%d' %
@@ -197,15 +196,15 @@
 res.setbodybytes('0\n%s\n' % b'Not Found')
 return True
 
-proto = httpv1protocolhandler(wsgireq, req, repo.ui,
+proto = httpv1protocolhandler(req, repo.ui,
   lambda perm: checkperm(rctx, wsgireq, perm))
 
 # The permissions checker should be the only thing that can raise an
 # ErrorResponse. It is kind of a layer violation to catch an hgweb
 # exception here. So consider refactoring into a exception type that
 # is associated with the wire protocol.
 try:
-_callhttp(repo, wsgireq, req, res, proto, cmd)
+_callhttp(repo, req, res, proto, cmd)
 except hgwebcommon.ErrorResponse as e:
 for k, v in e.headers:
 res.headers[k] = v
@@ -256,7 +255,7 @@
 opts = {'level': ui.configint('server', 'zliblevel')}
 return HGTYPE, util.compengines['zlib'], opts
 
-def _callhttp(repo, wsgireq, req, res, proto, cmd):
+def _callhttp(repo, req, res, proto, cmd):
 # Avoid cycle involving hg module.
 from .hgweb import common as hgwebcommon
 
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -129,6 +129,12 @@
 # of HTTP: Host header for hostname. This is likely what clients used.
 advertisedurl = attr.ib()
 advertisedbaseurl = attr.ib()
+# URL scheme (part before ``://``). e.g. ``http`` or ``https``.
+urlscheme = attr.ib()
+# Value of REMOTE_USER, if set, or None.
+remoteuser = attr.ib()
+# Value of REMOTE_HOST, if set, or None.
+remotehost = attr.ib()
 # WSGI application path.
 apppath = attr.ib()
 # List of path parts to be used for dispatch.
@@ -270,6 +276,9 @@
  url=fullurl, baseurl=baseurl,
  advertisedurl=advertisedfullurl,
  advertisedbaseurl=advertisedbaseurl,
+ urlscheme=env['wsgi.url_scheme'],
+ remoteuser=env.get('REMOTE_USER'),
+ remotehost=env.get('REMOTE_HOST'),
  apppath=apppath,
  dispatchparts=dispatchparts, 
dispatchpath=dispatchpath,
  havepathinfo='PATH_INFO' in env,



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2745: hgweb: store and use request method on parsed request

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG16292bbda39c: hgweb: store and use request method on parsed 
request (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2745?vs=6825=6900

REVISION DETAIL
  https://phab.mercurial-scm.org/D2745

AFFECTED FILES
  mercurial/hgweb/request.py
  mercurial/wireprotoserver.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -324,7 +324,7 @@
 # the HTTP response. In other words, it helps prevent deadlocks
 # on clients using httplib.
 
-if (wsgireq.env[r'REQUEST_METHOD'] == r'POST' and
+if (req.method == 'POST' and
 # But not if Expect: 100-continue is being used.
 (req.headers.get('Expect', '').lower() != '100-continue')):
 wsgireq.drain()
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -63,6 +63,8 @@
 class parsedrequest(object):
 """Represents a parsed WSGI request / static HTTP request parameters."""
 
+# Request method.
+method = attr.ib()
 # Full URL for this request.
 url = attr.ib()
 # URL without any path components. Just ://.
@@ -207,7 +209,8 @@
 if 'CONTENT_LENGTH' in env and 'HTTP_CONTENT_LENGTH' not in env:
 headers['Content-Length'] = env['CONTENT_LENGTH']
 
-return parsedrequest(url=fullurl, baseurl=baseurl,
+return parsedrequest(method=env['REQUEST_METHOD'],
+ url=fullurl, baseurl=baseurl,
  advertisedurl=advertisedfullurl,
  advertisedbaseurl=advertisedbaseurl,
  apppath=apppath,



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2769: hgweb: refactor the request draining code

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG2cdf47e14c30: hgweb: refactor the request draining code 
(authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2769?vs=6831=6907

REVISION DETAIL
  https://phab.mercurial-scm.org/D2769

AFFECTED FILES
  mercurial/hgweb/request.py
  mercurial/wireprotoserver.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -301,9 +301,6 @@
 wsgireq.respond(HTTP_OK, HGTYPE, body=rsp)
 return []
 elif isinstance(rsp, wireprototypes.pusherr):
-# This is the httplib workaround documented in _handlehttperror().
-wsgireq.drain()
-
 rsp = '0\n%s\n' % rsp.res
 wsgireq.respond(HTTP_OK, HGTYPE, body=rsp)
 return []
@@ -316,21 +313,6 @@
 def _handlehttperror(e, wsgireq, req):
 """Called when an ErrorResponse is raised during HTTP request 
processing."""
 
-# Clients using Python's httplib are stateful: the HTTP client
-# won't process an HTTP response until all request data is
-# sent to the server. The intent of this code is to ensure
-# we always read HTTP request data from the client, thus
-# ensuring httplib transitions to a state that allows it to read
-# the HTTP response. In other words, it helps prevent deadlocks
-# on clients using httplib.
-
-if (req.method == 'POST' and
-# But not if Expect: 100-continue is being used.
-(req.headers.get('Expect', '').lower() != '100-continue')):
-wsgireq.drain()
-else:
-wsgireq.headers.append((r'Connection', r'Close'))
-
 # TODO This response body assumes the failed command was
 # "unbundle." That assumption is not always valid.
 wsgireq.respond(e, HGTYPE, body='0\n%s\n' % pycompat.bytestr(e))
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -254,12 +254,6 @@
 self.server_write = None
 self.headers = []
 
-def drain(self):
-'''need to read all data from request, httplib is half-duplex'''
-length = int(self.env.get('CONTENT_LENGTH') or 0)
-for s in util.filechunkiter(self.inp, limit=length):
-pass
-
 def respond(self, status, type, filename=None, body=None):
 if not isinstance(type, str):
 type = pycompat.sysstr(type)
@@ -292,6 +286,53 @@
 elif isinstance(status, int):
 status = statusmessage(status)
 
+# Various HTTP clients (notably httplib) won't read the HTTP
+# response until the HTTP request has been sent in full. If servers
+# (us) send a response before the HTTP request has been fully sent,
+# the connection may deadlock because neither end is reading.
+#
+# We work around this by "draining" the request data before
+# sending any response in some conditions.
+drain = False
+close = False
+
+# If the client sent Expect: 100-continue, we assume it is smart
+# enough to deal with the server sending a response before reading
+# the request. (httplib doesn't do this.)
+if self.env.get(r'HTTP_EXPECT', r'').lower() == r'100-continue':
+pass
+# Only tend to request methods that have bodies. Strictly speaking,
+# we should sniff for a body. But this is fine for our existing
+# WSGI applications.
+elif self.env[r'REQUEST_METHOD'] not in (r'POST', r'PUT'):
+pass
+else:
+# If we don't know how much data to read, there's no guarantee
+# that we can drain the request responsibly. The WSGI
+# specification only says that servers *should* ensure the
+# input stream doesn't overrun the actual request. So there's
+# no guarantee that reading until EOF won't corrupt the stream
+# state.
+if not isinstance(self.inp, util.cappedreader):
+close = True
+else:
+# We /could/ only drain certain HTTP response codes. But 
200
+# and non-200 wire protocol responses both require 
draining.
+# Since we have a capped reader in place for all situations
+# where we drain, it is safe to read from that stream. 
We'll
+# either do a drain or no-op if we're already at EOF.
+drain = True
+
+if close:
+self.headers.append((r'Connection', r'Close'))
+
+if drain:
+assert isinstance(self.inp, util.cappedreader)
+while True:
+  

D2748: hgweb: remove wsgirequest.read()

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGd6cd1451212e: hgweb: remove wsgirequest.read() (authored by 
indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2748?vs=6828=6903

REVISION DETAIL
  https://phab.mercurial-scm.org/D2748

AFFECTED FILES
  mercurial/hgweb/request.py
  mercurial/wireprotoserver.py

CHANGE DETAILS

diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -83,7 +83,7 @@
 postlen = int(self._req.headers.get(b'X-HgArgs-Post', 0))
 if postlen:
 args.update(urlreq.parseqs(
-self._wsgireq.read(postlen), keep_blank_values=True))
+self._wsgireq.inp.read(postlen), keep_blank_values=True))
 return args
 
 argvalue = decodevaluefromheaders(self._req, b'X-HgArg')
@@ -97,7 +97,7 @@
 # If httppostargs is used, we need to read Content-Length
 # minus the amount that was consumed by args.
 length -= int(self._req.headers.get(b'X-HgArgs-Post', 0))
-for s in util.filechunkiter(self._wsgireq, limit=length):
+for s in util.filechunkiter(self._wsgireq.inp, limit=length):
 fp.write(s)
 
 @contextlib.contextmanager
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -249,9 +249,6 @@
 def __iter__(self):
 return iter([])
 
-def read(self, count=-1):
-return self.inp.read(count)
-
 def drain(self):
 '''need to read all data from request, httplib is half-duplex'''
 length = int(self.env.get('CONTENT_LENGTH') or 0)



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2778: tests: add test for a wire protocol request to wrong base URL

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG2859c6fa4fc2: tests: add test for a wire protocol request 
to wrong base URL (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2778?vs=6836=6912

REVISION DETAIL
  https://phab.mercurial-scm.org/D2778

AFFECTED FILES
  tests/test-hgweb-commands.t

CHANGE DETAILS

diff --git a/tests/test-hgweb-commands.t b/tests/test-hgweb-commands.t
--- a/tests/test-hgweb-commands.t
+++ b/tests/test-hgweb-commands.t
@@ -1916,6 +1916,19 @@
   
   lookup branchmap pushkey known getbundle unbundlehash batch 
changegroupsubset $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN 
httpheader=1024 httpmediatype=0.1rx,0.1tx,0.2tx 
compression=$BUNDLE2_COMPRESSIONS$
 
+wire protocol command to wrong base URL
+
+  $ get-with-headers.py $LOCALIP:$HGPORT 'foo?cmd=capabilities' -
+  404 Not Found
+  content-length: 12
+  content-type: application/mercurial-0.1
+  date: * (glob)
+  server: * (glob)
+  
+  0
+  Not Found
+  [1]
+
 heads
 
   $ get-with-headers.py $LOCALIP:$HGPORT '?cmd=heads'



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2749: hgweb: remove wsgirequest.__iter__

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGe3f809e0fe8e: hgweb: remove wsgirequest.__iter__ (authored 
by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2749?vs=6829=6904

REVISION DETAIL
  https://phab.mercurial-scm.org/D2749

AFFECTED FILES
  mercurial/hgweb/request.py

CHANGE DETAILS

diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -246,9 +246,6 @@
 self.server_write = None
 self.headers = []
 
-def __iter__(self):
-return iter([])
-
 def drain(self):
 '''need to read all data from request, httplib is half-duplex'''
 length = int(self.env.get('CONTENT_LENGTH') or 0)



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


D2773: hgweb: remove support for short query string based aliases (BC)

2018-03-12 Thread indygreg (Gregory Szorc)
This revision was automatically updated to reflect the committed changes.
Closed by commit rHG422be99519e5: hgweb: remove support for short query string 
based aliases (BC) (authored by indygreg, committed by ).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2773?vs=6835=6911

REVISION DETAIL
  https://phab.mercurial-scm.org/D2773

AFFECTED FILES
  mercurial/hgweb/request.py
  tests/test-hgweb-raw.t

CHANGE DETAILS

diff --git a/tests/test-hgweb-raw.t b/tests/test-hgweb-raw.t
--- a/tests/test-hgweb-raw.t
+++ b/tests/test-hgweb-raw.t
@@ -17,7 +17,7 @@
   $ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid
 
   $ cat hg.pid >> $DAEMON_PIDS
-  $ (get-with-headers.py localhost:$HGPORT 
'?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type 
content-length content-disposition) >getoutput.txt
+  $ (get-with-headers.py localhost:$HGPORT 
'raw-file/bf0ff59095c9/sub/some%20text%25.txt' content-type content-length 
content-disposition) >getoutput.txt
 
   $ killdaemons.py hg.pid
 
@@ -32,14 +32,14 @@
   It is very boring to read, but computers don't
   care about things like that.
   $ cat access.log error.log
-  $LOCALIP - - [*] "GET /?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw 
HTTP/1.1" 200 - (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /raw-file/bf0ff59095c9/sub/some%20text%25.txt 
HTTP/1.1" 200 - (glob)
 
   $ rm access.log error.log
   $ hg serve -p $HGPORT -A access.log -E error.log -d --pid-file=hg.pid \
   > --config web.guessmime=True
 
   $ cat hg.pid >> $DAEMON_PIDS
-  $ (get-with-headers.py localhost:$HGPORT 
'?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw' content-type 
content-length content-disposition) >getoutput.txt
+  $ (get-with-headers.py localhost:$HGPORT 
'raw-file/bf0ff59095c9/sub/some%20text%25.txt' content-type content-length 
content-disposition) >getoutput.txt
   $ killdaemons.py hg.pid
 
   $ cat getoutput.txt
@@ -53,6 +53,6 @@
   It is very boring to read, but computers don't
   care about things like that.
   $ cat access.log error.log
-  $LOCALIP - - [*] "GET /?f=bf0ff59095c9;file=sub/some%20text%25.txt;style=raw 
HTTP/1.1" 200 - (glob)
+  $LOCALIP - - [$LOGDATE$] "GET /raw-file/bf0ff59095c9/sub/some%20text%25.txt 
HTTP/1.1" 200 - (glob)
 
   $ cd ..
diff --git a/mercurial/hgweb/request.py b/mercurial/hgweb/request.py
--- a/mercurial/hgweb/request.py
+++ b/mercurial/hgweb/request.py
@@ -27,37 +27,6 @@
 util,
 )
 
-shortcuts = {
-'cl': [('cmd', ['changelog']), ('rev', None)],
-'sl': [('cmd', ['shortlog']), ('rev', None)],
-'cs': [('cmd', ['changeset']), ('node', None)],
-'f': [('cmd', ['file']), ('filenode', None)],
-'fl': [('cmd', ['filelog']), ('filenode', None)],
-'fd': [('cmd', ['filediff']), ('node', None)],
-'fa': [('cmd', ['annotate']), ('filenode', None)],
-'mf': [('cmd', ['manifest']), ('manifest', None)],
-'ca': [('cmd', ['archive']), ('node', None)],
-'tags': [('cmd', ['tags'])],
-'tip': [('cmd', ['changeset']), ('node', ['tip'])],
-'static': [('cmd', ['static']), ('file', None)]
-}
-
-def normalize(form):
-# first expand the shortcuts
-for k in shortcuts:
-if k in form:
-for name, value in shortcuts[k]:
-if value is None:
-value = form[k]
-form[name] = value
-del form[k]
-# And strip the values
-bytesform = {}
-for k, v in form.iteritems():
-bytesform[pycompat.bytesurl(k)] = [
-pycompat.bytesurl(i.strip()) for i in v]
-return bytesform
-
 @attr.s(frozen=True)
 class parsedrequest(object):
 """Represents a parsed WSGI request.
@@ -258,7 +227,7 @@
 self.run_once = wsgienv[r'wsgi.run_once']
 self.env = wsgienv
 self.req = parserequestfromenv(wsgienv, inp)
-self.form = normalize(self.req.querystringdict)
+self.form = self.req.querystringdict
 self._start_response = start_response
 self.server_write = None
 self.headers = []



To: indygreg, #hg-reviewers, durin42
Cc: mercurial-devel
___
Mercurial-devel mailing list
Mercurial-devel@mercurial-scm.org
https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel


  1   2   >