D2850: wireproto: define content negotiation for HTTPv2

indygreg (Gregory Szorc) phabricator at mercurial-scm.org
Tue Mar 13 22:49:21 EDT 2018


indygreg updated this revision to Diff 7016.

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D2850?vs=7013&id=7016

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

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,4 +1,5 @@
   $ HTTPV2=exp-http-v2-0001
+  $ MEDIATYPE=application/mercurial-tbd
 
   $ send() {
   >   hg --verbose debugwireproto --peer raw http://$LOCALIP:$HGPORT/
@@ -45,27 +46,6 @@
   $ hg -R server serve -p $HGPORT -d --pid-file hg.pid
   $ cat hg.pid > $DAEMON_PIDS
 
-Request to read-only command works out of the box
-
-  $ send << EOF
-  > httprequest POST api/$HTTPV2/ro/capabilities
-  >     user-agent: test
-  > EOF
-  using raw connection to peer
-  s>     POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
-  s>     Accept-Encoding: identity\r\n
-  s>     user-agent: test\r\n
-  s>     host: $LOCALIP:$HGPORT\r\n (glob)
-  s>     \r\n
-  s> makefile('rb', None)
-  s>     HTTP/1.1 200 OK\r\n
-  s>     Server: testing stub value\r\n
-  s>     Date: $HTTP_DATE$\r\n
-  s>     Content-Type: text/plain\r\n
-  s>     Content-Length: 16\r\n
-  s>     \r\n
-  s>     ro/capabilities\n
-
 Request to unknown command yields 404
 
   $ send << EOF
@@ -108,6 +88,100 @@
   s>     \r\n
   s>     commands require POST requests
 
+Missing Accept header results in 406
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/capabilities
+  >     user-agent: test
+  > EOF
+  using raw connection to peer
+  s>     POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
+  s>     Accept-Encoding: identity\r\n
+  s>     user-agent: test\r\n
+  s>     host: $LOCALIP:$HGPORT\r\n (glob)
+  s>     \r\n
+  s> makefile('rb', None)
+  s>     HTTP/1.1 406 Not Acceptable\r\n
+  s>     Server: testing stub value\r\n
+  s>     Date: $HTTP_DATE$\r\n
+  s>     Content-Type: text/plain\r\n
+  s>     Content-Length: 72\r\n
+  s>     \r\n
+  s>     client MUST specify Accept header with value: application/mercurial-tbd\n
+
+Bad Accept header results in 406
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/capabilities
+  >     accept: invalid
+  >     user-agent: test
+  > EOF
+  using raw connection to peer
+  s>     POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
+  s>     Accept-Encoding: identity\r\n
+  s>     accept: invalid\r\n
+  s>     user-agent: test\r\n
+  s>     host: $LOCALIP:$HGPORT\r\n (glob)
+  s>     \r\n
+  s> makefile('rb', None)
+  s>     HTTP/1.1 406 Not Acceptable\r\n
+  s>     Server: testing stub value\r\n
+  s>     Date: $HTTP_DATE$\r\n
+  s>     Content-Type: text/plain\r\n
+  s>     Content-Length: 72\r\n
+  s>     \r\n
+  s>     client MUST specify Accept header with value: application/mercurial-tbd\n
+
+Bad Content-Type header results in 415
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/capabilities
+  >     accept: $MEDIATYPE
+  >     user-agent: test
+  >     content-type: badmedia
+  > EOF
+  using raw connection to peer
+  s>     POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
+  s>     Accept-Encoding: identity\r\n
+  s>     accept: application/mercurial-tbd\r\n
+  s>     content-type: badmedia\r\n
+  s>     user-agent: test\r\n
+  s>     host: $LOCALIP:$HGPORT\r\n (glob)
+  s>     \r\n
+  s> makefile('rb', None)
+  s>     HTTP/1.1 415 Unsupported Media Type\r\n
+  s>     Server: testing stub value\r\n
+  s>     Date: $HTTP_DATE$\r\n
+  s>     Content-Type: text/plain\r\n
+  s>     Content-Length: 75\r\n
+  s>     \r\n
+  s>     client MUST send Content-Type header with value: application/mercurial-tbd\n
+
+Request to read-only command works out of the box
+
+  $ send << EOF
+  > httprequest POST api/$HTTPV2/ro/capabilities
+  >     accept: $MEDIATYPE
+  >     content-type: $MEDIATYPE
+  >     user-agent: test
+  > EOF
+  using raw connection to peer
+  s>     POST /api/exp-http-v2-0001/ro/capabilities HTTP/1.1\r\n
+  s>     Accept-Encoding: identity\r\n
+  s>     accept: application/mercurial-tbd\r\n
+  s>     content-type: application/mercurial-tbd\r\n
+  s>     user-agent: test\r\n
+  s>     host: $LOCALIP:$HGPORT\r\n (glob)
+  s>     \r\n
+  s> makefile('rb', None)
+  s>     HTTP/1.1 200 OK\r\n
+  s>     Server: testing stub value\r\n
+  s>     Date: $HTTP_DATE$\r\n
+  s>     Content-Type: text/plain\r\n
+  s>     Content-Length: 16\r\n
+  s>     \r\n
+  s>     ro/capabilities\n
+
 Request to read-write command fails because server is read-only by default
 
 GET to read-write request yields 405
@@ -192,10 +266,14 @@
   $ send << EOF
   > httprequest POST api/$HTTPV2/rw/capabilities
   >     user-agent: test
+  >     accept: $MEDIATYPE
+  >     content-type: $MEDIATYPE
   > EOF
   using raw connection to peer
   s>     POST /api/exp-http-v2-0001/rw/capabilities HTTP/1.1\r\n
   s>     Accept-Encoding: identity\r\n
+  s>     accept: application/mercurial-tbd\r\n
+  s>     content-type: application/mercurial-tbd\r\n
   s>     user-agent: test\r\n
   s>     host: $LOCALIP:$HGPORT\r\n (glob)
   s>     \r\n
@@ -213,10 +291,12 @@
   $ send << EOF
   > httprequest POST api/$HTTPV2/rw/badcommand
   >     user-agent: test
+  >     accept: $MEDIATYPE
   > EOF
   using raw connection to peer
   s>     POST /api/exp-http-v2-0001/rw/badcommand HTTP/1.1\r\n
   s>     Accept-Encoding: identity\r\n
+  s>     accept: application/mercurial-tbd\r\n
   s>     user-agent: test\r\n
   s>     host: $LOCALIP:$HGPORT\r\n (glob)
   s>     \r\n
diff --git a/mercurial/wireprotoserver.py b/mercurial/wireprotoserver.py
--- a/mercurial/wireprotoserver.py
+++ b/mercurial/wireprotoserver.py
@@ -32,6 +32,7 @@
 HGTYPE = 'application/mercurial-0.1'
 HGTYPE2 = 'application/mercurial-0.2'
 HGERRTYPE = 'application/hg-error'
+HTTPV2TYPE = 'application/mercurial-tbd'
 
 HTTPV2 = wireprototypes.HTTPV2
 SSHV1 = wireprototypes.SSHV1
@@ -335,6 +336,23 @@
         res.setbodybytes(_('invalid wire protocol command: %s') % command)
         return
 
+    if req.headers.get(b'Accept') != HTTPV2TYPE:
+        res.status = b'406 Not Acceptable'
+        res.headers[b'Content-Type'] = b'text/plain'
+        res.setbodybytes(_('client MUST specify Accept header with value: %s\n')
+                           % HTTPV2TYPE)
+        return
+
+    if (b'Content-Type' in req.headers
+        and req.headers[b'Content-Type'] != HTTPV2TYPE):
+        res.status = b'415 Unsupported Media Type'
+        # TODO we should send a response with appropriate media type,
+        # since client does Accept it.
+        res.headers[b'Content-Type'] = b'text/plain'
+        res.setbodybytes(_('client MUST send Content-Type header with '
+                           'value: %s\n') % HTTPV2TYPE)
+        return
+
     # We don't do anything meaningful yet.
     res.status = b'200 OK'
     res.headers[b'Content-Type'] = b'text/plain'
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
@@ -187,6 +187,19 @@
 Requests to unknown commands or URLS result in an HTTP 404.
 TODO formally define response type, how error is communicated, etc.
 
+HTTP request and response bodies use the *TBD Protocol* for media exchange.
+
+Clients and servers MUST advertise the ``TBD`` media type via the
+``Content-Type`` request and response headers. In addition, clients MUST
+advertise this media type value in their ``Accept`` request header in all
+requests.
+
+Servers receiving requests without an ``Accept`` header SHOULD respond with
+an HTTP 406.
+
+Servers receiving requests with an invalid ``Content-Type`` header SHOULD
+respond with an HTTP 415.
+
 SSH Protocol
 ============
 



To: indygreg, #hg-reviewers
Cc: mercurial-devel


More information about the Mercurial-devel mailing list