Coder Social home page Coder Social logo

nicferrier / elnode Goto Github PK

View Code? Open in Web Editor NEW
477.0 39.0 49.0 2.04 MB

evented io webserver right inside your emacs.

Home Page: http://nicferrier.github.com/elnode

License: GNU General Public License v3.0

Emacs Lisp 97.77% Makefile 0.12% JavaScript 0.87% HTML 0.46% CSS 0.27% Shell 0.52%

elnode's Introduction

Elnode

An evented IO webserver in Emacs Lisp.

Requirements

Elnode will not run properly on anything less than Emacs 24. Elnode requires Emacs 24's lexical binding as it makes extensive use of closures.

Rationale

Elnode is a great for these things:

  • nice simple server with few dependancies (just Emacs and cat basically)
  • prototyping webapps
  • browser testing
  • asynchronous apps, like chat apps

Installation

Elnode is packaged in marmalade.

For dealing with package repositories check out the Emacs Wiki but the short version is to add the following to your .emacs or your .emacs.d/init.el:

(add-to-list 
   'package-archives
   '("marmalade" . "http://marmalade-repo.org/packages/"))

And then do:

M-x list-packages

find Elnode in the list and press i or ENTER to install it.

If you don't want to use packages you can just install elnode.el on your load-path somewhere and:

(require 'elnode)

Install from this repository

Installing from the sources is complex and requires the dependancies declared in the file recipes/elnode.

The recipe file is used by elpakit or other tools to build the package.

elpakit can help build elnode, and help with running tests. Install elpakit from marmalade and then you can build Elnode with elpakit by doing:

M-x elpakit-make-multi

in the Elnode directory.

You can build the Elnode package and run the Elnode tests on that package with the following lisp:

(elpakit-test (list elnode-directory) 'elnode-tests 'elnode)

Where elnode-directory specifies your local Elnode repository directory.

The list can include more repository directories which will be combined into a single package archive.

Out of the box

When Elnode initializes it automatically starts a webserver and a Wiki engine.

If you:

M-x customize-group
elnode

you can alter a number of variables pertaining to the default configuration, including the directory used to keep files.

By default the package installs files in your .emacs.d - it uses a directory called elnode for the Wiki root and the webroot. Both are configurable with Elnode config variables.

You can also just ignore the built in stuff completely and write your own servers.

What Elnode servers are running?

Elnode tracks the servers an Emacs instance is running and you can see the view of that with:

M-x list-elnode-servers

The list shows TCP ports and handlers and you can press return on a handler and move to it's source code definition.

You can kill a server by hitting "k" on it.

How does it work?

The simplest thing that Elnode does is let you start a webserver on a directory:

M-x elnode-make-webserver [RET] 
Serve files from: [enter directory] [RET]
TCP Port (try something over 8000): 8009 [RET]

and there will be a webserver started on port 8009 serving files from whatever directory you specified.

By default Elnode starts that server on the host specified by elnode-init-host which is a variable you can customize:

M-x customize-variable [RET] elnode-init-host [RET]

Take care though, you don't want to expose your Emacs session to the Internet.

You can also use the prefix key to specify the host for just this one server:

C-u M-x elnode-make-webserver [RET] 
Docroot: [enter directory] [RET]
Port: 8009 [RET]
Host: 0.0.0.0

Basic Elnode for programmers

Elnode's power is most visible to programmers though.

You can define a handler function:

(defun my-test-handler (httpcon)
  "Demonstration function"
  (elnode-http-start httpcon 200 '("Content-type" . "text/html"))
  (elnode-http-return httpcon "<html><b>HELLO!</b></html>"))

And then start the server:

(elnode-start 'my-test-handler :port 8010 :host "localhost")

You can also start the server interactively... with:

M-x elnode-start

it interactively asks for the handler function and a port.

Stopping the server

If you can remember the port you started your server on then you'll be able to stop it, like:

(elnode-stop 8010)

You can also stop interactively:

M-x elnode-stop

API

Mapping paths to handlers

elnode-hostpath-dispatcher takes a-list of path/handler mappings:

##!emacs-lisp
(defvar 
   my-app-routes 
   '(("^my-host.example.com//wiki/\\(.*\\)" . elnode-wikiserver)
     ("^admin.example.com//admintool/\\(.*\\)" . user-admin)
     ("^.*//\\(.*\\)" . elnode-webserver)))

(defun root-handler (httpcon)
  (elnode-hostpath-dispatcher httpcon my-app-routes))

(elnode-start 'root-handler :port 8009)

This will create a server on port 8009 being handled by root-handler which will root the requests to the appropriate handler.

Any request for the host my-host.example.com with the path /wiki/ will be sent to the Elnode Wiki engine.

Any request for the host admin.example.com with the path /admintool/ will be sent to the user-admin handler, presumably that is defined somewhere.

Any other request will be sent to the default Elnode webserver.

Elnode itself uses a hostpath dispatcher on the default Elnode server. This can actually be configured with the variable elnode-hostpath-default-table, so you can actually change the default behaviour of the Elnode default server just with Emacs config.

The use of regexs in Elnode's mapping is supported by other tools. Sub-expressions are capturable in mapping support routines such as elnode-docroot-for.

When a handler is called by elnode-hostpath-dispatcher then the parts of the match are available through the function elnode-http-mapping. So we could code the user-admin handler like this:

##! emacs-lisp
(defun user-admin (httpcon)
  (let ((username (elnode-http-mapping httpcon 1)))
    (user-admin-send-admin-page httpcon username)))

The (elnode-http-mapping httpcon 1) accesses the first sub-expression of the regex that caused the match:

     ("^admin.example.com//admintool/\\(.*\\)" . user-admin)

so, everything AFTER the admintool/.

Some tools in Elnode do this for you, so you don't have to. Again, look at elnode-docroot-for.

Serving files

There are several helpers for serving files with Elnode. You can serve directories of files directly by making a webserver handler. A function elnode-webserver-handler-maker can make webservers:

##! emacs-lisp

(setq my-webserver
      (elnode-webserver-handler-maker "~/my-webroot"))

(elnode-start my-webserver :port 8010)

The Elnode webserver also produces index pages and can be configured with a number of variables:

  • elnode-webserver-index-page-template defines the page template used for the index
  • elnode-webserver-index-file-template defines the template for each file in the index, normally it's just an A tag ponting to the file.

More controlled serving

If you need more control over serving files you can write handlers with elnode-docroot-for. This does a lot of complex work for you to map a directory tree to a webserver namespace.

This example shows how to use elnode-docroot-for

##! emacs-lisp

(defun elnode-org-handler (httpcon)
  (elnode-docroot-for "~/work/org"
      with org-file
      on httpcon
      do (with-current-buffer (find-file-noselect org-file)
           (let ((org-html
                  ;; This might throw errors so you could condition-case it
                  (org-export-as-html 3 nil nil 'string)))
             (elnode-send-html httpcon org-html)))))

The first argument is the directory of files which you want to serve, then with variable specifies the name of a variable to use in the body of the code which will be bound to the filename of the file the user wants. Then on httpcon specifies the HTTP connection to use and then do .... specifies the code to use.

elnode-docroot-for processes incomming requests on the httpcon you specify by checking the request matches a file in the directory you specify (it sends a 404 if it does not find one).

It also does last modified caching on the file and sends an HTTP 304 response if the file has not been updated since the last request.

If a matching file exists and the it is not cached then elnode-docroot-for runs the do code to send the response correctly.

Sending files

Elnode also has elnode-send-file for sending files to the response, along with elnode-docroot-for this makes a powerful simple webserver tool. elnode-send-file can be used to send any arbitary file:

##! emacs-lisp
(defun my-status-page (httpcon)
  (elnode-http-start httpcon 200 '("Content-type" . "text/html"))
  (elnode-send-file httpcon "~/static-status-file.html"))

A handler that will only ever respond with one static file. Of course, this isn't very interesting, combined with elnode-docroot-for it can be used to serve directories and the like, or you could work out the filename to be sent with some other method.

There is another use for elnode-send-file which is simple templating. You can pass parameters to elnode-send-file and it will template them into the file:

(defun my-templater(httpcon)
  (let ((hash (make-hash-table 
                 :test 'equal 
                 :data "username" "nicferrier")))
     (elnode-http-start httpcon 200 '("Content-type" . "text/html"))
     (elnode-send-file 
         httpcon "~/my-template.html" 
         :replacements hash)))

The template file must have sections marked up like:

<<html &lt;!##E username E##!&gt; html>>

for each of the variables.

This makes for simple but quite powerful templating.

Really Really simple file sending

It's also possible to make send file functions automatically so if you want to map a handler that serves just one file in a dispatcher that's possible:

##! emacs-lisp
`(("^my-host.example.com//wiki/\\(.*\\)" . elnode-wikiserver)
  ("^.*//styles.css" . ,(elnode-make-send-file "~/mainstyles.css"))
  ("^.*//\\(.*\\)" . elnode-webserver))

It's also possible to use templating with this style of programming by passing a function returning the alist variable map as :replacements:

##! emacs-lisp
(defun my-templater ()
  '(("username" . "william occam")))

`(("^my-host.example.com//wiki/\\(.*\\)" . elnode-wikiserver)
  ("^.*//styles.css" . ,(elnode-make-send-file 
                           "~/mainstyles.css"
                           :replacements 'my-templater))
  ("^.*//\\(.*\\)" . elnode-webserver))

This makes templating and setting up very simple websites very easy indeed.

Accessing data in the HTTP request

There are a bunch of functions that do what you would expect about data in the HTTP request:

##! emacs-lisp

(elnode-http-method httpcon)
=> "POST"

(elnode-http-pathinfo httpcon)
=> "/wiki/blah.creole"

(elnode-http-query httpcon)
=> "a=10&b=20&c=the+quick+brown+fox"

(elnode-http-params httpcon)
=> (("a" . "10")("b" . "20")("c" . "the quick brown fox"))

(elnode-http-param httpcon "username")
=> "nicferrier"

(elnode-http-cookie httpcon "session-id")
=> "1213313"

(elnode-http-header httpcon "Date")
=> "Mon, Feb 27 2012 22:10:21 GMT"

(elnode-http-header httpcon 'date)
=> "Mon, Feb 27 2012 22:10:21 GMT"

(elnode-http-header httpcon 'date :time) ;; with convert flag set to :time
=> (20299 65357)

Note that Elnode generally can accept symbol's as well as strings to name things, if it can't it's a bug, please report it.

Also, Elnode can handle some conversions sometimes. I would like to respond to user demand about when and where to do that and what to do. Please give me feedback.

Elnode's raw data

Elnode stores most of it's internal state on the connection object and it's all accessible via a macro elnode/con-get.

Some interesting properties and how to access them:

##! emacs-lisp

(elnode/con-get httpcon :elnode-http-status)
=> "GET / HTTP/1.1"

(elnode/con-get httpcon :elnode-http-resource)
=> "/"

(elnode/con-get httpcon :elnode-http-version)
=> "1.1"

These are not supported by Elnode at all, there is no guarantee that the names of these properties won't change. If you feel that you want official support (ie: a function) then make an issue on the Elnode github.

To Do?

If you're playing with elnode but you can't think of anything to do with it...

  • make an elnode param handler that sanitzes input
    • one way to do that was found by aidalgol:
(require 'htmlize)
(htmlize-protect-string
 "<a href='/blah?a=10&b=2'></a><script>call_func()</script>")
  • an emacsclient with elnode
    • write a command line client that submits data to the server over HTTP
    • it should interact with the emacs user in the same way that emacs server does
    • why? because then a single Emacs could have just 1 server socket open for all sorts of different roles
  • alter elnode-webserver-handler-maker to do indexing better
    • take an optional index producing function?
    • take keyword flags that set the behaviour?
    • eg: :doindexes 't
  • browse-current-buffer
    • start an elnode server on some random port exposing the current buffer
    • automatically open a browser on the started server

elnode's People

Contributors

eschulte avatar fredericfrances avatar jschaf avatar kidd avatar magnars avatar mathiasdahl avatar nicferrier avatar philhudson avatar sabof avatar steelman avatar swsnr avatar takumikinjo avatar tali713 avatar tburette avatar thomasf avatar tinku99 avatar uucidl avatar wilfred avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

elnode's Issues

Info-level messages are shown as Errors

Info messages like calling handler, starting respose etc are shown as errors.
Can we change the level to elnode-info which is off by default?

*Messages* buffer contents

elnode-error: elnode--sentinel 'open from 127.0.0.1.' for process  *elnode-webserver-proc* <127.0.0.1:35689> with buffer nil
elnode-error: Elnode status: *elnode-webserver-proc* <127.0.0.1:35689> open from 127.0.0.1
elnode-error: filter: calling handler on *elnode-webserver-proc* <127.0.0.1:35689>
elnode-error: starting HTTP response on *elnode-webserver-proc* <127.0.0.1:35689>
elnode-error: elnode--process-send-eof on *elnode-webserver-proc* <127.0.0.1:35689>
elnode-error: elnode--http-end ending socket *elnode-webserver-proc* <127.0.0.1:35689>
elnode-error: elnode--sentinel 'deleted.' for process  *elnode-webserver-proc* <127.0.0.1:35689> with buffer  *elnode-request-35689*
elnode-error: Elnode status: *elnode-webserver-proc* <127.0.0.1:35689> deleted
elnode-error: filter: handler returned on *elnode-webserver-proc* <127.0.0.1:35689>

broken code in #33

My emacs wouldn't run that interactive call. It doesn't look right to me either.

elnode wiki does not have a way of creating a page

Need to come up with a nice design:

  • a new page page url? /wiki/newpage
    • or a new page page link? /wiki/?newpage
  • /wiki/ handler should probably receive the POST to create pages
  • should be linked from the 404
    • or maybe embed the form directly in the 404 page

Response content ends with ^M

Hi,

It seems elnode adds a ^M and the end of the response. To reproduce, save this to server.el:

(require 'elnode)

(defun some-handler (httpcon)
  (elnode-http-start httpcon 200 '("Content-Type" . "application/octet-stream"))
  (elnode-send-file httpcon load-file-name))

(elnode-start 'some-handler :port 8028 :host "localhost")

(while t (sit-for 100))

Run with:

$ emacs --script server.el -Q

Eval in Emacs:

(pop-to-buffer (url-retrieve-synchronously "http://127.0.0.1:8028"))

The buffer will contain (note the ^M at the end):

HTTP/1.1 200 Ok
Transfer-Encoding: chunked
Content-Type: application/octet-stream

(require 'elnode)

(defun some-handler (httpcon)
  (elnode-http-start httpcon 200 '("Content-Type" . "application/octet-stream"))
  (elnode-send-file httpcon load-file-name))

(elnode-start 'some-handler :port 8028 :host "localhost")

(while t (sit-for 100))
^M

elnode error log contains file contents, including binary data

This is a problem when the file contains bytes that form invalid utf-8 sequences, as it goes to an interactive prompt asking the user what encoding to use. It could just default to encoding the file/buffer as raw-text to begin with, or it could run the binary data through some kind of encoding first. For now I'm just turning off the logging by setting elnode--do-error-logging to nil.

Example:
20120813101139: elnode-http-send-string elnode-webserver-proc 10.0.38.28:14778 [[GIF87aT^@t^@\366^?^@\377\377\377^@^X\214\354]]

Possible error in the tutorial (URL regexp)

I tested the last part of tutorial ("More advanced again - Make a webapp around the service"), which I found clear and straightforward. However I did run into some trouble with the following piece of code.

(defconst my-elnode-editor-urls '(("^/text/$" . my-elnode-editor-handler) ("^/update/.*$" . my-elnode-editor-update-handler) ("^/[^/]+/.*$" . my-elnode-editor-webserver-handler)))

The last line I had to change to: ("^/.*$" . my-elnode-editor-webserver-handler)))
to be able to access http://localhost:8002/my-elnode-editor.html

add backtrace printing to elnode-errors

Here's is how to do backtrace collection:

(defun nic-test-2 ()
  (let ((standard-output (get-buffer-create "*nic-backtrace*")))
    (backtrace)))

An initial thought is that any condition-case capture could capture the backtrace in a unique buffer and add a link to the produced error message to elnode-log that will let you follow it. But that might be expensive on buffers.

Another alternative would be to add it as a property to the log-line so that a buffer viewer mode could handle it's display

Elnode can't show html pages

I have installed elnode by Marmalade, and everything looks OK. And I have read the manual carefully.

Now elnode has started, and I can view the folder public_html under .emacs.d\elnode folder.

If I input url http://localhost:8000 in my Chrome, it will show following content:


/

.
..
default-webserver-image.png
test.html


But when I click the test.html file, chrome just can't get the response from elnode. I check my Emacs, the Message file provides two errors:


elnode-error: elnode--sentinel 'open from 127.0.0.1.' for process elnode-webserver-proc 127.0.0.1:2224 with buffer nil
elnode-error: Elnode status: elnode-webserver-proc 127.0.0.1:2224 open from 127.0.0.1
elnode-error: filter: calling handler on elnode-webserver-proc 127.0.0.1:2224
elnode-error: starting HTTP response on elnode-webserver-proc 127.0.0.1:2224
error in process filter: apply: Spawning child process: invalid argument
error in process filter: Spawning child process: invalid argument


I just don't know how to debug it. I also tried elnode-send-file function, it also doesn't work.

Is it necessary to do some basic configuration after installing elnode?

Thanks

Water Lin

elnode should provide authentication mechanisms

Elnode should provide some easy to use authentication system. The Elnode Wiki engine should probably require authentication before allowing update.

The authentication mechanism should be extensible but implemented entirely in Emacs in the simplest case.

Don't start an elnode process on start-up by default

The current default is to start an elnode process listening on port 8000 when Emacs starts. This is quite unexpected in my opinion, because it will prompt users about running processes when they quit Emacs, even if they haven't used elnode. I installed it to be able to run the tests for web.el only.

elnode-auth-http-login cookie path rules

The rules for elnode-auth-http-login setting the path on the cookie need to be cleared up. I've got it so it sets the path on "/" for now, but that's bad. Using `logged-in`` doesn't seem right either.

Few issues

The example in the "How does it work?" requires that the standard server be stopped first. Also the example is missing the "localhost" param to elnode-start.

elnode should allow logging to processes and files

Elnode needs to be able to send errors and accesses to files and to processes instead of just buffers.

Elnode's current log handling is just buffers through elnode-log-access function and the elnode-error macro.

There tends to be 1 error log overall (accessed through the function elnode--get-error-log-buffer) and 1 access log per dispatcher instance (which is per application, normally) through elnode-hostpath-dispatcher.

Both deal with buffers only.

Processes

One should be able to specify a program name that could be invoked to handle log messages. Ideally it would be possible to maintain a connection to that program for a long period of time, to log many messages through it. If it fails it should automatically restart.

Files

There should be files backing up the log buffers. The implementation of these could be efficient through append-to-file and comint-truncate-buffer (which might need to be copied).

How to specify the file names is the biggest problem.

Clarify elnode-send-html docstring

The docstring for elnode-send-html does not specify that it is for sending a complete response; i.e. it sends 200 and ends the response on httpcon. The description makes it sound as if it is the same as elnode-http-send-string, but for sending HTML.

Missing Package-requires Header

Please add a Package-requires header to elnode.el with the contents of requirements.txt so that elnode can be installed from MELPA too:

;; Package-requires: ((web "0.1.4")(db "0.0.1")(kv "0.0.9")(fakir "0.0.14")(creole "0.8.14"))

[PATCH] fix Last-Modified header

elnode--rfc1123-date was incorrectly sending the month number as day
number, which leads to incorrect caching in browsers.

The defect went as such:

(elnode--rfc1123-date (list 20986 3923 0 0))
"Thu, 08 Aug 2013 07:33:39 GMT"

(format-time-string "%Y-%m-%d %T%z" (list 20986 3923 0 0))

"2013-08-01 09:33:39+0200"

elnode.el | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/elnode.el b/elnode.el
index 4f540d5..6bd401c 100644
--- a/elnode.el
+++ b/elnode.el
@@ -2893,7 +2893,7 @@ It should not be used otherwise.")
(month (nth (- (nth 4 decoded-time) 1) month-names)))
(format "%s, %s %s %s"
day

  •   (format-time-string "%m" time t)
    
  •   (format-time-string "%d" time t)
    month
    (format-time-string "%Y %H:%M:%S GMT" time t))))
    

1.8.0.msysgit.0

subdirs in the webserver don't work properly

mkdir ~/public_html/subdir
echo "<html>Hi</html>" > ~/public_html/subdir/index.html

then hit

http://localhost:8000/subdir

and then click on index.html, you get:

http://localhost:8000/subdirindex.html

(ie: no slash)

Clearly needs another test.

Other things while we're here:

  • index.html should work in subdirs as well
  • Falko reports that empty directories don't work (though we could not regenerate that error)

Setting elnode-log-files-directory doesn't work as documented

As per the doc:

Elnode can use files for storing logs and will write them to this
directory. If the directory does not exist it is created. If
this field is left blank no log saving is done.

When I wget a file without changing the variable I get (=> for eval result, ***> for Messages)

elnode-log-files-directory
=> "~/.elnodelogs"
***> Added to /home/fsmunoz/.elnodelogs/elnode-error

When I try to disable logging, and wget the same (http://localhost:8000):

(setq elnode-log-files-directory "")
elnode-log-files-directory
=> ""
***>error in process filter: Opening output file: permission denied, /elnode-error

(the wget process is blocked).

I would expect that no logging would be made, and as a side-effect no message would appear.

Info:

Debian GNU/Linux wheezy/sid
GNU Emacs 24.1.1 (x86_64-pc-linux-gnu, GTK+ Version 2.24.10) of 2012-07-05 on trouble, modified by Debian
2012-07-21 git clone of elnode

Problems with accessing request parameters depending on how a server is started

Define these two functions:

(defun my-stuff-handler (httpcon)
  "My stuff handler"
  (let ((stuff (elnode-http-param httpcon "stuff")))
    (elnode-http-start httpcon 200 '("Content-type" . "text/html"))
    (elnode-http-return
     httpcon
     (format "<html><body>Stuff = %s</body></html>" stuff))))

(defun stuff (httpcon)
  "My stuff server"
  (elnode-dispatcher
   httpcon
   '(("^/stuff" . my-stuff-handler))))

Now, if I start the "server", like this...

(elnode-start 'stuff :port 8010 :host "localhost")

... no parameters are sent to the page if I post things in the form on my page:

http://localhost:8010/stuff?stuff=bananas

It however works if I start it this way:

(elnode-start 'my-stuff-handler :port 8020 :host "localhost")

Test with:

http://localhost:8020/stuff?stuff=bananas

Thanks!

/Mathias

elnode-stop should use completing-read

Since we can be certain that we cannot close that which has not been opened, the currently open ports should be presented to the user when called interactively. See pull request.

Some requests don't get proccessed

Here is the request - which does get into the process filter.

GET /original/js/views/about.js HTTP/1.1
Host: localhost:8001
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: */*
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.56 Safari/537.36
Referer: http://localhost:8001/original/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

Here are the lines mentioning 56085 - value of (process-contact) for the above. I couldn't paste the whole log, as some characters wouldn't get accepted. Previous experiments have shown that the requests don't get to the stage of user-defined functions. If this is not helpful, I'll try to find more info or post a more complete log, a bit later.

elnode-error: elnode--sentinel 'open from 127.0.0.1.' for process  *elnode-webserver-proc* <127.0.0.1:56085> with buffer nil
elnode-error: Elnode status: *elnode-webserver-proc* <127.0.0.1:56085> open from 127.0.0.1
elnode-error: filter: calling handler on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: starting HTTP response on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: Elnode-child-process init *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: filter: handler returned on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: Elnode-child-process-filter http state: open data length: 306 on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: elnode-http-send-string *elnode-webserver-proc* <127.0.0.1:56085> [[a.plain:link.{ color]]
elnode-error: Elnode-child-process-sentinel Status @ finished: open -> exit on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: elnode-http-send-string *elnode-webserver-proc* <127.0.0.1:56085> [[]]
elnode-error: elnode--http-end ending socket *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: filter: calling handler on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: Http already started on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: Elnode-child-process init *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: filter: handler returned on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: elnode--sentinel 'deleted.' for process  *elnode-webserver-proc* <127.0.0.1:56085> with buffer  *elnode-request-56085*
elnode-error: Elnode status: *elnode-webserver-proc* <127.0.0.1:56085> deleted
elnode-error: Elnode-child-process-filter http state: closed data length: 306 on *elnode-webserver-proc* <127.0.0.1:56085>
elnode-error: Elnode-child-process-sentinel Status @ finished: closed -> exit on *elnode-webserver-proc* <127.0.0.1:56085>

About the mailing list

Hi,

On EmacsWiki you said:

We donโ€™t have a mailing list yet. If you would like to provide one (ad free please) then please get in touch .

We have a good mail and list server (CommuniGate Pro, running from a Linode vps based in London), managed by our non-profit organisation. If you are interested, just let me know the name of the list, and I will send you access to the admin panel. I cannot guarantee it will fulfill your needs, but it is free and ad-free. If you want to have it at something like [email protected] it can be done too, but of course you will have to add the proper MX records.

Question regarding elnode errors (everything seems to work anyway)

Hi,

context

I am developing org-trello, a minor mode to synchronize between org-mode and trello (through their public API).
At the moment, all of this is coupled and synchronous (thus blocking emacs for batch sync -- evil). The query is built and sent to trello for it to satisfy.

evolution

I am working on an evolution involving elnode as a proxy.
I kept the same functionality as above but now the idea is to enrich and wrap the query built for trello (with data org needs but trello does not care) and send it to the proxy.
The proxy unwraps the query and send the query to trello.
When trello answers, the proxy enrichs the response and bounce it to the client.

This has advantages:

  • evolutivity
  • I can avoid some actual hack regarding marker on the buffer (I can enrich the query with the position and the buffer I want to update)
  • more importantly, I can now exploit some concurrence (this has its own difficulties though :D)

current state of affairs

Anyway, I succeeded mostly for some basic synchronization but I got some elnode errors that keep poping-up during the process.
I do not understand those errors as everything seems to work.

Here is some Message buffer sample:

elnode-error: filter: handler returned on *elnode-webserver-proc*<1> <127.0.0.1:46186>
Request to trello server to wrap: #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:method "PUT" :uri "/cards/51e7e60bd23ccba35c00a588" :params (("name" . "yet another fail") ("idList" . "51e538a89c05f1e25c0027c6")) :position 5658 :buffername "TODO-tests.org"))
Request to proxy wrapped: #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:method "POST" :uri "/" :params #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:method "PUT" :uri "/cards/51e7e60bd23ccba35c00a588" :params (("name" . "yet another fail") ("idList" . "51e538a89c05f1e25c0027c6")) :position 5658 :buffername "TODO-tests.org"))))
elnode-error: elnode--sentinel 'open from 127.0.0.1.' for process  *elnode-webserver-proc*<1> <127.0.0.1:46188> with buffer nil
elnode-error: Elnode status: *elnode-webserver-proc*<1> <127.0.0.1:46188> open from 127.0.0.1
elnode-error: filter: calling handler on *elnode-webserver-proc*<1> <127.0.0.1:46188>
Proxy server: params -> #<process *elnode-webserver-proc*<1> <127.0.0.1:46188>>
Blocking call to accept-process-output with quit inhibited!! [2 times]
callback post/put/delete: ((url . "https://trello.com/c/pwwSdcg0/45-yet-another-fail") (pos . 65535) (name . "yet another fail") (labels . []) (manualCoverAttachment . :json-false) (idAttachmentCover) (idShort . 45) (idMembers . []) (idList . "51e538a89c05f1e25c0027c6") (idChecklists . []) (idBoard . "51d99bbc1e1d8988390047f2") (due) (desc . "") (dateLastActivity . "2013-08-13T23:44:24.347Z") (closed . :json-false) (checkItemStates . []) (badges (due) (description . :json-false) (attachments . 0) (comments . 0) (checkItemsChecked . 0) (checkItems . 0) (fogbugz . "") (subscribed . :json-false) (viewingMemberVoted . :json-false) (votes . 0)) (id . "51e7e60bd23ccba35c00a588")) - position: 5658
Responding to the proxy's client with ((id . "51e7e60bd23ccba35c00a588") (position . 5658) (buffername . "TODO-tests.org"))
elnode-error: starting HTTP response on *elnode-webserver-proc*<1> <127.0.0.1:46188>
elnode-error: elnode--process-send-eof on *elnode-webserver-proc*<1> <127.0.0.1:46188>
elnode-error: elnode--http-end ending socket *elnode-webserver-proc*<1> <127.0.0.1:46188>
elnode-error: elnode--sentinel 'deleted.' for process  *elnode-webserver-proc*<1> <127.0.0.1:46188> with buffer  *elnode-request-46188*
elnode-error: Elnode status: *elnode-webserver-proc*<1> <127.0.0.1:46188> deleted
orgtrello-query/--update-entity-id-to-buffer-callback: ((buffername . "TODO-tests.org") (position . 5658) (id . "51e7e60bd23ccba35c00a588")) - pos 51e7e60bd23ccba35c00a588 - id 5658
TRACE: :entry-meta: #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:buffername "TODO-tests.org" :position 5658 :level 1 :keyword "DELEGATED" :title "yet another fail" :id "51e7e60bd23ccba35c00a588" :due nil))
TRACE: :already-present-id: "51e7e60bd23ccba35c00a588"
TRACE: :current-name: "yet another fail"
Entity 'yet another fail' synced with id '51e7e60bd23ccba35c00a588'
elnode-error: filter: handler returned on *elnode-webserver-proc*<1> <127.0.0.1:46188>
Request to trello server to wrap: #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:method "PUT" :uri "/cards/51ffe96c32c0ac5e59000850" :params (("name" . "cancelled task") ("idList" . "51e538e6c7a68fa0510014ee")) :position 5746 :buffername "TODO-tests.org"))
Request to proxy wrapped: #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:method "POST" :uri "/" :params #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:method "PUT" :uri "/cards/51ffe96c32c0ac5e59000850" :params (("name" . "cancelled task") ("idList" . "51e538e6c7a68fa0510014ee")) :position 5746 :buffername "TODO-tests.org"))))
elnode-error: elnode--sentinel 'open from 127.0.0.1.' for process  *elnode-webserver-proc*<1> <127.0.0.1:46190> with buffer nil
elnode-error: Elnode status: *elnode-webserver-proc*<1> <127.0.0.1:46190> open from 127.0.0.1
elnode-error: filter: calling handler on *elnode-webserver-proc*<1> <127.0.0.1:46190>
Proxy server: params -> #<process *elnode-webserver-proc*<1> <127.0.0.1:46190>>
Blocking call to accept-process-output with quit inhibited!! [2 times]
callback post/put/delete: ((url . "https://trello.com/c/smLa9B2J/63-cancelled-task") (pos . 16384) (name . "cancelled task") (labels . []) (manualCoverAttachment . :json-false) (idAttachmentCover) (idShort . 63) (idMembers . []) (idList . "51e538e6c7a68fa0510014ee") (idChecklists . []) (idBoard . "51d99bbc1e1d8988390047f2") (due) (desc . "") (dateLastActivity . "2013-08-13T23:44:25.115Z") (closed . :json-false) (checkItemStates . []) (badges (due) (description . :json-false) (attachments . 0) (comments . 0) (checkItemsChecked . 0) (checkItems . 0) (fogbugz . "") (subscribed . :json-false) (viewingMemberVoted . :json-false) (votes . 0)) (id . "51ffe96c32c0ac5e59000850")) - position: 5746
Responding to the proxy's client with ((id . "51ffe96c32c0ac5e59000850") (position . 5746) (buffername . "TODO-tests.org"))
elnode-error: starting HTTP response on *elnode-webserver-proc*<1> <127.0.0.1:46190>
elnode-error: elnode--process-send-eof on *elnode-webserver-proc*<1> <127.0.0.1:46190>
elnode-error: elnode--http-end ending socket *elnode-webserver-proc*<1> <127.0.0.1:46190>
elnode-error: elnode--sentinel 'deleted.' for process  *elnode-webserver-proc*<1> <127.0.0.1:46190> with buffer  *elnode-request-46190*
elnode-error: Elnode status: *elnode-webserver-proc*<1> <127.0.0.1:46190> deleted
orgtrello-query/--update-entity-id-to-buffer-callback: ((buffername . "TODO-tests.org") (position . 5746) (id . "51ffe96c32c0ac5e59000850")) - pos 51ffe96c32c0ac5e59000850 - id 5746
TRACE: :entry-meta: #s(hash-table size 65 test equal rehash-size 1.5 rehash-threshold 0.8 data (:buffername "TODO-tests.org" :position 5746 :level 1 :keyword "CANCELLED" :title "cancelled task" :id "51ffe96c32c0ac5e59000850" :due nil))
TRACE: :already-present-id: "51ffe96c32c0ac5e59000850"
TRACE: :current-name: "cancelled task"
Entity 'cancelled task' synced with id '51ffe96c32c0ac5e59000850'
elnode-error: filter: handler returned on *elnode-webserver-proc*<1> <127.0.0.1:46190>
Saving file /home/tony/repositories/perso/org-trello/TODO-tests.org...
Wrote /home/tony/repositories/perso/org-trello/TODO-tests.org
ot is on! To begin with, hit C-c o h or M-x 'org-trello/help-describing-bindings
Org-mode restarted
Synchronizing org-mode file to the board 'api test board' - done!

version

  • GNU Emacs 24.3.1 (x86_64-pc-linux-gnu, X toolkit, Xaw3d scroll bars) of 2013-04-14 on marid, modified by Debian
  • ;; Package-Requires: ((org "8.0.7") (dash "1.5.0") (request "0.2.0") (cl-lib "0.3.0") (json "1.2") (elnode "0.9.9.7.6"))

some code

If you ever get [interested by the code](https://github.com/ardumont/org-trello/blob/0.1.2/org-trello.el#L496-L598][interested by the code)...

Starting elnode:

(defvar *ORGTRELLO-PROXY-HOST* "localhost")
(defvar *ORGTRELLO-PROXY-PORT* 8009)
(defvar *ORGTRELLO-PROXY-URL* (format "http://%s:%d/proxy" *ORGTRELLO-PROXY-HOST* *ORGTRELLO-PROXY-PORT*))

(defun orgtrello-proxy/--elnode-proxy (http-con)
  "A simple handler to extract the params information and make the request to trello."
  (orgtrello-log/msg 5 "Proxy server: params -> %S" http-con)
  (let* ((query-map-wrapped (orgtrello-proxy/--extract-trello-query http-con))
         (position          (assoc-default 'position query-map-wrapped))
         (buffer-name       (assoc-default 'buffername query-map-wrapped))
         (query-map         (orgtrello-proxy/--compute-trello-query query-map-wrapped))
         (method            (orgtrello-query/--method query-map))
         (fn-dispatch       (orgtrello-proxy/--dispatch-http-query method)))
    ;; Execute the request to trello (at the moment, synchronous)
    (funcall fn-dispatch http-con query-map (list position buffer-name) t)))

(defvar orgtrello-query/--app-routes '(;; proxy routine
                                       ("^localhost//proxy/\\(.*\\)" . orgtrello-proxy/--elnode-proxy)
                                       ;; whatever...
                                       ("^.*//\\(.*\\)" . elnode-webserver)))

(defun orgtrello-proxy/--proxy-handler (http-con)
  "Launch function to start the proxy"
  (elnode-hostpath-dispatcher http-con orgtrello-query/--app-routes))

(elnode-start 'orgtrello-proxy/--proxy-handler :port *ORGTRELLO-PROXY-PORT* :host *ORGTRELLO-PROXY-HOST*)

The client interface to request:

(defun orgtrello-proxy/http (query-map &optional sync success-callback error-callback)
  "Query the trello api asynchronously."
  (orgtrello-log/msg 5 "Request to trello server to wrap: %S" query-map)
  (let ((query-map-proxy (orgtrello-hash/make-hash "POST" "/" query-map)))
    (orgtrello-log/msg 5 "Request to proxy wrapped: %S" query-map-proxy)
    (orgtrello-query/--http *ORGTRELLO-PROXY-URL* query-map-proxy sync success-callback error-callback)))

Dealing with request answer takes place here:

(defun orgtrello-proxy/--response (http-con data)
  "A response wrapper"
  (orgtrello-log/msg 5 "Responding to the proxy's client with %S" data)
  (elnode-http-start http-con 201 '("Content-type" . "application/json"))
  (elnode-http-return http-con (json-encode data)))

(defun orgtrello-proxy/--standard-get-success-callback (http-connection)
  (lexical-let ((http-con http-connection))
    "Return a callback function able to deal with the position."
    (cl-defun get-some-insignificant-name (&key data &allow-other-keys)
      "Standard get response will simply relay the information to the client."
      (orgtrello-log/msg 5 "callback get: %S" data)
      (orgtrello-proxy/--response http-con data))))

(defun orgtrello-proxy/--standard-post-or-put-or-delete-success-callback (http-connection buffer-metadata)
  "Return a callback function able to deal with the position."
  (lexical-let ((http-con http-connection)
                (position (first buffer-metadata))
                (buffername (second buffer-metadata)))
    (cl-defun put-some-insignificant-name (&key data &allow-other-keys)
      "Will read the information from the response and simply return id and position."
      (orgtrello-log/msg 5 "callback post/put/delete: %S - position: %s" data position)
      (let ((orgtrello-query/--identifier (orgtrello-query/--id data)))
        (orgtrello-proxy/--response http-con `((id . ,orgtrello-query/--identifier)
                                               (position . ,position)
                                               (buffername . ,buffername)))))))

This is the main query to trello interface (first a dispatch takes place depending on the query method (get, post, put, delete) then the execution).

(defun orgtrello-proxy/--get (http-con query-map &optional buffer-metadata sync)
  "GET on trello with the callback"
  (orgtrello-query/http-trello query-map sync (orgtrello-proxy/--standard-get-success-callback http-con)))

(defun orgtrello-proxy/--post-or-put-or-delete (http-con query-map &optional buffer-metadata sync)
  "POST/PUT/DELETE"
  (orgtrello-query/http-trello query-map sync (orgtrello-proxy/--standard-post-or-put-or-delete-success-callback http-con buffer-metadata)))

(defun orgtrello-proxy/--dispatch-http-query (method)
  "Dispach query function depending on the http method input parameter."
  (cond ((string= "GET" method)         'orgtrello-proxy/--get)
        ((or (string= "POST" method)
             (string= "PUT" method)
             (string= "DELETE" method)) 'orgtrello-proxy/--post-or-put-or-delete)))

Extraction function from the query made to the proxy:

(defun orgtrello-proxy/--extract-trello-query (http-con)
  "Given an httpcon object, extract the params entry which corresponds to the real trello query."
  (let ((params-proxy-json (caar (elnode-http-params http-con))))
    (json-read-from-string params-proxy-json)))

(defun orgtrello-proxy/--compute-trello-query (query-map-wrapped)
  (let ((method  (assoc-default 'method query-map-wrapped))
        (uri     (assoc-default 'uri query-map-wrapped))
        (payload (assoc-default 'params query-map-wrapped)))
    (orgtrello-hash/make-hash method uri payload)))

Ideas?

Do you see if I did something wrong?

Thanks

Oh and by the way:

(thanks (and 'elnode 'marmalade 'tutorial-videos))

Thanks again for your time

Cheers,

installing elnode using el-get

installing elnode using el-get doesnt seem to work at the moment.

I got problems when the build process requires elnode-db.

package issue when upgrading to 0995

to reproduce:

  • select elnode for upgrade in the elpa package list
  • try to upgrade

result:
Debugger entered--Lisp error: (void-function define-elnode-handler)

its possible to work around the issue by evaling elnode.el so the define-elnode-handler macro becomes defined, and then again execute the upgrade.

so basically it appears the macro has not been defined when autoloads processing is happening. hopefully an easy fix.

531 response says "Ok"

If I call elnode-send-status' with531`, the response page says "Ok":

<h1>Ok</h1><p>[Optional message]</p>

instead of the name and code of the error:

<h1>Error 531: Access Denied</h1><p>[Optional message]</p>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.