GNUnet  0.19.2
httpdomain.py
Go to the documentation of this file.
1 """
2  sphinxcontrib.httpdomain
3  ~~~~~~~~~~~~~~~~~~~~~~~~
4 
5  The HTTP domain for documenting RESTful HTTP APIs.
6 
7  :copyright: Copyright 2011 by Hong Minhee
8  :license: BSD, see LICENSE for details.
9 
10 """
11 
12 import re
13 
14 from docutils import nodes
15 
16 from pygments.lexer import RegexLexer, bygroups
17 from pygments.lexers import get_lexer_by_name
18 from pygments.token import Literal, Text, Operator, Keyword, Name, Number
19 from pygments.util import ClassNotFound
20 
21 from sphinx import addnodes
22 from sphinx.roles import XRefRole
23 from sphinx.domains import Domain, ObjType, Index
24 from sphinx.directives import ObjectDescription, directives
25 from sphinx.util.nodes import make_refnode
26 from sphinx.util.docfields import GroupedField, TypedField
27 
28 # The env.get_doctree() lookup results in a pickle.load() call which is
29 # expensive enough to dominate the runtime entirely when the number of endpoints
30 # and references is large enough. The doctrees are generated during the read-
31 # phase and we can cache their lookup during the write-phase significantly
32 # improving performance.
33 # Currently sphinxcontrib-httpdomain does not declare to support parallel read
34 # support (parallel_read_safe is the default False) so we can simply use a
35 # module global to hold the cache.
36 _doctree_cache = {}
37 
38 
39 class DocRef(object):
40  """Represents a reference to an abstract specification."""
41 
42  def __init__(self, base_url, anchor, section):
43  self.base_urlbase_url = base_url
44  self.anchoranchor = anchor
45  self.sectionsection = section
46 
47  def __repr__(self):
48  """Returns the URL onto related specification section for the related
49  object."""
50  return '{0}#{1}{2}'.format(self.base_urlbase_url, self.anchoranchor, self.sectionsection)
51 
52 
54  """Represents a reference to RFC2616.
55  In 2014, RFC2616 was replaced by multiple RFCs (7230-7237)."""
56 
57  def __init__(self, section):
58  url = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec{0:d}.html'
59  url = url.format(int(section))
60  super(RFC2616Ref, self).__init__(url, 'sec', section)
61 
62 
63 class IETFRef(DocRef):
64  """Represents a reference to the specific IETF RFC."""
65 
66  def __init__(self, rfc, section):
67  url = 'https://tools.ietf.org/html/rfc{0:d}'.format(rfc)
68  super(IETFRef, self).__init__(url, 'section-', section)
69 
70 
72 
73  def __init__(self, section):
74  url = 'http://www.w3.org/TR/eventsource/'
75  super(EventSourceRef, self).__init__(url, section, '')
76 
77 
78 class CORSRef(DocRef):
79  """Represents a reference to W3 Cross-Origin Resource Sharing recommendation."""
80 
81  def __init__(self, name, type):
82  url = 'http://www.w3.org/TR/cors/'
83  super(CORSRef, self).__init__(url, name, '-' + type)
84 
85 
86 #: Mapping from lowercase HTTP method name to :class:`DocRef` object which
87 #: maintains the URL which points to the section of the RFC which defines that
88 #: HTTP method.
89 METHOD_REFS = {
90  'patch': IETFRef(5789, 2),
91  'options': IETFRef(7231, '4.3.7'),
92  'get': IETFRef(7231, '4.3.1'),
93  'head': IETFRef(7231, '4.3.2'),
94  'post': IETFRef(7231, '4.3.3'),
95  'put': IETFRef(7231, '4.3.4'),
96  'delete': IETFRef(7231, '4.3.5'),
97  'trace': IETFRef(7231, '4.3.8'),
98  'connect': IETFRef(7231, '4.3.6'),
99  'copy': IETFRef(2518, 8.8),
100  'any': ''
101 }
102 
103 
104 #: Mapping from HTTP header name to :class:`DocRef` object which
105 #: maintains the URL which points to the related section of the RFC.
106 HEADER_REFS = {
107  'Accept': IETFRef(7231, '5.3.2'),
108  'Accept-Charset': IETFRef(7231, '5.3.3'),
109  'Accept-Encoding': IETFRef(7231, '5.3.4'),
110  'Accept-Language': IETFRef(7231, '5.3.5'),
111  'Accept-Ranges': IETFRef(7233, 2.3),
112  'Age': IETFRef(7234, 5.1),
113  'Allow': IETFRef(7231, '7.4.1'),
114  'Authorization': IETFRef(7235, 4.2),
115  'Cache-Control': IETFRef(7234, 5.2),
116  'Connection': IETFRef(7230, 6.1),
117  'Content-Encoding': IETFRef(7231, '3.1.2.2'),
118  'Content-Language': IETFRef(7231, '3.1.3.2'),
119  'Content-Length': IETFRef(7230, '3.3.2'),
120  'Content-Location': IETFRef(7231, '3.1.4.2'),
121  'Content-MD5': RFC2616Ref(14.15), # removed
122  'Content-Range': IETFRef(7233, 4.2),
123  'Content-Type': IETFRef(7231, '3.1.1.5'),
124  'Cookie': IETFRef(2109, '4.3.4'), # also RFC6265 section 5.4
125  'Date': IETFRef(7231, '7.1.1.2'),
126  'Destination': IETFRef(2518, 9.3),
127  'ETag': IETFRef(7232, 2.3),
128  'Expect': IETFRef(7231, '5.1.1'),
129  'Expires': IETFRef(7234, 5.3),
130  'From': IETFRef(7231, '5.5.2'),
131  'Host': IETFRef(7230, 5.4),
132  'If-Match': IETFRef(7232, 3.1),
133  'If-Modified-Since': IETFRef(7232, 3.3),
134  'If-None-Match': IETFRef(7232, 3.2),
135  'If-Range': IETFRef(7233, 3.2),
136  'If-Unmodified-Since': IETFRef(7232, 3.4),
137  'Last-Event-ID': EventSourceRef('last-event-id'),
138  'Last-Modified': IETFRef(7232, 2.2),
139  'Link': IETFRef(5988, '5'),
140  'Location': IETFRef(7231, '7.1.2'),
141  'Max-Forwards': IETFRef(7231, '5.1.2'),
142  'Pragma': IETFRef(7234, 5.4),
143  'Proxy-Authenticate': IETFRef(7235, 4.3),
144  'Proxy-Authorization': IETFRef(7235, 4.4),
145  'Range': IETFRef(7233, 3.1),
146  'Referer': IETFRef(7231, '5.5.2'),
147  'Retry-After': IETFRef(7231, '7.1.3'),
148  'Server': IETFRef(7231, '7.4.2'),
149  'Set-Cookie': IETFRef(2109, '4.2.2'),
150  'TE': IETFRef(7230, 4.3),
151  'Trailer': IETFRef(7230, 4.4),
152  'Transfer-Encoding': IETFRef(7230, '3.3.1'),
153  'Upgrade': IETFRef(7230, 6.7),
154  'User-Agent': IETFRef(7231, '5.5.3'),
155  'Vary': IETFRef(7231, '7.1.4'),
156  'Via': IETFRef(7230, '5.7.1'),
157  'Warning': IETFRef(7234, 5.5),
158  'WWW-Authenticate': IETFRef(7235, 4.1),
159  'Access-Control-Allow-Origin': CORSRef('access-control-allow-origin',
160  'response-header'),
161  'Access-Control-Allow-Credentials': CORSRef('access-control-allow-credentials',
162  'response-header'),
163  'Access-Control-Expose-Headers': CORSRef('access-control-expose-headers',
164  'response-header'),
165  'Access-Control-Max-Age': CORSRef('access-control-max-age',
166  'response-header'),
167  'Access-Control-Allow-Methods': CORSRef('access-control-allow-methods',
168  'response-header'),
169  'Access-Control-Allow-Headers': CORSRef('access-control-allow-headers',
170  'response-header'),
171  'Origin': CORSRef('origin', 'request-header'),
172  'Access-Control-Request-Method': CORSRef('access-control-request-method',
173  'response-header'),
174  'Access-Control-Request-Headers': CORSRef('access-control-request-headers',
175  'response-header'),
176 }
177 
178 
179 HTTP_STATUS_CODES = {
180  100: 'Continue',
181  101: 'Switching Protocols',
182  102: 'Processing',
183  200: 'OK',
184  201: 'Created',
185  202: 'Accepted',
186  203: 'Non Authoritative Information',
187  204: 'No Content',
188  205: 'Reset Content',
189  206: 'Partial Content',
190  207: 'Multi Status',
191  226: 'IM Used', # see RFC 3229
192  300: 'Multiple Choices',
193  301: 'Moved Permanently',
194  302: 'Found',
195  303: 'See Other',
196  304: 'Not Modified',
197  305: 'Use Proxy',
198  307: 'Temporary Redirect',
199  308: 'Permanent Redirect',
200  400: 'Bad Request',
201  401: 'Unauthorized',
202  402: 'Payment Required', # unused
203  403: 'Forbidden',
204  404: 'Not Found',
205  405: 'Method Not Allowed',
206  406: 'Not Acceptable',
207  407: 'Proxy Authentication Required',
208  408: 'Request Timeout',
209  409: 'Conflict',
210  410: 'Gone',
211  411: 'Length Required',
212  412: 'Precondition Failed',
213  413: 'Request Entity Too Large',
214  414: 'Request URI Too Long',
215  415: 'Unsupported Media Type',
216  416: 'Requested Range Not Satisfiable',
217  417: 'Expectation Failed',
218  418: "I'm a teapot", # see RFC 2324
219  422: 'Unprocessable Entity',
220  423: 'Locked',
221  424: 'Failed Dependency',
222  425: 'Too Early', # RFC 8470
223  426: 'Upgrade Required',
224  429: 'Too Many Requests',
225  449: 'Retry With', # proprietary MS extension
226  451: 'Unavailable For Legal Reasons',
227  500: 'Internal Server Error',
228  501: 'Not Implemented',
229  502: 'Bad Gateway',
230  503: 'Service Unavailable',
231  504: 'Gateway Timeout',
232  505: 'HTTP Version Not Supported',
233  507: 'Insufficient Storage',
234  510: 'Not Extended'
235 }
236 
237 WEBDAV_STATUS_CODES = [207, 422, 423, 424, 507]
238 
239 http_sig_param_re = re.compile(r'\‍((?:(?P<type>[^:)]+):)?(?P<name>[\w_]+)\‍)',
240  re.VERBOSE)
241 
242 
243 def sort_by_method(entries):
244  def cmp(item):
245  order = ['HEAD', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH',
246  'OPTIONS', 'TRACE', 'CONNECT', 'COPY', 'ANY']
247  method = item[0].split(' ', 1)[0]
248  if method in order:
249  return order.index(method)
250  return 100
251  return sorted(entries, key=cmp)
252 
253 
254 def http_resource_anchor(method, path):
255  path = re.sub(r'[{}]', '', re.sub(r'[<>:/]', '-', path))
256  return method.lower() + '-' + path
257 
258 
259 class HTTPResource(ObjectDescription):
260 
261  doc_field_types = [
262  TypedField('parameter', label='Parameters',
263  names=('param', 'parameter', 'arg', 'argument'),
264  typerolename='obj', typenames=('paramtype', 'type')),
265  TypedField('jsonparameter', label='JSON Parameters',
266  names=('jsonparameter', 'jsonparam', 'json'),
267  typerolename='obj', typenames=('jsonparamtype', 'jsontype')),
268  TypedField('requestjsonobject', label='Request JSON Object',
269  names=('reqjsonobj', 'reqjson', '<jsonobj', '<json'),
270  typerolename='obj', typenames=('reqjsonobj', '<jsonobj')),
271  TypedField('requestjsonarray', label='Request JSON Array of Objects',
272  names=('reqjsonarr', '<jsonarr'),
273  typerolename='obj',
274  typenames=('reqjsonarrtype', '<jsonarrtype')),
275  TypedField('responsejsonobject', label='Response JSON Object',
276  names=('resjsonobj', 'resjson', '>jsonobj', '>json'),
277  typerolename='obj', typenames=('resjsonobj', '>jsonobj')),
278  TypedField('responsejsonarray', label='Response JSON Array of Objects',
279  names=('resjsonarr', '>jsonarr'),
280  typerolename='obj',
281  typenames=('resjsonarrtype', '>jsonarrtype')),
282  TypedField('queryparameter', label='Query Parameters',
283  names=('queryparameter', 'queryparam', 'qparam', 'query'),
284  typerolename='obj',
285  typenames=('queryparamtype', 'querytype', 'qtype')),
286  GroupedField('formparameter', label='Form Parameters',
287  names=('formparameter', 'formparam', 'fparam', 'form')),
288  GroupedField('requestheader', label='Request Headers',
289  rolename='header',
290  names=('<header', 'reqheader', 'requestheader')),
291  GroupedField('responseheader', label='Response Headers',
292  rolename='header',
293  names=('>header', 'resheader', 'responseheader')),
294  GroupedField('statuscode', label='Status Codes',
295  rolename='statuscode',
296  names=('statuscode', 'status', 'code'))
297  ]
298 
299  option_spec = {
300  'deprecated': directives.flag,
301  'noindex': directives.flag,
302  'synopsis': lambda x: x,
303  }
304 
305  method = NotImplemented
306 
307  def handle_signature(self, sig, signode):
308  method = self.methodmethod.upper() + ' '
309  signode += addnodes.desc_name(method, method)
310  offset = 0
311  path = None
312  for match in http_sig_param_re.finditer(sig):
313  path = sig[offset:match.start()]
314  signode += addnodes.desc_name(path, path)
315  params = addnodes.desc_parameterlist()
316  typ = match.group('type')
317  if typ:
318  typ += ': '
319  params += addnodes.desc_annotation(typ, typ)
320  name = match.group('name')
321  params += addnodes.desc_parameter(name, name)
322  signode += params
323  offset = match.end()
324  if offset < len(sig):
325  path = sig[offset:len(sig)]
326  signode += addnodes.desc_name(path, path)
327  assert path is not None, 'no matches for sig: %s' % sig
328  fullname = self.methodmethod.upper() + ' ' + path
329  signode['method'] = self.methodmethod
330  signode['path'] = sig
331  signode['fullname'] = fullname
332  return (fullname, self.methodmethod, sig)
333 
334  def needs_arglist(self):
335  return False
336 
337  def add_target_and_index(self, name_cls, sig, signode):
338  signode['ids'].append(http_resource_anchor(*name_cls[1:]))
339  if 'noindex' not in self.options:
340  self.env.domaindata['http'][self.methodmethod][sig] = (
341  self.env.docname,
342  self.options.get('synopsis', ''),
343  'deprecated' in self.options)
344 
345  def get_index_text(self, modname, name):
346  return ''
347 
348 
350 
351  method = 'options'
352 
353 
355 
356  method = 'head'
357 
358 
360 
361  method = 'patch'
362 
363 
365 
366  method = 'post'
367 
368 
370 
371  method = 'get'
372 
373 
375 
376  method = 'put'
377 
378 
380 
381  method = 'delete'
382 
383 
385 
386  method = 'trace'
387 
388 
390 
391  method = 'connect'
392 
393 
395 
396  method = 'copy'
397 
398 
400 
401  method = 'any'
402 
403 
404 class HTTPXRefRole(XRefRole):
405 
406  def __init__(self, method, **kwargs):
407  XRefRole.__init__(self, **kwargs)
408  self.methodmethod = method
409 
410  def process_link(self, env, refnode, has_explicit_title, title, target):
411  if not has_explicit_title:
412  title = self.methodmethod.upper() + ' ' + title
413  return title, target
414 
415 
416 class HTTPXRefMethodRole(XRefRole):
417 
418  def result_nodes(self, document, env, node, is_ref):
419  method = node[0][0].lower()
420  rawsource = node[0].rawsource
421  config = env.domains['http'].env.config
422  if method not in METHOD_REFS:
423  if not config['http_strict_mode']:
424  return [nodes.emphasis(method, method)], []
425  reporter = document.reporter
426  msg = reporter.error('%s is not valid HTTP method' % method,
427  line=node.line)
428  prb = nodes.problematic(method, method)
429  return [prb], [msg]
430  url = str(METHOD_REFS[method])
431  if not url:
432  return [nodes.emphasis(method, method)], []
433  node = nodes.reference(rawsource, method.upper(), refuri=url)
434  return [node], []
435 
436 
437 class HTTPXRefStatusRole(XRefRole):
438 
439  def result_nodes(self, document, env, node, is_ref):
440  def get_code_status(text):
441  if text.isdigit():
442  code = int(text)
443  return code, HTTP_STATUS_CODES.get(code)
444  else:
445  try:
446  code, status = re.split(r'\s', text.strip(), 1)
447  code = int(code)
448  except ValueError:
449  return None, None
450  known_status = HTTP_STATUS_CODES.get(code)
451  if known_status is None:
452  return code, None
453  elif known_status.lower() != status.lower():
454  return code, None
455  else:
456  return code, status
457 
458  def report_unknown_code():
459  if not config['http_strict_mode']:
460  return [nodes.emphasis(text, text)], []
461  reporter = document.reporter
462  msg = reporter.error('%d is unknown HTTP status code' % code,
463  line=node.line)
464  prb = nodes.problematic(text, text)
465  return [prb], [msg]
466 
467  def report_invalid_code():
468  if not config['http_strict_mode']:
469  return [nodes.emphasis(text, text)], []
470  reporter = document.reporter
471  msg = reporter.error(
472  'HTTP status code must be an integer (e.g. `200`) or '
473  'start with an integer (e.g. `200 OK`); %r is invalid' %
474  text,
475  line=node.line
476  )
477  prb = nodes.problematic(text, text)
478  return [prb], [msg]
479 
480  text = node[0][0]
481  rawsource = node[0].rawsource
482  config = env.domains['http'].env.config
483 
484  code, status = get_code_status(text)
485  if code is None:
486  return report_invalid_code()
487  elif status is None:
488  return report_unknown_code()
489  elif code == 226:
490  url = 'http://www.ietf.org/rfc/rfc3229.txt'
491  elif code == 418:
492  url = 'http://www.ietf.org/rfc/rfc2324.txt'
493  elif code == 429:
494  url = 'http://tools.ietf.org/html/rfc6585#section-4'
495  elif code == 449:
496  url = 'http://msdn.microsoft.com/en-us/library/dd891478(v=prot.10).aspx'
497  elif code == 451:
498  url = 'http://www.ietf.org/rfc/rfc7725.txt'
499  elif code in WEBDAV_STATUS_CODES:
500  url = 'http://tools.ietf.org/html/rfc4918#section-11.%d' % (WEBDAV_STATUS_CODES.index(code) + 1)
501  elif code in HTTP_STATUS_CODES:
502  url = 'http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html' \
503  '#sec10.' + ('%d.%d' % (code // 100, 1 + code % 100))
504  else:
505  url = ''
506  node = nodes.reference(rawsource, '%d %s' % (code, status), refuri=url)
507  return [node], []
508 
509 
510 class HTTPXRefHeaderRole(XRefRole):
511 
512  def result_nodes(self, document, env, node, is_ref):
513  header = node[0][0]
514  rawsource = node[0].rawsource
515  if header not in HEADER_REFS:
516  _header = '-'.join(map(lambda i: i.title(), header.split('-')))
517  if _header not in HEADER_REFS:
518  return [nodes.emphasis(header, header)], []
519  url = str(HEADER_REFS[header])
520  node = nodes.reference(rawsource, header, refuri=url)
521  return [node], []
522 
523 
524 class HTTPIndex(Index):
525 
526  name = 'routingtable'
527  localname = 'HTTP Routing Table'
528  shortname = 'routing table'
529 
530  def __init__(self, *args, **kwargs):
531  super(HTTPIndex, self).__init__(*args, **kwargs)
532 
533  self.ignoreignore = [
534  [l for l in x.split('/') if l]
535  for x in self.domain.env.config['http_index_ignore_prefixes']]
536  self.ignoreignore.sort(reverse=True)
537 
538  # During HTML generation these values pick from class,
539  # not from instance so we have a little hack the system
540  cls = self.__class__
541  cls.shortnameshortnameshortname = self.domain.env.config['http_index_shortname']
542  cls.localnamelocalnamelocalname = self.domain.env.config['http_index_localname']
543 
544  def grouping_prefix(self, path):
545  letters = [x for x in path.split('/') if x]
546  for prefix in self.ignoreignore:
547  if letters[:len(prefix)] == prefix:
548  return '/' + '/'.join(letters[:len(prefix) + 1])
549  return '/%s' % (letters[0] if letters else '',)
550 
551  def generate(self, docnames=None):
552  content = {}
553  items = ((method, path, info)
554  for method, routes in self.domain.routes.items()
555  for path, info in routes.items())
556  items = sorted(items, key=lambda item: item[1])
557  for method, path, info in items:
558  entries = content.setdefault(self.grouping_prefixgrouping_prefix(path), [])
559  entries.append([
560  method.upper() + ' ' + path, 0, info[0],
561  http_resource_anchor(method, path),
562  '', 'Deprecated' if info[2] else '', info[1]
563  ])
564  items = sorted(
565  (path, sort_by_method(entries))
566  for path, entries in content.items()
567  )
568  return (items, True)
569 
570 
571 class HTTPDomain(Domain):
572  """HTTP domain."""
573 
574  name = 'http'
575  label = 'HTTP'
576 
577  object_types = {
578  'options': ObjType('options', 'options', 'obj'),
579  'head': ObjType('head', 'head', 'obj'),
580  'post': ObjType('post', 'post', 'obj'),
581  'get': ObjType('get', 'get', 'obj'),
582  'put': ObjType('put', 'put', 'obj'),
583  'patch': ObjType('patch', 'patch', 'obj'),
584  'delete': ObjType('delete', 'delete', 'obj'),
585  'trace': ObjType('trace', 'trace', 'obj'),
586  'connect': ObjType('connect', 'connect', 'obj'),
587  'copy': ObjType('copy', 'copy', 'obj'),
588  'any': ObjType('any', 'any', 'obj')
589  }
590 
591  directives = {
592  'options': HTTPOptions,
593  'head': HTTPHead,
594  'post': HTTPPost,
595  'get': HTTPGet,
596  'put': HTTPPut,
597  'patch': HTTPPatch,
598  'delete': HTTPDelete,
599  'trace': HTTPTrace,
600  'connect': HTTPConnect,
601  'copy': HTTPCopy,
602  'any': HTTPAny
603  }
604 
605  roles = {
606  'options': HTTPXRefRole('options'),
607  'head': HTTPXRefRole('head'),
608  'post': HTTPXRefRole('post'),
609  'get': HTTPXRefRole('get'),
610  'put': HTTPXRefRole('put'),
611  'patch': HTTPXRefRole('patch'),
612  'delete': HTTPXRefRole('delete'),
613  'trace': HTTPXRefRole('trace'),
614  'connect': HTTPXRefRole('connect'),
615  'copy': HTTPXRefRole('copy'),
616  'any': HTTPXRefRole('any'),
617  'statuscode': HTTPXRefStatusRole(),
618  'method': HTTPXRefMethodRole(),
619  'header': HTTPXRefHeaderRole()
620  }
621 
622  initial_data = {
623  'options': {}, # path: (docname, synopsis)
624  'head': {},
625  'post': {},
626  'get': {},
627  'put': {},
628  'patch': {},
629  'delete': {},
630  'trace': {},
631  'connect': {},
632  'copy': {},
633  'any': {}
634  }
635 
636  indices = []
637 
638  @property
639  def routes(self):
640  return dict((key, self.data[key]) for key in self.object_typesobject_types)
641 
642  def clear_doc(self, docname):
643  for typ, routes in self.routesroutes.items():
644  for path, info in list(routes.items()):
645  if info[0] == docname:
646  del routes[path]
647 
648  def resolve_xref(self, env, fromdocname, builder, typ, target,
649  node, contnode):
650  try:
651  info = self.data[str(typ)][target]
652  except KeyError:
653  text = contnode.rawsource
654  role = self.rolesroles.get(typ)
655  if role is None:
656  return None
657 
658  if fromdocname not in _doctree_cache:
659  _doctree_cache[fromdocname] = env.get_doctree(fromdocname)
660  doctree = _doctree_cache[fromdocname]
661 
662  resnode = role.result_nodes(doctree, env, node, None)[0][0]
663  if isinstance(resnode, addnodes.pending_xref):
664  text = node[0][0]
665  reporter = doctree.reporter
666  reporter.warning('Cannot resolve reference to %r' % text,
667  line=node.line)
668  return None
669  return resnode
670  else:
671  anchor = http_resource_anchor(typ, target)
672  title = typ.upper() + ' ' + target
673  return make_refnode(builder, fromdocname, info[0], anchor,
674  contnode, title)
675 
676  def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode):
677  """Resolve the pending_xref *node* with the given *target*.
678 
679  The reference comes from an "any" or similar role, which means that Sphinx
680  don't know the type.
681 
682  For now sphinxcontrib-httpdomain doesn't resolve any xref nodes.
683 
684  :return:
685  list of tuples ``('domain:role', newnode)``, where ``'domain:role'``
686  is the name of a role that could have created the same reference,
687  """
688  return []
689 
690  def get_objects(self):
691  for method, routes in self.routesroutes.items():
692  for path, info in routes.items():
693  anchor = http_resource_anchor(method, path)
694  yield (path, path, method, info[0], anchor, 1)
695 
696 
697 class HTTPLexer(RegexLexer):
698  """Lexer for HTTP sessions."""
699 
700  name = 'HTTP'
701  aliases = ['http']
702 
703  flags = re.DOTALL
704 
705  def header_callback(self, match):
706  if match.group(1).lower() == 'content-type':
707  content_type = match.group(5).strip()
708  if ';' in content_type:
709  content_type = content_type[:content_type.find(';')].strip()
710  self.content_typecontent_type = content_type
711  yield match.start(1), Name.Attribute, match.group(1)
712  yield match.start(2), Text, match.group(2)
713  yield match.start(3), Operator, match.group(3)
714  yield match.start(4), Text, match.group(4)
715  yield match.start(5), Literal, match.group(5)
716  yield match.start(6), Text, match.group(6)
717 
718  def continuous_header_callback(self, match):
719  yield match.start(1), Text, match.group(1)
720  yield match.start(2), Literal, match.group(2)
721  yield match.start(3), Text, match.group(3)
722 
723  def content_callback(self, match):
724  content_type = getattr(self, 'content_type', None)
725  content = match.group()
726  offset = match.start()
727  if content_type:
728  from pygments.lexers import get_lexer_for_mimetype
729  try:
730  lexer = get_lexer_for_mimetype(content_type)
731  except ClassNotFound:
732  pass
733  else:
734  for idx, token, value in lexer.get_tokens_unprocessed(content):
735  yield offset + idx, token, value
736  return
737  yield offset, Text, content
738 
739  tokens = {
740  'root': [
741  (r'(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|TRACE|COPY)( +)([^ ]+)( +)'
742  r'(HTTPS?)(/)(1\.[01])(\r?\n|$)',
743  bygroups(Name.Function, Text, Name.Namespace, Text,
744  Keyword.Reserved, Operator, Number, Text),
745  'headers'),
746  (r'(HTTPS?)(/)(1\.[01])( +)(\d{3})( +)([^\r\n]+)(\r?\n|$)',
747  bygroups(Keyword.Reserved, Operator, Number, Text, Number,
748  Text, Name.Exception, Text),
749  'headers'),
750  ],
751  'headers': [
752  (r'([^\s:]+)( *)(:)( *)([^\r\n]+)(\r?\n|$)', header_callback),
753  (r'([\t ]+)([^\r\n]+)(\r?\n|$)', continuous_header_callback),
754  (r'\r?\n', Text, 'content')
755  ],
756  'content': [
757  (r'.+', content_callback)
758  ]
759  }
760 
761 
762 def setup(app):
763  app.add_domain(HTTPDomain)
764 
765  try:
766  get_lexer_by_name('http')
767  except ClassNotFound:
768  app.add_lexer('http', HTTPLexer())
769  app.add_config_value('http_index_ignore_prefixes', [], None)
770  app.add_config_value('http_index_shortname', 'routing table', True)
771  app.add_config_value('http_index_localname', 'HTTP Routing Table', True)
772  app.add_config_value('http_strict_mode', True, None)
773  app.add_config_value('http_headers_ignore_prefixes', ['X-'], None)
def __init__(self, name, type)
Definition: httpdomain.py:81
def __init__(self, base_url, anchor, section)
Definition: httpdomain.py:42
def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode)
Definition: httpdomain.py:649
def clear_doc(self, docname)
Definition: httpdomain.py:642
def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode)
Definition: httpdomain.py:676
def __init__(self, *args, **kwargs)
Definition: httpdomain.py:530
def generate(self, docnames=None)
Definition: httpdomain.py:551
def grouping_prefix(self, path)
Definition: httpdomain.py:544
def header_callback(self, match)
Definition: httpdomain.py:705
def continuous_header_callback(self, match)
Definition: httpdomain.py:718
def content_callback(self, match)
Definition: httpdomain.py:723
def get_index_text(self, modname, name)
Definition: httpdomain.py:345
def handle_signature(self, sig, signode)
Definition: httpdomain.py:307
def add_target_and_index(self, name_cls, sig, signode)
Definition: httpdomain.py:337
def result_nodes(self, document, env, node, is_ref)
Definition: httpdomain.py:512
def result_nodes(self, document, env, node, is_ref)
Definition: httpdomain.py:418
def process_link(self, env, refnode, has_explicit_title, title, target)
Definition: httpdomain.py:410
def __init__(self, method, **kwargs)
Definition: httpdomain.py:406
def result_nodes(self, document, env, node, is_ref)
Definition: httpdomain.py:439
def __init__(self, rfc, section)
Definition: httpdomain.py:66
def __init__(self, section)
Definition: httpdomain.py:57
static int list
Set if we should print a list of currently running services.
Definition: gnunet-arm.c:69
static struct GNUNET_CONTAINER_MultiPeerMap * map
Handle to the map used to store old latency values for peers.
static int get
Get DID Documement for DID Flag.
Definition: gnunet-did.c:66
uint16_t len
length of data (which is always a uint32_t, but presumably this can be used to specify that fewer byt...
def sort_by_method(entries)
Definition: httpdomain.py:243
def http_resource_anchor(method, path)
Definition: httpdomain.py:254