D2729: copyfile: preserve stat info (mtime, etc.) when doing copies/renames
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
# 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
# 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
# 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
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
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
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
On Mon, 12 Mar 2018 21:55:26 -0400, Jun Wuwrote: 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
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
# 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
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
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
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
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
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)
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
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
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
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
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
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
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
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
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
55 new changesets in mercurial: https://www.mercurial-scm.org/repo/hg/rev/09b58af83d44 changeset: 36789:09b58af83d44 user:Augie Facklerdate: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
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
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
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()
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
On Sun, 11 Mar 2018 05:49:26 -0400, Sushil khanchiwrote: # 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
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)
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()
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
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
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)
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
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
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
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
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
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
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
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
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
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()
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
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`
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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)
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
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)
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)
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
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
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
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
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
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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)
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
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
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
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()
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
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__
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)
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