Permalink
Browse files

first import

  • Loading branch information...
videlalvaro committed Sep 14, 2012
0 parents commit 54429b1982d64c6b51f9edbb2774bdc29252f468
@@ -0,0 +1,10 @@
/target
/lib
/classes
/checkouts
pom.xml
*.jar
*.class
.lein-deps-sum
.lein-failures
.lein-plugins
@@ -0,0 +1,79 @@
# gifsockets
"This library is the websockets of the '90s" - Somebody at Hacker News.
This library demoes how to achieve realtime text communication using GIF images as transport.
The interesting part is that you can even use IE6 with this library and get the data in Real Time (TM).
Of course this should have been delivered as an April's Fools joke but sadly we are in mid September here in the northern hemisphere.
## How does it work
The idea is pretty simple. We use Animated Gif images to stream data in real time to the browser. Since a gif image doesn't specify how many frames it has, once the browser opens it, it will keep waiting for new frames until you send the bits indicating that there's no more image to fetch.
Pretty simple uh!
And yes. It works in IE6.
## Usage
The usage now is a bit rudimentary and manual. Feel free to improve it and send a PR.
```bash
$ git clone
$ cd gifsockets
$ lein deps
% lein repl
```
Then perform the following commands on the Clojure REPL.
```clojure
;; in the repl do the following to import the libs
(use 'gifsockets.core)
(use 'gifsockets.server)
;;
;;Then we declare the tcp server
(def server (tcp-server :port 8081 :handler gif-handler))
(start2 server)
;; wait for a browser connection on port 8081
;; go and open http://localhost:8081/ in Safari or IE6
;; In Chrome it works a bit laggy and in Firefox it doesn't work at all
;;
;; Now let's create the gif encoder that we use to write messages to the browser.
(def encoder (create-gif (.getOutputStream client)))
;;
;;Now we are ready to send messages to that browser client
(add-message encoder "Hello gif-sockets")
;; now you should see a GIF image with the new message on it.
(add-message encoder "Zup zup zup")
(add-message encoder "And so forth")
;;
;; Now let's clean up and close the connection
(.finish encoder)
(.close client)
```
As you can see from the code this handles only one connection in what is called an UPoC (Uber Proof Of Concept).
## Possible uses:
All joking aside I think this is a very low tech way to have say, an website where you could tail logs for instance and you need to do it with browser that have zero websockets support or something like that.
## License
Copyright © 2012 Alvaro Videla
The following classes:
- AnimatedGifEncoder.java
- GifDecoder.java
- LZWEncoder.java
- NeuQuant.java
Where taken from this website: [http://www.fmsware.com/stuff/gif.html](http://www.fmsware.com/stuff/gif.html).
And the server code was taken from here [https://github.com/weavejester/tcp-server](https://github.com/weavejester/tcp-server)
Distributed under the Eclipse Public License, the same as Clojure.
@@ -0,0 +1,3 @@
# Introduction to gif-chat
TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/)
@@ -0,0 +1,9 @@
(defproject gifsockets "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:source-paths ["src/clojure"]
:java-source-paths ["src/java"]
:dependencies [[org.clojure/clojure "1.4.0"]]
:plugins [[lein-swank "1.4.4"]])
@@ -0,0 +1,53 @@
(ns gifsockets.core
(:import java.awt.image.BufferedImage
java.awt.Graphics2D
[java.io File IOException ByteArrayOutputStream]
javax.imageio.ImageIO
AnimatedGifEncoder
java.net.ServerSocket))
(defn save-to-disc [img format path]
(ImageIO/write img format (File. path)))
(defn write-text [text w h x y]
(let [bufferedImage (BufferedImage. w h BufferedImage/TYPE_INT_ARGB)
gd2 (.createGraphics bufferedImage)]
(.drawString gd2 text x y)
bufferedImage
))
(defn text-to-file [text w h x y]
(save-to-disc (write-text text w h x y) "jpg" "/tmp/text.jpg"))
(defn create-gif [output]
(let [e (AnimatedGifEncoder.)]
(.start e output)
(.setDelay e 1000)
e))
(defn add-frame [gif text w h x y]
(.addFrame gif (write-text text w h x y)))
(defn start-server [port]
(let [os (.getOutputStream (.accept (ServerSocket. port)))
e (create-gif os)]
;; see how to destroy the writer without closing the buffer
e))
(defn get-encoder []
(create-gif (ByteArrayOutputStream.)))
(defn add-message [encoder message]
"adds a message positioned at (20,20) in a 300x50 gif image"
(do
(add-frame encoder message 300 50 20 20)
(add-frame encoder message 300 50 20 20)))
(defn flush-encoder [encoder]
(.outFlush encoder))
(defn get-last-frame [encoder]
(.getFrameByteArray encoder))
(defn gif-handler [conn]
(def client conn))
@@ -0,0 +1,103 @@
(ns gifsockets.server
"Functions for creating a threaded TCP server."
(:require [clojure.java.io :as io])
(:import [java.net InetAddress ServerSocket Socket SocketException]))
(defn- server-socket [server]
(ServerSocket.
(:port server)
(:backlog server)
(InetAddress/getByName (:host server))))
(defn tcp-server
"Create a new TCP server. Takes the following keyword arguments:
:host - the host to bind to (defaults to 127.0.0.1)
:port - the port to bind to
:handler - a function to handle incoming connections, expects a socket as
an argument
:backlog - the maximum backlog of connections to keep (defaults to 50)"
[& {:as options}]
{:pre [(:port options)
(:handler options)]}
(merge
{:host "127.0.0.1"
:backlog 50
:socket (atom nil)
:connections (atom #{})}
options))
(defn close-socket [server socket]
(swap! (:connections server) disj socket)
(when-not (.isClosed socket)
(.close socket)))
(defn- open-server-socket [server]
(reset! (:socket server)
(server-socket server)))
(defn- accept-connection
[{:keys [handler connections socket] :as server}]
(let [conn (.accept @socket)]
(swap! connections conj conn)
(future
(try (handler conn)
(finally (close-socket server conn))))))
(defn- accept-connection2
[{:keys [handler connections socket] :as server}]
(let [conn (.accept @socket)]
(swap! connections conj conn)
(future
(try (handler conn)))))
(defn running?
"True if the server is running."
[server]
(if-let [socket @(:socket server)]
(not (.isClosed socket))))
(defn start
"Start a TCP server going."
[server]
(open-server-socket server)
(future
(while (running? server)
(try
(accept-connection server)
(catch SocketException _)))))
(defn start2
"Start a TCP server going."
[server]
(open-server-socket server)
(future
(while (running? server)
(try
(accept-connection2 server)
(catch SocketException _)))))
(defn stop
"Stop the TCP server and close all open connections."
[server]
(doseq [socket @(:connections server)]
(close-socket server socket))
(.close @(:socket server)))
(defn wrap-streams
"Wrap a handler so that it expects an InputStream and an OutputStream
as arguments, rather than a raw Socket."
[handler]
(fn [socket]
(with-open [input (.getInputStream socket)
output (.getOutputStream socket)]
(handler input output))))
(defn wrap-io
"Wrap a handler so that it expects a Reader and Writer as arguments, rather
than a raw Socket."
[handler]
(wrap-streams
(fn [input output]
(with-open [reader (io/reader input)
writer (io/writer output)]
(handler reader writer)))))
Oops, something went wrong.

0 comments on commit 54429b1

Please sign in to comment.