2018-01-22

遷移與保存

SourceForge 很直接的將 CVS web 關掉,需要該計畫的管理者將 CVS 轉為 GIT 才行。

所以目前我建立的 RPM spec/build script 中,至少有三個會出現問題(因為只是快速檢查,所以我不確定是否有我沒檢查到的)。

三個的狀況都不相同。TclSOAP 1.6.7 使用 TclXML,而 CVS 的發展版本才是使用 tDom 的版本。如果 openSUSE  build service 我在上面有檔案,我會先暫時放檔案上去 github。

tktable 是比較新的版本 2.11 有可能會遺失,如果 openSUSE  build service 我在上面有檔案,我會先暫時放檔案上去 github。

TclVFS 已經遷移到 core.tcl.tk,所以需要使用 Fossil 來取得新版本並且更新內容。(無法順利 clone,要嘗試下載檔案以後處理的方式)

另外,tcl-dbus 也已經遷移(而且還刪除了所有的檔案),我暫時先把舊的檔案放到 github 上,有時間再處理。 (重建一次,嘗試下載檔案以後處理的方式)

ffidl

ffidl: Ffidl allows you to define Tcl/Tk extensions with pure Tcl wrappers calling any shared library installed on your system, including the Tcl and Tk core libraries.


ffidl-spec

openSUSE 上的是舊的,所以我把目前比較有更新的 ffidl 寫成一個 RPM spec,試著製作  RPM 以後安裝。ffidl.patch 參考 openSUSE 之前 spec 的寫法,讓 ffidl 在編譯的時候使用系統的 libffi,而不是自己編譯的。

我有放上去 openSUSE build service 上我自己的 projects,有順利編譯。

JSMin-Tcl

JSMin-Tcl


使用 Tcl 寫的 JavaScript minifier,我發現的時候覺得很有趣(一方面也是有點意外),FlightAware 居然用 Tcl 實作了一個自己版本的 JSMin。

2018-01-18

Apache Rivet 3.0 released

Apache Rivet 3.0 released

Nonetheless Rivet 3.0 ships with a major rewriting of mod_rivet, whose code has been redesigned into a modular architecture with the purpose to preserve the basic features of the 2.x series of modules but also to provide support for both non threaded Apache MPMs (Multi Process Module), such as mod_mpm_prefork, and threaded MPMs such as mod_mpm_worker, mod_mpm_event and mod_mpm_winnt. As a consequence mod_rivet now also can run with the Apache Web Server on the MS-Windows family of operative systems.

Threaded MPM integration was achieved by making mod_rivet multi-threaded and modular itself, introducing the MPM-module bridge concept. We developed a set of loadable modules which are supposed not only to overcome the issues related to threading but also the best possible MPM mod_rivet integration. As a side effect of this modular design mod_rivet is not only able to integrate with its environment but also to work as a framework for writing more MPM bridges designed along different multi-threading schemes and workload management models.

mod_rivet is built using the traditional autotools based toolchain but with version 3.0 we also are supporting a CMake based script that can be used on any OS where CMake is available. The CMake tool is necessary in order to build mod_rivet on MS-Windows



快速解釋一下狀況。如果只支援傳統的 perfork mode,那麼就無法與 HTTP/2 module 共用(因為 HTTP/2 module 與 perfork mode 並不相容)。

也就是如果要使用 Apache Httpd server + mod_http2 + Apache Rivet,Apache Rivet 必須支援其它的模式。這是個很重要的進展。


更新:
apache2-mod_rivet-spec

我在 local 端建立 RPM 的時候會在最後的 check test 階段失敗,所以 test 整段都被我拿掉了。經測試,在一個 Aapache Http Server + SSL + mod_http2 (使用 MPM_event)的環境下加入 Apache Rivet 3.0.0 跟之前的行為不同,是可以運作的,所以目前可以確定,使用 Apache mod_http2 與 Apache Rivet 搭配是可行的。

tclBlend, javah and JDK8

我在追蹤 Java 接下來的變化時,才發現 JDK 10 預計將會移除 javah。

所以我對自己備份的 tclBlend 做了一些更動,主要是使用 javac -h 來產生 header file,然後移除掉 javah 的部份。不過這也表示,接下來的版本需要使用 JDK8 或者是更高的版本才行。

另外就是我在嘗試的過程中發現 file.toURL() 已經在 JDK6 被標記 deprecation,所以我也對這部份進行了小修改。

2018-01-10

yajl-tcl

yajl-tcl: Tcl bindings for Yet Another JSON Library


Ubuntu 我很快的就安裝成功了,openSUSE 則遇到一點問題。目前 openSUSE Leap 42.3 的 libyajl-devel 沒有包含 pkgconfig 的部份,需要使用有包進去的版本。

寫好的 openSUSE RPM spec:
yajl-tcl-spec


會需要編譯是因為最新釋出的 TclWS (2.5.0) 有加這句話:yajl-tcl from flightaware github (only for rest-flavour requests)

所以更新完 tclws-spec 以後,我也建了一份 yajl-tcl spec(卡在 pkgconfig 的問題一段時間),只是如果有人需要的話,就可以直接建 RPM 然後使用。

2018-01-05

DiffUtilTcl

DiffUtilTcl: A Tcl extension for diff utility functions, like Longest Common Substring


Magicsplat Tcl/Tk for Windows 和 BAWT 都新增加了這個套件,所以我也寫了一個 openSUSE RPM spec,這樣可以建立 RPM 來測試。

連結:
tcl-DiffUtilTcl-spec

2018-01-04

tcl-cmark

tcl-cmark: Tcl bindings to the cmark-gfm GitHub Flavoured CommonMark/Markdown library


會引起我的再次注意是因為 Magicsplat Tcl/Tk for Windows 在新版中加入了這個套件。

這個套件需要先安裝 libcmark-gfm 才行。經過搜尋,openSUSE LEAP 42.3 看起來有 cmark 套件,我會研究一下是不是可以先安裝 cmark 套件以後編譯成功。如果可以,我會試著建立一個 tcl-cmark 的 openSUSE RPM spec。

更新:
看起來不行,檢查以後,TCLCMARK_LOCATE_CMARK 會檢查是否有安裝 libcmark-gfm,而二者的 library 名稱不同。所以如果要使用 tcl-cmark,需要先有 libcmark-gfm RPM spec 才行。

更新:
cmark-gfm 使用 release 的 0.28.0 會有 header 問題,需要 git clone 使用較新的 code 才行。

2017-12-08

JSON Web Token and Base64Url

我在讀維基百科的條目也有跟著使用 Tcl 驗證,我卡住了一段時間,因為最後的 Token 跟我程式計算出來的不同,不過最後我發現是 base64 和 Base64Url 有一些小小的不同,最後在搜尋 Tcler's wiki 以後知道要怎麼做了,下面是測試程式:

package require sha256

proc base64url_encode {string} {
    tailcall string map {+ - / _ = {}} [binary encode base64 $string]
}

proc base64url_decode {string} {
    tailcall binary decode base64 [string map {- + _ /} $string]
}


set header {{"alg":"HS256","typ":"JWT"}}
set payload {{"loggedInAs":"admin","iat":1422779638}}
set key {secretkey}

set unsignedToken "[base64url_encode $header].[base64url_encode $payload]"
set signature [base64url_encode [::sha2::hmac -bin -key $key $unsignedToken]]
set token "$unsignedToken.$signature"
puts $token

所以按照參考資料,寫了一個簡單的修正,然後編碼就正確了。

小知識: base64 != base64Url

* 更新:加入 decode 的部份

2017-12-06

Apache Rivet: status code and content type

最近在學習 Vue.js,所以有做一些 server 端的測試。寫法大致上是這樣:

Vue.js + Axios  -> send POST request to server -> get response

也就是所謂的前後端分離。在這個情況下,server 端(例如 Apache HTTP server + Apache Rivet 的組合)就變成比較像 web API 的部份。

XML 的範例可以看 Apache Rivet 的手冊:
#
# Ajax query servelet: a pseudo database is built into the dictionary 'composers' with the
# purpose of emulating the role of a real data source. 
# The script answers with  2 types of responses: a catalog of the record ids and a database 
# entry matching a given rec_id. The script obviously misses the error handling and the
# likes. Just an example to see rivet sending xml data to a browser. The full Tcl, JavaScript
# and HTML code are available from http://people.apache.org/~mxmanghi/rivet-ajax.tar.gz

# This example requires Tcl8.5 or Tcl8.4 with package 'dict' 
# (http://pascal.scheffers.net/software/tclDict-8.5.2.tar.gz)
# 

# A pseudo database. rec_id matches a record in the db

set composers [dict create  \
                1 {first_name Claudio middle_name "" last_name Monteverdi   \
                    lifespan 1567-1643 era Renaissance/Baroque}             \
                2 {first_name Johann middle_name Sebastian last_name Bach   \
                    lifespan 1685-1750 era Baroque }                        \
                3 {first_name Ludwig middle_name "" last_name "van Beethoven" \
                    lifespan 1770-1827 era Classical/Romantic}              \
                4 {first_name Wolfgang middle_name Amadeus last_name Mozart \
                    lifespan 1756-1791 era Classical }                      \
                5 {first_name Robert middle_name "" last_name Schumann      \
                    lifespan 1810-1856 era Romantic} ]

# we use the 'load' argument in order to determine the type of query
#
# load=catalog:         we have to return a list of the names in the database
# load=composer&amp;res_id=<id>: the script is supposed to return the record
#               having <id> as record id

if {[::rivet::var exists load]} {

# the xml declaration is common to every message (error messages included)

    set xml "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
    switch [::rivet::var get load] {
        catalog {
            append xml "<catalog>\n"
            foreach nm [dict keys $composers] {
                set first_name  [dict get $composers $nm first_name]
                set middle_name [dict get $composers $nm middle_name]
                set last_name   [dict get $composers $nm last_name]
                append xml "    <composer key=\"$nm\">$first_name "
                if {[string length [string trim $middle_name]] > 0} {
                    append xml "$middle_name "
                }
                append xml "$last_name</composer>\n"
            }
            append xml "</catalog>\n"
        }
        composer {
            append xml "<composer>\n"
            if {[::rivet::var exists rec_id]} {
                set rec_id [::rivet::var get rec_id]
                if {[dict exists $composers $rec_id]} {
                    foreach {k v} [dict get $composers $rec_id] {
                        append xml "<$k>$v</$k>\n"
                    }
                }
            }
            append xml "</composer>\n"
        }
    }

# we have to tell the client this is an XML message. Failing to do so
# would result in an XMLResponse property set to null

    ::rivet::headers type "text/xml"
    ::rivet::headers add Content-Length [string length $xml]
    puts $xml
}

只要知道怎麼拿取 request 的資料跟如何回應,就可以組成 web API。下面是我快速驗證的 prototype:
<?
    package require rl_json
    package require tdbc::postgres

    ::rivet::load_response response

    if {[info exists response(sqlstring)]==1} {
        set sqlstring $response(sqlstring)
    }

    if {[info exists response(sqlstring)]==0 || [info exists sqlstring]==0} {
        ::rivet::headers  numeric 400
    } else {
        ::rivet::headers numeric 200
        ::rivet::headers type application/json

        tdbc::postgres::connection create db -user danilo -password danilo -port 5432 -database danilo

        set stmt [db prepare $sqlstring]
        set resultset [$stmt execute]

        set cols [$resultset columns]
        set param_colitem [list]
        foreach col $cols {
            lappend param_colitem "string \"$col\""
        }
        rl_json::json set data "column" [rl_json::json new "array" {*}$param_colitem]

        set param_rowitems [list]
        $resultset foreach -as lists result {
            set param_rowitem [list]
            lappend param_rowitem "array"
            foreach row $result {
                lappend param_rowitem "string \"$row\""
            }

            lappend param_rowitems $param_rowitem
        }

        $resultset close
        $stmt close
        db close

        set data [rl_json::json set data "row" [rl_json::json new "array" {*}$param_rowitems]]
        puts $data
    }
?>

Apache Rivet 有自己的 Database API,可是因為我只是要寫一個測試用的程式,所以直接使用 TDBC。這個範例是送出 SQL statement,然後 POST 到 server 上,server 執行以後回傳結果。

因為並沒有做使用者資料驗證和檢查,這個測試程式請不要在測試目的以外的地方使用,這只是用來說明,我們可以使用 Apache Rivet 用來實作 web api。