''JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. It uses [JSON] (RFC 4627) as data format, and is transport-independent. It is designed to be simple!'' - from the JSON-RPC specification [http://groups.google.com/group/json-rpc/web/json-rpc-1-2-proposal]. [APN] 20080705 A Tcl implementation of the JSON-RPC client-side API is now part of [TclSOAP]. Most of the server-side calls are implemented, but not integrated with any http servers. [MaxJarek] JSON-RPC is my primary technology for client-server applications. I use Tk GUI on client side and Wibble as JSON-RPC http server. Sample client example: ====== package require JSONRPC JSONRPC::create add -proxy "http://www.raboof.com/projects/jayrock/demo.ashx" \ -params { val1 int val2 int } puts [::add 2 3456] ====== Sample server side example (for wibble::jsonrpc::proxy package): ====== namespace eval ::wibble::json-rpc { proc add {a b} {return [expr {$a+$b}]} } ====== TclSOAP don't support https transport for JSON-RPC. I have created patch for SOAP::https which is waiting in repo. wibble::jsonrpc::proxy is my package thats change Wibble in good (for me) SOA application server. jsonrpc-proxy needs same corrects in Wibble core. I am waiting for new Wibble version then proxy package will be public available. :) [AMG]: Please let me know what changes you would like in the [Wibble] core, and I'll see what I can incorporate directly. For anything that I can't just drop in without breaking compatibility and/or adding prerequisites, I may have ideas on how to modify the core to be more receptive to outside customization. The next version of Wibble, when I have time to work on it, should feature charset support, rather than being tied to ISO8859-1. That's my primary goal. I'm also collecting little fixes and updates. For example, I modified [[getrequest]] to permit application/json-rpc to be parsed by a zone handler, and I modified [[listen]] to work better with [TLS], both thanks to your suggestions and contributions. Another feature I'd like in the next Wibble is support for virtual hosts, by request from [SDW]. [MaxJarek]: Ok, let's start with Wibble 0.2 :). This is initial version. ====== # Simple JSONRPC proxy for Wibble # J. Lewandowski, (MaxJarek) # Available under the Tcl/Tk license. http://tcl.tk/software/tcltk/license.html package provide wibble::jsonrpc::proxy 0.1 namespace eval ::wibble::zone { proc jsonrpc-proxy {namespace state} { dict set state response status 200 dict set state response header content-type "" application/json-rpc charset utf-8 dict with state request {} if {$method eq "POST" && [dict exists $header content-type ""] && [dict get $header content-type ""] eq "application/json-rpc"} { array set json_req [json::json2dict [dehex $rawpost]] # execute method with params catch {set result [eval ${namespace}::$json_req(method) $json_req(params)]} err } else { set err "JSON-RPC error" } if {![info exists result]} { dict set state response content [json::write object id null \ error [json::write object name [json::write string JSONRPCError] message [json::write string $err]]] ::wibble::log "JSON-RPC: $err" } else { dict set state response content [json::write object result [json::write string $result] id $json_req(id)] } sendresponse [dict get $state response] } } ====== Example Wibble startup script: ====== namespace eval ::wibble::json-rpc { proc strlen {a} {return "[string length $a]"} proc date {} {return [clock seconds]} proc add {a b} {return [expr {$a+$b}]} } package require wibble package require json package require json::write package require wibble::jsonrpc::proxy set ::wibble::zonehandlers {} ::wibble::handle /maxgui [list jsonrpc-proxy ::wibble::json-rpc] ::wibble::handle / notfound wibble::listen 8080 vwait forever ====== Example client code: ====== package require JSONRPC JSONRPC::configure -transport http JSONRPC::create add -proxy "http://localhost:8080/maxgui" \ -params { val1 int val2 int } puts [::add 2 3456] ====== If you want any new json-rpc method simple insert new funkcion in ::wibble::json-rpc namespace [AMG]: I don't see any core modifications here. One change I made in my development version of Wibble is to add a [[dict getnull]] command (see [[dict get]] for the implementation), which simplifies this: ====== [dict exists $header content-type ""] && [dict get $header content-type ""] eq "application/json-rpc" ====== to be this: ====== [dict getnull $header content-type ""] eq "application/json-rpc" ====== This piece of code: ====== namespace eval ::wibble::json-rpc { proc strlen {a} {return "[string length $a]"} proc date {} {return [clock seconds]} proc add {a b} {return [expr {$a+$b}]} } ====== can also be written: ====== namespace eval ::wibble::json-rpc { proc strlen {a} {string length $a} proc date {} {clock seconds} proc add {a b} {expr {$a + $b}} } ====== It's mostly [a question of style]. In your code above, I went ahead and modified the line: ====== dict set state response header content-type "" "application/json-rpc; charset=utf-8" ====== to instead read: ====== dict set state response header content-type "" application/json-rpc charset utf-8 ====== This is how header [dict]s are intended to work. The empty string key maps to the value, and the other keys map to the parameters. This way, Wibble is able to read, interpret, and modify the headers without having to run the HTTP encode and decode routines. Sadly, setting the charset here doesn't actually work, since Wibble will still convert your content to ISO8859-1. If the content consists solely of 7-bit ASCII, this has no effect, since 7-bit characters are encoded identically across ISO885-1, [ASCII], and [UTF-8]. But this will corrupt anything else. For now, you're better off omitting the charset, or explicitly setting it to "iso8859-1". Like I said, I intend to fix this in the next release of Wibble, though I'm still trying to figure out how it should work. Here's what I'm thinking, for now anyway. * Wibble will have a table mapping between [HTTP] charset names and Tcl [encoding] names. When parsing the client's `accept-charset` header, Wibble will ignore charsets not present in its table. If nothing remains, or if there was no `accept-charset` to begin with, Wibble will fall back on ISO8859-1. * If there's no `charset` in the `content-type` header, or the `content-type` header is missing altogether, Wibble assumes `content` is a native Tcl string, or that `contentchan` already has its encoding configured such that [[[read]]]ing it will produce a native Tcl string. (Not sure how to handle `contentfile`.) Wibble will then pick the client's favorite charset (according to the `accept-charset` header), configure the socket to encode using that charset, then write the content to it. * If there is a `charset` in the `content-type` header, Wibble assumes `content` or `contentfile` is encoded using that charset, or that `contentchan` is configured without encoding translation and will produce text in that charset when [[read]]. If that charset is among those advertised with nonzero `q`value in the client's `accept-charset` header, Wibble will turn off encoding translation for the socket before writing out the content. Otherwise, Wibble will decode it to a native Tcl string then perform as in the above case. By the way, it's an error for to specify an unsupported `charset` in the `content-type` header. Form submission remains a problem [http://htmlpurifier.org/docs/enduser-utf8.html] [http://web.archive.org/web/20060427015200/ppewww.ph.gla.ac.uk/~flavell/charset/form-i18n.html] [http://wiki.tcl.tk/27382#pagetoca0214e53]. The only approach that makes sense, given the current state of popular web browsers, is to require that all HTML `
`s specify `accept-charset="utf-8"`. This way the browsers will send UTF-8, a known encoding that supports every character. Without an explicit `accept-charset`, browser behavior varies wildly, and in no case is it possible to reliably autodetect the encoding, not without magical knowledge unavailable to Wibble (e.g. the encoding of the page from which the `` was submitted) or ridiculous amounts of effort (e.g. statistical analysis of the byte patterns compared with typical frequencies for various encodings). Sadly, I couldn't find any configurations in which the browser actually bothered to tell the server what encoding it was using. [MaxJarek]: I know about Wibble problem with encoding content but for JSON-RPC i delete this line with ISO8859-1 encoding. Works and i have utf-8 encoding. Header with charset=utf-8 (in wibble::jsonrpc::proxy) is ignored by Wibble but is important for http transport in SOAP::http. I don't know how Wibble encoding content should work. For now i adjusting rpc-proxy for Wibble 0.2 with this ISO8859-1 modification. I noticed also thats JSONRPC package is ASCII characters oriented. I patch this and for now intensively testing with my apllications. [AMG]: I did similar (delete the ISO8859-1 encoding conversion) to my copy of Wibble for investigation into how different browsers handle different encodings, but that's not really a total solution. This approach requires ''every'' zone handler that calls [[sendresponse]] to first ensure that it has named the charset in its outgoing content-type header and that the content is encoded using that same charset, and it interferes with correct operation of contentfile. Instead I am contemplating expanding the zone handler system to have multiple phases, such that [[sendresponse]] doesn't altogether terminate the zone handler search, but rather only the current phase. If a later phase exists, it is given a chance to further process the state dicts, including the response dict. That would be a great place to put postprocessing stuff, for instance common page headers/footers, charset encoding, and compression. However, I am not quite sure this is the right path to take. For one, the name [[sendresponse]] is no longer appropriate in this scheme, since it only forces the response to be sent when in the final phase. The bigger question to me is whether I can do better. Wibble is my testbed for innovative web server concepts, so I don't want to commit to a design approach simply because it's the first thing that came to mind. So, let's think about what I have from a different perspective. The phase system I describe above basically adds a minimal [goto]. The barrier between phases constitutes a line label, and [[sendresponse]] goes to the next line label in the sequence. So, it's a basic flow control concept. That raises two questions: 1. What other flow control concepts are useful? 1. Can the zone configuration be expressed in a more typical programming style? Now this has me interested in the possibility of virtual hosts. Vhost configuration is basically a [switch] statement. When the host is X, do this; when it's Y or Z, do that; otherwise, do the third thing. But it would be really cool if the different hosts had some opportunity to share parts of the configuration, since they perhaps make up the same "site". For instance, some of the hosts may have different docroots but the same header and footer, and all of the hosts may permit compression. I think it's clear that the zone handler configuration can be viewed as an executable program. The trouble is integrating that concept with the current model, which is basically an interpreter. Well, what kind of interpreter is it? It's multiprocessing; it cycles between the alternative state dicts. If I let Tcl be the interpreter, I'll need to retain that functionality, but implement it in terms of the Tcl core. It turns out Tcl already has the required capability: [coroutine]s!! I have some ideas on how this would actually work, but I want to leave a little space here for your own imagination. But anyway, this means a Wibble configuration would basically be a [proc] and can use any standard (or custom!) Tcl flow control and decision-making commands. One exciting possibility would be splitting up the configuration across multiple procs which can call each other. By the way, once we get something hammered out, I will move this discussion to [Wibble discussion archive]. I'm just writing that now so I don't forget. ;^) <> Acronym