I’m posting the code for my “project” — the technical details of which are terse.
I’m not going to describe a lot, here, but as I go…
(If you’re not a clojure coder, look away)
-
;; Fiddlr_To_Xml
-
;; my project to convert a Fiddler2 Session Archive (.SAZ) to XML file(s)
-
;; for info on Fiddler2 see http://fiddler2.com/fiddler2/
-
;; for this to work (the unzipping of the .SAZ, anyway)
-
;; I’m depending on 7-Zip (as you can see below)
-
(require ‘clojure.contrib.shell) ; not recommended, but to get things working…
-
(def zipExe “\”C:\\Program Files\\7-Zip\\7z.exe\”“) ; constant strings
-
(def FiddlrCaptsDir “C:\\Documents and Settings\\gparks1\\My Documents\\Fiddler2\\Captures”)
-
;; given a filename (a fiddler session arhive), unzip it
-
(defn unzip [fn]
-
(println “unzipping “ fn “…”)
-
(let [res (clojure.contrib.shell/sh zipExe "x" fn "-y" :dir FiddlrCaptsDir)]
-
;(prn res)
-
(println “Exit Code: “ (:exit res))
-
res))
-
; for whatever reason, I’m not getting a real exit code, but it works, so I don’t care
(via
http://quickhighlighter.com
)
Now, the following felt like real functional programming (because I’m actually treating a function as a first class object):
; (defn printElt [i]
; (if (. i hasMoreNodes)
; (let [n (. i nextNode)]
; (println n)
; (printElt i))));; my revised helper function — used for many things, including printing
;; given a SimpleNodeIterator, and a function
;; iterate and call fn for each
;;
;; (I bet there’s an easier way, but this works for now)
;; I was just happy to be passing a function around
;; as an argument to another function…
(defn iterEltFn [i fn]
(if (. i hasMoreNodes)
(let [n (. i nextNode)]
(fn n)
(recur i fn))))
And using that here and here:
;; new and improved printElt — call iterEltFn w/ a function (println)
(defn printElt [i]
(iterEltFn i (fn [x] (println x))))
;; 2nd function using iterEltFn; this time, print the element, and the children
(defn printEltChild [i]
(iterEltFn i (fn [x]
(println x)
(printElt (.. x (getChildren) (elements)))
(println)
)))
The following defines a “constant,” two helper functions and a structure (used below):
(def fStr (new org.htmlparser.filters.StringFilter “/prweb/PRTraceServlet?pzDebugRequest=Trace”)); helper function to take a URI and convert to a FileName string
(defn uri_to_filename [uri]
(if (= 0 (.. uri (substring 0 6) (toUpperCase) (compareTo “FILE:/”)))
(let [r (. uri substring 6)]
(.. r (replace “%20″ ” “) (replace “/” “\\“)))));; helper function to split a string into this line (up to CR), and the remainder…
(defn nlr [s] ; Next Line and Rest
(let [cr (.indexOf s "\n")
l (.substring s 0 cr)
r (.substring s (+ cr 1))] (list l r)))
;; creating a structure for HTTP header
(defstruct header :status :date :keep-alive :length :control :connection :type :language)
This header function is the first of things to re-do, when I move forward:
;; parse the lines from a Fiddler2 server response file
;;
;; here is an ‘example’
;; HTTP/1.1 200 OK
;; Date: Wed, 06 Jul 2011 18:22:56 GMT
;; Cache-Control: max-age=0
;; Keep-Alive: timeout=10, max=980
;; Connection: Keep-Alive
;; Content-Type: text/xml;charset=UTF-8
;; Content-Language: en-US
;; Content-Length: 76960
;;
;; (note the ‘blank’ line at the end!)
;; the HTTP (first line, w/ status) and Date have been parsed separately, so the str argument here
;; begins at the 3rd line. Although there is some semblence of “order” to the lines, it is not dependent on such,
;; save that the blank line be last and the last line is blank.
;; [And there's probably a better way to structure this, as well...]
(defn hdr [sm str]
(let [[ln rem] (nlr str)]
(if (.startsWith ln “Cache-Control:”)
(let [cc (.substring ln 15)]
(assert (nil? (:control sm)))
(recur (assoc sm :control cc) rem))
(if (.startsWith ln “Keep-Alive:”)
(let [keep (.substring ln 12)]
(assert (nil? (:keep-alive sm)))
(recur (assoc sm :keep-alive keep) rem))
(if (.startsWith ln “Content-Type:”)
(let [typ (.substring ln 14)]
(assert (nil? (:type sm)))
(recur (assoc sm :type typ) rem))
(if (.startsWith ln “Connection:”)
(let [conn (.substring ln 12)]
(assert (nil? (:connection sm)))
(recur (assoc sm :connection conn) rem))
(if (.startsWith ln “Content-Length:”)
(let [len (.substring ln 16)]
(assert (nil? (:length sm)))
(recur (assoc sm :length len) rem))
(if (.startsWith ln “Content-Language:”)
(let [lang (.substring ln 18)]
(assert (nil? (:language sm)))
(recur (assoc sm :language lang) rem))
(if (= 0 (count (.trim ln)))
(list sm rem)
(println “count wasn’t zero:” (count ln)))))))))))
The rest of this entry is presented in reverse order, so the logical flow is evident.
Here is the main function:
;; * main function to parse an index
;; (no params necessary — the filename is at a well-known location)
;; calls printRowInfo, which does all the heavy lifting
;; (this could actually be re-structured to do the primary filtering here,
;; and printRowInfo could be simplified…)
;; ****************************************************************************************************
(defn parseIndex []
(let [parser (new org.htmlparser.Parser (str "file:///" FiddlrCaptsDir "\\_index.htm"))
; iter (. parser elements)
; fChStr (new org.htmlparser.filters.HasChildFilter fStr)
fCh2Str (new org.htmlparser.filters.HasChildFilter
(new org.htmlparser.filters.HasChildFilter fStr))
t (. parser parse fCh2Str) ; a nodelist!
]
; (printElt iter); (printEltChild (. t elements))
(printRowInfo (. t elements))
)
)
Which calls printRowInfo for the entire list of elements:
(comments “extracted” from quickhighlighting for “clarity”
;; ****************************************************************************************************
;; main (internal) function
;; given a SingleNodeIterator argument, iterate
;; the function passed to iterEltFn gets the text (already known to contain "pzDebugRequest=Trace"),
;; and the server filename (from the children of the first child) --
;; the link from the first element of the list, which is the anchor containing the text "S" of the children of the 1st TD elt
;; [sSvrFile from datas, which is getFirstChild [TD] getChildren extractAllNodesThatMatch
;; {[anchors] with children which are strings equal to "S"} elementAt 0, extractLink
;; each iteration calls readXmlFile
;; ****************************************************************************************************
Of interest, see that this function relies exclusively on iterEltFn and only creates an (anonymous) function to pass to it — I could make this a defined function, separating that work out
(iterEltFn r (fn [n]
(let [datas (. (. n getFirstChild) getChildren) ; slow way to call a member of a result
; the children of the first child (a "TableColumn" [TD]) are:
; LinkTag [A] w/ text ‘C’ (Client)
; LinkTag [A] w/ text ‘S’ (Server)
; LinkTag [A] w/ text ‘M’ (M___)
fChStr (new org.htmlparser.filters.HasChildFilter fStr)
; the following is a short-cut
; n.getChildren().extractAllNodesThatMatch(fChStr).elementAt(0).getFirstChild()…
txt (.. n (getChildren) (extractAllNodesThatMatch fChStr) (elementAt 0)
(getFirstChild))
sSvrFile (.. datas (extractAllNodesThatMatch
(new org.htmlparser.filters.HasChildFilter
(new org.htmlparser.filters.StringFilter “S”)))
(elementAt 0)(extractLink))
]
; (println “Node =” n)
; (println “first child (data) w/ three <A> tags:” datas)
; (println “text node is” txt)
; (println “file is ” (uri_to_filename sSvrFile))
(readXmlFile (uri_to_filename sSvrFile))))))
And, finally, a good, stand-alone function to read an XML file:
;; N.B. “fn” as a param means “filename” not “function”
(defn readXmlFile [fn]
;; read the filename
(let [buffRd (new java.io.BufferedReader (new java.io.FileReader fn))
lns (slurp buffRd)
h (first lns)]
;; if the file contents (lns = lines [of the file])
;; starts with an HTTP <<status>>, continue
(if (. lns startsWith “HTTP”)
;; split the next line (the hard way), find the whitespace of this (the status) line –
;; what remains is the “status,” number and text –
;; and the linebreak of the next line
(let [lnbrk (.indexOf lns "\n")
dt (.substring lns (+ lnbrk 1))
idxSp (.indexOf lns " ")
status (. lns substring (+ 1 idxSp) (- lnbrk 1))
eol (.indexOf dt "\n")
]
; (println “HTTP status:” (. lns substring (+ 1 idxSp) (- lnbrk 1 )))
(if (not (= status “302 Found”))
;; call the hdr function…
(let [[hdr r2] (hdr (struct-map header :status status :date (.substring dt 6 eol))
(.substring dt (+ eol 1)))]
(println “header is” hdr)
;; then parse the remaining data from the file
(let [p (clojure.xml/parse (new java.io.ByteArrayInputStream (.getBytes r2)))]
;; ** more to do here **
p)
)
;; if this was a “302 Found” status, we’re dealing with a VIP exchange, and there’s nothing useful here…
(println “Location is:” (. dt substring 10 eol))
))
)))
Commentary, what there is, to follow…
August 13, 2011 at 10:21 pm |
[...] More than a Tweet Random Thoughts and Musings « My Clojure Code before Updates [...]