File: cgi.py

package info (click to toggle)
python-tinyrpc 0.6-4
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 324 kB
  • sloc: python: 1,700; makefile: 142; sh: 16
file content (96 lines) | stat: -rw-r--r-- 3,076 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
cgi extends the tinyrpc package.

The CGIServerTransport adds CGI as a supported server protocol that can be used
with the regular HTTP client.

(c) 2016, Leo Noordergraaf, Nextpertise BV
This code is made available under the same license as tinyrpc itself.
"""

from __future__ import print_function

import os
import sys
import json
import cgi
try:
    import urllib.parse as urlparse
except ImportError:
    import urllib as urlparse

from . import ServerTransport

class CGIServerTransport(ServerTransport):
    """CGI transport.
    
    Reading stdin is blocking but, given that we've been called, something is
    waiting.  The transport accepts both GET and POST request.

    A POST request provides the entire JSON-RPC request in the body of the HTTP
    request.

    A GET request provides the elements of the JSON-RPC request in separate query
    parameters and only the params field contains a JSON object or array.
    i.e. curl 'http://server?jsonrpc=2.0&id=1&method="doit"&params={"arg"="something"}'
    """

    def receive_message(self):
        """Receive a message from the transport.

        Blocks until another message has been received. May return a context
        opaque to clients that should be passed on
        :py:func:`~tinyrpc.transport.CGIServerTransport.send_reply` to identify the
        client later on.

        :return: A tuple consisting of ``(context, message)``.
        """

        if 'CONTENT_LENGTH' in os.environ:
            # POST
            content_length = int(os.environ['CONTENT_LENGTH'])
            request_json = sys.stdin.read(content_length)
            request_json = urlparse.unquote(request_json)
        else:
            # GET
            fields = cgi.FieldStorage()
            jsonrpc = fields.getfirst("jsonrpc")
            id = fields.getfirst("id")
            method = fields.getfirst("method")
            params = fields.getfirst("params")
            # Create request string
            request_json = json.dumps({
                'jsonrpc': jsonrpc,
                'id': id,
                'method': method,
                'params': params
            })
        return None, request_json


    def send_reply(self, context, reply):
        """Sends a reply to a client.

        The client is usually identified by passing ``context`` as returned
        from the original
        :py:func:`~tinyrpc.transport.Transport.receive_message` call.

        Messages must be strings, it is up to the sender to convert the
        beforehand. A non-string value raises a :py:exc:`TypeError`.

        :param context: A context returned by
                        :py:func:`~tinyrpc.transport.CGIServerTransport.receive_message`.
        :param reply: A string to send back as the reply.
        """

        # context isn't used with cgi
        print("Content-Type: application/json")
        print("Cache-Control: no-cache")
        print("Pragma: no-cache")
        print("Content-Length: %d" % len(reply))
        print()
        print(reply)