Skip to content
This repository has been archived by the owner on Dec 16, 2023. It is now read-only.

Commit

Permalink
Wrapped up API for accessing last request and last response.
Browse files Browse the repository at this point in the history
  • Loading branch information
assaf committed Dec 18, 2010
1 parent f97897e commit 60668c2
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 50 deletions.
28 changes: 23 additions & 5 deletions lib/zombie/browser.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ class Browser
# TODO: Fix
window.Image = ->

# TODO: Fix
responses = []
@__defineGetter__ "response", -> responses[responses.length - 1]
@__defineSetter__ "response", (response)-> responses.push response


# Events
# ------
Expand Down Expand Up @@ -340,4 +335,27 @@ class Browser
throw new Error("No BUTTON '#{name}'")


# Debugging
# ---------

trail = []
this.record = (request)->
trail.push pending = { request: request }
pending
# ### browser.last_request => Object
#
# Returns the last request sent by this browser. The object will have the
# properties url, method, headers, and if applicable, body.
@__defineGetter__ "last_request", -> trail[trail.length -1]?.request
# ### browser.last_response => Object
#
# Returns the last response received by this browser. The object will have the
# properties status, headers and body. Long bodies may be truncated.
@__defineGetter__ "last_response", -> trail[trail.length -1]?.response
# ### browser.last_error => Object
#
# Returns the last error received by this browser in lieu of response.
@__defineGetter__ "last_error", -> trail[trail.length -1]?.error


exports.Browser = Browser
20 changes: 16 additions & 4 deletions lib/zombie/eventloop.coffee
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
URL = require("url")


# Handles the Window event loop, timers and pending requests.
class EventLoop
constructor: (browser, window)->
Expand Down Expand Up @@ -120,11 +123,20 @@ class EventLoop
# Counts outstanding requests.
requests = 0
# Used internally for the duration of an internal request (loading
# resource, XHR). Function is invoked with single argument (done), a
# function to call when done processing the request.
this.request = (fn)->
# resource, XHR). Also collects request/response for debugging.
#
# Function is called with request object and the function to be called
# next. After storing the request, that function is called with a single
# argument, a done callback. It must call the done callback when it
# completes processing, passing error and response arguments.
this.request = (request, fn)->
++requests
fn ->
pending = browser.record request
fn (err, response)->
if err
pending.error = err
else
pending.response = response
if --requests == 0
wait() for wait in waiting
waiting = []
Expand Down
6 changes: 4 additions & 2 deletions lib/zombie/history.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class History
headers["content-length"] = body.length
headers["cookie"] = cookies._header(url)
path = url.pathname + (url.search || "")
window.request (done)=>
window.request { url: URL.format(url), method: method, headers: headers, body: body }, (done)=>
request = client.request(method, path, headers)
client.on "error", (err)->
console.error "Error requesting #{URL.format(url)}", error
Expand All @@ -138,10 +138,12 @@ class History
# application.
if error
console.error error
done error
event = document.createEvent("HTMLEvents")
event.initEvent "error", true, false
document.dispatchEvent event
done()
else
done null, { status: response.statusCode, headers: response.headers, body: body }
request.end body, "utf8"

# Called when we switch to a new page with the URL of the old page.
Expand Down
6 changes: 3 additions & 3 deletions lib/zombie/jsdom_patches.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ core.resourceLoader.load = (element, href, callback)->
window = document.parentWindow
ownerImplementation = document.implementation
if ownerImplementation.hasFeature('FetchExternalResources', element.tagName.toLowerCase())
url = URL.parse(@resolve(document, href))
window.request (done)=>
window.request { url: href, method: "GET", headers: {} }, (done)=>
url = URL.parse(@resolve(document, href))
loaded = (data, filename)->
done()
done null, { status: 200, headers: {}, body: data.slice(0,100) }
callback.call this, data, filename
if url.hostname
@download url, @enqueue(element, loaded, url.pathname)
Expand Down
69 changes: 33 additions & 36 deletions lib/zombie/xhr.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -29,47 +29,44 @@ XMLHttpRequest = (browser, window)->
@getResponseHeader = @getAllResponseHeader = ->
# Open method.
@open = (method, url, async, user, password)->
window.request (done)=>
method = method.toUpperCase()
throw new core.DOMException(core.SECURITY_ERR, "Unsupported HTTP method") if /^(CONNECT|TRACE|TRACK)$/.test(method)
throw new core.DOMException(core.SYNTAX_ERR, "Unsupported HTTP method") unless /^(DELETE|GET|HEAD|OPTIONS|POST|PUT)$/.test(method)
url = URL.parse(URL.resolve(window.location, url))
url.hash = null
throw new core.DOMException(core.SECURITY_ERR, "Cannot make request to different domain") unless url.host == window.location.host
throw new core.DOMException(core.NOT_SUPPORTED_ERR, "Only HTTP protocol supported") unless url.protocol == "http:"
[user, password] = url.auth.split(":") if url.auth
method = method.toUpperCase()
throw new core.DOMException(core.SECURITY_ERR, "Unsupported HTTP method") if /^(CONNECT|TRACE|TRACK)$/.test(method)
throw new core.DOMException(core.SYNTAX_ERR, "Unsupported HTTP method") unless /^(DELETE|GET|HEAD|OPTIONS|POST|PUT)$/.test(method)
url = URL.parse(URL.resolve(window.location, url))
url.hash = null
throw new core.DOMException(core.SECURITY_ERR, "Cannot make request to different domain") unless url.host == window.location.host
throw new core.DOMException(core.NOT_SUPPORTED_ERR, "Only HTTP protocol supported") unless url.protocol == "http:"
[user, password] = url.auth.split(":") if url.auth

# Aborting open request.
@_error = null
aborted = false
# Aborting open request.
@_error = null
aborted = false
@abort = ->
aborted = true
reset()

# Allow setting headers in this state.
headers = {}
@setRequestHandler = (header, value)-> headers[header.toString().toLowerCase()] = value.toString()
# Allow calling send method.
@send = (body)->
# Aborting request in progress.
@abort = ->
aborted = true
done()
@_error = new core.DOMException(core.ABORT_ERR, "Request aborted")
stateChanged 4
reset()

# Allow setting headers in this state.
headers = {}
@setRequestHandler = (header, value)-> headers[header.toString().toLowerCase()] = value.toString()
# Allow calling send method.
@send = (data)->
# Aborting request in progress.
@abort = ->
aborted = true
done()
@_error = new core.DOMException(core.ABORT_ERR, "Request aborted")
stateChanged 4
reset()

client = http.createClient(url.port, url.hostname)
if data && method != "GET" && method != "HEAD"
headers["content-type"] ||= "text/plain;charset=UTF-8"
else
data = ""
headers["cookie"] = cookies._header(url)
client = http.createClient(url.port, url.hostname)
if body && method != "GET" && method != "HEAD"
headers["content-type"] ||= "text/plain;charset=UTF-8"
else
body = ""
headers["cookie"] = cookies._header(url)
window.request { url: URL.format(url), method: method, headers: headers, body: body }, (done)=>
request = client.request(method, url.pathname, headers)
request.end data, "utf8"
request.end body, "utf8"
request.on "response", (response)=>
return request.destroy() if aborted
response.setEncoding "utf8"
# At this state, allow retrieving of headers and status code.
@getResponseHeader = (header)-> response.headers[header.toLowerCase()]
Expand All @@ -89,11 +86,11 @@ XMLHttpRequest = (browser, window)->
browser.response = [response.statusCode, response.headers, body]
cookies._update url, response.headers["set-cookie"]
stateChanged 4
done()
done null, { status: response.statusCode, headers: response.headers, body: body }

client.on "error", (err)=>
console.error "XHR error", err
done()
done err
@_error = new core.DOMException(core.NETWORK_ERR, err.message)
stateChanged 4
reset()
Expand Down

0 comments on commit 60668c2

Please sign in to comment.