USING: accessors arrays assocs byte-vectors checksums checksums.md5 constructors continuations destructors fry hashtables io.encodings.binary io.encodings.string io.encodings.utf8 io.sockets io.streams.duplex kernel locals math math.parser mongodb.cmd mongodb.msg strings namespaces sequences splitting ; IN: mongodb.connection : md5-checksum ( string -- digest ) utf8 encode md5 checksum-bytes hex-string ; inline TUPLE: mdb-db name username pwd-digest nodes collections ; TUPLE: mdb-node master? { address inet } remote ; CONSTRUCTOR: mdb-node ( address master? -- mdb-node ) ; TUPLE: mdb-connection instance node handle remote local buffer ; : connection-buffer ( -- buffer ) mdb-connection get buffer>> 0 >>length ; inline USE: mongodb.operations CONSTRUCTOR: mdb-connection ( instance -- mdb-connection ) ; : check-ok ( result -- errmsg ? ) [ [ "errmsg" ] dip at ] [ [ "ok" ] dip at ] bi ; inline : ( name nodes -- mdb-db ) mdb-db new swap >>nodes swap >>name H{ } clone >>collections ; : master-node ( mdb -- node ) nodes>> t of ; : slave-node ( mdb -- node ) nodes>> f of ; : with-connection ( connection quot -- * ) [ mdb-connection ] dip with-variable ; inline : mdb-instance ( -- mdb ) mdb-connection get instance>> ; inline : index-collection ( -- ns ) mdb-instance name>> "system.indexes" "." glue ; inline : namespaces-collection ( -- ns ) mdb-instance name>> "system.namespaces" "." glue ; inline : cmd-collection ( cmd -- ns ) admin?>> [ "admin" ] [ mdb-instance name>> ] if "$cmd" "." glue ; inline : index-ns ( colname -- index-ns ) [ mdb-instance name>> ] dip "." glue ; inline : send-message ( message -- ) [ mdb-connection get handle>> ] dip '[ _ write-message ] with-stream* ; : send-query-plain ( query-message -- result ) [ mdb-connection get handle>> ] dip '[ _ write-message read-message ] with-stream* ; : send-query-1result ( collection assoc -- result ) -1 >>return# send-query-plain objects>> ?first ; : send-cmd ( cmd -- result ) [ cmd-collection ] [ assoc>> ] bi send-query-1result ; inline > ] [ pwd-digest>> ] bi and ; : calculate-key-digest ( nonce -- digest ) mdb-instance [ username>> ] [ pwd-digest>> ] bi 3array concat md5-checksum ; inline : build-auth-cmd ( cmd -- cmd ) mdb-instance username>> "user" set-cmd-opt get-nonce [ "nonce" set-cmd-opt ] [ ] bi calculate-key-digest "key" set-cmd-opt ; inline : perform-authentication ( -- ) authenticate-cmd make-cmd build-auth-cmd send-cmd check-ok [ drop ] [ throw ] if ; inline : authenticate-connection ( mdb-connection -- ) [ mdb-connection get instance>> auth? [ perform-authentication ] when ] with-connection ; inline : open-connection ( mdb-connection node -- mdb-connection ) [ >>node ] [ address>> ] bi [ >>remote ] keep binary [ >>handle ] dip >>local 4096 >>buffer ; : get-ismaster ( -- result ) "admin.$cmd" H{ { "ismaster" 1 } } send-query-1result ; : split-host-str ( hoststr -- host port ) ":" split [ first ] [ second string>number ] bi ; inline : eval-ismaster-result ( node result -- ) [ [ "ismaster" ] dip at dup string? [ >integer 1 = ] [ ] if >>master? drop ] [ [ "remote" ] dip at [ split-host-str f >>remote ] when* drop ] 2bi ; : check-node ( mdb node -- ) [ &dispose ] dip [ [ open-connection ] [ 3drop f ] recover ] keep swap [ [ get-ismaster eval-ismaster-result ] with-connection ] [ drop ] if* ; : nodelist>table ( seq -- assoc ) [ [ master?>> ] keep 2array ] map >hashtable ; PRIVATE> :: verify-nodes ( mdb -- ) [ V{ } clone :> acc mdb dup master-node [ check-node ] keep :> node1 mdb node1 remote>> [ [ check-node ] keep ] [ drop f ] if* :> node2 node1 [ acc push ] when* node2 [ acc push ] when* mdb acc nodelist>table >>nodes drop ] with-destructors ; ERROR: mongod-connection-error address message ; : mdb-open ( mdb -- mdb-connection ) clone [ verify-nodes ] [ ] [ ] tri master-node [ open-connection [ authenticate-connection ] keep ] [ drop nip address>> "Could not open connection to mongod" mongod-connection-error ] recover ; : mdb-close ( mdb-connection -- ) [ [ dispose ] when* f ] change-handle drop ; M: mdb-connection dispose mdb-close ;