Skip to content

Commit

Permalink
phantomjs multi-file package now includes the web server javascript
Browse files Browse the repository at this point in the history
  • Loading branch information
nicferrier committed Apr 13, 2012
1 parent ee59f13 commit 71ac05e
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/.elpa
/phantomjs-*
26 changes: 26 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# package.el multi-file package install

NAME=phantomjs
VERSION=0.0.8
DOC="Control phantomjs from Emacs"
package_parts = phantomjs.el phantomjs-pkg.el ghostweb.js

all: $(NAME)-$(VERSION).tar


clean:
rm -rf $(NAME)-$(VERSION).tar
rm -rf $(NAME)-$(VERSION)
rm -rf $(NAME)-pkg.el

$(NAME)-$(VERSION).tar: $(NAME)-$(VERSION)
tar cf $@ $<

$(NAME)-$(VERSION): $(package_parts)
mkdir $@
cp $(package_parts) $@

$(NAME)-pkg.el:
echo "(define-package \"$(NAME)\" \"$(VERSION)\" \"$(DOC)\")" > $@

# End
132 changes: 132 additions & 0 deletions ghostweb.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
ghostweb: phantomjs controlling server.
Copyright (C) 2012 by Nic Ferrier
This implements a phantom js module that starts a webserver on
whatever you specify as the first arg:
phantomjs ghostweb.js 8081
the server accepts commands using a verb/noun HTTP header
system; three commands (verbs) are supported: Open, Call and Exit.
To open a url inside the page in phantomjs send a GET request to the
server with these headers:
Command: Open
CommandArg: http://example.com
for example, from curl:
curl -i -H 'command: open' \
-H 'commandarg: http://localhost:8005/talk/stuff/html/index.html' \
http://localhost:8080/
will open the page: http://localhost:8005/talk/stuff/html/index.html
(presuming the ghostweb server is listening on port 8080)
Urls which fail to load will cause the ghostweb to return a 400.
To call a Javascript function inside the page in phantomjs send a
GET request to the server with these headers:
Command: Call
CommandArg: setTimeout(function () { console.log("blah!"); }, 2000)
the whole string is taken to be a Javascript expression that can be
called.
Any command causing an exception will cause a 400 and spit out some
details about the exception:
curl -i -H 'command: call' \
-H 'commandarg: (function(){throw new Error("hello");})()' \
http://localhost:8080/HTTP/1.1
produces:
400 Bad Request
Error:hello
Finally, to exit phantomjs send "exit":
curl -i -H 'command: exit' \
http://localhost:8080/HTTP/1.1
the server responds before quitting:
200 Ok
*/
try {
var system = require('system');
var page = require('webpage').create();

page.onConsoleMessage = function(msg) {
if (msg == "__quit__") {
console.log("all done");
phantom.exit();
}
else {
console.log("page: " + msg);
}
};

var server = require('webserver').create();
// Start the server on whatever we were told to listen to.
var service = server.listen(
system.args[1], function (request, response) {
if (request.headers.command == "open") {
page.open(request.headers.commandarg, function (status) {
if (status !== "success") {
console.log("server: open " + request.headers.commandarg + " bad");
response.statusCode = 400;
response.write("Bad\n");
response.close();
}
else {
console.log("server: open " + request.headers.commandarg + " ok");
response.statusCode = 200;
response.write("Ok\n");
response.close();
}
});
}
else if (request.headers.command == "call") {
var f = Function(
"try { return "
+ request.headers.commandarg
// we have to do a lot of wierd work to get errors to come out from evaluate
+ "} catch (e) { return {'type': 'error', 'name': e.name, 'message': e.message }; }"
);
var retval = page.evaluate(f);

if (retval["type"] == 'error') {
response.statusCode = 400;
response.write(retval.name + ":" + retval.message + "\n");
}
else {
response.statusCode = 200;
response.write(retval + "\n");
}
response.close();
}
else if (request.headers.command == "exit") {
response.statusCode = 200;
response.write("Ok\n");
response.close();
phantom.exit();
}
else {
console.log("server: unknown request");
response.statusCode = 404;
response.write("Unknown\n");
response.close();
}
});
}
catch (e) {
console.log(e);
phantom.exit();
}
84 changes: 82 additions & 2 deletions phantomjs.el
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
;;; phantom.el --- handle phantomjs
;;; phantomjs.el --- handle phantomjs -*- lexical-binding: t -*-

;; Copyright (C) 2012 Nic Ferrier

;; Author: Nic Ferrier <nferrier@ferrier.me.uk>
;; Maintainer: Nic Ferrier <nferrier@ferrier.me.uk>
;; Created: 12 April 2012
;; Version: 0.0.3
;; Keywords: lisp

;; This file is NOT part of GNU Emacs.
Expand Down Expand Up @@ -82,6 +81,87 @@ SCRIPTS is a list of scripts to be passed to the process."
(set-process-sentinel proc 'phantomjs--sentinel)
(puthash name proc phantomjs--proc)))

(defconst phantomjs--base (file-name-directory load-file-name)
"The base directory for the phantomjs elisp files.")

;;;###autoload
(defun phantomjs-server (name port complete-callback)
(interactive)
"Run a phantomjs process with NAME and a webserver on PORT.
COMPLETE-CALLBACK is called when it completes.
The webserver running on PORT is used to send commands to the
phantomjs instance using a special protocol.
Returns the process object representing the connection to
phantomjs."
;; need to check phantomjs--proc for name clash
(let ((proc
(start-process
(symbol-name name)
(concat "* phantomjs-" (symbol-name name) " *")
(expand-file-name (format "%s/bin/phantomjs" phantomjs-home))
(expand-file-name "ghostweb.js" phantomjs--base)
(format "%d" port))))
(process-put proc :phantomjs-end-callback complete-callback)
(process-put proc :phantomjs-web-port port)
(set-process-sentinel proc 'phantomjs--sentinel)
(puthash name proc phantomjs--proc)
proc))

(defun phantomjs--callback (status args)
(let ((http-status (save-match-data
(re-search-forward "HTTP/1.1 \\([0-9]+\\) .*" nil 't)
(match-string 1))))
(message "phantomjs callback got status %s" http-status)
(when (eq 200 (string-to-number http-status))
(funcall (car args) http-status (cadr args)))))

(defun phantomjs-open (proc url callback)
"Open URL in PROCESS, the phantomjs instance.
When the url is opened call CALLBACK in the same way as with
`url-retrieve'."
(assert (process-get proc :phantomjs-web-port))
(let ((port (process-get proc :phantomjs-web-port))
(url-request-extra-headers
`(("command" . "open")
("commandarg" . ,url))))
(url-retrieve
(format "http://localhost:%d" port)
'phantomjs--callback
(list (list callback proc)))))

(defun phantomjs-call (proc javascript callback)
"Call JAVASCRIPT in PROCESS, the phantomjs instance.
When the Javascript completes call CALLBACK in the same way as
with `url-retrieve'."
(assert (process-get proc :phantomjs-web-port))
(let ((port (process-get proc :phantomjs-web-port))
(url-request-extra-headers
`(("command" . "call")
("commandarg" . ,javascript))))
(url-retrieve
(format "http://localhost:%d" port)
'phantomjs--callback
(list (list callback proc)))))

(defun phantomjs-exit (proc callback)
"Make PROCESS, the phantomjs instance, exit.
When the exit is acknowledged call CALLBACK in the same way as
with `url-retrieve'."
(assert (process-get proc :phantomjs-web-port))
(let ((port (process-get proc :phantomjs-web-port))
(url-request-extra-headers
`(("command" . "exit"))))
(url-retrieve
(format "http://localhost:%d" port)
'phantomjs--callback
(list (list callback proc)))))

(provide 'phantomjs)

;;; phantom.el ends here

0 comments on commit 71ac05e

Please sign in to comment.