JSON-RPC over HTTP?

  Thread Rating:
  • 1 Votes - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Post Reply
Adam Offline
Junior Member
Posts: 3
Joined: Nov 2009
Reputation: 0
Post: #11
Here's my example in .NET:


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Web;
using System.Web.Script.Serialization;

namespace XBMCLib
{
    public class XBMCHelper
    {
        private static string _ServerName = "http://localhost:9091/jsonrpc";

        private static string PostCommand(string istrCommand)
        {
            byte[] byteArray = Encoding.ASCII.GetBytes(istrCommand);

            WebRequest wreq = WebRequest.Create(_ServerName);
            wreq.Method = "POST";
            wreq.ContentLength = byteArray.Length;
            wreq.ContentType = "application/x-www-form-urlencoded";
            Stream dataStream = wreq.GetRequestStream ();
            dataStream.Write (byteArray, 0, byteArray.Length);
            dataStream.Close();

            WebResponse wresp = wreq.GetResponse();
            dataStream = wresp.GetResponseStream();
            StreamReader reader = new StreamReader (dataStream);
            // Read the content.
            string responseFromServer = reader.ReadToEnd();
            
            reader.Close();
            dataStream.Close();
            wresp.Close();

            return responseFromServer.Replace(" ", " ");
        }

        protected struct PingResp
        {
            public string id;
            public string jsonrpc;
            public string result;
        }

        public static bool XBMCPing()
        {
            PingResp res;

            string cmd = "{\"jsonrpc\": \"2.0\", \"method\": \"JSONRPC.Ping\", \"id\": \"1\"}";

            JavaScriptSerializer ser = new JavaScriptSerializer();
            res = ser.Deserialize<PingResp>(PostCommand(cmd));

            return res.result == "pong";
        }
    }
}
find quote
Montellese Offline
Team-XBMC Developer
Posts: 2,789
Joined: Jan 2009
Reputation: 20
Location: Switzerland
Post: #12
I wrote the following python code yesterday to be able to make json rpc calls over HTTP or TCP to run some test commands:
Code:
#!/usr/bin/python

import socket
import select
import json

class JsonRpcResponseError(Exception):

    def __init__(self, errorCode, message = ""):
        self.ErrorCode = errorCode
        self.Message = message

    def __str__(self):
        return "Error %d in Json RPC response: %s" % (self.ErrorCode, self.Message)

class JsonRpcProtocol(object):
    HTTP = 0
    TCP = 1

class JsonRpcConnection(object):

    def __init__(self, protocol, address, port, username = "", password = ""):
        if protocol != JsonRpcProtocol.HTTP and protocol != JsonRpcProtocol.TCP:
            raise ValueError("Invalid protocol (HTTP or TCP)")
        if len(address) <= 0:
            raise ValueError("Invalid address")
        if port < 0:
            raise ValueError("Invalid port")
            
        self.__protocol = protocol
        self.__address = address
        self.__port = port
        self.__username = username
        self.__password = password

        self.__con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__connected = False
        self.__id = 1

    def __del__(self):
        if self.__connected:
            self.__con.shutdown(socket.SHUT_RDWR)
            self.__con.close()

    def call(self, method, params = None):
        return self.__send(method, params)

    def __connect(self):
        if not self.__connected:
            socket.setdefaulttimeout(5.0)
            addr = self.__address
            if len(self.__username) > 0:
                addr = "@%s" % addr
                if len(self.__password) > 0:
                    addr = ":%s%s" % (self.__password, addr)
                addr = "%s%s" % (self.__username, addr)
            
            self.__con.connect((addr, self.__port))
            self.__connected = True

    def __send(self, method, params):
        self.__connect()
        request = "{\"jsonrpc\": \"2.0\", \"method\": \"%s\", " % method
        if not (params is None):
            request += "\"params\": %s, " % json.dumps(params)
            pass

        request += "\"id\": %d}" % self.__id
        
        self.__id += 1

        if self.__protocol == JsonRpcProtocol.HTTP:
            self.__con.send("POST /jsonrpc HTTP/1.1\x0D\x0A".encode("utf-8"))
            self.__con.send("\x0D\x0A".encode("utf-8"))

        self.__con.send(bytearray(request, 'utf-8'))
        
        response = ""
        while True:
            response += self.__con.recv(4096).decode("utf-8")

            if len(select.select([self.__con], [], [], 0)[0]) == 0:
                break

        return self.__parseResponse(response)

    def __parseResponse(self, response):
        if response.find("HTTP") == 0:
            response = response[response.find("\x0D\x0A\x0D\x0A") + 4:len(response)]
        
        jsonObj = json.loads(response)
        
        if "error" in jsonObj:
            raise JsonRpcResponseError(jsonObj["error"]["code"],
                                       jsonObj["error"]["message"])
        if "result" in jsonObj:
            return jsonObj["result"]
        
        raise JsonRpcResponseError("Invalid response")

I'm sure there are better ways to do this as this is basically my first python code I have ever written but maybe it will get you started.

Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.

[Image: badge.gif]
find quote
toenuff Offline
Junior Member
Posts: 6
Joined: Dec 2009
Reputation: 0
Post: #13
The following works:

Code:
curl.exe -i -X POST -d "{\"jsonrpc\": \"2.0\", \"method\": \"JSONRPC.Version\", \"id\": 1}" http://localhost:8080/jsonrpc
find quote
toenuff Offline
Junior Member
Posts: 6
Joined: Dec 2009
Reputation: 0
Post: #14
Here's the PowerShell way of doing it - Ported from Adam's post:


Code:
$url = "http://localhost:8080/jsonrpc"

$command = '{"jsonrpc": "2.0", "method": "JSONRPC.Version", "id": 1}'

$bytes = [System.Text.Encoding]::ASCII.GetBytes($command)
$web = [System.Net.WebRequest]::Create($url)
$web.Method = "POST"
$web.ContentLength = $bytes.Length
$web.ContentType = "application/x-www-form-urlencoded"
$stream = $web.GetRequestStream()
$stream.Write($bytes,0,$bytes.Length)
$stream.close()

$reader = New-Object System.IO.Streamreader -ArgumentList $web.GetResponse().GetResponseStream()
$reader.ReadToEnd()
$reader.Close()
find quote
manxam Offline
Senior Member
Posts: 155
Joined: Jan 2005
Reputation: 3
Post: #15
Thanks for posting thing. I notice that there's no hardcoded method, ip, or port so I'm left to assume that these are command line options? If so, what's the format?

Thanks!

Montellese Wrote:I wrote the following python code yesterday to be able to make json rpc calls over HTTP or TCP to run some test commands:
Code:
#!/usr/bin/python

import socket
import select
import json

class JsonRpcResponseError(Exception):

    def __init__(self, errorCode, message = ""):
        self.ErrorCode = errorCode
        self.Message = message

    def __str__(self):
        return "Error %d in Json RPC response: %s" % (self.ErrorCode, self.Message)

class JsonRpcProtocol(object):
    HTTP = 0
    TCP = 1

class JsonRpcConnection(object):

    def __init__(self, protocol, address, port, username = "", password = ""):
        if protocol != JsonRpcProtocol.HTTP and protocol != JsonRpcProtocol.TCP:
            raise ValueError("Invalid protocol (HTTP or TCP)")
        if len(address) <= 0:
            raise ValueError("Invalid address")
        if port < 0:
            raise ValueError("Invalid port")
            
        self.__protocol = protocol
        self.__address = address
        self.__port = port
        self.__username = username
        self.__password = password

        self.__con = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.__connected = False
        self.__id = 1

    def __del__(self):
        if self.__connected:
            self.__con.shutdown(socket.SHUT_RDWR)
            self.__con.close()

    def call(self, method, params = None):
        return self.__send(method, params)

    def __connect(self):
        if not self.__connected:
            socket.setdefaulttimeout(5.0)
            addr = self.__address
            if len(self.__username) > 0:
                addr = "@%s" % addr
                if len(self.__password) > 0:
                    addr = ":%s%s" % (self.__password, addr)
                addr = "%s%s" % (self.__username, addr)
            
            self.__con.connect((addr, self.__port))
            self.__connected = True

    def __send(self, method, params):
        self.__connect()
        request = "{\"jsonrpc\": \"2.0\", \"method\": \"%s\", " % method
        if not (params is None):
            request += "\"params\": %s, " % json.dumps(params)
            pass

        request += "\"id\": %d}" % self.__id
        
        self.__id += 1

        if self.__protocol == JsonRpcProtocol.HTTP:
            self.__con.send("POST /jsonrpc HTTP/1.1\x0D\x0A".encode("utf-8"))
            self.__con.send("\x0D\x0A".encode("utf-8"))

        self.__con.send(bytearray(request, 'utf-8'))
        
        response = ""
        while True:
            response += self.__con.recv(4096).decode("utf-8")

            if len(select.select([self.__con], [], [], 0)[0]) == 0:
                break

        return self.__parseResponse(response)

    def __parseResponse(self, response):
        if response.find("HTTP") == 0:
            response = response[response.find("\x0D\x0A\x0D\x0A") + 4:len(response)]
        
        jsonObj = json.loads(response)
        
        if "error" in jsonObj:
            raise JsonRpcResponseError(jsonObj["error"]["code"],
                                       jsonObj["error"]["message"])
        if "result" in jsonObj:
            return jsonObj["result"]
        
        raise JsonRpcResponseError("Invalid response")

I'm sure there are better ways to do this as this is basically my first python code I have ever written but maybe it will get you started.
find quote
Montellese Offline
Team-XBMC Developer
Posts: 2,789
Joined: Jan 2009
Reputation: 20
Location: Switzerland
Post: #16
The "address" parameter of the class I posted takes the IP of your computer running XBMC. If you run your script on the same computer as XBMC this is "127.0.0.1". The port is either the HTTP or TCP port (depending on what you chose as protocol). The HTTP port is configurable in XBMCs network settings. The TCP port is set to 9090 and cannot be changed atm.
All available methods can be retrieved by calling the "JSONRPC.Introspect" method which returns a list of all available methods and some comments about how to use them.

Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.

[Image: badge.gif]
find quote
TheyKilledKenny Offline
Junior Member
Posts: 2
Joined: Jan 2011
Reputation: 0
Post: #17
Hi!
I need to develop a custom interface with Flash and actionscript 2 (not AS3), but I am not able to figure it out how to send the json request to xbmc.

With loadVars.load() I can open an http connection to the server and, if the server need no other parameter, i can receive the json string and cast it into my object.
XBMC server needs to receive a json object with the call to obtain an answer.

With loadVars.sendAndLoad() I can send variables to the server in post mode, but I need to know the variable name that the xbmc server is expecting from my client.
All your examples stream the string (byte array) directly to the server immediately after opening a connection, but no variable name is specified.

Following you can see the 2 ways i tryed yet, obtaining only a -32700 parse error message, someone can help me with this?

Code:
var snd:LoadVars = new LoadVars();
var rcv:LoadVars = new LoadVars();

rcv.onData = function(thedata) { trace("Data Received: " + thedata); }

snd.jsonrpc = '{"jsonrpc": "2.0", "method": "JSONRPC.Introspect", "id": "1"}'
snd.sendAndLoad("http://xbmc.ip.address/jsonrpc", rcv, "POST");
and
Code:
var snd:LoadVars = new LoadVars();
var rcv:LoadVars = new LoadVars();

rcv.onData = function(thedata) { trace("Data Received: " + thedata); }

snd.jsonrpc = "2.0";
snd.method = "JSONRPC.Introspect";
snd.id = "1";
snd.sendAndLoad("http://xbmc.ip.address/jsonrpc", rcv, "POST");

I'am not able to use the tcp socket because AS2 needs a null char to terminate the string transmision and xbmc seems not sending a null char at the endo of the json strings.

Sorry for bad English!

Please help.

Thank you.

Bye!
(This post was last modified: 2011-01-07 13:23 by TheyKilledKenny.)
find quote
Montellese Offline
Team-XBMC Developer
Posts: 2,789
Joined: Jan 2009
Reputation: 20
Location: Switzerland
Post: #18
TheyKilledKenny Wrote:All your examples stream the string (byte array) directly to the server immediately after opening a connection, but no variable name is specified.

What do you mean by "variable name"? I have no idea about Flash or ActionScript so I can't help you there but if you could take a look into XBMCs log file you should see an entry for the json parse error where you can see the exact string that XBMC received and couldn't parse. That might provide helpful information about what is going wrong.

Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.

[Image: badge.gif]
find quote
TheyKilledKenny Offline
Junior Member
Posts: 2
Joined: Jan 2011
Reputation: 0
Post: #19
Thanks for your reply.

By "variable name" I mean the name of the field that contains the data I have to send in post.

Like a html form for example:
Code:
<form method="POST" action="http://my-server.address/page.php">
   <input type="text" name="phone" value="1234"/>
   <input type="text" name="address" value="my street"/>
</form>

So I have to find the field name containing the json stiring to send to xbmc, like:
Code:
<form method="POST" action="http://xbmx.ip.address/jsonrpc">
   <input type="text" name="jsonrpc???" value='{"jsonrpc": "2.0", "method": "JSONRPC.Introspect", "id": "1"}'/>
</form>

I hope is more clear now.

Thanks.
Bye.
find quote
Montellese Offline
Team-XBMC Developer
Posts: 2,789
Joined: Jan 2009
Reputation: 20
Location: Switzerland
Post: #20
There is no such thing. The json rpc request (everything between { and }) should be the only thing in the whole HTTP POST message.

Always read the XBMC online-manual, FAQ and search the forum before posting.
Do not e-mail XBMC-Team members directly asking for support. Read/follow the forum rules.
For troubleshooting and bug reporting please make sure you read this first.

[Image: badge.gif]
find quote