├── .gitignore ├── client.js ├── lib └── net │ └── sourceforge │ └── htmlunit │ └── htmlunit │ ├── 2.9-SNAPSHOT │ ├── _maven.repositories │ ├── htmlunit-2.9-SNAPSHOT-sources.jar │ ├── htmlunit-2.9-SNAPSHOT.jar │ ├── htmlunit-2.9-SNAPSHOT.jar.lastUpdated │ ├── htmlunit-2.9-SNAPSHOT.pom │ ├── htmlunit-2.9-SNAPSHOT.pom.lastUpdated │ ├── maven-metadata-local.xml │ └── resolver-status.properties │ └── maven-metadata-local.xml ├── pom.xml ├── server.js └── src ├── main └── java │ └── dnode │ ├── Callback.java │ ├── ClientHandler.java │ ├── Connection.java │ ├── DNode.java │ ├── DNodeObject.java │ ├── Server.java │ ├── netty │ ├── DNodePipelineFactory.java │ ├── DNodeServerHandler.java │ ├── NettyConnection.java │ └── NettyServer.java │ ├── socketio │ ├── SocketIOCodec.java │ ├── SocketIOConnection.java │ ├── SocketIOListener.java │ └── SocketIOWebSocketHandler.java │ └── webbit │ ├── DNodeWebSocketHandler.java │ ├── WebbitConnection.java │ └── WebbitServer.java └── test ├── java └── dnode │ ├── DNodeObjectTest.java │ ├── DNodeTest.java │ ├── socketio │ └── SocketIOCodecTest.java │ └── webbit │ ├── DNodeExample.java │ └── WebbitIntegrationTest.java └── resources └── dnode └── js ├── dnode.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *.ipr 3 | *.iml 4 | *.iws 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var DNode = require('dnode'); 3 | var m = process.ARGV[2]; 4 | 5 | var client = DNode.connect(6060, function (remote) { 6 | remote[m](function (x) { 7 | console.log(x); 8 | }); 9 | }); 10 | 11 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/_maven.repositories: -------------------------------------------------------------------------------- 1 | #NOTE: This is an internal implementation file, its format can be changed without prior notice. 2 | #Sun Feb 13 08:50:36 AST 2011 3 | htmlunit-2.9-SNAPSHOT.pom>= 4 | htmlunit-2.9-SNAPSHOT-sources.jar>= 5 | htmlunit-2.9-SNAPSHOT.jar>= 6 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT-sources.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslakhellesoy/dnode-java/02d5da6378a0014b8f584917f6710542573dc916/lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT-sources.jar -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslakhellesoy/dnode-java/02d5da6378a0014b8f584917f6710542573dc916/lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT.jar -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT.jar.lastUpdated: -------------------------------------------------------------------------------- 1 | #NOTE: This is an internal implementation file, its format can be changed without prior notice. 2 | #Sun Feb 13 08:46:51 AST 2011 3 | http\://google-gson.googlecode.com/svn/mavenrepo/.lastUpdated=1297601206938 4 | http\://google-gson.googlecode.com/svn/mavenrepo/.error= 5 | http\://oss.sonatype.org/content/repositories/releases/.error= 6 | http\://oss.sonatype.org/content/repositories/releases/.lastUpdated=1297601207232 7 | file\:///Users/ahellesoy/scm/dnode-java/dnode/lib/.error= 8 | http\://repository.jboss.org/nexus/content/groups/public/.error= 9 | http\://repository.jboss.org/nexus/content/groups/public/.lastUpdated=1297601211367 10 | file\:///Users/ahellesoy/scm/dnode-java/dnode/lib/.lastUpdated=1297601204904 11 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT.pom: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | net.sourceforge.htmlunit 6 | htmlunit 7 | 2.9-SNAPSHOT 8 | HtmlUnit 9 | 10 | Gargoyle Software Inc. 11 | http://www.GargoyleSoftware.com/ 12 | 13 | jar 14 | A headless browser intended for use in testing web-based applications. 15 | http://htmlunit.sourceforge.net 16 | 17 | UTF-8 18 | 19 | 23 | 24 | org.sonatype.oss 25 | oss-parent 26 | 3 27 | 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-compiler-plugin 33 | 2.3 34 | 35 | 36 | 1.5 37 | 1.5 38 | 39 | **/CodeChecker.java 40 | 41 | 42 | 43 | 44 | org.apache.maven.plugins 45 | maven-checkstyle-plugin 46 | 2.4 47 | 48 | checkstyle.xml 49 | true 50 | true 51 | true 52 | 53 | checkstyle.suppressions.file=checkstyle_suppressions.xml 54 | checkstyle.header.file=LICENSE.txt 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-jar-plugin 61 | 2.3 62 | 63 | 64 | 65 | true 66 | true 67 | 68 | 69 | ${pom.url} 70 | 71 | ${svnrevision} 72 | ${label} 73 | 74 | 75 | 76 | 77 | 78 | 79 | test-jar 80 | 81 | 82 | 83 | 84 | 85 | org.apache.maven.plugins 86 | maven-site-plugin 87 | 2.2 88 | 89 | ${basedir}/src/site/maven-site.vm 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-assembly-plugin 95 | 2.2-beta-4 96 | 97 | 98 | make-assembly 99 | package 100 | 101 | single 102 | 103 | 104 | 105 | 106 | false 107 | 108 | ${basedir}/src/assembly/bin-distribution.xml 109 | ${basedir}/src/assembly/src-distribution.xml 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-source-plugin 116 | 2.1 117 | 118 | 119 | 120 | jar 121 | 122 | 123 | 124 | 125 | 126 | maven-antrun-plugin 127 | 1.3 128 | 129 | 130 | moveFiles 131 | package 132 | 133 | run 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | org.apache.maven.plugins 158 | maven-eclipse-plugin 159 | 2.7 160 | 161 | true 162 | true 163 | 164 | 165 | 178 | 179 | 180 | 181 | 182 | with-library-tests 183 | 184 | true 185 | 186 | 187 | 188 | 189 | org.apache.maven.plugins 190 | maven-surefire-plugin 191 | 2.7.1 192 | 193 | -Xmx512m -Xms128m 194 | 195 | maven 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | without-library-tests 204 | 205 | 206 | withoutLibs 207 | 208 | 209 | 210 | 211 | 212 | org.apache.maven.plugins 213 | maven-surefire-plugin 214 | 2.7.1 215 | 216 | 217 | **/libraries/*.java 218 | 219 | -Xmx512m -Xms128m 220 | 221 | maven 222 | 223 | 224 | 225 | 226 | 227 | 228 | src/test/resources 229 | 230 | libraries/**/*.* 231 | 232 | 233 | 234 | 235 | 236 | 237 | only-library-tests 238 | 239 | 240 | onlyLibs 241 | 242 | 243 | 244 | 245 | 246 | org.apache.maven.plugins 247 | maven-surefire-plugin 248 | 2.7.1 249 | 250 | 251 | **/libraries/*.java 252 | 253 | -Xmx512m -Xms128m 254 | 255 | maven 256 | 257 | 258 | 259 | 260 | 261 | 262 | src/test/resources 263 | 264 | libraries/**/*.* 265 | 266 | 267 | 268 | 269 | 270 | 271 | mikeCI 272 | 273 | 274 | mikeCI 275 | 276 | 277 | 278 | 279 | 280 | org.apache.maven.plugins 281 | maven-surefire-plugin 282 | 2.7.1 283 | 284 | 285 | **/H*Applet*Test.java 286 | **/HtmlFileInputTest.java 287 | 288 | -Xmx512m -Xms128m 289 | 290 | maven 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | org.apache.maven.plugins 302 | maven-javadoc-plugin 303 | 2.7 304 | 305 | com.gargoylesoftware.htmlunit.javascript:com.gargoylesoftware.htmlunit.protocol:com.gargoylesoftware.htmlunit.ssl:com.gargoylesoftware.htmlunit.html.xpath:com.gargoylesoftware.htmlunit.html.applets:com.gargoylesoftware.htmlunit.html.impl 306 | 307 | http://java.sun.com/j2se/1.5.0/docs/api/ 308 | http://commons.apache.org/logging/apidocs/ 309 | http://commons.apache.org/codec/apidocs/ 310 | http://hc.apache.org/httpcomponents-client/httpclient/apidocs/ 311 | 312 | maven 313 | 314 | 315 | 316 | org.apache.maven.plugins 317 | maven-jxr-plugin 318 | 319 | 320 | org.apache.maven.plugins 321 | maven-pmd-plugin 322 | 323 | 1.5 324 | 325 | 326 | 327 | org.apache.maven.plugins 328 | maven-changes-plugin 329 | 2.4 330 | 331 | 332 | 333 | changes-report 334 | 335 | 336 | 337 | 338 | 339 | http://sourceforge.net/support/tracker.php?aid=%ISSUE% 340 | 341 | 342 | 343 | 344 | org.codehaus.mojo 345 | jdepend-maven-plugin 346 | 347 | 348 | org.apache.maven.plugins 349 | maven-surefire-report-plugin 350 | 2.7.1 351 | 352 | false 353 | 354 | 355 | 356 | 357 | 358 | sourceforge 359 | http://sourceforge.net/tracker/?group_id=47038 360 | 361 | 2002 362 | 363 | 364 | Apache License, Version 2.0 365 | http://www.apache.org/licenses/LICENSE-2.0.txt 366 | repo 367 | 368 | 369 | 370 | scm:svn:https://htmlunit.svn.sourceforge.net/svnroot/htmlunit/trunk/htmlunit 371 | scm:svn:https://htmlunit.svn.sourceforge.net/svnroot/htmlunit/trunk/htmlunit 372 | http://htmlunit.svn.sourceforge.net/viewvc/htmlunit 373 | 374 | 375 | Cruise Control 376 | http://build.canoo.com/htmlunit 377 | 378 | 379 | 380 | HtmlUnit Users 381 | http://lists.sourceforge.net/lists/listinfo/htmlunit-user 382 | http://lists.sourceforge.net/lists/listinfo/htmlunit-user 383 | http://lists.sourceforge.net/lists/listinfo/htmlunit-user 384 | htmlunit-user@lists.sourceforge.net 385 | 386 | http://www.nabble.com/HtmlUnit---General-f2599.html 387 | http://htmlunit.markmail.org/ 388 | http://marc.info/?l=htmlunit-user 389 | 390 | 391 | 392 | HtmlUnit Developers 393 | http://lists.sourceforge.net/lists/listinfo/htmlunit-develop 394 | http://lists.sourceforge.net/lists/listinfo/htmlunit-develop 395 | http://lists.sourceforge.net/lists/listinfo/htmlunit-develop 396 | htmlunit-develop@lists.sourceforge.net 397 | 398 | http://www.nabble.com/HtmlUnit---Dev-f2598.html 399 | http://htmlunit.markmail.org/ 400 | http://marc.info/?l=htmlunit-develop 401 | 402 | 403 | 404 | 405 | 406 | Mike Bowler 407 | mbowler 408 | mbowler@GargoyleSoftware.com 409 | Gargoyle Software Inc. 410 | http://www.sphericalimprovement.com/blogs/mbowler/ 411 | -5 412 | 413 | 414 | David K. Taylor 415 | dktaylor 416 | 417 | 418 | Brad Clarke 419 | bradclarke 420 | bradclarke@users.sourceforge.net 421 | http://www.bradclarke.com/ 422 | -6 423 | 424 | 425 | Marc Guillemot 426 | mguillem 427 | mguillem@users.sourceforge.net 428 | http://mguillem.wordpress.com/ 429 | +1 430 | 431 | 432 | Chris Erskine 433 | cerskine 434 | cerskine@users.sourceforge.net 435 | -7 436 | 437 | 438 | Daniel Gredler 439 | sdanig 440 | sdanig@users.sourceforge.net 441 | http://daniel.gredler.net/ 442 | -5 443 | 444 | 445 | Ahmed Ashour 446 | asashour 447 | asashour@users.sourceforge.net 448 | http://asashour.blogspot.com/ 449 | +3 450 | 451 | 452 | Sudhan Moghe 453 | sudhan_moghe 454 | sudhan_moghe@users.sourceforge.net 455 | +5.5 456 | 457 | 458 | Ronald Brill 459 | rbri 460 | rbri@rbri.de 461 | http://www.wetator.org/ 462 | +1 463 | 464 | 465 | 466 | 467 | Noboru Sinohara 468 | 469 | 470 | Mike J. Bresnahan 471 | gudujarlson@sf.net 472 | 473 | 474 | Dominique Broeglin 475 | 476 | 477 | Alex Nikiforoff 478 | 479 | 480 | Barnaby Court 481 | 482 | 483 | Andreas Hangler 484 | 485 | 486 | Jun Chen 487 | chen_jun@users.sourceforge.net 488 | 489 | 490 | Christian Sell 491 | cse@dynabean.de 492 | 493 | 494 | Darrell DeBoer 495 | 496 | 497 | David D. Kilzer 498 | 499 | 500 | Ben Curren 501 | bcurren@esomnie.com 502 | 503 | 504 | Mike Williams 505 | 506 | 507 | Mike Gallaher 508 | 509 | 510 | Dierk Koenig 511 | 512 | 513 | Mike Bresnahan 514 | 515 | 516 | Sergey Gorelkin 517 | 518 | 519 | Chris Eldredge 520 | 521 | 522 | Hans Donner 523 | 524 | 525 | Michael Ottati 526 | 527 | 528 | George Murnock 529 | 530 | 531 | Kent Tong 532 | 533 | 534 | Alfred Nathaniel 535 | 536 | 537 | Bruce Faulkner 538 | 539 | 540 | Ray Suliteanu 541 | 542 | 543 | Denis N. Antonioli 544 | 545 | 546 | Stefan Anzinger 547 | 548 | 549 | Lothar Märkle 550 | 551 | 552 | Ian Lovejoy 553 | 554 | 555 | Paul King 556 | 557 | 558 | Vikram Shitole 559 | 560 | 561 | Mark van Leeuwen 562 | 563 | 564 | Brad Murray 565 | 566 | 567 | Julien Henry 568 | 569 | 570 | Andre Soereng 571 | 572 | 573 | Karel Kolman 574 | 575 | 576 | Bruce Chapman 577 | 578 | 579 | Kristian Muntau 580 | 581 | 582 | Sam Hough 583 | 584 | 585 | Deryk Sinotte 586 | 587 | 588 | Martin Tamme 589 | 590 | 591 | Philip Graf 592 | 593 | 594 | Rodney Gitzel 595 | 596 | 597 | Matt Ryall 598 | 599 | 600 | Rob Di Marco 601 | 602 | 603 | Gareth Davis 604 | 605 | 606 | David Bylsma 607 | 608 | 609 | Dmitri Zoubkov 610 | 611 | 612 | Stuart Begg 613 | 614 | 615 | Rene Schwietzke 616 | 617 | 618 | Ethan Glasser-Camp 619 | 620 | 621 | Marco Cova 622 | 623 | 624 | Mike Dirolf 625 | 626 | 627 | Mirko Friedenhagen 628 | 629 | 630 | Richard Eggert 631 | 632 | 633 | Tomasz Kalkosinski 634 | 635 | 636 | Peter Faller 637 | 638 | 639 | Benoit Heinrich 640 | 641 | 642 | Amit Manjhi 643 | 644 | 645 | Nicolas Belisle 646 | 647 | 648 | Amit Khanna 649 | 650 | 651 | 652 | 653 | xalan 654 | xalan 655 | 2.7.1 656 | 657 | 658 | xerces 659 | xercesImpl 660 | 661 | 662 | 663 | 664 | commons-collections 665 | commons-collections 666 | 3.2.1 667 | 668 | 669 | commons-lang 670 | commons-lang 671 | 2.6 672 | 673 | 674 | org.apache.httpcomponents 675 | httpclient 676 | 4.1 677 | 678 | 679 | org.apache.httpcomponents 680 | httpclient 681 | 4.1 682 | test-jar 683 | test 684 | 685 | 686 | org.apache.httpcomponents 687 | httpmime 688 | 4.1 689 | 690 | 691 | commons-codec 692 | commons-codec 693 | 1.4 694 | 695 | 696 | net.sourceforge.htmlunit 697 | htmlunit-core-js 698 | 2.9-SNAPSHOT 699 | 700 | 701 | 702 | xerces 703 | xercesImpl 704 | 2.9.1 705 | 706 | 707 | net.sourceforge.nekohtml 708 | nekohtml 709 | 1.9.15-SNAPSHOT 710 | 711 | 712 | xerces 713 | xercesImpl 714 | 715 | 716 | 717 | 718 | net.sourceforge.cssparser 719 | cssparser 720 | 0.9.5 721 | 722 | 723 | commons-io 724 | commons-io 725 | 2.0 726 | 727 | 728 | commons-logging 729 | commons-logging 730 | 1.1.1 731 | 732 | 733 | 734 | junit 735 | junit 736 | 4.8.2 737 | test 738 | 739 | 740 | gsbase 741 | gsbase 742 | 2.0.1 743 | test 744 | 745 | 746 | org.easymock 747 | easymock 748 | 3.0 749 | test 750 | 751 | 752 | log4j 753 | log4j 754 | 1.2.16 755 | test 756 | 757 | 758 | commons-fileupload 759 | commons-fileupload 760 | 1.2.2 761 | test 762 | 763 | 764 | org.eclipse.jetty 765 | jetty-security 766 | 7.1.6.v20100715 767 | test 768 | 769 | 770 | org.eclipse.jetty 771 | jetty-server 772 | 7.1.6.v20100715 773 | test 774 | 775 | 776 | org.eclipse.jetty 777 | jetty-util 778 | 7.1.6.v20100715 779 | 780 | 781 | org.eclipse.jetty 782 | jetty-webapp 783 | 7.1.6.v20100715 784 | test 785 | 786 | 787 | org.eclipse.jetty 788 | jetty-websocket 789 | 7.1.6.v20100715 790 | test 791 | 792 | 793 | 794 | org.slf4j 795 | slf4j-api 796 | 1.6.1 797 | test 798 | 799 | 800 | org.slf4j 801 | slf4j-log4j12 802 | 1.6.1 803 | test 804 | 805 | 806 | jfree 807 | jfreechart 808 | 1.0.12 809 | test 810 | 811 | 812 | 813 | org.seleniumhq.selenium 814 | selenium-htmlunit-driver 815 | 2.0b1 816 | test 817 | 818 | 819 | net.sourceforge.htmlunit 820 | htmlunit 821 | 822 | 823 | 824 | 825 | org.seleniumhq.selenium 826 | selenium-ie-driver 827 | 2.0b1 828 | test 829 | 830 | 831 | org.seleniumhq.selenium 832 | selenium-firefox-driver 833 | 2.0b1 834 | test 835 | 836 | 837 | 838 | 839 | htmlunit-website 840 | HtmlUnit WebSite - Sourceforge 841 | 842 | scp://shell.sourceforge.net/home/groups/h/ht/htmlunit/htdocs/ 843 | 844 | 845 | 846 | htmlunit-m2-repo 847 | 848 | sftp://web.sourceforge.net/home/groups/h/ht/htmlunit/htdocs/m2-repo 849 | 850 | HtmlUnit Maven 2 Repository 851 | 852 | 853 | htmlunit-m2-repo-snapshots 854 | 855 | sftp://web.sourceforge.net/home/groups/h/ht/htmlunit/htdocs/m2-repo-snapshots 856 | 857 | HtmlUnit Maven 2 Snapshots Repository 858 | false 859 | 860 | 861 | 862 | 863 | 864 | true 865 | always 866 | 867 | 868 | false 869 | 870 | HtmlUnitSnapshots 871 | HtmlUnit Snapshots 872 | http://htmlunit.sourceforge.net/m2-repo-snapshots 873 | default 874 | 875 | 876 | 877 | true 878 | always 879 | 880 | 881 | false 882 | 883 | NekoHTMLSnapshots 884 | NekoHTML Snapshots 885 | http://build.canoo.com/NekoHTML/artifacts/m2-repo 886 | default 887 | 888 | 889 | 890 | true 891 | always 892 | 893 | 894 | true 895 | 896 | jetty-with-staging 897 | Jetty with staging 898 | https://oss.sonatype.org/content/groups/jetty-with-staging 899 | default 900 | 901 | 902 | 903 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/htmlunit-2.9-SNAPSHOT.pom.lastUpdated: -------------------------------------------------------------------------------- 1 | #NOTE: This is an internal implementation file, its format can be changed without prior notice. 2 | #Sun Feb 13 08:46:44 AST 2011 3 | http\://google-gson.googlecode.com/svn/mavenrepo/.lastUpdated=1297601201638 4 | http\://google-gson.googlecode.com/svn/mavenrepo/.error= 5 | http\://oss.sonatype.org/content/repositories/releases/.error= 6 | http\://oss.sonatype.org/content/repositories/releases/.lastUpdated=1297601201965 7 | file\:///Users/ahellesoy/scm/dnode-java/dnode/lib/.error= 8 | http\://repository.jboss.org/nexus/content/groups/public/.error= 9 | http\://repository.jboss.org/nexus/content/groups/public/.lastUpdated=1297601204817 10 | file\:///Users/ahellesoy/scm/dnode-java/dnode/lib/.lastUpdated=1297601200181 11 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | net.sourceforge.htmlunit 4 | htmlunit 5 | 2.9-SNAPSHOT 6 | 7 | 8 | true 9 | 10 | 20110213125036 11 | 12 | 13 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/2.9-SNAPSHOT/resolver-status.properties: -------------------------------------------------------------------------------- 1 | #NOTE: This is an internal implementation file, its format can be changed without prior notice. 2 | #Sun Feb 13 08:46:40 AST 2011 3 | maven-metadata-Sonatype.xml.lastUpdated=1297601199659 4 | maven-metadata-Local.xml.error= 5 | maven-metadata-Local.xml.lastUpdated=1297601198868 6 | maven-metadata-GSon.xml.error= 7 | maven-metadata-Sonatype.xml.error= 8 | maven-metadata-GSon.xml.lastUpdated=1297601199758 9 | maven-metadata-JBoss.xml.lastUpdated=1297601200176 10 | maven-metadata-JBoss.xml.error= 11 | -------------------------------------------------------------------------------- /lib/net/sourceforge/htmlunit/htmlunit/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | net.sourceforge.htmlunit 4 | htmlunit 5 | 6 | 7 | 2.9-SNAPSHOT 8 | 9 | 20110213125036 10 | 11 | 12 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.aslakhellesoy 5 | dnode 6 | jar 7 | 1.0-SNAPSHOT 8 | dnode 9 | http://maven.apache.org 10 | 11 | 12 | com.google.code.gson 13 | gson 14 | 1.6 15 | 16 | 17 | webbit 18 | webbit 19 | 0.0.1 20 | 21 | 22 | webbit 23 | webbit 24 | 0.0.1 25 | 26 | 27 | org.jboss.netty 28 | netty 29 | 3.2.3.Final 30 | 31 | 32 | junit 33 | junit 34 | 4.8.2 35 | test 36 | 37 | 38 | net.sourceforge.htmlunit 39 | htmlunit 40 | 2.9-SNAPSHOT 41 | test 42 | 43 | 44 | commons-codec 45 | commons-codec 46 | 1.4 47 | test 48 | 49 | 50 | 51 | 52 | Local 53 | file://${basedir}/lib/ 54 | 55 | true 56 | 57 | 58 | 59 | GSon 60 | http://google-gson.googlecode.com/svn/mavenrepo/ 61 | 62 | 63 | Sonatype 64 | http://oss.sonatype.org/content/repositories/releases/ 65 | 66 | 67 | JBoss 68 | http://repository.jboss.org/nexus/content/groups/public/ 69 | 70 | 71 | 72 | 73 | 74 | org.apache.maven.plugins 75 | maven-compiler-plugin 76 | 77 | UTF-8 78 | 1.6 79 | 1.6 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var DNode = require('dnode'); 3 | var sys = require('sys'); 4 | 5 | var server = DNode({ 6 | moo : function (reply) { 7 | reply(100); 8 | server.close(); 9 | }, 10 | boo : function (n, reply) { 11 | reply(n+1); 12 | server.close(); 13 | } 14 | }).listen(6060); 15 | -------------------------------------------------------------------------------- /src/main/java/dnode/Callback.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | public interface Callback { 4 | void call(Object... args) throws RuntimeException; 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dnode/ClientHandler.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | public interface ClientHandler { 4 | void onConnect(T client); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/dnode/Connection.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | import com.google.gson.JsonElement; 4 | 5 | public interface Connection { 6 | void close(); 7 | void write(JsonElement data); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dnode/DNode.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | import com.google.gson.*; 4 | 5 | import java.io.IOException; 6 | import java.lang.reflect.*; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class DNode { 11 | private final List connections = new ArrayList(); 12 | private final DNodeObject instance; 13 | private final ClientHandler clientHandler; 14 | 15 | public DNode(Object instance) { 16 | this(instance, null); 17 | } 18 | 19 | public DNode(Object instance, ClientHandler clientHandler) { 20 | this.instance = new DNodeObject(instance); 21 | this.clientHandler = clientHandler; 22 | } 23 | 24 | public void listen(Server server) throws IOException { 25 | server.listen(this); 26 | } 27 | 28 | private JsonElement methods() { 29 | JsonArray arguments = new JsonArray(); 30 | arguments.add(instance.getSignature()); 31 | return response("methods", arguments, instance.getCallbacks(), new JsonArray()); 32 | } 33 | 34 | private JsonElement response(String method, JsonArray arguments, JsonObject callbacks, JsonArray links) { 35 | return response(new JsonPrimitive(method), arguments, callbacks, links); 36 | } 37 | 38 | public JsonElement response(int method, JsonArray arguments, JsonObject callbacks, JsonArray links) { 39 | return response(new JsonPrimitive(method), arguments, callbacks, links); 40 | } 41 | 42 | private JsonElement response(JsonElement method, JsonArray arguments, JsonObject callbacks, JsonArray links) { 43 | JsonObject response = new JsonObject(); 44 | response.add("method", method); 45 | response.add("arguments", arguments); 46 | response.add("callbacks", callbacks); 47 | response.add("links", links); 48 | return response; 49 | } 50 | 51 | public void onOpen(Connection connection) { 52 | connections.add(connection); 53 | connection.write(methods()); 54 | if(clientHandler != null) { 55 | T clientProxy = createClientProxy(connection); 56 | clientHandler.onConnect(clientProxy); 57 | } 58 | } 59 | 60 | private T createClientProxy(final Connection connection) { 61 | ParameterizedType type = (ParameterizedType) clientHandler.getClass().getGenericInterfaces()[0]; 62 | Class clientType = (Class) type.getActualTypeArguments()[0]; 63 | T clientProxy = (T) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{clientType}, new InvocationHandler() { 64 | @Override 65 | public Object invoke(Object target, Method method, Object[] args) throws Throwable { 66 | JsonArray arguments = transform(args); 67 | JsonObject callbacks = new JsonObject(); 68 | JsonArray links = new JsonArray(); 69 | connection.write(response(method.getName(), arguments, callbacks, links)); 70 | return null; 71 | } 72 | }); 73 | return clientProxy; 74 | } 75 | 76 | public void onMessage(Connection connection, String msg) { 77 | JsonObject json = new JsonParser().parse(msg).getAsJsonObject(); 78 | JsonPrimitive method = json.getAsJsonPrimitive("method"); 79 | if (method.isString() && method.getAsString().equals("methods")) { 80 | defineClientMethods(json.getAsJsonArray("arguments").get(0).getAsJsonObject()); 81 | } else { 82 | instance.invoke(this, json, connection); 83 | } 84 | } 85 | 86 | public JsonArray transform(Object[] args) { 87 | JsonArray result = new JsonArray(); 88 | for (Object arg : args) { 89 | result.add(toJson(arg)); 90 | } 91 | return result; 92 | } 93 | 94 | private JsonElement toJson(Object o) { 95 | JsonElement e; 96 | if (o instanceof String) { 97 | e = new JsonPrimitive((String) o); 98 | } else if (o instanceof Number) { 99 | e = new JsonPrimitive((Number) o); 100 | } else { 101 | throw new RuntimeException("Unsupported type: " + o.getClass()); 102 | } 103 | return e; 104 | } 105 | 106 | private void defineClientMethods(JsonObject methods) { 107 | // TODO: Verify that the client's reported methods have the same 108 | // signatures as our client proxy, and fail fast if it doesn't. 109 | } 110 | 111 | public void closeAllConnections() { 112 | for (Connection connection : connections) { 113 | connection.close(); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/dnode/DNodeObject.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | import com.google.gson.JsonArray; 4 | import com.google.gson.JsonElement; 5 | import com.google.gson.JsonObject; 6 | import com.google.gson.JsonPrimitive; 7 | 8 | import java.lang.reflect.InvocationTargetException; 9 | import java.lang.reflect.Method; 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | public class DNodeObject { 14 | private final Object instance; 15 | 16 | public DNodeObject(Object instance) { 17 | this.instance = instance; 18 | } 19 | 20 | public JsonElement getSignature() { 21 | Class klass = this.instance.getClass(); 22 | JsonObject signature = new JsonObject(); 23 | for (Method m : klass.getDeclaredMethods()) { 24 | signature.addProperty(m.getName(), "[Function]"); 25 | } 26 | return signature; 27 | } 28 | 29 | public JsonObject getCallbacks() { 30 | Class klass = this.instance.getClass(); 31 | JsonObject callbacks = new JsonObject(); 32 | int index = 0; 33 | for (Method m : klass.getDeclaredMethods()) { 34 | Class[] parameterTypes = m.getParameterTypes(); 35 | for (Class parameterType : parameterTypes) { 36 | if (Callback.class.isAssignableFrom(parameterType)) { 37 | JsonArray path = new JsonArray(); 38 | path.add(new JsonPrimitive("0")); 39 | path.add(new JsonPrimitive(m.getName())); 40 | callbacks.add(String.valueOf(index++), path); 41 | } 42 | } 43 | } 44 | return callbacks; 45 | } 46 | 47 | public void invoke(final DNode dNode, JsonObject invocation, final Connection connection) { 48 | try { 49 | int methodIndex = invocation.get("method").getAsInt(); 50 | Set> callbacks = invocation.get("callbacks").getAsJsonObject().entrySet(); 51 | for (Map.Entry callback : callbacks) { 52 | final int callbackId = Integer.parseInt(callback.getKey()); 53 | JsonArray path = callback.getValue().getAsJsonArray(); 54 | Callback cb = new Callback() { 55 | @Override 56 | public void call(Object... args) throws RuntimeException { 57 | JsonArray jsonArgs = dNode.transform(args); 58 | connection.write(dNode.response(callbackId, jsonArgs, new JsonObject(), new JsonArray())); 59 | } 60 | }; 61 | instance.getClass().getDeclaredMethods()[methodIndex].invoke(instance, cb); 62 | } 63 | } catch (IllegalAccessException e) { 64 | e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 65 | } catch (InvocationTargetException e) { 66 | e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/dnode/Server.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | import java.io.IOException; 4 | 5 | public interface Server { 6 | void listen(DNode dnode) throws IOException; 7 | void shutdown() throws IOException; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/dnode/netty/DNodePipelineFactory.java: -------------------------------------------------------------------------------- 1 | package dnode.netty; 2 | 3 | import dnode.DNode; 4 | import org.jboss.netty.channel.ChannelPipeline; 5 | import org.jboss.netty.channel.ChannelPipelineFactory; 6 | import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder; 7 | import org.jboss.netty.handler.codec.frame.Delimiters; 8 | import org.jboss.netty.handler.codec.string.StringDecoder; 9 | import org.jboss.netty.handler.codec.string.StringEncoder; 10 | 11 | import static org.jboss.netty.channel.Channels.pipeline; 12 | 13 | public class DNodePipelineFactory implements ChannelPipelineFactory { 14 | private final DNode dnode; 15 | 16 | public DNodePipelineFactory(DNode dnode) { 17 | this.dnode = dnode; 18 | } 19 | 20 | @Override 21 | public ChannelPipeline getPipeline() throws Exception { 22 | ChannelPipeline pipeline = pipeline(); 23 | pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); 24 | pipeline.addLast("decoder", new StringDecoder()); 25 | pipeline.addLast("encoder", new StringEncoder()); 26 | pipeline.addLast("handler", new DNodeServerHandler(dnode)); 27 | return pipeline; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/dnode/netty/DNodeServerHandler.java: -------------------------------------------------------------------------------- 1 | package dnode.netty; 2 | 3 | import dnode.Connection; 4 | import dnode.DNode; 5 | import org.jboss.netty.channel.*; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class DNodeServerHandler extends SimpleChannelUpstreamHandler { 11 | private final DNode dnode; 12 | private Map connections = new HashMap(); 13 | 14 | public DNodeServerHandler(DNode dnode) { 15 | this.dnode = dnode; 16 | } 17 | 18 | @Override 19 | public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 20 | Connection connection = new NettyConnection(e.getChannel()); 21 | connections.put(e.getChannel(), connection); 22 | dnode.onOpen(connection); 23 | } 24 | 25 | @Override 26 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { 27 | String message = (String) e.getMessage(); 28 | dnode.onMessage(connections.get(e.getChannel()), message); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/dnode/netty/NettyConnection.java: -------------------------------------------------------------------------------- 1 | package dnode.netty; 2 | 3 | import com.google.gson.JsonElement; 4 | import dnode.Connection; 5 | import org.jboss.netty.channel.Channel; 6 | 7 | public class NettyConnection implements Connection { 8 | private final Channel channel; 9 | 10 | public NettyConnection(Channel channel) { 11 | this.channel = channel; 12 | } 13 | 14 | @Override 15 | public void close() { 16 | channel.close(); 17 | } 18 | 19 | @Override 20 | public void write(JsonElement data) { 21 | channel.write(data.toString() + "\n"); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/dnode/netty/NettyServer.java: -------------------------------------------------------------------------------- 1 | package dnode.netty; 2 | 3 | import dnode.DNode; 4 | import dnode.Server; 5 | import org.jboss.netty.bootstrap.ServerBootstrap; 6 | import org.jboss.netty.channel.Channel; 7 | import org.jboss.netty.channel.ChannelFactory; 8 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 9 | 10 | import java.io.IOException; 11 | import java.net.InetSocketAddress; 12 | import java.util.concurrent.Executors; 13 | 14 | public class NettyServer implements Server { 15 | private final int port; 16 | private Channel channel; 17 | 18 | public NettyServer(int port) { 19 | this.port = port; 20 | } 21 | 22 | @Override 23 | public void listen(DNode dnode) throws IOException { 24 | ChannelFactory factory = new NioServerSocketChannelFactory( 25 | Executors.newCachedThreadPool(), 26 | Executors.newCachedThreadPool()); 27 | 28 | ServerBootstrap bootstrap = new ServerBootstrap(factory); 29 | bootstrap.setPipelineFactory(new DNodePipelineFactory(dnode)); 30 | channel = bootstrap.bind(new InetSocketAddress(port)); 31 | } 32 | 33 | @Override 34 | public void shutdown() throws IOException { 35 | if(channel != null) { 36 | channel.close(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/dnode/socketio/SocketIOCodec.java: -------------------------------------------------------------------------------- 1 | package dnode.socketio; 2 | 3 | import com.google.gson.JsonElement; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | public class SocketIOCodec { 10 | private static final String FRAME = "~m~"; 11 | 12 | public String encode(String message) { 13 | return encode(Collections.singletonList(message)); 14 | } 15 | 16 | public String encode(List messages) { 17 | StringBuilder sb = new StringBuilder(); 18 | for (Object message : messages) { 19 | String m = stringify(message); 20 | sb.append(FRAME).append(m.length()).append(FRAME).append(m); 21 | } 22 | return sb.toString(); 23 | } 24 | 25 | private String stringify(Object message) { 26 | if (message instanceof JsonElement) { 27 | return "~j~" + message.toString(); 28 | } else { 29 | return String.valueOf(message); 30 | } 31 | } 32 | 33 | public List decode(String data) { 34 | List messages = new ArrayList(); 35 | do { 36 | String number = ""; 37 | if (!data.substring(0, 3).equals(FRAME)) return messages; 38 | data = data.substring(3); 39 | for (int i = 0, l = data.length(); i < l; i++) { 40 | try { 41 | String sub = data.substring(i, i + 1); 42 | Integer.parseInt(sub); 43 | number += data.substring(i, i + 1); 44 | } catch (NumberFormatException e) { 45 | data = data.substring(number.length() + FRAME.length()); 46 | break; 47 | } 48 | } 49 | int l = Integer.parseInt(number); 50 | messages.add(data.substring(0, l)); 51 | data = data.substring(l); 52 | } while (!data.equals("")); 53 | return messages; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/dnode/socketio/SocketIOConnection.java: -------------------------------------------------------------------------------- 1 | package dnode.socketio; 2 | 3 | import webbit.HttpRequest; 4 | import webbit.WebSocketConnection; 5 | 6 | import java.util.Map; 7 | import java.util.Set; 8 | import java.util.concurrent.Executor; 9 | 10 | class SocketIOConnection implements WebSocketConnection { 11 | private final WebSocketConnection connection; 12 | private final SocketIOCodec codec; 13 | 14 | public SocketIOConnection(WebSocketConnection connection, SocketIOCodec codec) { 15 | this.connection = connection; 16 | this.codec = codec; 17 | String id = String.valueOf(System.currentTimeMillis()); 18 | connection.send(codec.encode(id)); 19 | } 20 | 21 | @Override 22 | public HttpRequest httpRequest() { 23 | return connection.httpRequest(); 24 | } 25 | 26 | @Override 27 | public WebSocketConnection send(String message) { 28 | String encodedMessage = codec.encode(message); 29 | connection.send(encodedMessage); 30 | return this; 31 | } 32 | 33 | @Override 34 | public WebSocketConnection close() { 35 | connection.close(); 36 | return this; 37 | } 38 | 39 | @Override 40 | public Map data() { 41 | return connection.data(); 42 | } 43 | 44 | @Override 45 | public Object data(String key) { 46 | return connection.data(key); 47 | } 48 | 49 | @Override 50 | public WebSocketConnection data(String key, Object value) { 51 | return connection.data(key, value); 52 | } 53 | 54 | @Override 55 | public Set dataKeys() { 56 | return connection.dataKeys(); 57 | } 58 | 59 | @Override 60 | public Executor handlerExecutor() { 61 | return connection.handlerExecutor(); 62 | } 63 | 64 | @Override 65 | public void execute(Runnable runnable) { 66 | connection.execute(runnable); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/dnode/socketio/SocketIOListener.java: -------------------------------------------------------------------------------- 1 | package dnode.socketio; 2 | 3 | import com.google.gson.JsonElement; 4 | 5 | public interface SocketIOListener { 6 | void onClientMessage(JsonElement json, SocketIOWebSocketHandler socketIOWebSocketHandler); 7 | 8 | void onClientMessage(String message, SocketIOWebSocketHandler socketIOWebSocketHandler); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/dnode/socketio/SocketIOWebSocketHandler.java: -------------------------------------------------------------------------------- 1 | package dnode.socketio; 2 | 3 | import webbit.WebSocketConnection; 4 | import webbit.WebSocketHandler; 5 | 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | /** 11 | * Handler that wraps/unwraps messages sent with a SocketIO client. 12 | */ 13 | public class SocketIOWebSocketHandler implements WebSocketHandler { 14 | private static final SocketIOCodec codec = new SocketIOCodec(); 15 | private final WebSocketHandler handler; 16 | private Map socketIOConnections = new HashMap(); 17 | 18 | public SocketIOWebSocketHandler(WebSocketHandler handler) { 19 | this.handler = handler; 20 | } 21 | 22 | @Override 23 | public void onOpen(WebSocketConnection connection) throws Exception { 24 | SocketIOConnection socketIOConnection = new SocketIOConnection(connection, codec); 25 | socketIOConnections.put(connection, socketIOConnection); 26 | handler.onOpen(socketIOConnection); 27 | } 28 | 29 | @Override 30 | public void onMessage(WebSocketConnection connection, String msg) throws Exception { 31 | List messages = codec.decode(msg); 32 | for (String message : messages) { 33 | String frame = message.substring(0, 3); 34 | if (frame.equals("~j~")) { 35 | // Heartbeat 36 | return; 37 | } else if (frame.equals("~j~")) { 38 | // TODO: Should we parse into JSON here? We also seem to get JSON that is *not* prefixed with ~j~ (??) 39 | message = message.substring(3); 40 | } 41 | handler.onMessage(socketIOConnections.get(connection), message); 42 | } 43 | } 44 | 45 | @Override 46 | public void onClose(WebSocketConnection connection) throws Exception { 47 | handler.onClose(socketIOConnections.remove(connection)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/dnode/webbit/DNodeWebSocketHandler.java: -------------------------------------------------------------------------------- 1 | package dnode.webbit; 2 | 3 | import dnode.DNode; 4 | import webbit.WebSocketConnection; 5 | import webbit.WebSocketHandler; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class DNodeWebSocketHandler implements WebSocketHandler { 11 | private final DNode dnode; 12 | 13 | private final Map connections = new HashMap(); 14 | 15 | public DNodeWebSocketHandler(DNode dnode) { 16 | this.dnode = dnode; 17 | } 18 | 19 | public void onOpen(WebSocketConnection connection) throws Exception { 20 | WebbitConnection c = getFor(connection); 21 | dnode.onOpen(c); 22 | } 23 | 24 | public void onMessage(WebSocketConnection connection, String msg) throws Exception { 25 | WebbitConnection c = getFor(connection); 26 | dnode.onMessage(c, msg); 27 | } 28 | 29 | public void onClose(WebSocketConnection connection) throws Exception { 30 | connections.remove(connection); 31 | } 32 | 33 | private WebbitConnection getFor(WebSocketConnection connection) { 34 | WebbitConnection wc = connections.get(connection); 35 | if (wc == null) { 36 | wc = new WebbitConnection(connection); 37 | connections.put(connection, wc); 38 | } 39 | return wc; 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/java/dnode/webbit/WebbitConnection.java: -------------------------------------------------------------------------------- 1 | package dnode.webbit; 2 | 3 | import com.google.gson.JsonElement; 4 | import dnode.Connection; 5 | import webbit.WebSocketConnection; 6 | 7 | public class WebbitConnection implements Connection { 8 | private final WebSocketConnection webSocketConnection; 9 | 10 | public WebbitConnection(WebSocketConnection webSocketConnection) { 11 | if (webSocketConnection == null) { 12 | throw new NullPointerException("Where is the connection?"); 13 | } 14 | this.webSocketConnection = webSocketConnection; 15 | } 16 | 17 | @Override 18 | public void write(JsonElement data) { 19 | webSocketConnection.send(data.toString() + "\n"); 20 | } 21 | 22 | @Override 23 | public void close() { 24 | webSocketConnection.close(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/dnode/webbit/WebbitServer.java: -------------------------------------------------------------------------------- 1 | package dnode.webbit; 2 | 3 | import dnode.DNode; 4 | import dnode.Server; 5 | import dnode.socketio.SocketIOWebSocketHandler; 6 | import webbit.WebServer; 7 | 8 | import java.io.IOException; 9 | 10 | public class WebbitServer implements Server { 11 | private final WebServer server; 12 | 13 | public WebbitServer(WebServer server) { 14 | this.server = server; 15 | } 16 | 17 | public void listen(DNode dnode) throws IOException { 18 | server.add("/socket.io/websocket", new SocketIOWebSocketHandler(new DNodeWebSocketHandler(dnode))); 19 | } 20 | 21 | public void shutdown() throws IOException { 22 | server.stop(); 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/test/java/dnode/DNodeObjectTest.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | public class DNodeObjectTest { 8 | public static class Cat { 9 | public void say(Callback callback) { 10 | 11 | } 12 | 13 | public void meow(Callback callback) { 14 | 15 | } 16 | } 17 | 18 | @Test 19 | public void shouldReportSignature() { 20 | DNodeObject dcat = new DNodeObject(new Cat()); 21 | assertEquals("{\"say\":\"[Function]\",\"meow\":\"[Function]\"}", dcat.getSignature().toString()); 22 | } 23 | 24 | @Test 25 | public void shouldReportCallbacks() { 26 | DNodeObject dcat = new DNodeObject(new Cat()); 27 | assertEquals("{\"0\":[\"0\",\"say\"],\"1\":[\"0\",\"meow\"]}", dcat.getCallbacks().toString()); 28 | } 29 | } -------------------------------------------------------------------------------- /src/test/java/dnode/DNodeTest.java: -------------------------------------------------------------------------------- 1 | package dnode; 2 | 3 | import dnode.netty.NettyServer; 4 | import junit.framework.AssertionFailedError; 5 | import org.junit.After; 6 | import org.junit.Test; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStreamReader; 10 | import java.io.Reader; 11 | 12 | import static org.junit.Assert.assertEquals; 13 | 14 | public class DNodeTest { 15 | private DNode dNode; 16 | private final Server server = new NettyServer(6060); 17 | 18 | public class Mooer { 19 | private final int moo; 20 | public DNode dNode; 21 | 22 | public Mooer(int moo) { 23 | this.moo = moo; 24 | } 25 | 26 | public void moo(Callback cb) throws IOException { 27 | cb.call(moo); 28 | dNode.closeAllConnections(); 29 | server.shutdown(); 30 | } 31 | 32 | public void boo(Callback cb) throws IOException { 33 | cb.call(moo * 10); 34 | dNode.closeAllConnections(); 35 | server.shutdown(); 36 | } 37 | } 38 | 39 | @After 40 | public void shutdownServer() throws IOException { 41 | server.shutdown(); 42 | } 43 | 44 | @Test 45 | public void shouldTalk() throws IOException, InterruptedException { 46 | createDnode(100); 47 | dNode.listen(server); 48 | assertEquals("100\n", runClient("moo")); 49 | } 50 | 51 | @Test 52 | public void shouldUseDataInInstance() throws IOException, InterruptedException { 53 | createDnode(200); 54 | dNode.listen(server); 55 | assertEquals("200\n", runClient("moo")); 56 | } 57 | 58 | @Test 59 | public void shouldCallRightMethod() throws IOException, InterruptedException { 60 | createDnode(300); 61 | dNode.listen(server); 62 | assertEquals("3000\n", runClient("boo")); 63 | } 64 | 65 | public static interface SomeClient { 66 | void hello(); 67 | } 68 | 69 | @Test 70 | public void shouldBeAbleToCallClient() { 71 | Mooer mooer = new Mooer(345); 72 | dNode = new DNode(mooer, new ClientHandler() { 73 | @Override 74 | public void onConnect(SomeClient client) { 75 | System.out.println("client = " + client); 76 | client.hello(); 77 | } 78 | }); 79 | } 80 | 81 | 82 | private void createDnode(int moo) { 83 | Mooer instance = new Mooer(moo); 84 | dNode = new DNode(instance); 85 | instance.dNode = dNode; 86 | } 87 | 88 | private String runClient(String method) throws IOException, InterruptedException { 89 | String node = System.getProperty("node", "/usr/local/bin/node"); 90 | String clientScript = System.getProperty("client", "client.js"); 91 | ProcessBuilder pb = new ProcessBuilder(node, clientScript, method); 92 | pb.redirectErrorStream(true); 93 | Process client = pb.start(); 94 | 95 | Reader clientStdOut = new InputStreamReader(client.getInputStream(), "UTF-8"); 96 | StringBuilder result = new StringBuilder(); 97 | int c; 98 | while ((c = clientStdOut.read()) != -1) { 99 | result.append((char) c); 100 | } 101 | int exit = client.waitFor(); 102 | if (exit != 0) 103 | throw new AssertionFailedError("Exit value from external process was " + exit + 104 | " (with stdout/stderr: " + result + ")"); 105 | return result.toString(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/test/java/dnode/socketio/SocketIOCodecTest.java: -------------------------------------------------------------------------------- 1 | package dnode.socketio; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import static org.junit.Assert.assertEquals; 9 | 10 | public class SocketIOCodecTest { 11 | private final SocketIOCodec codec = new SocketIOCodec(); 12 | 13 | @Test 14 | public void shouldEncodeOneElement() { 15 | assertEncoding(Arrays.asList("Hello")); 16 | } 17 | 18 | @Test 19 | public void shouldEncodeTwoElements() { 20 | assertEncoding(Arrays.asList("Hello", "World")); 21 | } 22 | 23 | private void assertEncoding(List m) { 24 | String encoded = codec.encode(m); 25 | assertEquals(m, codec.decode(encoded)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/dnode/webbit/DNodeExample.java: -------------------------------------------------------------------------------- 1 | package dnode.webbit; 2 | 3 | import dnode.Callback; 4 | import dnode.ClientHandler; 5 | import dnode.DNode; 6 | import webbit.WebServer; 7 | import webbit.handler.EmbeddedResourceHandler; 8 | import webbit.netty.NettyWebServer; 9 | 10 | import java.io.IOException; 11 | 12 | import static java.util.concurrent.Executors.newFixedThreadPool; 13 | 14 | public class DNodeExample { 15 | public static class Cat { 16 | public void howAreYou(Callback cb) { 17 | cb.call("I am fine"); 18 | } 19 | } 20 | 21 | public interface MyClient { 22 | void greet(String what); 23 | } 24 | 25 | public static void main(String[] args) throws IOException { 26 | WebServer server = new NettyWebServer(6061); 27 | new DNode(new Cat(), new ClientHandler() { 28 | @Override 29 | public void onConnect(MyClient client) { 30 | client.greet("How are you?"); 31 | } 32 | }).listen(new WebbitServer(server)); 33 | server.add("/.*", new EmbeddedResourceHandler("dnode/js", newFixedThreadPool(4))); 34 | server.start(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/dnode/webbit/WebbitIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package dnode.webbit; 2 | 3 | import com.gargoylesoftware.htmlunit.AlertHandler; 4 | import com.gargoylesoftware.htmlunit.BrowserVersion; 5 | import com.gargoylesoftware.htmlunit.Page; 6 | import com.gargoylesoftware.htmlunit.WebClient; 7 | import com.gargoylesoftware.htmlunit.html.HtmlPage; 8 | import dnode.Callback; 9 | import dnode.DNode; 10 | import org.junit.Ignore; 11 | import org.junit.Test; 12 | import webbit.WebServer; 13 | import webbit.handler.EmbeddedResourceHandler; 14 | import webbit.netty.NettyWebServer; 15 | 16 | import java.io.IOException; 17 | 18 | import static java.lang.Thread.sleep; 19 | import static java.util.concurrent.Executors.newFixedThreadPool; 20 | 21 | public class WebbitIntegrationTest { 22 | public static class Cat { 23 | public void cat(Callback cb) { 24 | System.out.println("cb = " + cb); 25 | cb.call("GROWL"); 26 | } 27 | } 28 | 29 | @Test 30 | @Ignore 31 | public void should_work_with_browser() throws IOException, InterruptedException { 32 | WebServer server = new NettyWebServer(6061); 33 | new DNode(new Cat()).listen(new WebbitServer(server)); 34 | server.add("/.*", new EmbeddedResourceHandler("dnode/js", newFixedThreadPool(4))); 35 | server.start(); 36 | 37 | WebClient client = new WebClient(BrowserVersion.INTERNET_EXPLORER_8); 38 | client.setAlertHandler(new AlertHandler() { 39 | @Override 40 | public void handleAlert(Page page, String message) { 41 | System.out.println("alert = " + message); 42 | } 43 | }); 44 | HtmlPage page = client.getPage("http://localhost:6061/"); 45 | 46 | sleep(5000); 47 | // Expected to see GROWL in stdout at least, but not happening... 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/test/resources/dnode/js/dnode.js: -------------------------------------------------------------------------------- 1 | /** Socket.IO 0.6.1 - Built with build.js */ 2 | /** 3 | * Socket.IO client 4 | * 5 | * @author Guillermo Rauch 6 | * @license The MIT license. 7 | * @copyright Copyright (c) 2010 LearnBoost 8 | */ 9 | 10 | this.io = { 11 | version: '0.6.1', 12 | 13 | setPath: function(path){ 14 | if (window.console && console.error) console.error('io.setPath will be removed. Please set the variable WEB_SOCKET_SWF_LOCATION pointing to WebSocketMain.swf'); 15 | this.path = /\/$/.test(path) ? path : path + '/'; 16 | WEB_SOCKET_SWF_LOCATION = path + 'lib/vendor/web-socket-js/WebSocketMain.swf'; 17 | } 18 | }; 19 | 20 | if ('jQuery' in this) jQuery.io = this.io; 21 | 22 | if (typeof window != 'undefined'){ 23 | // WEB_SOCKET_SWF_LOCATION = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//cdn.socket.io/' + this.io.version + '/WebSocketMain.swf'; 24 | if (typeof WEB_SOCKET_SWF_LOCATION === 'undefined') 25 | WEB_SOCKET_SWF_LOCATION = '/socket.io/lib/vendor/web-socket-js/WebSocketMain.swf'; 26 | } 27 | 28 | /** 29 | * Socket.IO client 30 | * 31 | * @author Guillermo Rauch 32 | * @license The MIT license. 33 | * @copyright Copyright (c) 2010 LearnBoost 34 | */ 35 | 36 | (function(){ 37 | 38 | var _pageLoaded = false; 39 | 40 | io.util = { 41 | 42 | ios: false, 43 | 44 | load: function(fn){ 45 | if (/loaded|complete/.test(document.readyState) || _pageLoaded) return fn(); 46 | if ('attachEvent' in window){ 47 | window.attachEvent('onload', fn); 48 | } else { 49 | window.addEventListener('load', fn, false); 50 | } 51 | }, 52 | 53 | inherit: function(ctor, superCtor){ 54 | // no support for `instanceof` for now 55 | for (var i in superCtor.prototype){ 56 | ctor.prototype[i] = superCtor.prototype[i]; 57 | } 58 | }, 59 | 60 | indexOf: function(arr, item, from){ 61 | for (var l = arr.length, i = (from < 0) ? Math.max(0, l + from) : from || 0; i < l; i++){ 62 | if (arr[i] === item) return i; 63 | } 64 | return -1; 65 | }, 66 | 67 | isArray: function(obj){ 68 | return Object.prototype.toString.call(obj) === '[object Array]'; 69 | }, 70 | 71 | merge: function(target, additional){ 72 | for (var i in additional) 73 | if (additional.hasOwnProperty(i)) 74 | target[i] = additional[i]; 75 | } 76 | 77 | }; 78 | 79 | io.util.ios = /iphone|ipad/i.test(navigator.userAgent); 80 | io.util.android = /android/i.test(navigator.userAgent); 81 | io.util.opera = /opera/i.test(navigator.userAgent); 82 | 83 | io.util.load(function(){ 84 | _pageLoaded = true; 85 | }); 86 | 87 | })(); 88 | 89 | /** 90 | * Socket.IO client 91 | * 92 | * @author Guillermo Rauch 93 | * @license The MIT license. 94 | * @copyright Copyright (c) 2010 LearnBoost 95 | */ 96 | 97 | // abstract 98 | 99 | (function(){ 100 | 101 | var frame = '~m~', 102 | 103 | stringify = function(message){ 104 | if (Object.prototype.toString.call(message) == '[object Object]'){ 105 | if (!('JSON' in window)){ 106 | if ('console' in window && console.error) console.error('Trying to encode as JSON, but JSON.stringify is missing.'); 107 | return '{ "$error": "Invalid message" }'; 108 | } 109 | return '~j~' + JSON.stringify(message); 110 | } else { 111 | return String(message); 112 | } 113 | }; 114 | 115 | Transport = io.Transport = function(base, options){ 116 | this.base = base; 117 | this.options = { 118 | timeout: 15000 // based on heartbeat interval default 119 | }; 120 | io.util.merge(this.options, options); 121 | }; 122 | 123 | Transport.prototype.send = function(){ 124 | throw new Error('Missing send() implementation'); 125 | }; 126 | 127 | Transport.prototype.connect = function(){ 128 | throw new Error('Missing connect() implementation'); 129 | }; 130 | 131 | Transport.prototype.disconnect = function(){ 132 | throw new Error('Missing disconnect() implementation'); 133 | }; 134 | 135 | Transport.prototype._encode = function(messages){ 136 | var ret = '', message, 137 | messages = io.util.isArray(messages) ? messages : [messages]; 138 | for (var i = 0, l = messages.length; i < l; i++){ 139 | message = messages[i] === null || messages[i] === undefined ? '' : stringify(messages[i]); 140 | ret += frame + message.length + frame + message; 141 | } 142 | return ret; 143 | }; 144 | 145 | Transport.prototype._decode = function(data){ 146 | var messages = [], number, n; 147 | do { 148 | if (data.substr(0, 3) !== frame) return messages; 149 | data = data.substr(3); 150 | number = '', n = ''; 151 | for (var i = 0, l = data.length; i < l; i++){ 152 | n = Number(data.substr(i, 1)); 153 | if (data.substr(i, 1) == n){ 154 | number += n; 155 | } else { 156 | data = data.substr(number.length + frame.length); 157 | number = Number(number); 158 | break; 159 | } 160 | } 161 | messages.push(data.substr(0, number)); // here 162 | data = data.substr(number); 163 | } while(data !== ''); 164 | return messages; 165 | }; 166 | 167 | Transport.prototype._onData = function(data){ 168 | this._setTimeout(); 169 | var msgs = this._decode(data); 170 | if (msgs && msgs.length){ 171 | for (var i = 0, l = msgs.length; i < l; i++){ 172 | this._onMessage(msgs[i]); 173 | } 174 | } 175 | }; 176 | 177 | Transport.prototype._setTimeout = function(){ 178 | var self = this; 179 | if (this._timeout) clearTimeout(this._timeout); 180 | this._timeout = setTimeout(function(){ 181 | self._onTimeout(); 182 | }, this.options.timeout); 183 | }; 184 | 185 | Transport.prototype._onTimeout = function(){ 186 | this._onDisconnect(); 187 | }; 188 | 189 | Transport.prototype._onMessage = function(message){ 190 | if (!this.sessionid){ 191 | this.sessionid = message; 192 | this._onConnect(); 193 | } else if (message.substr(0, 3) == '~h~'){ 194 | this._onHeartbeat(message.substr(3)); 195 | } else if (message.substr(0, 3) == '~j~'){ 196 | this.base._onMessage(JSON.parse(message.substr(3))); 197 | } else { 198 | this.base._onMessage(message); 199 | } 200 | }, 201 | 202 | Transport.prototype._onHeartbeat = function(heartbeat){ 203 | this.send('~h~' + heartbeat); // echo 204 | }; 205 | 206 | Transport.prototype._onConnect = function(){ 207 | this.connected = true; 208 | this.connecting = false; 209 | this.base._onConnect(); 210 | this._setTimeout(); 211 | }; 212 | 213 | Transport.prototype._onDisconnect = function(){ 214 | this.connecting = false; 215 | this.connected = false; 216 | this.sessionid = null; 217 | this.base._onDisconnect(); 218 | }; 219 | 220 | Transport.prototype._prepareUrl = function(){ 221 | return (this.base.options.secure ? 'https' : 'http') 222 | + '://' + this.base.host 223 | + ':' + this.base.options.port 224 | + '/' + this.base.options.resource 225 | + '/' + this.type 226 | + (this.sessionid ? ('/' + this.sessionid) : '/'); 227 | }; 228 | 229 | })(); 230 | /** 231 | * Socket.IO client 232 | * 233 | * @author Guillermo Rauch 234 | * @license The MIT license. 235 | * @copyright Copyright (c) 2010 LearnBoost 236 | */ 237 | 238 | (function(){ 239 | 240 | var empty = new Function, 241 | 242 | XMLHttpRequestCORS = (function(){ 243 | if (!('XMLHttpRequest' in window)) return false; 244 | // CORS feature detection 245 | var a = new XMLHttpRequest(); 246 | return a.withCredentials != undefined; 247 | })(), 248 | 249 | request = function(xdomain){ 250 | if ('XDomainRequest' in window && xdomain) return new XDomainRequest(); 251 | if ('XMLHttpRequest' in window && (!xdomain || XMLHttpRequestCORS)) return new XMLHttpRequest(); 252 | if (!xdomain){ 253 | try { 254 | var a = new ActiveXObject('MSXML2.XMLHTTP'); 255 | return a; 256 | } catch(e){} 257 | 258 | try { 259 | var b = new ActiveXObject('Microsoft.XMLHTTP'); 260 | return b; 261 | } catch(e){} 262 | } 263 | return false; 264 | }, 265 | 266 | XHR = io.Transport.XHR = function(){ 267 | io.Transport.apply(this, arguments); 268 | this._sendBuffer = []; 269 | }; 270 | 271 | io.util.inherit(XHR, io.Transport); 272 | 273 | XHR.prototype.connect = function(){ 274 | this._get(); 275 | return this; 276 | }; 277 | 278 | XHR.prototype._checkSend = function(){ 279 | if (!this._posting && this._sendBuffer.length){ 280 | var encoded = this._encode(this._sendBuffer); 281 | this._sendBuffer = []; 282 | this._send(encoded); 283 | } 284 | }; 285 | 286 | XHR.prototype.send = function(data){ 287 | if (io.util.isArray(data)){ 288 | this._sendBuffer.push.apply(this._sendBuffer, data); 289 | } else { 290 | this._sendBuffer.push(data); 291 | } 292 | this._checkSend(); 293 | return this; 294 | }; 295 | 296 | XHR.prototype._send = function(data){ 297 | var self = this; 298 | this._posting = true; 299 | this._sendXhr = this._request('send', 'POST'); 300 | this._sendXhr.onreadystatechange = function(){ 301 | var status; 302 | if (self._sendXhr.readyState == 4){ 303 | self._sendXhr.onreadystatechange = empty; 304 | try { status = self._sendXhr.status; } catch(e){} 305 | self._posting = false; 306 | if (status == 200){ 307 | self._checkSend(); 308 | } else { 309 | self._onDisconnect(); 310 | } 311 | } 312 | }; 313 | this._sendXhr.send('data=' + encodeURIComponent(data)); 314 | }; 315 | 316 | XHR.prototype.disconnect = function(){ 317 | // send disconnection signal 318 | this._onDisconnect(); 319 | return this; 320 | }; 321 | 322 | XHR.prototype._onDisconnect = function(){ 323 | if (this._xhr){ 324 | this._xhr.onreadystatechange = empty; 325 | try { 326 | this._xhr.abort(); 327 | } catch(e){} 328 | this._xhr = null; 329 | } 330 | if (this._sendXhr){ 331 | this._sendXhr.onreadystatechange = empty; 332 | try { 333 | this._sendXhr.abort(); 334 | } catch(e){} 335 | this._sendXhr = null; 336 | } 337 | this._sendBuffer = []; 338 | io.Transport.prototype._onDisconnect.call(this); 339 | }; 340 | 341 | XHR.prototype._request = function(url, method, multipart){ 342 | var req = request(this.base._isXDomain()); 343 | if (multipart) req.multipart = true; 344 | req.open(method || 'GET', this._prepareUrl() + (url ? '/' + url : '')); 345 | if (method == 'POST' && 'setRequestHeader' in req){ 346 | req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=utf-8'); 347 | } 348 | return req; 349 | }; 350 | 351 | XHR.check = function(xdomain){ 352 | try { 353 | if (request(xdomain)) return true; 354 | } catch(e){} 355 | return false; 356 | }; 357 | 358 | XHR.xdomainCheck = function(){ 359 | return XHR.check(true); 360 | }; 361 | 362 | XHR.request = request; 363 | 364 | })(); 365 | 366 | /** 367 | * Socket.IO client 368 | * 369 | * @author Guillermo Rauch 370 | * @license The MIT license. 371 | * @copyright Copyright (c) 2010 LearnBoost 372 | */ 373 | 374 | (function(){ 375 | 376 | var WS = io.Transport.websocket = function(){ 377 | io.Transport.apply(this, arguments); 378 | }; 379 | 380 | io.util.inherit(WS, io.Transport); 381 | 382 | WS.prototype.type = 'websocket'; 383 | 384 | WS.prototype.connect = function(){ 385 | var self = this; 386 | this.socket = new WebSocket(this._prepareUrl()); 387 | this.socket.onmessage = function(ev){ self._onData(ev.data); }; 388 | this.socket.onclose = function(ev){ self._onClose(); }; 389 | this.socket.onerror = function(e){ self._onError(e); }; 390 | return this; 391 | }; 392 | 393 | WS.prototype.send = function(data){ 394 | if (this.socket) this.socket.send(this._encode(data)); 395 | return this; 396 | }; 397 | 398 | WS.prototype.disconnect = function(){ 399 | if (this.socket) this.socket.close(); 400 | return this; 401 | }; 402 | 403 | WS.prototype._onClose = function(){ 404 | this._onDisconnect(); 405 | return this; 406 | }; 407 | 408 | WS.prototype._onError = function(e){ 409 | this.base.emit('error', [e]); 410 | }; 411 | 412 | WS.prototype._prepareUrl = function(){ 413 | return (this.base.options.secure ? 'wss' : 'ws') 414 | + '://' + this.base.host 415 | + ':' + this.base.options.port 416 | + '/' + this.base.options.resource 417 | + '/' + this.type 418 | + (this.sessionid ? ('/' + this.sessionid) : ''); 419 | }; 420 | 421 | WS.check = function(){ 422 | // we make sure WebSocket is not confounded with a previously loaded flash WebSocket 423 | return 'WebSocket' in window && WebSocket.prototype && ( WebSocket.prototype.send && !!WebSocket.prototype.send.toString().match(/native/i)) && typeof WebSocket !== "undefined"; 424 | }; 425 | 426 | WS.xdomainCheck = function(){ 427 | return true; 428 | }; 429 | 430 | })(); 431 | 432 | /** 433 | * Socket.IO client 434 | * 435 | * @author Guillermo Rauch 436 | * @license The MIT license. 437 | * @copyright Copyright (c) 2010 LearnBoost 438 | */ 439 | 440 | (function(){ 441 | 442 | var Flashsocket = io.Transport.flashsocket = function(){ 443 | io.Transport.websocket.apply(this, arguments); 444 | }; 445 | 446 | io.util.inherit(Flashsocket, io.Transport.websocket); 447 | 448 | Flashsocket.prototype.type = 'flashsocket'; 449 | 450 | Flashsocket.prototype.connect = function(){ 451 | var self = this, args = arguments; 452 | WebSocket.__addTask(function(){ 453 | io.Transport.websocket.prototype.connect.apply(self, args); 454 | }); 455 | return this; 456 | }; 457 | 458 | Flashsocket.prototype.send = function(){ 459 | var self = this, args = arguments; 460 | WebSocket.__addTask(function(){ 461 | io.Transport.websocket.prototype.send.apply(self, args); 462 | }); 463 | return this; 464 | }; 465 | 466 | Flashsocket.check = function(){ 467 | if (typeof WebSocket == 'undefined' || !('__addTask' in WebSocket)) return false; 468 | if (io.util.opera) return false; // opera is buggy with this transport 469 | if ('navigator' in window && 'plugins' in navigator && navigator.plugins['Shockwave Flash']){ 470 | return !!navigator.plugins['Shockwave Flash'].description; 471 | } 472 | if ('ActiveXObject' in window) { 473 | try { 474 | return !!new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); 475 | } catch (e) {} 476 | } 477 | return false; 478 | }; 479 | 480 | Flashsocket.xdomainCheck = function(){ 481 | return true; 482 | }; 483 | 484 | })(); 485 | /** 486 | * Socket.IO client 487 | * 488 | * @author Guillermo Rauch 489 | * @license The MIT license. 490 | * @copyright Copyright (c) 2010 LearnBoost 491 | */ 492 | 493 | (function(){ 494 | 495 | var HTMLFile = io.Transport.htmlfile = function(){ 496 | io.Transport.XHR.apply(this, arguments); 497 | }; 498 | 499 | io.util.inherit(HTMLFile, io.Transport.XHR); 500 | 501 | HTMLFile.prototype.type = 'htmlfile'; 502 | 503 | HTMLFile.prototype._get = function(){ 504 | var self = this; 505 | this._open(); 506 | window.attachEvent('onunload', function(){ self._destroy(); }); 507 | }; 508 | 509 | HTMLFile.prototype._open = function(){ 510 | this._doc = new ActiveXObject('htmlfile'); 511 | this._doc.open(); 512 | this._doc.write(''); 513 | this._doc.parentWindow.s = this; 514 | this._doc.close(); 515 | 516 | var _iframeC = this._doc.createElement('div'); 517 | this._doc.body.appendChild(_iframeC); 518 | this._iframe = this._doc.createElement('iframe'); 519 | _iframeC.appendChild(this._iframe); 520 | this._iframe.src = this._prepareUrl() + '/' + (+ new Date); 521 | }; 522 | 523 | HTMLFile.prototype._ = function(data, doc){ 524 | this._onData(data); 525 | var script = doc.getElementsByTagName('script')[0]; 526 | script.parentNode.removeChild(script); 527 | }; 528 | 529 | HTMLFile.prototype._destroy = function(){ 530 | if (this._iframe){ 531 | this._iframe.src = 'about:blank'; 532 | this._doc = null; 533 | CollectGarbage(); 534 | } 535 | }; 536 | 537 | HTMLFile.prototype.disconnect = function(){ 538 | this._destroy(); 539 | return io.Transport.XHR.prototype.disconnect.call(this); 540 | }; 541 | 542 | HTMLFile.check = function(){ 543 | if ('ActiveXObject' in window){ 544 | try { 545 | var a = new ActiveXObject('htmlfile'); 546 | return a && io.Transport.XHR.check(); 547 | } catch(e){} 548 | } 549 | return false; 550 | }; 551 | 552 | HTMLFile.xdomainCheck = function(){ 553 | // we can probably do handling for sub-domains, we should test that it's cross domain but a subdomain here 554 | return false; 555 | }; 556 | 557 | })(); 558 | /** 559 | * Socket.IO client 560 | * 561 | * @author Guillermo Rauch 562 | * @license The MIT license. 563 | * @copyright Copyright (c) 2010 LearnBoost 564 | */ 565 | 566 | (function(){ 567 | 568 | var XHRMultipart = io.Transport['xhr-multipart'] = function(){ 569 | io.Transport.XHR.apply(this, arguments); 570 | }; 571 | 572 | io.util.inherit(XHRMultipart, io.Transport.XHR); 573 | 574 | XHRMultipart.prototype.type = 'xhr-multipart'; 575 | 576 | XHRMultipart.prototype._get = function(){ 577 | var self = this; 578 | this._xhr = this._request('', 'GET', true); 579 | this._xhr.onreadystatechange = function(){ 580 | if (self._xhr.readyState == 3) self._onData(self._xhr.responseText); 581 | }; 582 | this._xhr.send(null); 583 | }; 584 | 585 | XHRMultipart.check = function(){ 586 | return 'XMLHttpRequest' in window && 'prototype' in XMLHttpRequest && 'multipart' in XMLHttpRequest.prototype; 587 | }; 588 | 589 | XHRMultipart.xdomainCheck = function(){ 590 | return true; 591 | }; 592 | 593 | })(); 594 | 595 | /** 596 | * Socket.IO client 597 | * 598 | * @author Guillermo Rauch 599 | * @license The MIT license. 600 | * @copyright Copyright (c) 2010 LearnBoost 601 | */ 602 | 603 | (function(){ 604 | 605 | var empty = new Function(), 606 | 607 | XHRPolling = io.Transport['xhr-polling'] = function(){ 608 | io.Transport.XHR.apply(this, arguments); 609 | }; 610 | 611 | io.util.inherit(XHRPolling, io.Transport.XHR); 612 | 613 | XHRPolling.prototype.type = 'xhr-polling'; 614 | 615 | XHRPolling.prototype.connect = function(){ 616 | if (io.util.ios || io.util.android){ 617 | var self = this; 618 | io.util.load(function(){ 619 | setTimeout(function(){ 620 | io.Transport.XHR.prototype.connect.call(self); 621 | }, 10); 622 | }); 623 | } else { 624 | io.Transport.XHR.prototype.connect.call(this); 625 | } 626 | }; 627 | 628 | XHRPolling.prototype._get = function(){ 629 | var self = this; 630 | this._xhr = this._request(+ new Date, 'GET'); 631 | this._xhr.onreadystatechange = function(){ 632 | var status; 633 | if (self._xhr.readyState == 4){ 634 | self._xhr.onreadystatechange = empty; 635 | try { status = self._xhr.status; } catch(e){} 636 | if (status == 200){ 637 | self._onData(self._xhr.responseText); 638 | self._get(); 639 | } else { 640 | self._onDisconnect(); 641 | } 642 | } 643 | }; 644 | this._xhr.send(null); 645 | }; 646 | 647 | XHRPolling.check = function(){ 648 | return io.Transport.XHR.check(); 649 | }; 650 | 651 | XHRPolling.xdomainCheck = function(){ 652 | return io.Transport.XHR.xdomainCheck(); 653 | }; 654 | 655 | })(); 656 | 657 | /** 658 | * Socket.IO client 659 | * 660 | * @author Guillermo Rauch 661 | * @license The MIT license. 662 | * @copyright Copyright (c) 2010 LearnBoost 663 | */ 664 | 665 | io.JSONP = []; 666 | 667 | JSONPPolling = io.Transport['jsonp-polling'] = function(){ 668 | io.Transport.XHR.apply(this, arguments); 669 | this._insertAt = document.getElementsByTagName('script')[0]; 670 | this._index = io.JSONP.length; 671 | io.JSONP.push(this); 672 | }; 673 | 674 | io.util.inherit(JSONPPolling, io.Transport['xhr-polling']); 675 | 676 | JSONPPolling.prototype.type = 'jsonp-polling'; 677 | 678 | JSONPPolling.prototype._send = function(data){ 679 | var self = this; 680 | if (!('_form' in this)){ 681 | var form = document.createElement('FORM'), 682 | area = document.createElement('TEXTAREA'), 683 | id = this._iframeId = 'socket_io_iframe_' + this._index, 684 | iframe; 685 | 686 | form.style.position = 'absolute'; 687 | form.style.top = '-1000px'; 688 | form.style.left = '-1000px'; 689 | form.target = id; 690 | form.method = 'POST'; 691 | form.action = this._prepareUrl() + '/' + (+new Date) + '/' + this._index; 692 | area.name = 'data'; 693 | form.appendChild(area); 694 | this._insertAt.parentNode.insertBefore(form, this._insertAt); 695 | document.body.appendChild(form); 696 | 697 | this._form = form; 698 | this._area = area; 699 | } 700 | 701 | function complete(){ 702 | initIframe(); 703 | self._posting = false; 704 | self._checkSend(); 705 | }; 706 | 707 | function initIframe(){ 708 | if (self._iframe){ 709 | self._form.removeChild(self._iframe); 710 | } 711 | 712 | try { 713 | // ie6 dynamic iframes with target="" support (thanks Chris Lambacher) 714 | iframe = document.createElement('