From 67be80ca8c70138ee6ada0d49647aab855e25572 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 22 Jun 2013 21:29:42 +0800 Subject: [PATCH] 05.0.md --- README.md | 2 +- eBook/05.0.md | 17 +- eBook/05.1.md | 17 +- eBook/directory.md | 5 + ...ications - The Go Programming Language.htm | 1027 ----------------- .../all.css | 205 ---- .../ga.js | 47 - .../godocs.js | 190 --- eBook/examples/chapter_15/client.go | 36 - .../p/go.net/.hg/00changelog.i | Bin 57 -> 0 bytes .../code.google.com/p/go.net/.hg/branch | 1 - .../p/go.net/.hg/cache/branchheads | 2 - .../code.google.com/p/go.net/.hg/cache/tags | 2 - .../code.google.com/p/go.net/.hg/dirstate | Bin 652 -> 0 bytes .../code.google.com/p/go.net/.hg/hgrc | 2 - .../code.google.com/p/go.net/.hg/requires | 4 - .../p/go.net/.hg/store/00changelog.i | Bin 1486 -> 0 bytes .../p/go.net/.hg/store/00manifest.i | Bin 1382 -> 0 bytes .../p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i | Bin 205 -> 0 bytes .../.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i | Bin 202 -> 0 bytes .../p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i | Bin 850 -> 0 bytes .../p/go.net/.hg/store/data/_r_e_a_d_m_e.i | Bin 670 -> 0 bytes .../p/go.net/.hg/store/data/codereview.cfg.i | Bin 159 -> 0 bytes .../p/go.net/.hg/store/data/dict/dict.go.i | Bin 1730 -> 0 bytes .../p/go.net/.hg/store/data/spdy/read.go.i | Bin 1936 -> 0 bytes .../.hg/store/data/spdy/spdy__test.go.i | Bin 1862 -> 0 bytes .../p/go.net/.hg/store/data/spdy/types.go.i | Bin 3380 -> 0 bytes .../p/go.net/.hg/store/data/spdy/write.go.i | Bin 1288 -> 0 bytes .../.hg/store/data/websocket/client.go.i | Bin 1309 -> 0 bytes .../.hg/store/data/websocket/hixie.go.i | Bin 5567 -> 0 bytes .../.hg/store/data/websocket/hixie__test.go.i | Bin 1958 -> 0 bytes .../go.net/.hg/store/data/websocket/hybi.go.i | Bin 4493 -> 0 bytes .../.hg/store/data/websocket/hybi__test.go.i | Bin 3292 -> 0 bytes .../.hg/store/data/websocket/server.go.i | Bin 1045 -> 0 bytes .../.hg/store/data/websocket/websocket.go.i | Bin 3039 -> 0 bytes .../store/data/websocket/websocket__test.go.i | Bin 1855 -> 0 bytes .../p/go.net/.hg/store/data/~2ehgignore.i | Bin 89 -> 0 bytes .../p/go.net/.hg/store/fncache | 19 - .../code.google.com/p/go.net/.hg/store/undo | Bin 531 -> 0 bytes .../p/go.net/.hg/undo.bookmarks | 0 .../code.google.com/p/go.net/.hg/undo.branch | 1 - .../code.google.com/p/go.net/.hg/undo.desc | 3 - .../p/go.net/.hg/undo.dirstate | 0 .../code.google.com/p/go.net/.hgignore | 2 - .../code.google.com/p/go.net/AUTHORS | 3 - .../code.google.com/p/go.net/CONTRIBUTORS | 3 - .../code.google.com/p/go.net/LICENSE | 27 - .../code.google.com/p/go.net/README | 3 - .../code.google.com/p/go.net/codereview.cfg | 2 - .../code.google.com/p/go.net/dict/dict.go | 210 ---- .../code.google.com/p/go.net/spdy/read.go | 312 ----- .../p/go.net/spdy/spdy_test.go | 497 -------- .../code.google.com/p/go.net/spdy/types.go | 369 ------ .../code.google.com/p/go.net/spdy/write.go | 285 ----- .../p/go.net/websocket/client.go | 137 --- .../p/go.net/websocket/hixie.go | 695 ----------- .../p/go.net/websocket/hixie_test.go | 201 ---- .../p/go.net/websocket/hybi.go | 549 --------- .../p/go.net/websocket/hybi_test.go | 584 ---------- .../p/go.net/websocket/server.go | 102 -- .../p/go.net/websocket/websocket.go | 412 ------- .../p/go.net/websocket/websocket_test.go | 274 ----- eBook/examples/chapter_15/dial.go | 29 - .../chapter_15/elaborated_webserver.go | 146 --- .../chapter_15/hello_world_webserver.go | 24 - eBook/examples/chapter_15/http_fetch.go | 23 - eBook/examples/chapter_15/pipeline1.go | 17 - eBook/examples/chapter_15/poll_url.go | 30 - .../chapter_15/predefined_functions.go | 14 - eBook/examples/chapter_15/robust_webserver.go | 57 - eBook/examples/chapter_15/rpc_client.go | 39 - eBook/examples/chapter_15/rpc_objects.go | 13 - eBook/examples/chapter_15/rpc_server.go | 32 - eBook/examples/chapter_15/server.go | 37 - .../examples/chapter_15/simple_tcp_server.go | 86 -- eBook/examples/chapter_15/simple_webserver.go | 42 - eBook/examples/chapter_15/smtp.go | 30 - eBook/examples/chapter_15/smtp_auth.go | 29 - eBook/examples/chapter_15/socket.go | 32 - eBook/examples/chapter_15/template_field.go | 23 - eBook/examples/chapter_15/template_ifelse.go | 26 - .../chapter_15/template_validation.go | 22 - .../examples/chapter_15/template_variables.go | 24 - .../examples/chapter_15/template_with_end.go | 20 - eBook/examples/chapter_15/twitter_status.go | 42 - eBook/examples/chapter_15/websocket_client.go | 29 - eBook/examples/chapter_15/websocket_server.go | 29 - eBook/examples/chapter_15/wiki/ANewPage.txt | 2 - eBook/examples/chapter_15/wiki/TestPage.txt | 1 - eBook/examples/chapter_15/wiki/edit.html | 6 - eBook/examples/chapter_15/wiki/page.txt | 2 - eBook/examples/chapter_15/wiki/page1.txt | 1 - eBook/examples/chapter_15/wiki/page5.txt | 3 - eBook/examples/chapter_15/wiki/view.html | 5 - eBook/examples/chapter_15/wiki/wiki.go | 97 -- eBook/examples/chapter_15/wiki/wiki_part1.go | 32 - eBook/examples/chapter_15/wiki/wiki_part2.go | 39 - eBook/examples/chapter_5/booleans.go | 12 + eBook/examples/chapter_5/for1.go | 9 + eBook/examples/chapter_5/for2.go | 12 + eBook/examples/chapter_5/for3.go | 15 + eBook/examples/chapter_5/for4.go | 13 + eBook/examples/chapter_5/for5.go | 11 + eBook/examples/chapter_5/for6.go | 17 + eBook/examples/chapter_5/for_string.go | 57 + eBook/examples/chapter_5/goto.go | 12 + eBook/examples/chapter_5/goto2.go | 13 + eBook/examples/chapter_5/ifelse.go | 22 + eBook/examples/chapter_5/range_string.go | 80 ++ .../examples/chapter_5/string_conversion2.go | 25 + eBook/examples/chapter_5/switch1.go | 16 + eBook/examples/chapter_5/switch2.go | 16 + .../exercises/chapter_5/bitwise_complement.go | 22 + eBook/exercises/chapter_5/fizzbuzz.go | 24 + eBook/exercises/chapter_5/for_character.go | 17 + eBook/exercises/chapter_5/for_loop.go | 16 + eBook/exercises/chapter_5/i_undefined.go | 14 + eBook/exercises/chapter_5/multiple_for.go | 17 + eBook/exercises/chapter_5/rectangle_stars.go | 26 + eBook/exercises/chapter_5/season.go | 17 + 120 files changed, 506 insertions(+), 7307 deletions(-) delete mode 100644 eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm delete mode 100644 eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/all.css delete mode 100644 eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/ga.js delete mode 100644 eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/godocs.js delete mode 100644 eBook/examples/chapter_15/client.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/branch delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/branchheads delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/tags delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/dirstate delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/codereview.cfg.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/dict/dict.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/types.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/write.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/client.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie__test.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi__test.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket__test.go.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/README delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go delete mode 100644 eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go delete mode 100644 eBook/examples/chapter_15/dial.go delete mode 100644 eBook/examples/chapter_15/elaborated_webserver.go delete mode 100644 eBook/examples/chapter_15/hello_world_webserver.go delete mode 100644 eBook/examples/chapter_15/http_fetch.go delete mode 100644 eBook/examples/chapter_15/pipeline1.go delete mode 100644 eBook/examples/chapter_15/poll_url.go delete mode 100644 eBook/examples/chapter_15/predefined_functions.go delete mode 100644 eBook/examples/chapter_15/robust_webserver.go delete mode 100644 eBook/examples/chapter_15/rpc_client.go delete mode 100644 eBook/examples/chapter_15/rpc_objects.go delete mode 100644 eBook/examples/chapter_15/rpc_server.go delete mode 100644 eBook/examples/chapter_15/server.go delete mode 100644 eBook/examples/chapter_15/simple_tcp_server.go delete mode 100644 eBook/examples/chapter_15/simple_webserver.go delete mode 100644 eBook/examples/chapter_15/smtp.go delete mode 100644 eBook/examples/chapter_15/smtp_auth.go delete mode 100644 eBook/examples/chapter_15/socket.go delete mode 100644 eBook/examples/chapter_15/template_field.go delete mode 100644 eBook/examples/chapter_15/template_ifelse.go delete mode 100644 eBook/examples/chapter_15/template_validation.go delete mode 100644 eBook/examples/chapter_15/template_variables.go delete mode 100644 eBook/examples/chapter_15/template_with_end.go delete mode 100644 eBook/examples/chapter_15/twitter_status.go delete mode 100644 eBook/examples/chapter_15/websocket_client.go delete mode 100644 eBook/examples/chapter_15/websocket_server.go delete mode 100644 eBook/examples/chapter_15/wiki/ANewPage.txt delete mode 100644 eBook/examples/chapter_15/wiki/TestPage.txt delete mode 100644 eBook/examples/chapter_15/wiki/edit.html delete mode 100644 eBook/examples/chapter_15/wiki/page.txt delete mode 100644 eBook/examples/chapter_15/wiki/page1.txt delete mode 100644 eBook/examples/chapter_15/wiki/page5.txt delete mode 100644 eBook/examples/chapter_15/wiki/view.html delete mode 100644 eBook/examples/chapter_15/wiki/wiki.go delete mode 100644 eBook/examples/chapter_15/wiki/wiki_part1.go delete mode 100644 eBook/examples/chapter_15/wiki/wiki_part2.go create mode 100644 eBook/examples/chapter_5/booleans.go create mode 100644 eBook/examples/chapter_5/for1.go create mode 100644 eBook/examples/chapter_5/for2.go create mode 100644 eBook/examples/chapter_5/for3.go create mode 100644 eBook/examples/chapter_5/for4.go create mode 100644 eBook/examples/chapter_5/for5.go create mode 100644 eBook/examples/chapter_5/for6.go create mode 100644 eBook/examples/chapter_5/for_string.go create mode 100644 eBook/examples/chapter_5/goto.go create mode 100644 eBook/examples/chapter_5/goto2.go create mode 100644 eBook/examples/chapter_5/ifelse.go create mode 100644 eBook/examples/chapter_5/range_string.go create mode 100644 eBook/examples/chapter_5/string_conversion2.go create mode 100644 eBook/examples/chapter_5/switch1.go create mode 100644 eBook/examples/chapter_5/switch2.go create mode 100644 eBook/exercises/chapter_5/bitwise_complement.go create mode 100644 eBook/exercises/chapter_5/fizzbuzz.go create mode 100644 eBook/exercises/chapter_5/for_character.go create mode 100644 eBook/exercises/chapter_5/for_loop.go create mode 100644 eBook/exercises/chapter_5/i_undefined.go create mode 100644 eBook/exercises/chapter_5/multiple_for.go create mode 100644 eBook/exercises/chapter_5/rectangle_stars.go create mode 100644 eBook/exercises/chapter_5/season.go diff --git a/README.md b/README.md index b43e85e..b4edb92 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 该翻译版本已获得原作者(Ivo Balbaert)本人授权,并表示支持开源事业的发展! ##翻译进度 -4.9 [指针](eBook/04.9.md) +第5章:[控制结构](eBook/05.0.md) ##支持本书 如果你喜欢本书《Go入门指南》,你可以参与到本书的翻译或纠正工作中来,具体请联系【无闻 E-mail:joe2010xtmf#163.com】,一同完善本书并帮助壮大 Go 语言在国内的学习群体,给大家提供更好的学习资源。 diff --git a/eBook/05.0.md b/eBook/05.0.md index 8bbdaa8..f50fc94 100644 --- a/eBook/05.0.md +++ b/eBook/05.0.md @@ -1,23 +1,8 @@ -##啊哦,亲,你看得也太快了。。。还没翻译完呢 0 0 -要不等到 ***2013 年 6 月 20 日*** 再来看看吧~~ - -这里还有一些其它的学习资源噢~ - - - [《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming):已更新至 [第12课](https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture12.md) - - [《Go Web编程》](https://github.com/astaxie/build-web-application-with-golang) - -神马?你说你不想学习?那好吧,去逛逛看看行情也行~ - -- [Go Walker](http://gowalker.org) **Go 项目文档在线浏览工具** -- [Golang中文社区](http://bbs.mygolang.com/forum.php) -- [Go语言学习园地](http://studygolang.com/) -- [Golang中国](http://golang.tc) - #5.0 控制结构 到目前为止,我们看到的都是 Go 程序都是从 main() 函数开始执行,然后按顺序执行该函数体中的代码。但我们经常会需要只有在满足一些特定情况时才执行某些代码,也就是说在代码里进行条件判断。针对这种需求,Go 提供了下面这些条件结构和分支结构: if-else 结构 - switch-else 结构 + switch 结构 select 结构,用于 channel 的选择(第 14.4 节) 可以使用迭代或循环结构来重复执行一次或多次某段代码(任务): diff --git a/eBook/05.1.md b/eBook/05.1.md index f0f015d..a071c10 100644 --- a/eBook/05.1.md +++ b/eBook/05.1.md @@ -1,3 +1,18 @@ +##啊哦,亲,你看得也太快了。。。还没翻译完呢 0 0 +要不等到 ***2013 年 6 月 24 日*** 再来看看吧~~ + +这里还有一些其它的学习资源噢~ + + - [《Go编程基础》](https://github.com/Unknwon/go-fundamental-programming):已更新至 [第12课](https://github.com/Unknwon/go-fundamental-programming/blob/master/lectures/lecture12.md) + - [《Go Web编程》](https://github.com/astaxie/build-web-application-with-golang) + +神马?你说你不想学习?那好吧,去逛逛看看行情也行~ + +- [Go Walker](http://gowalker.org) **Go 项目文档在线浏览工具** +- [Golang中文社区](http://bbs.mygolang.com/forum.php) +- [Go语言学习园地](http://studygolang.com/) +- [Golang中国](http://golang.tc) + #5.1 if-else 结构 if 是用于测试某个条件(布尔型或逻辑型)的语句,如果该条件成立,则会执行 if 后由大括号括起来的代码块,否则就忽略该代码块继续执行后续的代码。 @@ -189,4 +204,4 @@ if value := process(data); value > max { ##链接 - [目录](directory.md) - 上一节:[控制结构](05.0.md) -- 下一节: [TODO](05.2.md) +- 下一节: [测试多返回值函数的错误](05.2.md) diff --git a/eBook/directory.md b/eBook/directory.md index bbf23d2..d4d7118 100644 --- a/eBook/directory.md +++ b/eBook/directory.md @@ -38,6 +38,11 @@ - 4.9 [指针](04.9.md) - 第5章:[控制结构](05.0.md) - 5.1 [if-else 结构](05.1.md) + - 5.2 [测试多返回值函数的错误](05.2.md) + - 5.3 [switch 结构](05.3.md) + - 5.4 [for 结构](05.4.md) + - 5.5 [Break 与 continue](05.5.md) + - 5.6 [标签与 goto](05.6.md) - 第6章:函数(function) - 第7章:数组(array)与切片(slice) - 第8章:Maps diff --git a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm b/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm deleted file mode 100644 index a54b9d5..0000000 --- a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm +++ /dev/null @@ -1,1027 +0,0 @@ - - - - - Codelab: Writing Web Applications - The Go Programming Language - - - - - - -
-
-

The Go Programming Language

- - -
-
- - -

Codelab: Writing Web Applications

- - - - - - -

Introduction[Top]

- -

-Covered in this codelab: -

-
    -
  • Creating a data structure with load and save methods
  • -
  • Using the http package to build web applications -
  • Using the template package to process HTML templates
  • -
  • Using the regexp package to validate user input
  • -
  • Using closures
  • -
- -

-Assumed knowledge: -

-
    -
  • Programming experience
  • -
  • Understanding of basic web technologies (HTTP, HTML)
  • -
  • Some UNIX command-line knowledge
  • -
- -

Getting Started[Top]

- -

-At present, you need to have a Linux, OS X, or FreeBSD machine to run Go. If -you don't have access to one, you could set up a Linux Virtual Machine (using -VirtualBox or similar) or a -Virtual -Private Server. -

- -

-Install Go (see the Installation Instructions). -

- -

-Make a new directory for this codelab and cd to it: -

- -
$ mkdir ~/gowiki
-$ cd ~/gowiki
-
- -

-Create a file named wiki.go, open it in your favorite editor, and -add the following lines: -

- -
package main
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-)
-
- -

-We import the fmt, ioutil and os -packages from the Go standard library. Later, as we implement additional -functionality, we will add more packages to this import -declaration. -

- -

Data Structures[Top]

- -

-Let's start by defining the data structures. A wiki consists of a series of -interconnected pages, each of which has a title and a body (the page content). -Here, we define Page as a struct with two fields representing -the title and body. -

- -
type Page struct {
-	Title	string
-	Body	[]byte
-}
-
- -

-The type []byte means "a byte slice". -(See Effective Go -for more on slices.) -The Body element is a []byte rather than -string because that is the type expected by the io -libraries we will use, as you'll see below. -

- -

-The Page struct describes how page data will be stored in memory. -But what about persistent storage? We can address that by creating a -save method on Page: -

- -
func (p *Page) save() os.Error {
-	filename := p.Title + ".txt"
-	return ioutil.WriteFile(filename, p.Body, 0600)
-}
-
- -

-This method's signature reads: "This is a method named save that -takes as its receiver p, a pointer to Page . It takes -no parameters, and returns a value of type os.Error." -

- -

-This method will save the Page's Body to a text -file. For simplicity, we will use the Title as the file name. -

- -

-The save method returns an os.Error value because -that is the return type of WriteFile (a standard library function -that writes a byte slice to a file). The save method returns the -error value, to let the application handle it should anything go wrong while -writing the file. If all goes well, Page.save() will return -nil (the zero-value for pointers, interfaces, and some other -types). -

- -

-The octal integer constant 0600, passed as the third parameter to -WriteFile, indicates that the file should be created with -read-write permissions for the current user only. (See the Unix man page -open(2) for details.) -

- -

-We will want to load pages, too: -

- -
func loadPage(title string) *Page {
-	filename := title + ".txt"
-	body, _ := ioutil.ReadFile(filename)
-	return &Page{Title: title, Body: body}
-}
-
- -

-The function loadPage constructs the file name from -Title, reads the file's contents into a new -Page, and returns a pointer to that new page. -

- -

-Functions can return multiple values. The standard library function -io.ReadFile returns []byte and os.Error. -In loadPage, error isn't being handled yet; the "blank identifier" -represented by the underscore (_) symbol is used to throw away the -error return value (in essence, assigning the value to nothing). -

- -

-But what happens if ReadFile encounters an error? For example, -the file might not exist. We should not ignore such errors. Let's modify the -function to return *Page and os.Error. -

- -
func loadPage(title string) (*Page, os.Error) {
-	filename := title + ".txt"
-	body, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return nil, err
-	}
-	return &Page{Title: title, Body: body}, nil
-}
-
- -

-Callers of this function can now check the second parameter; if it is -nil then it has successfully loaded a Page. If not, it will be an -os.Error that can be handled by the caller (see the os package documentation for -details). -

- -

-At this point we have a simple data structure and the ability to save to and -load from a file. Let's write a main function to test what we've -written: -

- -
func main() {
-	p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
-	p1.save()
-	p2, _ := loadPage("TestPage")
-	fmt.Println(string(p2.Body))
-}
-
- -

-After compiling and executing this code, a file named TestPage.txt -would be created, containing the contents of p1. The file would -then be read into the struct p2, and its Body element -printed to the screen. -

- -

-You can compile and run the program like this: -

- -
$ 8g wiki.go
-$ 8l wiki.8
-$ ./8.out
-This is a sample page.
-
- -

-(The 8g and 8l commands are applicable to -GOARCH=386. If you're on an amd64 system, -substitute 6's for the 8's.) -

- -

-Click here to view the code we've written so far. -

- -

Introducing the http package (an interlude)[Top]

- -

-Here's a full working example of a simple web server: -

- -
package main
-
-import (
-	"fmt"
-	"http"
-)
-
-func handler(w http.ResponseWriter, r *http.Request) {
-	fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
-}
-
-func main() {
-	http.HandleFunc("/", handler)
-	http.ListenAndServe(":8080", nil)
-}
-
- -

-The main function begins with a call to -http.HandleFunc, which tells the http package to -handle all requests to the web root ("/") with -handler. -

- -

-It then calls http.ListenAndServe, specifying that it should -listen on port 8080 on any interface (":8080"). (Don't -worry about its second parameter, nil, for now.) -This function will block until the program is terminated. -

- -

-The function handler is of the type http.HandlerFunc. -It takes an http.ResponseWriter and an http.Request as -its arguments. -

- -

-An http.ResponseWriter value assembles the HTTP server's response; by writing -to it, we send data to the HTTP client. -

- -

-An http.Request is a data structure that represents the client -HTTP request. The string r.URL.Path is the path component -of the request URL. The trailing [1:] means -"create a sub-slice of Path from the 1st character to the end." -This drops the leading "/" from the path name. -

- -

-If you run this program and access the URL: -

-
http://localhost:8080/monkeys
-

-the program would present a page containing: -

-
Hi there, I love monkeys!
- -

Using http to serve wiki pages[Top]

- -

-To use the http package, it must be imported: -

- -
import (
-	"fmt"
-	"http"
-	"io/ioutil"
-	"os"
-)
-
- -

-Let's create a handler to view a wiki page: -

- -
const lenPath = len("/view/")
-
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)
-}
-
- -

-First, this function extracts the page title from r.URL.Path, -the path component of the request URL. The global constant -lenPath is the length of the leading "/view/" -component of the request path. -The Path is re-sliced with [lenPath:] to drop the -first 6 characters of the string. This is because the path will invariably -begin with "/view/", which is not part of the page title. -

- -

-The function then loads the page data, formats the page with a string of simple -HTML, and writes it to w, the http.ResponseWriter. -

- -

-Again, note the use of _ to ignore the os.Error -return value from loadPage. This is done here for simplicity -and generally considered bad practice. We will attend to this later. -

- -

-To use this handler, we create a main function that -initializes http using the viewHandler to handle -any requests under the path /view/. -

- -
func main() {
-	http.HandleFunc("/view/", viewHandler)
-	http.ListenAndServe(":8080", nil)
-}
-
- -

-Click here to view the code we've written so far. -

- -

-Let's create some page data (as test.txt), compile our code, and -try serving a wiki page: -

- -
$ echo "Hello world" > test.txt
-$ 8g wiki.go
-$ 8l wiki.8
-$ ./8.out
-
- -

-With this web server running, a visit to http://localhost:8080/view/test -should show a page titled "test" containing the words "Hello world". -

- -

Editing Pages[Top]

- -

-A wiki is not a wiki without the ability to edit pages. Let's create two new -handlers: one named editHandler to display an 'edit page' form, -and the other named saveHandler to save the data entered via the -form. -

- -

-First, we add them to main(): -

- -
func main() {
-	http.HandleFunc("/view/", viewHandler)
-	http.HandleFunc("/edit/", editHandler)
-	http.HandleFunc("/save/", saveHandler)
-	http.ListenAndServe(":8080", nil)
-}
-
- -

-The function editHandler loads the page -(or, if it doesn't exist, create an empty Page struct), -and displays an HTML form. -

- -
func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &Page{Title: title}
-	}
-	fmt.Fprintf(w, "<h1>Editing %s</h1>"+
-		"<form action=\"/save/%s\" method=\"POST\">"+
-		"<textarea name=\"body\">%s</textarea><br>"+
-		"<input type=\"submit\" value=\"Save\">"+
-		"</form>",
-		p.Title, p.Title, p.Body)
-}
-
- -

-This function will work fine, but all that hard-coded HTML is ugly. -Of course, there is a better way. -

- -

The template package[Top]

- -

-The template package is part of the Go standard library. We can -use template to keep the HTML in a separate file, allowing -us to change the layout of our edit page without modifying the underlying Go -code. -

- -

-First, we must add template to the list of imports: -

- -
import (
-	"http"
-	"io/ioutil"
-	"os"
-	"template"
-)
-
- -

-Let's create a template file containing the HTML form. -Open a new file named edit.html, and add the following lines: -

- -
<h1>Editing {Title}</h1>
-
-<form action="/save/{Title}" method="POST">
-<div><textarea name="body" rows="20" cols="80">{Body|html}</textarea></div>
-<div><input type="submit" value="Save"></div>
-</form>
-
- -

-Modify editHandler to use the template, instead of the hard-coded -HTML: -

- -
func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &Page{Title: title}
-	}
-	t, _ := template.ParseFile("edit.html", nil)
-	t.Execute(w, p)
-}
-
- -

-The function template.ParseFile will read the contents of -edit.html and return a *template.Template. -

- -

-The method t.Execute replaces all occurrences of -{Title} and {Body} with the values of -p.Title and p.Body, and writes the resultant -HTML to the http.ResponseWriter. -

- -

-Note that we've used {Body|html} in the above template. -The |html part asks the template engine to pass the value -Body through the html formatter before outputting it, -which escapes HTML characters (such as replacing > with -&gt;). -This will prevent user data from corrupting the form HTML. -

- -

-Now that we've removed the fmt.Fprintf statement, we can remove -"fmt" from the import list. -

- -

-While we're working with templates, let's create a template for our -viewHandler called view.html: -

- -
<h1>{Title}</h1>
-
-<p>[<a href="/edit/{Title}">edit</a>]</p>
-
-<div>{Body}</div>
-
- -

-Modify viewHandler accordingly: -

- -
func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	t, _ := template.ParseFile("view.html", nil)
-	t.Execute(w, p)
-}
-
- -

-Notice that we've used almost exactly the same templating code in both -handlers. Let's remove this duplication by moving the templating code -to its own function: -

- -
func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	renderTemplate(w, "view", p)
-}
-
-func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &Page{Title: title}
-	}
-	renderTemplate(w, "edit", p)
-}
-
-func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
-	t, _ := template.ParseFile(tmpl+".html", nil)
-	t.Execute(w, p)
-}
-
- -

-The handlers are now shorter and simpler. -

- -

Handling non-existent pages[Top]

- -

-What if you visit /view/APageThatDoesntExist? The program will -crash. This is because it ignores the error return value from -loadPage. Instead, if the requested Page doesn't exist, it should -redirect the client to the edit Page so the content may be created: -

- -
func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	p, err := loadPage(title)
-	if err != nil {
-		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
-		return
-	}
-	renderTemplate(w, "view", p)
-}
-
- -

-The http.Redirect function adds an HTTP status code of -http.StatusFound (302) and a Location -header to the HTTP response. -

- -

Saving Pages[Top]

- -

-The function saveHandler will handle the form submission. -

- -
func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	body := r.FormValue("body")
-	p := &Page{Title: title, Body: []byte(body)}
-	p.save()
-	http.Redirect(w, r, "/view/"+title, http.StatusFound)
-}
-
- -

-The page title (provided in the URL) and the form's only field, -Body, are stored in a new Page. -The save() method is then called to write the data to a file, -and the client is redirected to the /view/ page. -

- -

-The value returned by FormValue is of type string. -We must convert that value to []byte before it will fit into -the Page struct. We use []byte(body) to perform -the conversion. -

- -

Error handling[Top]

- -

-There are several places in our program where errors are being ignored. This -is bad practice, not least because when an error does occur the program will -crash. A better solution is to handle the errors and return an error message -to the user. That way if something does go wrong, the server will continue to -function and the user will be notified. -

- -

-First, let's handle the errors in renderTemplate: -

- -
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
-	t, err := template.ParseFile(tmpl+".html", nil)
-	if err != nil {
-		http.Error(w, err.String(), http.StatusInternalServerError)
-		return
-	}
-	err = t.Execute(w, p)
-	if err != nil {
-		http.Error(w, err.String(), http.StatusInternalServerError)
-	}
-}
-
- -

-The http.Error function sends a specified HTTP response code -(in this case "Internal Server Error") and error message. -Already the decision to put this in a separate function is paying off. -

- -

-Now let's fix up saveHandler: -

- -
func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	body := r.FormValue("body")
-	p := &Page{Title: title, Body: []byte(body)}
-	err = p.save()
-	if err != nil {
-		http.Error(w, err.String(), http.StatusInternalServerError)
-		return
-	}
-	http.Redirect(w, r, "/view/"+title, http.StatusFound)
-}
-
- -

-Any errors that occur during p.save() will be reported -to the user. -

- -

Template caching[Top]

- -

-There is an inefficiency in this code: renderTemplate calls -ParseFile every time a page is rendered. -A better approach would be to call ParseFile once for each -template at program initialization, and store the resultant -*Template values in a data structure for later use. -

- -

-First we create a global map named templates in which to store -our *Template values, keyed by string -(the template name): -

- -
var templates = make(map[string]*template.Template)
-
- -

-Then we create an init function, which will be called before -main at program initialization. The function -template.MustParseFile is a convenience wrapper around -ParseFile that does not return an error code; instead, it panics -if an error is encountered. A panic is appropriate here; if the templates can't -be loaded the only sensible thing to do is exit the program. -

- -
func init() {
-	for _, tmpl := range []string{"edit", "view"} {
-		templates[tmpl] = template.MustParseFile(tmpl+".html", nil)
-	}
-}
-
- -

-A for loop is used with a range statement to iterate -over an array constant containing the names of the templates we want parsed. -If we were to add more templates to our program, we would add their names to -that array. -

- -

-We then modify our renderTemplate function to call -the Execute method on the appropriate Template from -templates: - -

func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
-	err := templates[tmpl].Execute(w, p)
-	if err != nil {
-		http.Error(w, err.String(), http.StatusInternalServerError)
-	}
-}
-
- -

Validation[Top]

- -

-As you may have observed, this program has a serious security flaw: a user -can supply an arbitrary path to be read/written on the server. To mitigate -this, we can write a function to validate the title with a regular expression. -

- -

-First, add "regexp" to the import list. -Then we can create a global variable to store our validation regexp: -

- -
var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
-
- -

-The function regexp.MustCompile will parse and compile the -regular expression, and return a regexp.Regexp. -MustCompile, like template.MustParseFile, -is distinct from Compile in that it will panic if -the expression compilation fails, while Compile returns an -os.Error as a second parameter. -

- -

-Now, let's write a function that extracts the title string from the request -URL, and tests it against our TitleValidator expression: -

- -
func getTitle(w http.ResponseWriter, r *http.Request) (title string, err os.Error) {
-	title = r.URL.Path[lenPath:]
-	if !titleValidator.MatchString(title) {
-		http.NotFound(w, r)
-		err = os.NewError("Invalid Page Title")
-	}
-	return
-}
-
- -

-If the title is valid, it will be returned along with a nil -error value. If the title is invalid, the function will write a -"404 Not Found" error to the HTTP connection, and return an error to the -handler. -

- -

-Let's put a call to getTitle in each of the handlers: -

- -
func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	p, err := loadPage(title)
-	if err != nil {
-		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
-		return
-	}
-	renderTemplate(w, "view", p)
-}
-
-func editHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	p, err := loadPage(title)
-	if err != nil {
-		p = &Page{Title: title}
-	}
-	renderTemplate(w, "edit", p)
-}
-
-func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	body := r.FormValue("body")
-	p := &Page{Title: title, Body: []byte(body)}
-	err = p.save()
-	if err != nil {
-		http.Error(w, err.String(), http.StatusInternalServerError)
-		return
-	}
-	http.Redirect(w, r, "/view/"+title, http.StatusFound)
-}
-
- -

Introducing Function Literals and Closures[Top]

- -

-Catching the error condition in each handler introduces a lot of repeated code. -What if we could wrap each of the handlers in a function that does this -validation and error checking? Go's -function -literals provide a powerful means of abstracting functionality -that can help us here. -

- -

-First, we re-write the function definition of each of the handlers to accept -a title string: -

- -
func viewHandler(w http.ResponseWriter, r *http.Request, title string)
-func editHandler(w http.ResponseWriter, r *http.Request, title string)
-func saveHandler(w http.ResponseWriter, r *http.Request, title string)
-
- -

-Now let's define a wrapper function that takes a function of the above -type, and returns a function of type http.HandlerFunc -(suitable to be passed to the function http.HandleFunc): -

- -
func makeHandler(fn func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		// Here we will extract the page title from the Request,
-		// and call the provided handler 'fn'
-	}
-}
-
- -

-The returned function is called a closure because it encloses values defined -outside of it. In this case, the variable fn (the single argument -to makeHandler) is enclosed by the closure. The variable -fn will be one of our save, edit, or view handlers. -

- -

-Now we can take the code from getTitle and use it here -(with some minor modifications): -

- -
func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		title := r.URL.Path[lenPath:]
-		if !titleValidator.MatchString(title) {
-			http.NotFound(w, r)
-			return
-		}
-		fn(w, r, title)
-	}
-}
-
- -

-The closure returned by makeHandler is a function that takes -an http.ResponseWriter and http.Request (in other -words, an http.HandlerFunc). -The closure extracts the title from the request path, and -validates it with the TitleValidator regexp. If the -title is invalid, an error will be written to the -ResponseWriter using the http.NotFound function. -If the title is valid, the enclosed handler function -fn will be called with the ResponseWriter, -Request, and title as arguments. -

- -

-Now we can wrap the handler functions with makeHandler in -main, before they are registered with the http -package: -

- -
func main() {
-	http.HandleFunc("/view/", makeHandler(viewHandler))
-	http.HandleFunc("/edit/", makeHandler(editHandler))
-	http.HandleFunc("/save/", makeHandler(saveHandler))
-	http.ListenAndServe(":8080", nil)
-}
-
- -

-Finally we remove the calls to getTitle from the handler functions, -making them much simpler: -

- -
func viewHandler(w http.ResponseWriter, r *http.Request, title string) {
-	p, err := loadPage(title)
-	if err != nil {
-		http.Redirect(w, r, "/edit/"+title, http.StatusFound)
-		return
-	}
-	renderTemplate(w, "view", p)
-}
-
-func editHandler(w http.ResponseWriter, r *http.Request, title string) {
-	p, err := loadPage(title)
-	if err != nil {
-		p = &Page{Title: title}
-	}
-	renderTemplate(w, "edit", p)
-}
-
-func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
-	body := r.FormValue("body")
-	p := &Page{Title: title, Body: []byte(body)}
-	err := p.save()
-	if err != nil {
-		http.Error(w, err.String(), http.StatusInternalServerError)
-		return
-	}
-	http.Redirect(w, r, "/view/"+title, http.StatusFound)
-}
-
- -

Try it out![Top]

- -

-Click here to view the final code listing. -

- -

-Recompile the code, and run the app: -

- -
$ 8g wiki.go
-$ 8l wiki.8
-$ ./8.out
-
- -

-Visiting http://localhost:8080/view/ANewPage -should present you with the page edit form. You should then be able to -enter some text, click 'Save', and be redirected to the newly created page. -

- -

Other tasks[Top]

- -

-Here are some simple tasks you might want to tackle on your own: -

- -
    -
  • Store templates in tmpl/ and page data in data/. -
  • Add a handler to make the web root redirect to - /view/FrontPage.
  • -
  • Spruce up the page templates by making them valid HTML and adding some - CSS rules.
  • -
  • Implement inter-page linking by converting instances of - [PageName] to
    - <a href="/view/PageName">PageName</a>. - (hint: you could use regexp.ReplaceAllFunc to do this) -
  • -
- -
-
-

release.r58.1 8699. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.

-
-
- - - - - \ No newline at end of file diff --git a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/all.css b/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/all.css deleted file mode 100644 index a985d8f..0000000 --- a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/all.css +++ /dev/null @@ -1,205 +0,0 @@ -/* General Styles */ -body { - font-family: "Bitstream Vera Sans", Verdana, sans-serif; - font-size: 81.25%; - line-height: 1.23em; - padding: 0; - margin: 1.23em; - background: white; - color: black; -} -a { - color: #04a; - text-decoration: none; -} -a:visited { - color: #04a; -} -a:hover { - color: #a40; - text-decoration: underline; -} -a:active { - color: #c00; -} -code, pre { - font-size: 1.2em; -} -pre { - background: #F0F0F0; - padding: 0.5em 1em; -} - -/* Top bar */ -#container { - width: 100%; - margin: auto; -} -#topnav { - height: 55px; - background: url(/doc/logo.png) no-repeat top left; -} -a#logo-box { - display: block; - height: 55px; -} -h1#title { - display: none; -} -#nav-main { - float: right; - width: 500px; - margin-top: -5px; - text-align: center; -} -#nav-main ul { - padding-left: 0; - margin-left: 0; - margin-bottom: 0.5em; -} -#nav-main li a { - display: inline; - display: inline-block; - padding: .46em .62em .38em .62em; -} -#nav-main li a:link, -#nav-main li a:visited { - color: #000; -} -#nav-main li { - display: inline; - display: inline-block; - background: #e6e6e6 url(/doc/button_background.png) repeat-x; - border: solid 1px #999; - margin-left: -1px; - text-shadow: #fff 0 1px 0; - box-shadow: 0 1px 1px #ccc; - -moz-box-shadow: 0 1px 1px #ccc; - -webkit-box-shadow: 0 1px 1px #ccc; -} -#nav-main li:first-child { - -moz-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -moz-border-bottom-left-radius: 4px; - border-bottom-left-radius: 4px; -} -#nav-main li:last-child { - -moz-border-top-right-radius: 4px; - border-top-right-radius: 4px; - -moz-border-bottom-right-radius: 4px; - border-bottom-right-radius: 4px; -} -#nav-main .quickref { - color: #444; -} -#nav-main .quickref .sep { - color: #999; -} -#search { - width: 120px; - margin-left: 0.5em; -} -#search.inactive { - text-align: center; - color: #444; -} - -/* Footer */ -#site-info { - position: relative; - text-align: center; -} -#site-info, #site-info a:link, #site-info a:visited { - color: #aaa; -} - -/* Content */ -#content { - clear: both; - padding: 0; - position: relative; - margin-top: 1.5em; - margin-bottom: 1.5em; - border-top: solid 1px #aaa; - border-bottom: solid 1px #aaa; -} -.left-column { - width: 49%; - float: left; -} -.right-column { - width: 49%; - float: right; -} -.end-columns { - clear: both; -} -#content h1 { - margin-bottom: -0em; - padding: 0; -} -#content h2 { - border-top: 2px solid #ddd; - padding: 8px 0; - margin: 1.5em 0 0; -} -#content .subtitle { - margin-top: 1em; - display: block; -} -.navtop a { - font-weight: normal; font-size: 7pt; - float: right; color: #999; -} - -/* Content and Code Highlighting */ -pre.ebnf, pre.grammar { - background: #FFFFE0; -} -span.ln { - font-size: 80%; - color: #777777; -} -span.comment { - color: #002090; -} -span.highlight { - background: #FF9900; - font-weight: bold; -} -span.highlight-comment { - background: #FF9900; - font-weight: bold; - color: #002090; -} -span.selection { - background: #FFFF00 -} -span.selection-comment { - color: #002090; - background: #FFFF00 -} -span.selection-highlight { - background: #FF9900; - font-weight: bold; -} -span.selection-highlight-comment { - background: #FF9900; - font-weight: bold; - color: #002090; -} -span.alert { - color: #D00000; -} -#nav table { - width: 100%; -} -.detail { - padding: 0.25em 1em; - background: #F4F4F4; -} -sup.new { - color: red; - font-size: 8px; - line-height: 0; -} diff --git a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/ga.js b/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/ga.js deleted file mode 100644 index 24ce548..0000000 --- a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/ga.js +++ /dev/null @@ -1,47 +0,0 @@ -(function(){var g=void 0,h=null,aa=encodeURIComponent,ba=decodeURIComponent,i=Math;function ca(a,b){return a.name=b}var k="push",da="load",l="charAt",ea="value",m="indexOf",fa="match",ga="name",ha="host",o="toString",r="length",s="prototype",t="split",u="stopPropagation",ia="scope",v="location",w="getString",x="substring",ja="navigator",y="join",z="toLowerCase",A;function ka(a,b){switch(b){case 0:return""+a;case 1:return a*1;case 2:return!!a;case 3:return a*1E3}return a}function B(a){return g==a||"-"==a||""==a}function la(a){if(!a||""==a)return"";for(;a&&" \n\r\t"[m](a[l](0))>-1;)a=a[x](1);for(;a&&" \n\r\t"[m](a[l](a[r]-1))>-1;)a=a[x](0,a[r]-1);return a}function ma(a){var b=1,c=0,d;if(!B(a)){b=0;for(d=a[r]-1;d>=0;d--)c=a.charCodeAt(d),b=(b<<6&268435455)+c+(c<<14),c=b&266338304,b=c!=0?b^c>>21:b}return b}function na(){return i.round(i.random()*2147483647)} -function oa(){}function C(a,b){return aa instanceof Function?b?encodeURI(a):aa(a):(D(68),escape(a))}function E(a){a=a[t]("+")[y](" ");if(ba instanceof Function)try{return ba(a)}catch(b){D(17)}else D(68);return unescape(a)}var pa=function(a,b,c,d){a.addEventListener?a.addEventListener(b,c,!!d):a.attachEvent&&a.attachEvent("on"+b,c)},qa=function(a,b,c,d){a.removeEventListener?a.removeEventListener(b,c,!!d):a.detachEvent&&a.detachEvent("on"+b,c)};function ra(a){return a&&a[r]>0?a[0]:""} -function sa(a){var b=a?a[r]:0;return b>0?a[b-1]:""}var ta=function(){this.prefix="ga.";this.F={}};ta[s].set=function(a,b){this.F[this.prefix+a]=b};ta[s].get=function(a){return this.F[this.prefix+a]};ta[s].contains=function(a){return this.get(a)!==g};function ua(a){a[m]("www.")==0&&(a=a[x](4));return a[z]()}function va(a,b){var c,d={url:a,protocol:"http",host:"",path:"",c:new ta,anchor:""};if(!a)return d;c=a[m]("://");if(c>=0)d.protocol=a[x](0,c),a=a[x](c+3);c=a.search("/|\\?|#");if(c>=0)d.host=a[x](0,c)[z](),a=a[x](c);else return d.host=a[z](),d;c=a[m]("#");if(c>=0)d.anchor=a[x](c+1),a=a[x](0,c);c=a[m]("?");c>=0&&(wa(d.c,a[x](c+1)),a=a[x](0,c));d.anchor&&b&&wa(d.c,d.anchor);a&&a[l](0)=="/"&&(a=a[x](1));d.path=a;return d} -function wa(a,b){function c(b,c){a.contains(b)||a.set(b,[]);a.get(b)[k](c)}for(var d=la(b)[t]("&"),e=0;e-1)?!0:!1},cc=function(a){var b=a.get(J),c=a[w](L,"/");bc(b,c)&&a[u]()};var gc=function(){var a={},b={},c=new dc;this.h=function(a,b){c.add(a,b)};var d=new dc;this.d=function(a,b){d.add(a,b)};var e=!1,f=!1,j=!0;this.G=function(){e=!0};this.f=function(a){this[da]();this.set(Mb,a,!0);e=!1;d.execute(this);e=!0;b={};this.i()};this.load=function(){e&&(e=!1,this.na(),ec(this),f||(f=!0,c.execute(this),fc(this),ec(this)),e=!0)};this.i=function(){if(e)if(f)e=!1,fc(this),e=!0;else this[da]()};this.get=function(c){c&&c[l](0)=="_"&&this[da]();return b[c]!==g?b[c]:a[c]};this.set= -function(c,d,e){c&&c[l](0)=="_"&&this[da]();e?b[c]=d:a[c]=d;c&&c[l](0)=="_"&&this.i()};this.m=function(b){a[b]=this.b(b,0)+1};this.b=function(a,b){var c=this.get(a);return c==g||c===""?b:c*1};this.getString=function(a,b){var c=this.get(a);return c==g?b:c+""};this.na=function(){if(j){var b=this[w](J,""),c=this[w](L,"/");bc(b,c)||(a[K]=a[Ha]&&b!=""?ma(b):1,j=!1)}}};gc[s].stopPropagation=function(){throw"aborted";};function T(a,b){for(var b=b||[],c=0;c=0&&e>0&&f>0&&j>0&&d>=0))return D(110),!1;a.set(N,c);a.set(sb,e);a.set(tb,f);a.set(ub,j);a.set(vb,d);return!0},ic=function(a){var b=a.get(N),c=a.get(sb),d=a.get(tb),e=a.get(ub),f=a.b(vb,1);b==g?D(113):b==NaN&&D(114);b>=0&&c>0&&d>0&&e>0&&f>=0||D(115);return[a.b(K,1),b!=g?b:"-",c||"-",d||"-",e||"-",f][y](".")},jc=function(a){return[a.b(K,1),a.b(yb,0),a.b(O,1),a.b(zb, -0)][y](".")},kc=function(a,b){var c=b[t]("."),d=a.b(K,1);if(c[r]!==4||c[0]!=d)c=h;a.set(yb,c?c[1]*1:0);a.set(O,c?c[2]*1:10);a.set(zb,c?c[3]*1:a.get(H));return c!=h||b==d},lc=function(a,b){var c=C(a[w](ob,"")),d=[],e=a.get(M);if(!b&&e){for(var f=0;f0&&(c+="|"+d[y](","))}return c?a.b(K,1)+"."+c:h},mc=function(a,b){var c=a.b(K,1),d=b[t](".");if(d[r]<2||d[0]!=c)return!1;c=d.slice(1)[y](".")[t]("|");c[r]>0&&a.set(ob,E(c[0])); -if(c[r]<=1)return!0;for(var d=c[1][t](","),e=0;e=0&&D(125);return!0},oc=function(a,b){var c=nc(a,b);return c?[a.b(K,1),a.b(Ab,0),a.b(Bb,1),a.b(Cb,1),c][y]("."):""},nc=function(a){function b(b,e){if(!B(a.get(b))){var f=a[w](b,""),f=f[t](" ")[y]("%20"),f=f[t]("+")[y]("%20");c[k](e+"="+f)}}var c=[];b(Eb,"utmcid");b(Ib,"utmcsr");b(Gb,"utmgclid");b(Hb,"utmdclid");b(Fb,"utmccn");b(Jb, -"utmcmd");b(Kb,"utmctr");b(Lb,"utmcct");return c[y]("|")},qc=function(a,b){var c=a.b(K,1),d=b[t](".");if(d[r]<5||d[0]!=c)return a.set(Ab,g),a.set(Bb,g),a.set(Cb,g),a.set(Eb,g),a.set(Fb,g),a.set(Ib,g),a.set(Jb,g),a.set(Kb,g),a.set(Lb,g),a.set(Gb,g),a.set(Hb,g),!1;a.set(Ab,d[1]*1);a.set(Bb,d[2]*1);a.set(Cb,d[3]*1);pc(a,d.slice(4)[y]("."));return!0},pc=function(a,b){function c(a){return(a=b[fa](a+"=(.*?)(?:\\|utm|$)"))&&a[r]==2?a[1]:g}function d(b,c){c&&(c=e?E(c):c[t]("%20")[y](" "),a.set(b,c))}b[m]("=")== --1&&(b=E(b));var e=c("utmcvr")=="2";d(Eb,c("utmcid"));d(Fb,c("utmccn"));d(Ib,c("utmcsr"));d(Jb,c("utmcmd"));d(Kb,c("utmctr"));d(Lb,c("utmcct"));d(Gb,c("utmgclid"));d(Hb,c("utmdclid"))};var dc=function(){this.q=[]};dc[s].add=function(a,b){this.q[k]({name:a,ua:b})};dc[s].execute=function(a){try{for(var b=0;b=a.get(Ua)*100&&a[u]()}function sc(a){tc()&&a[u]()}function uc(a){F[v].protocol=="file:"&&a[u]()}function vc(a){a.get(eb)||a.set(eb,F.title,!0);a.get(db)||a.set(db,F[v].pathname+F[v].search,!0)};var wc=new function(){var a=[];this.set=function(b){a[b]=!0};this.va=function(){for(var b=[],c=0;c=0){b=b.replace(/\n|\r/g," ");f=0;for(var j=b[r];f2E3&&(b=b[x](0, -2E3),D(69));a=a+"="+b+"; path="+c+"; ";e&&(a+="expires="+(new Date((new Date).getTime()+e)).toGMTString()+"; ");d&&(a+="domain="+d+";");F.cookie=a}};var yc,zc,Ac=function(){if(!yc){var a={},b=U[ja],c=U.screen;a.C=c?c.width+"x"+c.height:"-";a.B=c?c.colorDepth+"-bit":"-";a.language=(b&&(b.language||b.browserLanguage)||"-")[z]();a.javaEnabled=b&&b.javaEnabled()?1:0;a.characterSet=F.characterSet||F.charset||"-";yc=a}},Bc=function(){Ac();for(var a=yc,b=U[ja],a=b.appName+b.version+a.language+b.platform+b.userAgent+a.javaEnabled+a.C+a.B+(F.cookie?F.cookie:"")+(F.referrer?F.referrer:""),b=a[r],c=U.history[r];c>0;)a+=c--^b++;return ma(a)},Cc=function(a){Ac(); -var b=yc;a.set(hb,b.C);a.set(ib,b.B);a.set(lb,b.language);a.set(mb,b.characterSet);a.set(jb,b.javaEnabled);if(a.get(Ia)&&a.get(Ja)){if(!(b=zc)){var c,d,e;d="ShockwaveFlash";if((b=(b=U[ja])?b.plugins:g)&&b[r]>0)for(c=0;c-1&&(e=d.description[t]("Shockwave Flash ")[1]);else{d=d+"."+d;try{c=new ActiveXObject(d+".7"),e=c.GetVariable("$version")}catch(f){}if(!e)try{c=new ActiveXObject(d+".6"),e="WIN 6,0,21,0",c.AllowScriptAccess="always",e=c.GetVariable("$version")}catch(j){}if(!e)try{c= -new ActiveXObject(d),e=c.GetVariable("$version")}catch(p){}e&&(e=e[t](" ")[1][t](","),e=e[0]+"."+e[1]+" r"+e[2])}b=e?e:"-"}zc=b;a.set(kb,zc)}else a.set(kb,"-")};var Y=function(){P(Y[s],"push",Y[s][k],5);P(Y[s],"_createAsyncTracker",Y[s].wa,33);P(Y[s],"_getAsyncTracker",Y[s].xa,34)};Y[s].wa=function(a,b){return Z.k(a,b||"")};Y[s].xa=function(a){return Z.p(a)};Y[s].push=function(a){for(var b=arguments,c=0,d=0;d0&&(e=f[x](0,j),f=f[x](j+1));var p=e=="_gat"?Z:e=="_gaq"?Dc:Z.p(e);p[f].apply(p,b[d].slice(1))}}catch(n){c++}return c};var Gc=function(){function a(a,b,c,d){g==f[a]&&(f[a]={});g==f[a][b]&&(f[a][b]=[]);f[a][b][c]=d}function b(a,b,c){if(g!=f[a]&&g!=f[a][b])return f[a][b][c]}function c(a,b){if(g!=f[a]&&g!=f[a][b]){f[a][b]=g;var c=!0,d;for(d=0;d0)&&(X("__utmd","1",a[w](L,"/"),a[w](J,""),1E4),V("__utmd")[r]==0&&a[u]())};var Qc=function(a){a.get(N)==g?Pc(a):a.get(rb)&&!a.get(Yb)?Pc(a):a.get(xb)&&(a.set(tb,a.get(ub)),a.set(ub,a.get(H)),a.m(vb),a.set(wb,!0),a.set(yb,0),a.set(O,10),a.set(zb,a.get(H)),a.set(xb,!1))},Pc=function(a){var b=a.get(H);a.set(nb,!0);a.set(N,na()^Bc(a)&2147483647);a.set(ob,"");a.set(sb,b);a.set(tb,b);a.set(ub,b);a.set(vb,1);a.set(wb,!0);a.set(yb,0);a.set(O,10);a.set(zb,b);a.set(M,[]);a.set(rb,!1);a.set(xb,!1)};var Rc="daum:q,eniro:search_word,naver:query,pchome:q,images.google:q,google:q,yahoo:p,yahoo:q,msn:q,bing:q,aol:query,aol:encquery,aol:q,lycos:query,ask:q,altavista:q,netscape:query,cnn:query,about:terms,mamma:q,alltheweb:q,voila:rdata,virgilio:qs,live:q,baidu:wd,alice:qs,yandex:text,najdi:q,mama:query,seznam:q,search:q,wp:szukaj,onet:qt,szukacz:q,yam:k,kvasir:q,sesam:q,ozu:q,terra:query,mynet:q,ekolay:q,rambler:query".split(","),Xc=function(a){if(a.get(Ka)&&!a.get(Yb)){for(var b=!B(a.get(Eb))||!B(a.get(Ib))|| -!B(a.get(Gb))||!B(a.get(Hb)),c={},d=0;d=0)||c&&c[ha][m]("google")>-1&&c.c.contains("q")&&c.path=="cse")return!1;if((b=Yc(a,c))&& -!b[2])return Vc(a,g,b[0],g,g,"(organic)","organic",b[1],g),!0;else if(b)return!1;if(a.get(wb))a:{for(var b=a.get($a),d=ua(c[ha]),e=0;e-1){a=!1;break a}Vc(a,g,d,g,g,"(referral)","referral",g,"/"+c.path);a=!0}else a=!1;return a},Yc=function(a,b){for(var c=a.get(Ya),d=0;d-1){var f=ra(b.c.get(e[1]));if(f){a:{for(var c=f,d=a.get(Za),c=E(c)[z](),j=0;j0&&(c=b[x](e),b=b[x](0,e)),f<0?b+"?"+d+c:b+"&"+d+c)};var bd="|",dd=function(a,b,c,d,e,f,j,p,n){var q=cd(a,b);q||(q={},a.get(ab)[k](q));q.id_=b;q.affiliation_=c;q.total_=d;q.tax_=e;q.shipping_=f;q.city_=j;q.state_=p;q.country_=n;q.items_=[];return q},ed=function(a,b,c,d,e,f,j){var a=cd(a,b)||dd(a,b,"",0,0,0,"","",""),p;a:{if(a&&a.items_){p=a.items_;for(var n=0;n=a.b(Xb,0))return!1;var c=hd();c==g&&(c=id());if(c==g||c==Infinity||isNaN(c))return!1;c>0?b(jd(c)):pa(U,"load",function(){ld(a,b)},!1);return!0},jd=function(a){var b=new Gc,c=i.min(i.floor(a/100),5E3);b.e(14,1,c>0?c+"00":"0");b.j(14,1,a);return b},hd=function(){var a=U.performance||U.webkitPerformance;return(a=a&&a.timing)&&a.loadEventStart-a.fetchStart},id=function(){if(U.top==U){var a=U.external,b=a&&a.onloadT;a&&!a.isValidLoadTime&&(b=g);b>2147483648&&(b=g); -b>0&&a.setPageReadyTime();return b}};var Q=function(a,b,c){function d(a){return function(b){if((b=b.get(Zb)[a])&&b[r])for(var c=ac(e,a),d=0;d-1?(D(13),this.set(db,a,!0)):typeof a==="object"&&a!==h&&this.oa(a);this.a.f("page")};A.t=function(a,b,c,d){if(a==""||!Ec(a)||b==""||!Ec(b))return!1;if(c!=g&&!Ec(c))return!1;if(d!=g&&!Fc(d))return!1;this.set(Ob,a,!0);this.set(Pb,b,!0);this.set(Qb,c,!0);this.set(Tb,d,!0);this.a.f("event");return!0}; -A.la=function(a,b,c,d){if(!a||!b)return!1;this.set(Ub,a[x](0,15),!0);this.set(Vb,b[x](0,15),!0);this.set(Wb,c||F[v].href,!0);d&&this.set(db,d,!0);this.a.f("social");return!0};A.ja=function(){var a=this;return ld(this.a,function(b){a.s(b)})};A.ma=function(){this.a.f("trans")};A.s=function(a){this.set(cb,a,!0);this.a.f("event")};A.S=function(a){this.l();var b=this;return{_trackEvent:function(c,d,e){D(91);b.t(a,c,d,e)}}};A.V=function(a){return this.get(a)}; -A.da=function(a,b){if(a)if(a!=g&&(a.constructor+"")[m]("String")>-1)this.set(a,b);else if(typeof a=="object")for(var c in a)a.hasOwnProperty(c)&&this.set(c,a[c])};A.addEventListener=function(a,b){var c=this.get(Zb)[a];c&&c[k](b)};A.removeEventListener=function(a,b){for(var c=this.get(Zb)[a],d=0;c&&de.get(Xa))a=!1;else if(!b||!c||C(b)[r]+C(c)[r]>64)a=!1;else{d!=1&&d!=2&&(d=3);var f={};ca(f,b);f.value=c;f.scope=d;e.get(M)[a]=f;a=!0}a&&this.a.i();return a};A.U=function(a){this.a.get(M)[a]=g;this.a.i()};A.Y=function(a){return(a=this.a.get(M)[a])&&a[ia]==1?a[ea]:g};A.ha=function(a,b,c){this.g().e(a,b,c)};A.ia=function(a,b,c){this.g().j(a,b,c)};A.Z=function(a,b){return this.g().w(a,b)}; -A.$=function(a,b){return this.g().z(a,b)};A.P=function(a){this.g().u(a)};A.Q=function(a){this.g().v(a)};A.T=function(){return new Gc};A.H=function(a){a&&this.get(Za)[k](a[z]())};A.M=function(){this.set(Za,[])};A.I=function(a){a&&this.get($a)[k](a[z]())};A.N=function(){this.set($a,[])};A.K=function(a,b,c){if(a&&b){var d=this.get(Ya);d.splice(c?0:d[r],0,a+":"+b[z]())}};A.O=function(){this.set(Ya,[])}; -A.R=function(a){this.a[da]();var b=this.get(L),c=ra(V("__utmx"))||"";this.set(L,a);this.a.i();Nc(this.a,"__utmx",c);this.set(L,b)};A.l=function(){this.a[da]()};A.ga=function(a){a&&a!=""&&(this.set(ob,a),this.a.f("var"))};var md=function(a){a.get(Mb)!=="trans"&&a.b(yb,0)>=500&&a[u]();if(a.get(Mb)==="event"){var b=(new Date).getTime(),c=a.b(zb,0),d=a.b(ub,0),c=i.floor(0.2*((b-(c!=d?c:c*1E3))/1E3));c>0&&(a.set(zb,b),a.set(O,i.min(10,a.b(O,0)+c)));a.b(O,0)<=0&&a[u]()}},od=function(a){a.get(Mb)==="event"&&a.set(O,i.max(0,a.b(O,10)-1))};var pd=function(){var a=[];this.add=function(b,c,d){d&&(c=C(""+c));a[k](b+"="+c)};this.toString=function(){return a[y]("&")}},qd=function(a,b){(b||a.get(Wa)!=2)&&a.m(yb)},rd=function(a,b){b.add("utmwv","5.1.2");b.add("utms",a.get(yb));b.add("utmn",na());var c=F[v].hostname;B(c)||b.add("utmhn",c,!0);c=a.get(Ua);c!=100&&b.add("utmsp",c,!0)},td=function(a,b){b.add("utmac",a.get(za));sd(a,b);Z.o&&b.add("aip",1);b.add("utmu",wc.va())},sd=function(a,b){function c(a,b){b&&d[k](a+"="+b+";")}var d=[];c("__utma", -ic(a));c("__utmz",oc(a,!1));c("__utmv",lc(a,!0));c("__utmx",ra(V("__utmx")));b.add("utmcc",d[y]("+"),!0)},ud=function(a,b){a.get(Ia)&&(b.add("utmcs",a.get(mb),!0),b.add("utmsr",a.get(hb)),b.add("utmsc",a.get(ib)),b.add("utmul",a.get(lb)),b.add("utmje",a.get(jb)),b.add("utmfl",a.get(kb),!0))},vd=function(a,b){a.get(La)&&a.get(eb)&&b.add("utmdt",a.get(eb),!0);b.add("utmhid",a.get(gb));b.add("utmr",xa(a.get(fb),a.get(L)),!0);b.add("utmp",C(a.get(db),!0),!0)},wd=function(a,b){for(var c=a.get(bb),d=a.get(cb), -e=a.get(M)||[],f=0;f=0&&![].reduce)throw new Dd(a[r]);Fd(a,b)||Gd(a,b)}else throw new Cd(a[r]);},Ed=function(a,b,c){var c=c||Bd+"/__utm.gif?",d=new Image(1,1);d.src=c+a;d.onload=function(){d.onload= -h;b()}},Fd=function(a,b){var c,d=Bd+"/p/__utm.gif",e=U.XDomainRequest;if(e)c=new e,c.open("POST",d);else if(e=U.XMLHttpRequest)e=new e,"withCredentials"in e&&(c=e,c.open("POST",d,!0),c.setRequestHeader("Content-Type","text/plain"));if(c)return c.onreadystatechange=function(){c.readyState==4&&(b(),c=h)},c.send(a),!0},Gd=function(a,b){if(F.body){a=aa(a);try{var c=F.createElement('')}catch(d){c=F.createElement("iframe"),ca(c,a)}c.height="0";c.width="0";c.style.display="none"; -c.style.visibility="hidden";var e=F[v],e=Bd+"/u/post_iframe.html#"+aa(e.protocol+"//"+e[ha]+"/favicon.ico"),f=function(){c.src="";c.parentNode&&c.parentNode.removeChild(c)};pa(U,"beforeunload",f);var j=!1,p=0,n=function(){if(!j){try{if(p>9||c.contentWindow[v][ha]==F[v][ha]){j=!0;f();qa(U,"beforeunload",f);b();return}}catch(a){}p++;setTimeout(n,200)}};pa(c,"load",n);F.body.appendChild(c);c.src=e}else xc(function(){Gd(a,b)},100)};var $=function(){this.o=!1;this.A={};this.ra=0;this._gasoCPath=this._gasoDomain=g;P($[s],"_createTracker",$[s].k,55);P($[s],"_getTracker",$[s].ta,0);P($[s],"_getTrackerByName",$[s].p,51);P($[s],"_anonymizeIp",$[s].sa,16);$b()};$[s].ta=function(a,b){return this.k(a,g,b)};$[s].k=function(a,b,c){b&&D(23);c&&D(67);b==g&&(b="~"+Z.ra++);return Z.A[b]=new Q(b,a,c)};$[s].p=function(a){a=a||"";return Z.A[a]||Z.k(g,a)};$[s].sa=function(){this.o=!0};var Hd=function(a){if(F.webkitVisibilityState=="prerender")return!1;a();return!0};var Z=new $;var Id=U._gat;Id&&typeof Id._getTracker=="function"?Z=Id:U._gat=Z;var Dc=new Y;(function(a){if(!Hd(a)){D(123);var b=!1,c=function(){!b&&Hd(a)&&(D(124),b=!0,qa(F,"webkitvisibilitychange",c))};pa(F,"webkitvisibilitychange",c)}})(function(){var a=U._gaq,b=!1;if(a&&typeof a[k]=="function"&&(b=Object[s][o].call(Object(a))=="[object Array]",!b)){Dc=a;return}U._gaq=Dc;b&&Dc[k].apply(Dc,a)});})(); diff --git a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/godocs.js b/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/godocs.js deleted file mode 100644 index 946c4c3..0000000 --- a/eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/godocs.js +++ /dev/null @@ -1,190 +0,0 @@ -// Except as noted, this content is licensed under Creative Commons -// Attribution 3.0 - -/* A little code to ease navigation of these documents. - * - * On window load we: - * + Generate a table of contents (godocs_generateTOC) - * + Add links up to the top of the doc from each section (godocs_addTopLinks) - */ - -/* We want to do some stuff on page load (after the HTML is rendered). - So listen for that: - */ -function bindEvent(el, e, fn) { - if (el.addEventListener){ - el.addEventListener(e, fn, false); - } else if (el.attachEvent){ - el.attachEvent('on'+e, fn); - } -} -bindEvent(window, 'load', godocs_onload); - -function godocs_onload() { - godocs_bindSearchEvents(); - godocs_generateTOC(); - godocs_addTopLinks(); -} - -function godocs_bindSearchEvents() { - var search = document.getElementById('search'); - if (!search) { - // no search box (index disabled) - return; - } - function clearInactive() { - if (search.className == "inactive") { - search.value = ""; - search.className = ""; - } - } - function restoreInactive() { - if (search.value != "") { - return; - } - if (search.type != "search") { - search.value = search.getAttribute("placeholder"); - } - search.className = "inactive"; - } - restoreInactive(); - bindEvent(search, 'focus', clearInactive); - bindEvent(search, 'blur', restoreInactive); -} - -/* Generates a table of contents: looks for h2 and h3 elements and generates - * links. "Decorates" the element with id=="nav" with this table of contents. - */ -function godocs_generateTOC() { - var navbar = document.getElementById('nav'); - if (!navbar) { return; } - - var toc_items = []; - - var i; - for (i = 0; i < navbar.parentNode.childNodes.length; i++) { - var node = navbar.parentNode.childNodes[i]; - if ((node.tagName == 'h2') || (node.tagName == 'H2')) { - if (!node.id) { - node.id = 'tmp_' + i; - } - var text = godocs_nodeToText(node); - if (!text) { continue; } - - var textNode = document.createTextNode(text); - - var link = document.createElement('a'); - link.href = '#' + node.id; - link.appendChild(textNode); - - // Then create the item itself - var item = document.createElement('dt'); - - item.appendChild(link); - toc_items.push(item); - } - if ((node.tagName == 'h3') || (node.tagName == 'H3')) { - if (!node.id) { - node.id = 'tmp_' + i; - } - var text = godocs_nodeToText(node); - if (!text) { continue; } - - var textNode = document.createTextNode(text); - - var link = document.createElement('a'); - link.href = '#' + node.id; - link.appendChild(textNode); - - // Then create the item itself - var item = document.createElement('dd'); - - item.appendChild(link); - toc_items.push(item); - } - } - - if (toc_items.length <= 1) { return; } - - var dl1 = document.createElement('dl'); - var dl2 = document.createElement('dl'); - - var split_index = (toc_items.length / 2) + 1; - if (split_index < 8) { - split_index = toc_items.length; - } - - for (i = 0; i < split_index; i++) { - dl1.appendChild(toc_items[i]); - } - for (/* keep using i */; i < toc_items.length; i++) { - dl2.appendChild(toc_items[i]); - } - - var tocTable = document.createElement('table'); - navbar.appendChild(tocTable); - tocTable.className = 'unruled'; - var tocBody = document.createElement('tbody'); - tocTable.appendChild(tocBody); - - var tocRow = document.createElement('tr'); - tocBody.appendChild(tocRow); - - // 1st column - var tocCell = document.createElement('td'); - tocCell.className = 'first'; - tocRow.appendChild(tocCell); - tocCell.appendChild(dl1); - - // 2nd column - tocCell = document.createElement('td'); - tocRow.appendChild(tocCell); - tocCell.appendChild(dl2); -} - -/* Returns the "This sweet header" from

This sweet header

. - * Takes a node, returns a string. - */ -function godocs_nodeToText(node) { - var TEXT_NODE = 3; // Defined in Mozilla but not MSIE :( - - var text = ''; - for (var j = 0; j != node.childNodes.length; j++) { - var child = node.childNodes[j]; - if (child.nodeType == TEXT_NODE) { - if (child.nodeValue != '[Top]') { //ok, that's a hack, but it works. - text = text + child.nodeValue; - } - } else { - text = text + godocs_nodeToText(child); - } - } - return text; -} - -/* For each H2 heading, add a link up to the #top of the document. - * (As part of this: ensure existence of 'top' named anchor link - * (theoretically at doc's top).) - */ -function godocs_addTopLinks() { - /* Make sure there's a "top" to link to. */ - var top = document.getElementById('top'); - if (!top) { - document.body.id = 'top'; - } - - if (!document.getElementsByTagName) return; // no browser support - - var headers = document.getElementsByTagName('h2'); - - for (var i = 0; i < headers.length; i++) { - var span = document.createElement('span'); - span.className = 'navtop'; - var link = document.createElement('a'); - span.appendChild(link); - link.href = '#top'; - var textNode = document.createTextNode('[Top]'); - link.appendChild(textNode); - headers[i].appendChild(span); - } -} diff --git a/eBook/examples/chapter_15/client.go b/eBook/examples/chapter_15/client.go deleted file mode 100644 index 9c6d861..0000000 --- a/eBook/examples/chapter_15/client.go +++ /dev/null @@ -1,36 +0,0 @@ -package main - -import ( - "fmt" - "os" - "net" - "bufio" - "strings" -) - -func main() { - conn, err := net.Dial("tcp", "localhost:50000") - if err != nil { - // No connection could be made because the target machine actively refused it. - fmt.Println("Error dialing", err.Error()) - return // terminate program - } - - inputReader := bufio.NewReader(os.Stdin) - fmt.Println("First, what is your name?") - clientName, _ := inputReader.ReadString('\n') - // fmt.Printf("CLIENTNAME %s",clientName) - trimmedClient := strings.Trim(clientName, "\r\n") // "\r\n" on Windows, "\n" on Linux - - for { - fmt.Println("What to send to the server? Type Q to quit.") - input, _ := inputReader.ReadString('\n') - trimmedInput := strings.Trim(input, "\r\n") - // fmt.Printf("input:--%s--",input) - // fmt.Printf("trimmedInput:--%s--",trimmedInput) - if trimmedInput == "Q" { - return - } - _, err = conn.Write([]byte(trimmedClient + " says: " + trimmedInput)) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i deleted file mode 100644 index d3a8311050e54c57c5be7cfe169e60a95768812c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57 zcmWN_K?=Yi3^`2(u71I;=;DIK1u2#K zAOe@M!U*e=b23x&u=xT>O>t^bS!xj$Z-^nx$*4@q#AO{{a(+r`QEFLcYPnu=S~|LW zc~UZyOY}hm7Api%t-zv!7wp~gqRbL(A8sdQ^@YsS}9bJ4e!j4lfBRw-cFTW@i g-IWk)N-7Idi?KKYDJ0+y#-fY|?AD^x#1x<;0N!XBvH$=8 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc deleted file mode 100644 index 692ebb4..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc +++ /dev/null @@ -1,2 +0,0 @@ -[paths] -default = https://code.google.com/p/go.net diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires deleted file mode 100644 index ca69271..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires +++ /dev/null @@ -1,4 +0,0 @@ -revlogv1 -store -fncache -dotencode diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i deleted file mode 100644 index aa4cdf80bcb4f4c33adaefb54abf68063095d4ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1486 zcmXw1dpHw%7~W>&PVQz3Gnva;N6LLdO~RH#wBootBug$!XhcN0O%fvI_ROV>XpS_w zG`9{d*MqI)uE^~qCkNwfJI?QUzW4oo&-4ECzTXP~0)T-39+O9#`+vaR{%a7Aqh&vO z7tTKK5t}^aNL*+FZv3y|b5x;I#wlk8N*1uXEsh32yRT1Ig6za{CILB)TY%Av^sK-? z>p8hQhcuCvOlSaYy|lrTD(x&v#V=rvHY_hWEGWKFqWpxIgNe-ijUS9dE;m2kX<3Gv zP}aZ=58q_jjHZq}l?81cbMz^{`*K_+#C%AShURVR#r=`=e=%`p$e zTSKvR1_UK}MZI%&^N2dtT7x1PG1y!c4iD&?+1tC_(W`#0|K~{%nbHyA#`zJPZt5~c zc6J*IL=^n3ytIV16*%MP000I70HCviowx9r9|-zTx%BR*9Vf4uLil_!dqnQc_apln z0Yw!?WVZ6t=*oX}>?O&^I%pjoj86fc>Kvx{Vw_`~uud~QRt3L@09N0LWVXq#p-|xn zm*P^7-HR=&Rz+4tg-fHS&nawGImY6yn%&o4HGoij6#N<5E*GI!!V2z&O`2b82{0f+ z?50p+Wl^(f8XMeLVzx0_glqj2vPUz3U5O=Bz#!>&)V!OU1$=@)5opDx!nv$n~LO-OXUr_!58 zZUgQwS5o@yjWMi-_KAG#e9S4=Ov~D*e%=}Vuj^}qsaMg+v&nU6`xZc0U;8V5+#nM{ z=&d~b%wP8h$iEarL4I1JSl7+r>{6_9Ai+ypCWa+Y?*p&5%(uKtDOgW&t&$GMZ&lQ& zQ$XM#3iAG)q;clkV_7cjdXC+~kdUtrH@U2AqaEQy@=uTOLVTOMkB?3oyi{gpNq(Nq zY$J5o=+kisq!#IhikD4+r=&&Bnxw-jX}q^4?^*>(A3mJ^B=A!CAGRRT6im>xd*;%G z6wa6T>ydbL?{H=FfX#X|1k%<$^jp+DngyzwZG5^q)Qn5q3^F{Nq$1~1;$NCWFC;i; z$SmlkTkbU9n277a!1yjeKLuX?;q?>CUwI?MXTJT>J@w2ZmoH>fc2LmP3GtAW*^L;1 zdLN<+gEO7@MNOhJQ(;kwW4P|RnH|@7YhQ+RJMekOGn3@w@|`-tAAyaN;LFKU%Iy@F zq+(d?U>_+&Ddfbqs@kSrc$FHm*gM5u^MzW~Lns(kE&(S}7LQY-V5RJ!`T~pV?qdWD zlEw}h6`?;rsemCW!V~fy=0#^cXwo(6Dm5OK9x>YVh)}wPmD(M|)9XU*@V7$a%Gj)3 zcQROp-7aBV1ggDblt??!sv)9A*OVe_7yi{dmi1)Yyp)9#hH~CE3x5_mB9phBN|YKh zRgL_NnKTMz-9j5odz2&})R7?Ir4rTpe@%Mf5ny^ncuz!hEc_kcztEZ>g=ajZ@dms< zLj2(FVw)h5B){m4GwxFzB3HlWW=2y5>V4q#rk5k0fKrK{TKDWkYD5sxOWCQ`7NT0E zHQp<1M~9QH=_@UXqGqJ%NyBJBr=zG`!c^sJp@9S%++(<8SQ1W~Jl@om>$P>Sg1yk+ zS)oq1aOOM<)ilv%sFdXcKB$X-Iw;n((sC*JHV0QYVG9k@iQs;jZ850Q f6IUL<-d^VFHzGbDkWFc}CF$Vu%>&>Q#?*fSS{sG0 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i deleted file mode 100644 index 9fb8a62d970fd617e43463d5ff5f8dded503124b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1382 zcmZQzWME`~fa44dj7(5A!+$8K`H~(-IQlUg z@-Q5*eV?p#yl?K02rpF&hozV74)i@Z!RFt0@@`-MYdC$HRV-_?sEo}n zt8-@3zdcwUT(Y=3wdiiVzW(yh$CJ2h_Npy3!&Kack}P}c~98A*?X42_E$%FwHInCUQYO& zcetv(^iHzfv+ue8-niv?HzqONib_4CG z_r}v-H%#|PIoZa&pvX3XMd{+1jtQ#^RQy`4m0bms9v0>A`?Y`7w{5xmx99%e?Dwby7$sz$44+a$}jr{ojwzYF;T{wJ~vHpnJwp~=Iny?C3Y6rX(fmroV)&t;o$?Nv_onQ|;wDOu<0 z%&i6Qb+fA8n||)Hozpk{hM~CbLlAU z;f9)yzJL0^_oxeGoN2i{ZLPP!ZdSas^K2E=PKHlG3edEkV?Q_f5$8c?bVPbV{=F%{}BUyjz|9oaK zyb~LA&|N3DWL3zPHGfa;a!+r!RQ>ot{LH~W;6%Z+1{~kcNQnfLXBa_=#Mn#tk!aXz z!EFA^(z4vC;`Sd4mLew-pt6-f`~WCsUR;n;sb5lAkXo#lp3h)zl5CceVriI`W@esZ zl$w;9Xl!6=U}RxwV4j$kW@2JtVs4a{WSPhXGLGpzh-CPMm1uQX1Lc1Th$t6?yE*YM^@o4;#GhU$Zk zJ6-E}bYCUBl>K9~{^^u;?}K+$`n_CLyX$Rv^6U2FANTz@{_tVx^hIKtN!QpWon+9L ztX&lnl9$bM`qib=&v^Fh#&^r#lv|s5&ft(VSN2!F%_c7e_i7g#8XWbT{^s<-@AXfY z@TC}YKQz%VP0_omJ>4+&XzjY^3}1i7f5<&8s9UTNxV9^`tm~lO3{~$hLA??R&r6k` zFxiG~Kcvx?b4A+uaN5EgiN>AG87C8;BtJ;2z31<>ys^C-(~x7qP?e9D4B0l@qc91OO=kh6yd2n$FU6mp6`1Ny(rZ@elqd8e1= z+I%%$&z;AhYGBkXAOnOAeLS6A{eoQ?lFd^rjm%9`Q%o$<(u|T#jLgj~ERBr}4b6-! ROcKou6O+wNO)QcUxd4u&LRu_+d=V;<|J$OUC zMf&iKH5<%3k9PhNJ#}<}*~&F57QXiXAbd3-LD}>H%SZj6mTwc(9~^uirrz=_?a1$= oPn)Ma<5iRr*<2;dwphw2zwmscD*xl^UH0?VU--+gcd?BJ0Q|#P*#H0l diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_c_o_n_t_r_i_b_u_t_o_r_s.i deleted file mode 100644 index b853cf0278f8694379eba29794e655ca1d56c491..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmZQzWME`~fG!}l3d&~q4+Z?12Iu$PIjDz|!`6fO>}j6r`ZW={oRUItgWlSnXo4$v*4@Tz5C{<@WX~=)HlZ19}X5H~AUR z!vWG050mj`dNTct5NUer!({J|!MYLJv4^8KU`O!oICUo7+}p98USQ{@!DsMn!yep} z{@pQy- zgaE)N_(;lw+r6>rTyJ1F`hXfji!38#d+(lN>0|*JU9g?Wux33R5Gev&P8<8*sNlHn z(AvRFv%fRINf#!(F+g?w5m~*HAnzpLe@Kv%u-dp$%rT?ZKZd zPJ6*3xuky5QyH7uBI?)G!SO!vCVIsc zD6!BRDjDFpmg0^Vtbq9z^ol_)>aFC8drbU*9{@Dg%0c$4n6@Jn= zFS*{Pc?+(qIBOv!p-@w)dES&%Lfyz(CG!E&N8BsOMIUdjphFYlHW+zO=el++9i%`%t zU81z>e>j3G$s5Vmkuut#nz_$5^@^Wjk}S(tC%b3l7wiR diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i deleted file mode 100644 index 75b7cc96268c9d18a8b8acd1cf0e97c902dfa717..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 670 zcmZQzWME_f1ICpM3`|!cYzBt^P;hzo)@{F%!@Z>!IcrT;wTarXW<>)`4T!FoQyb)) zeb+$1#(jU%!qz?PeKqITs`Sq(^1Q#aB_O3zxBFC-^4do;Cj7rQb#5Jtmt(@ixi{y| zy;%80^2rPii?e$U$vW!A9-6r+-D@?Qh477*HbEYj7`toIU*9}sQ~q3YciXh8D}5(e zuS{E3mM6P)+5DP6UaFCqg4G%ukF)Hrt~k^CsMPI&;C9Z7J~tX#-A+uO}KVNUgXNV&8aj)gx?^qb1f~ZB;Hf@j-uM)U)u^ zSBJiR_^EK>f$?F{LsuE5uV4S`+Vbt&Pya9KzwJ;lf71^E7Q3?te{d{Yd`O@(@x#XN z7ai~QuH=<|vE)U0->IWfXDT%m*St>)$-Db`ea#fBuXX=_7yK%!u5V(#9>6aDA;R{B z;q528?cdd%$uAD}3Y@wp*-KCDz>Ab+oqjI9$J&=&Qro|CR-ncvZ`St`Yxjj-f4xj_ zf7OT5`@4no+%C7q`m8;0IBX(= z>i;*IhZ5D~ckWV`Uo~<0uOeYV`K`Y=kDp}d21OPqih-aMi2H#U6!GB52gS8%({{Ec zx7Q}!$iHxi>vVF~8ivpnKzTT*m=k*DATP5b59`I>VGo&@-z-d^kmly>IjK%*-?E-CZ~|<+WAk7am4?!#((V zA>mI0Zx{Z;ST_K8TNJ?S4y<(2E@eJQc6Z?)C&5g?UnmF8ENmtrGr0sDo|~n#TIMjh z1_AF*j$S%{T}V1rs7z`+=`VzbOlX)$n3{#oL1~GNP|AEdR$_9|0(4EqPLw)n1; zUeZxh-uUDIv?}OuCpbbwmXUOBlg{@(d=CupI zkKj1>EI4sPC-zyxZJ#zqfLR7&ivJK! zd+L14xa6yZE0pu0;2St7gz8=+??A&M5t{ULOs~ysQhA+A6;Sg$?VkMg_cu6t*L`db zC7pQs#0%@cNLLzUn^ff}0I2|LA+8_mytUfSzPPECf5YMS{M(jr1s-sl4w1AY<4KrH zf2k<8nKgQ~N7m;YEmSram$JsUkUC{sdZ=%T>8NOn6*`iiW3@R=KI8GJ8smgOoisO9 zvdCSKfGbp9Xfz2GJ$M5nKHai<%>xw=A5)0auF^|UsJxMf`_P3K(3E{kwhTCp4?vvm zjd{%3O5@?USe<4cFQ_kuHc4ggHYp29Y~f^|PS6Si%^@bSqGBRKH?(dpS5i>v zaHY(s3pHdizjU8>VJ-#I1ga>ExukZBK9U+b8wg>?wRmIjpwuV7AeFBX=s_RDWXh64 zBc>kf9wE$WM4l6HBmwst9d9vmzKyUr8fnhOv|0zBDeO5V(<3E3I6z5$Jo7cba=7T)*4J&Z)UVfKL5^*ume= zpHL6a{7x_6irE>lM9>xdaZnxS)rpnja=l)k?{0|O*$MlvxE{kzfpRV)P+8nzA7+TC zrvVu}!%!)#t>J5U4YOD(tZcSv%zvhimUg-)c7i9-msvFaY^RbxV=&rjzU9c(o&6O~ zaWnrd>1I-vQs?QiUnK}d;Ce2QNHawyG7&rV7^4nm}`g*vmzwEg9>d4g97jJPl+(zQJ{osSn<&PAvmEE(mE-~v~ zv#mH*Fd_HC1rJ?LI;%#7+SnCrgk*w|voUfE%jDFKlZZ+yFKx&&y6MI}t<8 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i deleted file mode 100644 index 8f166d485251ca373b9c016a37b5736f70520032..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1936 zcmV;B2XFWQ0RRC20000000&S203vh%0000000061|NsC0|Ns9s#0-(1qrtEwn!Lt- z^tS>Rch%|u00000000000001Zob6d{Pa8)N{)~UcfK+ssIENt-soWBYU`UY=K|m5! z)T++DwQt4w*1cQXaR~ptGy8Jh^Baaj=!fvZvUhjhc6Mf-8Me0IK=j8M@5T~#YTMgz z88i4GV1FQEk>vsGCkd$S95R-(Y{;UZvb6=Dat5LUGUhqtVvw~Nv_-@Ke|E)?WhslG zHHHx0Uo>9ja-1-tN_d;4NGan`LOV>M#h@bwX#_mQMFuCw2ZyH@htS~(3o4a<*uDzcU|L zmWd2*Dr*^&gDizkKy+nZUW_D)Y2|iS=sD{rkS3 z-l2V{?}5Jg;GB4(T2;I7o_7z^h=*wvHZ=#W!*S&QrfApqAms@OuvXz}W$pH%%tJZI z?__O$;_j?tn3VKR9`BNdVFu}-XBoqQr}FjAeeH8N+-aWH;wTBb$I<678L%8R>4n$K zyJ{BpO?IoUdOnh)Eu8eyntC-9$l-VQ6fV4csYE}-ORi>Dycx`U3?-Y$s5w>#7+Sg8}rv7|*_YQX6_zPLn`8`EIe~)CV`nH0(%U|iK5dFt{p-Tjv;iW!r zzE~ksEYbgi*dK)t%(9PV!AY3Q4?;vMXTBx&Xk8wdol0fHiq-^A})$%XLeO8i2 z3qE*lXQ`^?`6RF{1*#&h>Au>E!vU^b6}+12Lf7$ta|>uTUfm%vxxvpm9H+xD;gKu1 z6d)wU^Q-p0DBn(msm#~EnpV0=MtVNDnxhW1Rn+i0%sB)%VD)4pZyGz?Rtp}gCf8vl z9xfEHEQ)fyf*RAMy->qs#g)`37e32^G7XZ`-<>R+tYpb4f2>OGq{cxNJgl+sABIZ=?zHuTHqoEVTGc<0lu>0rgGdRTu?r&XcU2@{2lJvtYXQe`6Seb&N3WS@i4M_uCVhT%1p|=`r zb^N(8wNRST+7UXhOwDy+!qTpcALu_#yqfbmhy-)jC-mF%jQsh?)NZ;kRENj3b9JoU z%-%kJB|%kVC@P|hV-e5`EB&%)oqm>d%GUTm!x9TbR=XWr%Z+X>_EBEf_9^uIJ`wF} zFQY_e6HacRm2o5bn%e%rf}ygREBauw2fX*Wssue3Kh-!Ea zU$+UX>E>(d#H0b6dc$PRz06tkglBD0U090ld5_6GFAUMi-o7tii~qZLUdBsDgCrph z3i^9VBu09G<4qW7NosrDZs)9T=CDW!yfkmxdH45A@J*d51br(h1My#6@{8( zI85`a!p2`;Yqbs4ZsXfV@k1iX!nDg^!bORS1B6-XzVVb_G{kgabD|BK7v|=WxH{E3 zO$x}o8QuK$Y5r{R&Or}dnhEtHD?Z(9>^{8r5jnLvzQ^cNJf)nojEjuR@rqL_dmKyN z9&Ep*VtlLvK79W^yR)u#=n}MkCi{HJMA|(l zAnw@&*H@u->-u)FgA}api6PUIer%i~PDma`F~biAtwFC3pA7<&0$vA-t6f2dzSi!+ zn29IBUb>ZpiiCAKPFN+ee)`f-!7k|&1404ihf5@ za2xjjOHLqBgLFU*%@nLzO`N7(VL0`q;kmn7bp8y^=Lycd|18m29#$?3&hilFXKenA z%^x0{j~JZvZnzs!xteBcWZi5Dk!UbqzP?{}l-(wJ%jA2rg_Z&lSdz252#Vmh|B<^# z>iFSlV`|JaJ0W;cY(CpaNR1Fx-YRsl(PNF)eW?2C{$rQ?qG;3*$tb*RYNSbt1QvCL W3!6e^>V(0)20ELifzH2VY&HbBP{WV_ diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i deleted file mode 100644 index cfd88991418ebab3d19a5a8baf2bb7536bff176f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1862 zcmV-M2f6qF0RRC20000000#yD05t^w0000000061|NsC0|Ns9zhCK_SG%lDf5h4Ud z%bjmKlS$bC00000000000001Zob6g|ZyGre{tUljiqr-Wx0Lk5NmTie_eN@?D5O1A zcc+uXnq}3^de1hYT$KNQ!(b?3_jRWO zS3?S?4E9HI$hh!eKZ+pj7Qm^X{FeG&wcUn~f&v?W9EJh}8}UAcKJzKy$AH~Z9#bE# zCqUq!cl4W(lZdKWQP`(3&XhwUp-*DCrZ8Zm*oQF2MhfrG4v)`!$1n&Z>Q$==>3<;s z1(EoZYBjt`7?)75R%+K1Nkt9cgiO80RJMmwCh9$>gNXK}dW&OZ7zed#qgow|;yzqq z!#hfR$}b2P*j1O%%tm=v4Y;dThH1Be?j9(Mo*s@JmD)!h)w-bm{rR_$9OvBC)R=C& zJsyv}fUy8k_t{OmcHdGHK2VIJujb?v0xGlKPv|COF?PG=+8wWx4!N&ZZV3mC=o5)X z^dMsWF9)Lmr0e(a-GK6HWz0iK-xY?{UzJR3W!3~nq|#@xkZ{d{k?2WI$<3J$d(f#? zVmj8=L<=bADi#UGYgHGH#YVLf4pJ*G_8<-;72}HZPKYE?y;f?rTT9(GR0Pu5FS0%L zjwnrz{~h7X;S^tvwc(l$nCwE$9?*i?U-6j4y4?fjgJ+QMN#$1aT6xSUt`O*WTul<% z1@&qStIkvq*)Da9x_h|rmMCup=~XiQWz@w$=vJ?cXM07z8I%oz3KL9&o6L@<1*EWO5c6PIZ9N| z2C4QL9S*Xo_w#ft3J{uQ$mS@!bgV0KQCx${C|HP;(18R5%Z>__9jB?RG3tWKx>2&; zls!17W3A_UrkqSci?`;4U`6vmGU%uIMaeYVUT0%ALbz4f_KjqFIMbdz(dHMGOPWNJ zpQxtf>&;3^-mu<^lG=UJ1tpKn)2t^s{*rQHEpB5Xw*fRuU@eunZJ?lV2me#AB#&X* zRuqx~#hx?6=9G|2A$75NRQWL3V6*(=Q;;U{Upg&o%>2f%N&VArpIzwak3^1yvT_#R zk|^}e@MS`ASFw^(HjsZygy06Vm~7qN4QYWFAT-O6EmlKu6}B9BaSij2R8%Snh;5dG z$S~%|AiH6m$Dquv(ZWeYf-^t8UJ>eiVjSn~srftAFzvih4unj5TL$al2#FHQ!*T4l zpz~TEe#+)iKcDn5i5T$@NbHY8Uk-8N>IWh_Mq4C4XYXbXIu7U!l_3{EvjoEMyV;At5Vs3*&-U7m=q~&IyOY#ICtDEKv_&%3$abv z*up!Pt{0{E{o6y=1VI_PUQ9o%k*><7sS4_B^rrLX&F?F4*C5*>?wSA5cTFw?Oip8C1tW&z4#hFXYidioaL0ylHDs=`aZ%KbiPLNj@*uNQyJiQ?(~#QN|` zQGa*5e{_7=Yq;0B%bt`f3G+0rh3B8(HU8b}|I$B1Gvnb29o8Q6luEt#`TU>W)#dU2 zhsGw(X~j*PiqAVxnyok1c3KTiAk%?`Ia{BZ&Qu}=wo7)3HnBHFt+`aG}X-~szA4rNH9@IRV9Gp#pEcoS zg(<@=ntAHl6O?@GIw$0HQH$XLM~@|E4=|ipySCWTvI4xk0=wSs>kSab~PT4E* z28cc%*n$5500000000000001ZoZT90a~rquGx;k}cBVtcB6Y`ytazf%(4y=pwPlV( zMaiU@40s2UXpV;qa3_kc^546Q$36H^Bw1=Yy|F0*SS%L%-sK$}kQ0%tB%jSw((!zs zjOUEJ7i5s7b0Jlm45EnWWkn=YOy01tT|YP=SBeoaCFz_iqC_SGMgkEs0>hcOVKQbR znXCvUZ!d=}m98R&yCNR27eqBtV!t_#E*GPH=&rF-s5TX__?Zdv!dR3~5Tq zJ4qLeyc2RkQ$72t^@q5%XN8lF>!$Y)G12f5KC>XBX}siIGerI#Z;1w1)%o zfyJ|QPMVz_aabRXvHIvMSD!~=)UYQaPNj&xgmwNM`JPF|McnlJ=5BXnyh>QpKeWUD zf7ZPrmp`8Wayh;@9ejK=|GdhxK8A_s=N}<|KPG5`$ZF zrjUKPIKG^F=bt`}o||(oqR+>~`_j9PKuw^PvTQmq*g4u#HE|7=M#lLS)2gB2g=XF!yrCvk1KVAQFH5v}ar_U{!7gFv20l`3t!=jY7u6Z1aW$TiEUW@9Fz=)8xz{Bz% z%lJf!R0JTU8#^CoiwTpa?$X>viYAbLpZ>$=LN(EH> z`kWNUQ_dpYK%kJF80DDiYIV+N$Yi^&p*GNDJWlz1soF6b>^WD94akf-YAT9tM*Ic;MiW{!44WfkW!@xS>7 z++BJ-_n*09;JqBz z_II)k)sn8Y(8?yXhPUA5$vey5^0P^By=Jzbg%v={kXq4$8jBpzE#~49F*y5-<5qPh3Pc6QrRn=?}fm?!g6_o&wS zSVFmljrX$7apXRlbsOOXy9hZ|hHzyX4BG8PU_oHzwA`{W^cCCfVTH3C*`(tX_07ny z1P-35UOS6#XvD)Ep%yca+69}UD0YHC5Wb34mf%kjEc`yVz!C_=-V+)JEZPzB0Hlw! zfI-EyOl}3_5G__LEyK4!9h2AAujkprlQf~)7AtGR8s4X>gKZs^#o@HUrZVV>%KAWc z{4_4tb%&~YTHd%GHF44bj8H%-{hmhI+AfTY{#IZXZV`uf>!Ro`qLJrNY$8t~7=#Bm z`pkrK`GAWQ>?-cz)~=FB(C{sUyCn~erzYi%TD9GHD3zAxf!v(M5E75VAs zEf=@6WAX4Ly0Il$p|!yDFdt&h$AaWZ=VP0&a(;p7fj%tH11iEaRnL=1GQGD7QDtoR zBrMy=!$#M8A;NW6Ysj?W_epfx9#@6X3ceS}33LJT6k>;i@nEx(S|js;*)+MFi!2JU z+VQm0D9SX|-g2Kjo6ZX|Q;BJr{q!@|P!(|ud4PmdxpL=+#SGpH6CRw#7a53qtwG3x zkQD>{>c!~|!yaqQ|pu%l|M3+>CDv{!L)v6q<_ewT}|B%u_@+VtBTWm@2_3t4a&+Fi4r{i@y zUa#ZzJKkZ}^SYkj^*UXz+x2=~uiwSV*FDecd4A99^t^7*>-D^T&pYhl{87*Qy61a6 z-|zXIp5N{Hy`JCi`G-CKb>H*)p5OO6eXra1dVQ~7W~;fO0>Xf)vq%_e7qRV9r{BGnrTV z-J3XOT2HWD1De1i;2;pHfS38kESX7AsT)r1(fMYL-|0d=*41b}vKMvZMc8*=HACD0 ztsfZ!O2olbBK#pB*8~RYD8#Sas>4U5cQuCS2Y;?v80#FT>I{w|9K8Dx*iryxBSH$xWKyUnv0+*itO@hzl9#1}x>K@!i* zmwzSp%ZyKHl0?>Y4{jGxWr$lfjwGTyHj+hMhOO=#ZDhw>iPq6k|Lc~YrPJ2Y>@!b7 z25C^rz8#N82Y%Z(#g*+BWHS2at>-ngcN&{@fB{nn>-rjRoN$0pwhwFnU@U13B7Va**GAf5S9iO$vGbg#)^oz5SzD(~B`-qU zy#y5P*Ez^Lf)oQ{3I}>V10WjM#(pH_{p{;Xh)10%ImmFUWUhBihA-~~a2428+%}q;5EDmgT zYngys%sKl+nUw%;fC4vZcmmErSL^UEfix$P=qRF|vaakE5W?(3D6J%L zqB#ZA>1GMfzFB|ER9aH#eOos)*K_baID>0%Oa1}`@CeCrZVZO{)OziA+yB=D{LNeh z0he#qb#n>kUf4puT&6GwC~V+OsVu>Dxq%m_f6pE4)UpyN1aM&3!kb3__gmU|u0000000000 z00000c${Nk&~;;A&|SsAz>vwAoS$1zlv-SzU!QtJh15xCZT3q4`70t}g(@}_yO{y$O)$mA7Oi3*QX#$ES7FB9$ KasdFS)F1AqJC4&) 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 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i deleted file mode 100644 index 83339c2159aa575279dceaac52d85997bca1df52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4493 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 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i deleted file mode 100644 index 702fc385d3b42090703fcef49a407b95d284df37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1045 zcmV+w1nT<$0RRC20000000Y$k011i!0000000061|NsC0|NsB==112~YWme7CAY$` zPfBEpKETod00000000000001ZoV`{}kJ~m7Jxjl0Dj)^rdga}uX;BwGIIhza2!f#6 zwwGLJYGko1lL|?#*9i8%cSy=#iQDuLw7c`D_HG-!;A8l zE9-VG`dh6OsOY9^xC6lztPpD5s|uu|O@!CKEtYS}CDgLTB27Ck?)VyEiz}9<9rP3BrMqM;ikgqKhapJm5((c+KQK*k;>H`Vhq`<+uOI3o8ktZTtBPeiG63@sca0< zdmy27-1YVr9~|?$d43V6L~(U+c{TW)9s$d~^Hs#^tk-3}%m%_kGcFC9UySr+bG zI-v}UrKg_d`{S2wZyV-U23>DdnmiuJ_=bui+mJcc!c#9FB>wor=h;7@|9`RnS)b3p zlFu(y;V)m4m}jxCKZKamG&$ru7XmwXo^gDW`K3&Q_{>f-pspWY>{lb=-!fG(r8j3f z6v7}e+81!R@|dQRQR?$lNL~HJq4P0pZT_>TXt?deWYH?{A30#z1PPW(bA%h%VMl+tM@shk=x@K4 z)}flKDj2YAdh_(z_swlho}xgnhAm7A%Yf zqL0`M*L|OX^p)A5qABW$J4w%+6t~tURu>|zaeA?4?;t-SvMmfV$#$A1>VHft$}DtQ z=(mBPZNXv;@^9)9LLPpxi*IY7C7M9ZX*O3ESvl!?O5cA<#AXEB=)SFhuY3cZwT>4& zg~IunM4qLi(N9l40C+>uN}>%aS%K1y@aS8CL$jBK_$9I7PBds?D_sNKASnUDb|_>m z1@{i9O)IP^9O9!0W8dd14r4q{?ekBCfw!}%dP2?ll!;IM6XJS>-2>%FFUOyFt1K#Z P6Fwz!di4JQH+l|mmA>>I diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i deleted file mode 100644 index 3e421a64766eaf7820cd254dc1b45716e4cf6ac3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3039 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 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i deleted file mode 100644 index 365461422dd1218d7a1b95273064edfd550a39ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 89 zcmZQzWME`~07)Pv0cA7%hl2WfJCjP1j-HsUS68~{6GLF>@16EAH6XgQxH7LKvBD}n UCqIcRC$YFhH#s9QFFlnD0D03KKL7v# diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache deleted file mode 100644 index a0058a5..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache +++ /dev/null @@ -1,19 +0,0 @@ -data/websocket/hybi.go.i -data/AUTHORS.i -data/CONTRIBUTORS.i -data/spdy/read.go.i -data/dict/dict.go.i -data/codereview.cfg.i -data/README.i -data/websocket/websocket_test.go.i -data/.hgignore.i -data/websocket/hixie.go.i -data/websocket/hixie_test.go.i -data/websocket/websocket.go.i -data/LICENSE.i -data/spdy/types.go.i -data/spdy/spdy_test.go.i -data/websocket/client.go.i -data/spdy/write.go.i -data/websocket/hybi_test.go.i -data/websocket/server.go.i diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo deleted file mode 100644 index 93c2690e811759f3ad075249f0272b67e4666ee1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 531 zcmaiw(GG$z3`Ko5@mFBKfJh>VVgmSV##TlnGP5lrf3Kinj9`3ey7cV!_7cJ>+6btn z@Nh*oYeH_c!4kCjyg4=0Z=ukVUBTGyVtPc#MXJIl!p~t>;Q!wXgue66S3tR|9G;Rq zjko9glx7n*1KpPeC3_F65t`W@hd7LqEShLpUW0moat$!aLBRu|^;r3hafpp0Gk#xO w-l|paDBRiLx>aaq@df96@7|Y2!R|OU=~V(nBNa@H4|1lvgLci_@% diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks deleted file mode 100644 index e69de29..0000000 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch deleted file mode 100644 index 331d858..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch +++ /dev/null @@ -1 +0,0 @@ -default \ No newline at end of file diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc deleted file mode 100644 index 48a1e39..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc +++ /dev/null @@ -1,3 +0,0 @@ -0 -pull -https://code.google.com/p/go.net diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate b/eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate deleted file mode 100644 index e69de29..0000000 diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore b/eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore deleted file mode 100644 index 571db5f..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore +++ /dev/null @@ -1,2 +0,0 @@ -syntax:glob -last-change diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS b/eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS deleted file mode 100644 index 15167cd..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code refers to The Go Authors for copyright purposes. -# The master list of authors is in the main Go distribution, -# visible at http://tip.golang.org/AUTHORS. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS b/eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS deleted file mode 100644 index 1c4577e..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS +++ /dev/null @@ -1,3 +0,0 @@ -# This source code was written by the Go contributors. -# The master list of contributors is in the main Go distribution, -# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE b/eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE deleted file mode 100644 index 6a66aea..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/README b/eBook/examples/chapter_15/code.google.com/p/go.net/README deleted file mode 100644 index 6b13d8e..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/README +++ /dev/null @@ -1,3 +0,0 @@ -This repository holds supplementary Go networking libraries. - -To submit changes to this repository, see http://golang.org/doc/contribute.html. diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg b/eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg deleted file mode 100644 index e3eb47c..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg +++ /dev/null @@ -1,2 +0,0 @@ -defaultcc: golang-dev@googlegroups.com -contributors: http://go.googlecode.com/hg/CONTRIBUTORS diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go b/eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go deleted file mode 100644 index e7f5290..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package dict implements the Dictionary Server Protocol -// as defined in RFC 2229. -package dict - -import ( - "net/textproto" - "strconv" - "strings" -) - -// A Client represents a client connection to a dictionary server. -type Client struct { - text *textproto.Conn -} - -// Dial returns a new client connected to a dictionary server at -// addr on the given network. -func Dial(network, addr string) (*Client, error) { - text, err := textproto.Dial(network, addr) - if err != nil { - return nil, err - } - _, _, err = text.ReadCodeLine(220) - if err != nil { - text.Close() - return nil, err - } - return &Client{text: text}, nil -} - -// Close closes the connection to the dictionary server. -func (c *Client) Close() error { - return c.text.Close() -} - -// A Dict represents a dictionary available on the server. -type Dict struct { - Name string // short name of dictionary - Desc string // long description -} - -// Dicts returns a list of the dictionaries available on the server. -func (c *Client) Dicts() ([]Dict, error) { - id, err := c.text.Cmd("SHOW DB") - if err != nil { - return nil, err - } - - c.text.StartResponse(id) - defer c.text.EndResponse(id) - - _, _, err = c.text.ReadCodeLine(110) - if err != nil { - return nil, err - } - lines, err := c.text.ReadDotLines() - if err != nil { - return nil, err - } - _, _, err = c.text.ReadCodeLine(250) - - dicts := make([]Dict, len(lines)) - for i := range dicts { - d := &dicts[i] - a, _ := fields(lines[i]) - if len(a) < 2 { - return nil, textproto.ProtocolError("invalid dictionary: " + lines[i]) - } - d.Name = a[0] - d.Desc = a[1] - } - return dicts, err -} - -// A Defn represents a definition. -type Defn struct { - Dict Dict // Dict where definition was found - Word string // Word being defined - Text []byte // Definition text, typically multiple lines -} - -// Define requests the definition of the given word. -// The argument dict names the dictionary to use, -// the Name field of a Dict returned by Dicts. -// -// The special dictionary name "*" means to look in all the -// server's dictionaries. -// The special dictionary name "!" means to look in all the -// server's dictionaries in turn, stopping after finding the word -// in one of them. -func (c *Client) Define(dict, word string) ([]*Defn, error) { - id, err := c.text.Cmd("DEFINE %s %q", dict, word) - if err != nil { - return nil, err - } - - c.text.StartResponse(id) - defer c.text.EndResponse(id) - - _, line, err := c.text.ReadCodeLine(150) - if err != nil { - return nil, err - } - a, _ := fields(line) - if len(a) < 1 { - return nil, textproto.ProtocolError("malformed response: " + line) - } - n, err := strconv.Atoi(a[0]) - if err != nil { - return nil, textproto.ProtocolError("invalid definition count: " + a[0]) - } - def := make([]*Defn, n) - for i := 0; i < n; i++ { - _, line, err = c.text.ReadCodeLine(151) - if err != nil { - return nil, err - } - a, _ := fields(line) - if len(a) < 3 { - // skip it, to keep protocol in sync - i-- - n-- - def = def[0:n] - continue - } - d := &Defn{Word: a[0], Dict: Dict{a[1], a[2]}} - d.Text, err = c.text.ReadDotBytes() - if err != nil { - return nil, err - } - def[i] = d - } - _, _, err = c.text.ReadCodeLine(250) - return def, err -} - -// Fields returns the fields in s. -// Fields are space separated unquoted words -// or quoted with single or double quote. -func fields(s string) ([]string, error) { - var v []string - i := 0 - for { - for i < len(s) && (s[i] == ' ' || s[i] == '\t') { - i++ - } - if i >= len(s) { - break - } - if s[i] == '"' || s[i] == '\'' { - q := s[i] - // quoted string - var j int - for j = i + 1; ; j++ { - if j >= len(s) { - return nil, textproto.ProtocolError("malformed quoted string") - } - if s[j] == '\\' { - j++ - continue - } - if s[j] == q { - j++ - break - } - } - v = append(v, unquote(s[i+1:j-1])) - i = j - } else { - // atom - var j int - for j = i; j < len(s); j++ { - if s[j] == ' ' || s[j] == '\t' || s[j] == '\\' || s[j] == '"' || s[j] == '\'' { - break - } - } - v = append(v, s[i:j]) - i = j - } - if i < len(s) { - c := s[i] - if c != ' ' && c != '\t' { - return nil, textproto.ProtocolError("quotes not on word boundaries") - } - } - } - return v, nil -} - -func unquote(s string) string { - if strings.Index(s, "\\") < 0 { - return s - } - b := []byte(s) - w := 0 - for r := 0; r < len(b); r++ { - c := b[r] - if c == '\\' { - r++ - c = b[r] - } - b[w] = c - w++ - } - return string(b[0:w]) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go deleted file mode 100644 index 4830a1d..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "compress/zlib" - "encoding/binary" - "io" - "net/http" - "strings" -) - -func (frame *SynStreamFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readSynStreamFrame(h, frame) -} - -func (frame *SynReplyFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readSynReplyFrame(h, frame) -} - -func (frame *RstStreamFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if err := binary.Read(f.r, binary.BigEndian, &frame.Status); err != nil { - return err - } - return nil -} - -func (frame *SettingsFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - var numSettings uint32 - if err := binary.Read(f.r, binary.BigEndian, &numSettings); err != nil { - return err - } - frame.FlagIdValues = make([]SettingsFlagIdValue, numSettings) - for i := uint32(0); i < numSettings; i++ { - if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Id); err != nil { - return err - } - frame.FlagIdValues[i].Flag = SettingsFlag((frame.FlagIdValues[i].Id & 0xff000000) >> 24) - frame.FlagIdValues[i].Id &= 0xffffff - if err := binary.Read(f.r, binary.BigEndian, &frame.FlagIdValues[i].Value); err != nil { - return err - } - } - return nil -} - -func (frame *NoopFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - return nil -} - -func (frame *PingFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.Id); err != nil { - return err - } - return nil -} - -func (frame *GoAwayFrame) read(h ControlFrameHeader, f *Framer) error { - frame.CFHeader = h - if err := binary.Read(f.r, binary.BigEndian, &frame.LastGoodStreamId); err != nil { - return err - } - return nil -} - -func (frame *HeadersFrame) read(h ControlFrameHeader, f *Framer) error { - return f.readHeadersFrame(h, frame) -} - -func newControlFrame(frameType ControlFrameType) (controlFrame, error) { - ctor, ok := cframeCtor[frameType] - if !ok { - return nil, &Error{Err: InvalidControlFrame} - } - return ctor(), nil -} - -var cframeCtor = map[ControlFrameType]func() controlFrame{ - TypeSynStream: func() controlFrame { return new(SynStreamFrame) }, - TypeSynReply: func() controlFrame { return new(SynReplyFrame) }, - TypeRstStream: func() controlFrame { return new(RstStreamFrame) }, - TypeSettings: func() controlFrame { return new(SettingsFrame) }, - TypeNoop: func() controlFrame { return new(NoopFrame) }, - TypePing: func() controlFrame { return new(PingFrame) }, - TypeGoAway: func() controlFrame { return new(GoAwayFrame) }, - TypeHeaders: func() controlFrame { return new(HeadersFrame) }, - // TODO(willchan): Add TypeWindowUpdate -} - -func (f *Framer) uncorkHeaderDecompressor(payloadSize int64) error { - if f.headerDecompressor != nil { - f.headerReader.N = payloadSize - return nil - } - f.headerReader = io.LimitedReader{R: f.r, N: payloadSize} - decompressor, err := zlib.NewReaderDict(&f.headerReader, []byte(HeaderDictionary)) - if err != nil { - return err - } - f.headerDecompressor = decompressor - return nil -} - -// ReadFrame reads SPDY encoded data and returns a decompressed Frame. -func (f *Framer) ReadFrame() (Frame, error) { - var firstWord uint32 - if err := binary.Read(f.r, binary.BigEndian, &firstWord); err != nil { - return nil, err - } - if (firstWord & 0x80000000) != 0 { - frameType := ControlFrameType(firstWord & 0xffff) - version := uint16(0x7fff & (firstWord >> 16)) - return f.parseControlFrame(version, frameType) - } - return f.parseDataFrame(firstWord & 0x7fffffff) -} - -func (f *Framer) parseControlFrame(version uint16, frameType ControlFrameType) (Frame, error) { - var length uint32 - if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { - return nil, err - } - flags := ControlFlags((length & 0xff000000) >> 24) - length &= 0xffffff - header := ControlFrameHeader{version, frameType, flags, length} - cframe, err := newControlFrame(frameType) - if err != nil { - return nil, err - } - if err = cframe.read(header, f); err != nil { - return nil, err - } - return cframe, nil -} - -func parseHeaderValueBlock(r io.Reader, streamId uint32) (http.Header, error) { - var numHeaders uint16 - if err := binary.Read(r, binary.BigEndian, &numHeaders); err != nil { - return nil, err - } - var e error - h := make(http.Header, int(numHeaders)) - for i := 0; i < int(numHeaders); i++ { - var length uint16 - if err := binary.Read(r, binary.BigEndian, &length); err != nil { - return nil, err - } - nameBytes := make([]byte, length) - if _, err := io.ReadFull(r, nameBytes); err != nil { - return nil, err - } - name := string(nameBytes) - if name != strings.ToLower(name) { - e = &Error{UnlowercasedHeaderName, streamId} - name = strings.ToLower(name) - } - if h[name] != nil { - e = &Error{DuplicateHeaders, streamId} - } - if err := binary.Read(r, binary.BigEndian, &length); err != nil { - return nil, err - } - value := make([]byte, length) - if _, err := io.ReadFull(r, value); err != nil { - return nil, err - } - valueList := strings.Split(string(value), "\x00") - for _, v := range valueList { - h.Add(name, v) - } - } - if e != nil { - return h, e - } - return h, nil -} - -func (f *Framer) readSynStreamFrame(h ControlFrameHeader, frame *SynStreamFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - if err = binary.Read(f.r, binary.BigEndian, &frame.AssociatedToStreamId); err != nil { - return err - } - if err = binary.Read(f.r, binary.BigEndian, &frame.Priority); err != nil { - return err - } - frame.Priority >>= 14 - - reader := f.r - if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int64(h.length - 10)) - reader = f.headerDecompressor - } - - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && ((err == io.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - // Remove this condition when we bump Version to 3. - if Version >= 3 { - for h := range frame.Headers { - if invalidReqHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - } - return nil -} - -func (f *Framer) readSynReplyFrame(h ControlFrameHeader, frame *SynReplyFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - var unused uint16 - if err = binary.Read(f.r, binary.BigEndian, &unused); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int64(h.length - 6)) - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && ((err == io.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - // Remove this condition when we bump Version to 3. - if Version >= 3 { - for h := range frame.Headers { - if invalidRespHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - } - return nil -} - -func (f *Framer) readHeadersFrame(h ControlFrameHeader, frame *HeadersFrame) error { - frame.CFHeader = h - var err error - if err = binary.Read(f.r, binary.BigEndian, &frame.StreamId); err != nil { - return err - } - var unused uint16 - if err = binary.Read(f.r, binary.BigEndian, &unused); err != nil { - return err - } - reader := f.r - if !f.headerCompressionDisabled { - f.uncorkHeaderDecompressor(int64(h.length - 6)) - reader = f.headerDecompressor - } - frame.Headers, err = parseHeaderValueBlock(reader, frame.StreamId) - if !f.headerCompressionDisabled && ((err == io.EOF && f.headerReader.N == 0) || f.headerReader.N != 0) { - err = &Error{WrongCompressedPayloadSize, 0} - } - if err != nil { - return err - } - - // Remove this condition when we bump Version to 3. - if Version >= 3 { - var invalidHeaders map[string]bool - if frame.StreamId%2 == 0 { - invalidHeaders = invalidReqHeaders - } else { - invalidHeaders = invalidRespHeaders - } - for h := range frame.Headers { - if invalidHeaders[h] { - return &Error{InvalidHeaderPresent, frame.StreamId} - } - } - } - return nil -} - -func (f *Framer) parseDataFrame(streamId uint32) (*DataFrame, error) { - var length uint32 - if err := binary.Read(f.r, binary.BigEndian, &length); err != nil { - return nil, err - } - var frame DataFrame - frame.StreamId = streamId - frame.Flags = DataFlags(length >> 24) - length &= 0xffffff - frame.Data = make([]byte, length) - if _, err := io.ReadFull(f.r, frame.Data); err != nil { - return nil, err - } - return &frame, nil -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go deleted file mode 100644 index c1cad4b..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go +++ /dev/null @@ -1,497 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "bytes" - "io" - "net/http" - "reflect" - "testing" -) - -func TestHeaderParsing(t *testing.T) { - headers := http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - } - var headerValueBlockBuf bytes.Buffer - writeHeaderValueBlock(&headerValueBlockBuf, headers) - - const bogusStreamId = 1 - newHeaders, err := parseHeaderValueBlock(&headerValueBlockBuf, bogusStreamId) - if err != nil { - t.Fatal("parseHeaderValueBlock:", err) - } - - if !reflect.DeepEqual(headers, newHeaders) { - t.Fatal("got: ", newHeaders, "\nwant: ", headers) - } -} - -func TestCreateParseSynStreamFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer := &Framer{ - headerCompressionDisabled: true, - w: buffer, - headerBuf: new(bytes.Buffer), - r: buffer, - } - synStreamFrame := SynStreamFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSynStream, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - if err := framer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame without compression:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame without compression:", err) - } - parsedSynStreamFrame, ok := frame.(*SynStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } - - // Test again with compression - buffer.Reset() - framer, err = NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - if err := framer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame with compression:", err) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame with compression:", err) - } - parsedSynStreamFrame, ok = frame.(*SynStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } -} - -func TestCreateParseSynReplyFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer := &Framer{ - headerCompressionDisabled: true, - w: buffer, - headerBuf: new(bytes.Buffer), - r: buffer, - } - synReplyFrame := SynReplyFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSynReply, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - if err := framer.WriteFrame(&synReplyFrame); err != nil { - t.Fatal("WriteFrame without compression:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame without compression:", err) - } - parsedSynReplyFrame, ok := frame.(*SynReplyFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { - t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) - } - - // Test again with compression - buffer.Reset() - framer, err = NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - if err := framer.WriteFrame(&synReplyFrame); err != nil { - t.Fatal("WriteFrame with compression:", err) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame with compression:", err) - } - parsedSynReplyFrame, ok = frame.(*SynReplyFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(synReplyFrame, *parsedSynReplyFrame) { - t.Fatal("got: ", *parsedSynReplyFrame, "\nwant: ", synReplyFrame) - } -} - -func TestCreateParseRstStream(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - rstStreamFrame := RstStreamFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeRstStream, - }, - StreamId: 1, - Status: InvalidStream, - } - if err := framer.WriteFrame(&rstStreamFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedRstStreamFrame, ok := frame.(*RstStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(rstStreamFrame, *parsedRstStreamFrame) { - t.Fatal("got: ", *parsedRstStreamFrame, "\nwant: ", rstStreamFrame) - } -} - -func TestCreateParseSettings(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - settingsFrame := SettingsFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSettings, - }, - FlagIdValues: []SettingsFlagIdValue{ - {FlagSettingsPersistValue, SettingsCurrentCwnd, 10}, - {FlagSettingsPersisted, SettingsUploadBandwidth, 1}, - }, - } - if err := framer.WriteFrame(&settingsFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedSettingsFrame, ok := frame.(*SettingsFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(settingsFrame, *parsedSettingsFrame) { - t.Fatal("got: ", *parsedSettingsFrame, "\nwant: ", settingsFrame) - } -} - -func TestCreateParseNoop(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - noopFrame := NoopFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeNoop, - }, - } - if err := framer.WriteFrame(&noopFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedNoopFrame, ok := frame.(*NoopFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(noopFrame, *parsedNoopFrame) { - t.Fatal("got: ", *parsedNoopFrame, "\nwant: ", noopFrame) - } -} - -func TestCreateParsePing(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - pingFrame := PingFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypePing, - }, - Id: 31337, - } - if err := framer.WriteFrame(&pingFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedPingFrame, ok := frame.(*PingFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(pingFrame, *parsedPingFrame) { - t.Fatal("got: ", *parsedPingFrame, "\nwant: ", pingFrame) - } -} - -func TestCreateParseGoAway(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - goAwayFrame := GoAwayFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeGoAway, - }, - LastGoodStreamId: 31337, - } - if err := framer.WriteFrame(&goAwayFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedGoAwayFrame, ok := frame.(*GoAwayFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(goAwayFrame, *parsedGoAwayFrame) { - t.Fatal("got: ", *parsedGoAwayFrame, "\nwant: ", goAwayFrame) - } -} - -func TestCreateParseHeadersFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer := &Framer{ - headerCompressionDisabled: true, - w: buffer, - headerBuf: new(bytes.Buffer), - r: buffer, - } - headersFrame := HeadersFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeHeaders, - }, - } - headersFrame.Headers = http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - } - if err := framer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame without compression:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame without compression:", err) - } - parsedHeadersFrame, ok := frame.(*HeadersFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } - - // Test again with compression - buffer.Reset() - framer, err = NewFramer(buffer, buffer) - if err := framer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame with compression:", err) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame with compression:", err) - } - parsedHeadersFrame, ok = frame.(*HeadersFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } -} - -func TestCreateParseDataFrame(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - dataFrame := DataFrame{ - StreamId: 1, - Data: []byte{'h', 'e', 'l', 'l', 'o'}, - } - if err := framer.WriteFrame(&dataFrame); err != nil { - t.Fatal("WriteFrame:", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame:", err) - } - parsedDataFrame, ok := frame.(*DataFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(dataFrame, *parsedDataFrame) { - t.Fatal("got: ", *parsedDataFrame, "\nwant: ", dataFrame) - } -} - -func TestCompressionContextAcrossFrames(t *testing.T) { - buffer := new(bytes.Buffer) - framer, err := NewFramer(buffer, buffer) - if err != nil { - t.Fatal("Failed to create new framer:", err) - } - headersFrame := HeadersFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeHeaders, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - if err := framer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame (HEADERS):", err) - } - synStreamFrame := SynStreamFrame{ControlFrameHeader{Version, TypeSynStream, 0, 0}, 0, 0, 0, nil} - synStreamFrame.Headers = http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - } - if err := framer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame (SYN_STREAM):", err) - } - frame, err := framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (HEADERS):", err, buffer.Bytes()) - } - parsedHeadersFrame, ok := frame.(*HeadersFrame) - if !ok { - t.Fatalf("expected HeadersFrame; got %T %v", frame, frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } - frame, err = framer.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (SYN_STREAM):", err, buffer.Bytes()) - } - parsedSynStreamFrame, ok := frame.(*SynStreamFrame) - if !ok { - t.Fatalf("expected SynStreamFrame; got %T %v", frame, frame) - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } -} - -func TestMultipleSPDYFrames(t *testing.T) { - // Initialize the framers. - pr1, pw1 := io.Pipe() - pr2, pw2 := io.Pipe() - writer, err := NewFramer(pw1, pr2) - if err != nil { - t.Fatal("Failed to create writer:", err) - } - reader, err := NewFramer(pw2, pr1) - if err != nil { - t.Fatal("Failed to create reader:", err) - } - - // Set up the frames we're actually transferring. - headersFrame := HeadersFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeHeaders, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - synStreamFrame := SynStreamFrame{ - CFHeader: ControlFrameHeader{ - version: Version, - frameType: TypeSynStream, - }, - Headers: http.Header{ - "Url": []string{"http://www.google.com/"}, - "Method": []string{"get"}, - "Version": []string{"http/1.1"}, - }, - } - - // Start the goroutines to write the frames. - go func() { - if err := writer.WriteFrame(&headersFrame); err != nil { - t.Fatal("WriteFrame (HEADERS): ", err) - } - if err := writer.WriteFrame(&synStreamFrame); err != nil { - t.Fatal("WriteFrame (SYN_STREAM): ", err) - } - }() - - // Read the frames and verify they look as expected. - frame, err := reader.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (HEADERS): ", err) - } - parsedHeadersFrame, ok := frame.(*HeadersFrame) - if !ok { - t.Fatal("Parsed incorrect frame type:", frame) - } - if !reflect.DeepEqual(headersFrame, *parsedHeadersFrame) { - t.Fatal("got: ", *parsedHeadersFrame, "\nwant: ", headersFrame) - } - frame, err = reader.ReadFrame() - if err != nil { - t.Fatal("ReadFrame (SYN_STREAM):", err) - } - parsedSynStreamFrame, ok := frame.(*SynStreamFrame) - if !ok { - t.Fatal("Parsed incorrect frame type.") - } - if !reflect.DeepEqual(synStreamFrame, *parsedSynStreamFrame) { - t.Fatal("got: ", *parsedSynStreamFrame, "\nwant: ", synStreamFrame) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go deleted file mode 100644 index 7c57d36..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "bytes" - "compress/zlib" - "io" - "net/http" -) - -// Data Frame Format -// +----------------------------------+ -// |0| Stream-ID (31bits) | -// +----------------------------------+ -// | flags (8) | Length (24 bits) | -// +----------------------------------+ -// | Data | -// +----------------------------------+ -// -// Control Frame Format -// +----------------------------------+ -// |1| Version(15bits) | Type(16bits) | -// +----------------------------------+ -// | flags (8) | Length (24 bits) | -// +----------------------------------+ -// | Data | -// +----------------------------------+ -// -// Control Frame: SYN_STREAM -// +----------------------------------+ -// |1|000000000000001|0000000000000001| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 12 -// +----------------------------------+ -// |X| Stream-ID(31bits) | -// +----------------------------------+ -// |X|Associated-To-Stream-ID (31bits)| -// +----------------------------------+ -// |Pri| unused | Length (16bits)| -// +----------------------------------+ -// -// Control Frame: SYN_REPLY -// +----------------------------------+ -// |1|000000000000001|0000000000000010| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 8 -// +----------------------------------+ -// |X| Stream-ID(31bits) | -// +----------------------------------+ -// | unused (16 bits)| Length (16bits)| -// +----------------------------------+ -// -// Control Frame: RST_STREAM -// +----------------------------------+ -// |1|000000000000001|0000000000000011| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 4 -// +----------------------------------+ -// |X| Stream-ID(31bits) | -// +----------------------------------+ -// | Status code (32 bits) | -// +----------------------------------+ -// -// Control Frame: SETTINGS -// +----------------------------------+ -// |1|000000000000001|0000000000000100| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | -// +----------------------------------+ -// | # of entries (32) | -// +----------------------------------+ -// -// Control Frame: NOOP -// +----------------------------------+ -// |1|000000000000001|0000000000000101| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 0 -// +----------------------------------+ -// -// Control Frame: PING -// +----------------------------------+ -// |1|000000000000001|0000000000000110| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 4 -// +----------------------------------+ -// | Unique id (32 bits) | -// +----------------------------------+ -// -// Control Frame: GOAWAY -// +----------------------------------+ -// |1|000000000000001|0000000000000111| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 4 -// +----------------------------------+ -// |X| Last-accepted-stream-id | -// +----------------------------------+ -// -// Control Frame: HEADERS -// +----------------------------------+ -// |1|000000000000001|0000000000001000| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | >= 8 -// +----------------------------------+ -// |X| Stream-ID (31 bits) | -// +----------------------------------+ -// | unused (16 bits)| Length (16bits)| -// +----------------------------------+ -// -// Control Frame: WINDOW_UPDATE -// +----------------------------------+ -// |1|000000000000001|0000000000001001| -// +----------------------------------+ -// | flags (8) | Length (24 bits) | = 8 -// +----------------------------------+ -// |X| Stream-ID (31 bits) | -// +----------------------------------+ -// | Delta-Window-Size (32 bits) | -// +----------------------------------+ - -// Version is the protocol version number that this package implements. -const Version = 2 - -// ControlFrameType stores the type field in a control frame header. -type ControlFrameType uint16 - -// Control frame type constants -const ( - TypeSynStream ControlFrameType = 0x0001 - TypeSynReply = 0x0002 - TypeRstStream = 0x0003 - TypeSettings = 0x0004 - TypeNoop = 0x0005 - TypePing = 0x0006 - TypeGoAway = 0x0007 - TypeHeaders = 0x0008 - TypeWindowUpdate = 0x0009 -) - -// ControlFlags are the flags that can be set on a control frame. -type ControlFlags uint8 - -const ( - ControlFlagFin ControlFlags = 0x01 -) - -// DataFlags are the flags that can be set on a data frame. -type DataFlags uint8 - -const ( - DataFlagFin DataFlags = 0x01 - DataFlagCompressed = 0x02 -) - -// MaxDataLength is the maximum number of bytes that can be stored in one frame. -const MaxDataLength = 1<<24 - 1 - -// Frame is a single SPDY frame in its unpacked in-memory representation. Use -// Framer to read and write it. -type Frame interface { - write(f *Framer) error -} - -// ControlFrameHeader contains all the fields in a control frame header, -// in its unpacked in-memory representation. -type ControlFrameHeader struct { - // Note, high bit is the "Control" bit. - version uint16 - frameType ControlFrameType - Flags ControlFlags - length uint32 -} - -type controlFrame interface { - Frame - read(h ControlFrameHeader, f *Framer) error -} - -// SynStreamFrame is the unpacked, in-memory representation of a SYN_STREAM -// frame. -type SynStreamFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - AssociatedToStreamId uint32 - // Note, only 2 highest bits currently used - // Rest of Priority is unused. - Priority uint16 - Headers http.Header -} - -// SynReplyFrame is the unpacked, in-memory representation of a SYN_REPLY frame. -type SynReplyFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - Headers http.Header -} - -// StatusCode represents the status that led to a RST_STREAM -type StatusCode uint32 - -const ( - ProtocolError StatusCode = 1 - InvalidStream = 2 - RefusedStream = 3 - UnsupportedVersion = 4 - Cancel = 5 - InternalError = 6 - FlowControlError = 7 -) - -// RstStreamFrame is the unpacked, in-memory representation of a RST_STREAM -// frame. -type RstStreamFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - Status StatusCode -} - -// SettingsFlag represents a flag in a SETTINGS frame. -type SettingsFlag uint8 - -const ( - FlagSettingsPersistValue SettingsFlag = 0x1 - FlagSettingsPersisted = 0x2 -) - -// SettingsFlag represents the id of an id/value pair in a SETTINGS frame. -type SettingsId uint32 - -const ( - SettingsUploadBandwidth SettingsId = 1 - SettingsDownloadBandwidth = 2 - SettingsRoundTripTime = 3 - SettingsMaxConcurrentStreams = 4 - SettingsCurrentCwnd = 5 -) - -// SettingsFlagIdValue is the unpacked, in-memory representation of the -// combined flag/id/value for a setting in a SETTINGS frame. -type SettingsFlagIdValue struct { - Flag SettingsFlag - Id SettingsId - Value uint32 -} - -// SettingsFrame is the unpacked, in-memory representation of a SPDY -// SETTINGS frame. -type SettingsFrame struct { - CFHeader ControlFrameHeader - FlagIdValues []SettingsFlagIdValue -} - -// NoopFrame is the unpacked, in-memory representation of a NOOP frame. -type NoopFrame struct { - CFHeader ControlFrameHeader -} - -// PingFrame is the unpacked, in-memory representation of a PING frame. -type PingFrame struct { - CFHeader ControlFrameHeader - Id uint32 -} - -// GoAwayFrame is the unpacked, in-memory representation of a GOAWAY frame. -type GoAwayFrame struct { - CFHeader ControlFrameHeader - LastGoodStreamId uint32 -} - -// HeadersFrame is the unpacked, in-memory representation of a HEADERS frame. -type HeadersFrame struct { - CFHeader ControlFrameHeader - StreamId uint32 - Headers http.Header -} - -// DataFrame is the unpacked, in-memory representation of a DATA frame. -type DataFrame struct { - // Note, high bit is the "Control" bit. Should be 0 for data frames. - StreamId uint32 - Flags DataFlags - Data []byte -} - -// HeaderDictionary is the dictionary sent to the zlib compressor/decompressor. -// Even though the specification states there is no null byte at the end, Chrome sends it. -const HeaderDictionary = "optionsgetheadpostputdeletetrace" + - "acceptaccept-charsetaccept-encodingaccept-languageauthorizationexpectfromhost" + - "if-modified-sinceif-matchif-none-matchif-rangeif-unmodifiedsince" + - "max-forwardsproxy-authorizationrangerefererteuser-agent" + - "100101200201202203204205206300301302303304305306307400401402403404405406407408409410411412413414415416417500501502503504505" + - "accept-rangesageetaglocationproxy-authenticatepublicretry-after" + - "servervarywarningwww-authenticateallowcontent-basecontent-encodingcache-control" + - "connectiondatetrailertransfer-encodingupgradeviawarning" + - "content-languagecontent-lengthcontent-locationcontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookie" + - "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" + - "JanFebMarAprMayJunJulAugSepOctNovDec" + - "chunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplication/xhtmltext/plainpublicmax-age" + - "charset=iso-8859-1utf-8gzipdeflateHTTP/1.1statusversionurl\x00" - -// A SPDY specific error. -type ErrorCode string - -const ( - UnlowercasedHeaderName ErrorCode = "header was not lowercased" - DuplicateHeaders ErrorCode = "multiple headers with same name" - WrongCompressedPayloadSize ErrorCode = "compressed payload size was incorrect" - UnknownFrameType ErrorCode = "unknown frame type" - InvalidControlFrame ErrorCode = "invalid control frame" - InvalidDataFrame ErrorCode = "invalid data frame" - InvalidHeaderPresent ErrorCode = "frame contained invalid header" -) - -// Error contains both the type of error and additional values. StreamId is 0 -// if Error is not associated with a stream. -type Error struct { - Err ErrorCode - StreamId uint32 -} - -func (e *Error) Error() string { - return string(e.Err) -} - -var invalidReqHeaders = map[string]bool{ - "Connection": true, - "Keep-Alive": true, - "Proxy-Connection": true, - "Transfer-Encoding": true, -} - -var invalidRespHeaders = map[string]bool{ - "Connection": true, - "Keep-Alive": true, - "Transfer-Encoding": true, -} - -// Framer handles serializing/deserializing SPDY frames, including compressing/ -// decompressing payloads. -type Framer struct { - headerCompressionDisabled bool - w io.Writer - headerBuf *bytes.Buffer - headerCompressor *zlib.Writer - r io.Reader - headerReader io.LimitedReader - headerDecompressor io.ReadCloser -} - -// NewFramer allocates a new Framer for a given SPDY connection, repesented by -// a io.Writer and io.Reader. Note that Framer will read and write individual fields -// from/to the Reader and Writer, so the caller should pass in an appropriately -// buffered implementation to optimize performance. -func NewFramer(w io.Writer, r io.Reader) (*Framer, error) { - compressBuf := new(bytes.Buffer) - compressor, err := zlib.NewWriterLevelDict(compressBuf, zlib.BestCompression, []byte(HeaderDictionary)) - if err != nil { - return nil, err - } - framer := &Framer{ - w: w, - headerBuf: compressBuf, - headerCompressor: compressor, - r: r, - } - return framer, nil -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go b/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go deleted file mode 100644 index 3dd2ca1..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package spdy - -import ( - "encoding/binary" - "io" - "net/http" - "strings" -) - -func (frame *SynStreamFrame) write(f *Framer) error { - return f.writeSynStreamFrame(frame) -} - -func (frame *SynReplyFrame) write(f *Framer) error { - return f.writeSynReplyFrame(frame) -} - -func (frame *RstStreamFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeRstStream - frame.CFHeader.length = 8 - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.Status); err != nil { - return - } - return -} - -func (frame *SettingsFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSettings - frame.CFHeader.length = uint32(len(frame.FlagIdValues)*8 + 4) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint32(len(frame.FlagIdValues))); err != nil { - return - } - for _, flagIdValue := range frame.FlagIdValues { - flagId := (uint32(flagIdValue.Flag) << 24) | uint32(flagIdValue.Id) - if err = binary.Write(f.w, binary.BigEndian, flagId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, flagIdValue.Value); err != nil { - return - } - } - return -} - -func (frame *NoopFrame) write(f *Framer) error { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeNoop - - // Serialize frame to Writer - return writeControlFrameHeader(f.w, frame.CFHeader) -} - -func (frame *PingFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypePing - frame.CFHeader.length = 4 - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.Id); err != nil { - return - } - return -} - -func (frame *GoAwayFrame) write(f *Framer) (err error) { - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeGoAway - frame.CFHeader.length = 4 - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.LastGoodStreamId); err != nil { - return - } - return nil -} - -func (frame *HeadersFrame) write(f *Framer) error { - return f.writeHeadersFrame(frame) -} - -func (frame *DataFrame) write(f *Framer) error { - return f.writeDataFrame(frame) -} - -// WriteFrame writes a frame. -func (f *Framer) WriteFrame(frame Frame) error { - return frame.write(f) -} - -func writeControlFrameHeader(w io.Writer, h ControlFrameHeader) error { - if err := binary.Write(w, binary.BigEndian, 0x8000|h.version); err != nil { - return err - } - if err := binary.Write(w, binary.BigEndian, h.frameType); err != nil { - return err - } - flagsAndLength := (uint32(h.Flags) << 24) | h.length - if err := binary.Write(w, binary.BigEndian, flagsAndLength); err != nil { - return err - } - return nil -} - -func writeHeaderValueBlock(w io.Writer, h http.Header) (n int, err error) { - n = 0 - if err = binary.Write(w, binary.BigEndian, uint16(len(h))); err != nil { - return - } - n += 2 - for name, values := range h { - if err = binary.Write(w, binary.BigEndian, uint16(len(name))); err != nil { - return - } - n += 2 - name = strings.ToLower(name) - if _, err = io.WriteString(w, name); err != nil { - return - } - n += len(name) - v := strings.Join(values, "\x00") - if err = binary.Write(w, binary.BigEndian, uint16(len(v))); err != nil { - return - } - n += 2 - if _, err = io.WriteString(w, v); err != nil { - return - } - n += len(v) - } - return -} - -func (f *Framer) writeSynStreamFrame(frame *SynStreamFrame) (err error) { - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSynStream - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 10) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.AssociatedToStreamId); err != nil { - return err - } - if err = binary.Write(f.w, binary.BigEndian, frame.Priority<<14); err != nil { - return err - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return err - } - f.headerBuf.Reset() - return nil -} - -func (f *Framer) writeSynReplyFrame(frame *SynReplyFrame) (err error) { - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeSynReply - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 6) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint16(0)); err != nil { - return - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return - } - f.headerBuf.Reset() - return -} - -func (f *Framer) writeHeadersFrame(frame *HeadersFrame) (err error) { - // Marshal the headers. - var writer io.Writer = f.headerBuf - if !f.headerCompressionDisabled { - writer = f.headerCompressor - } - if _, err = writeHeaderValueBlock(writer, frame.Headers); err != nil { - return - } - if !f.headerCompressionDisabled { - f.headerCompressor.Flush() - } - - // Set ControlFrameHeader - frame.CFHeader.version = Version - frame.CFHeader.frameType = TypeHeaders - frame.CFHeader.length = uint32(len(f.headerBuf.Bytes()) + 6) - - // Serialize frame to Writer - if err = writeControlFrameHeader(f.w, frame.CFHeader); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - if err = binary.Write(f.w, binary.BigEndian, uint16(0)); err != nil { - return - } - if _, err = f.w.Write(f.headerBuf.Bytes()); err != nil { - return - } - f.headerBuf.Reset() - return -} - -func (f *Framer) writeDataFrame(frame *DataFrame) (err error) { - // Validate DataFrame - if frame.StreamId&0x80000000 != 0 || len(frame.Data) >= 0x0f000000 { - return &Error{InvalidDataFrame, frame.StreamId} - } - - // Serialize frame to Writer - if err = binary.Write(f.w, binary.BigEndian, frame.StreamId); err != nil { - return - } - flagsAndLength := (uint32(frame.Flags) << 24) | uint32(len(frame.Data)) - if err = binary.Write(f.w, binary.BigEndian, flagsAndLength); err != nil { - return - } - if _, err = f.w.Write(frame.Data); err != nil { - return - } - - return nil -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go deleted file mode 100644 index 1b82e9c..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "crypto/tls" - "io" - "net" - "net/url" -) - -// DialError is an error that occurs while dialling a websocket server. -type DialError struct { - *Config - Err error -} - -func (e *DialError) Error() string { - return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() -} - -// NewConfig creates a new WebSocket config for client connection. -func NewConfig(server, origin string) (config *Config, err error) { - config = new(Config) - config.Version = ProtocolVersionHybi13 - config.Location, err = url.ParseRequestURI(server) - if err != nil { - return - } - config.Origin, err = url.ParseRequestURI(origin) - if err != nil { - return - } - return -} - -// NewClient creates a new WebSocket client connection over rwc. -func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - switch config.Version { - case ProtocolVersionHixie75: - err = hixie75ClientHandshake(config, br, bw) - case ProtocolVersionHixie76, ProtocolVersionHybi00: - err = hixie76ClientHandshake(config, br, bw) - case ProtocolVersionHybi08, ProtocolVersionHybi13: - err = hybiClientHandshake(config, br, bw) - default: - err = ErrBadProtocolVersion - } - if err != nil { - return - } - buf := bufio.NewReadWriter(br, bw) - switch config.Version { - case ProtocolVersionHixie75, ProtocolVersionHixie76, ProtocolVersionHybi00: - ws = newHixieClientConn(config, buf, rwc) - case ProtocolVersionHybi08, ProtocolVersionHybi13: - ws = newHybiClientConn(config, buf, rwc) - } - return -} - -/* -Dial opens a new client connection to a WebSocket. - -A trivial example client: - - package main - - import ( - "log" - "net/http" - "strings" - "websocket" - ) - - func main() { - origin := "http://localhost/" - url := "ws://localhost/ws" - ws, err := websocket.Dial(url, "", origin) - if err != nil { - log.Fatal(err) - } - if _, err := ws.Write([]byte("hello, world!\n")); err != nil { - log.Fatal(err) - } - var msg = make([]byte, 512); - if n, err := ws.Read(msg); err != nil { - log.Fatal(err) - } - // use msg[0:n] - } -*/ -func Dial(url_, protocol, origin string) (ws *Conn, err error) { - config, err := NewConfig(url_, origin) - if err != nil { - return nil, err - } - return DialConfig(config) -} - -// DialConfig opens a new client connection to a WebSocket with a config. -func DialConfig(config *Config) (ws *Conn, err error) { - var client net.Conn - if config.Location == nil { - return nil, &DialError{config, ErrBadWebSocketLocation} - } - if config.Origin == nil { - return nil, &DialError{config, ErrBadWebSocketOrigin} - } - switch config.Location.Scheme { - case "ws": - client, err = net.Dial("tcp", config.Location.Host) - - case "wss": - client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig) - - default: - err = ErrBadScheme - } - if err != nil { - goto Error - } - - ws, err = NewClient(config, client) - if err != nil { - goto Error - } - return - -Error: - return nil, &DialError{config, err} -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go deleted file mode 100644 index 6d215b9..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go +++ /dev/null @@ -1,695 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -// This file implements a protocol of Hixie draft version 75 and 76 -// (draft 76 equals to hybi 00) - -import ( - "bufio" - "bytes" - "crypto/md5" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net/http" - "net/url" - "strconv" - "strings" -) - -// An aray of characters to be randomly inserted to construct Sec-WebSocket-Key -// value. It holds characters from ranges U+0021 to U+002F and U+003A to U+007E. -// See Step 21 in Section 4.1 Opening handshake. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#page-22 -var secKeyRandomChars [0x30 - 0x21 + 0x7F - 0x3A]byte - -func init() { - i := 0 - for ch := byte(0x21); ch < 0x30; ch++ { - secKeyRandomChars[i] = ch - i++ - } - for ch := byte(0x3a); ch < 0x7F; ch++ { - secKeyRandomChars[i] = ch - i++ - } -} - -type byteReader interface { - ReadByte() (byte, error) -} - -// readHixieLength reads frame length for frame type 0x80-0xFF -// as defined in Hixie draft. -// See section 4.2 Data framing. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-4.2 -func readHixieLength(r byteReader) (length int64, lengthFields []byte, err error) { - for { - c, err := r.ReadByte() - if err != nil { - return 0, nil, err - } - lengthFields = append(lengthFields, c) - length = length*128 + int64(c&0x7f) - if c&0x80 == 0 { - break - } - } - return -} - -// A hixieLengthFrameReader is a reader for frame type 0x80-0xFF -// as defined in hixie draft. -type hixieLengthFrameReader struct { - reader io.Reader - FrameType byte - Length int64 - header *bytes.Buffer - length int -} - -func (frame *hixieLengthFrameReader) Read(msg []byte) (n int, err error) { - return frame.reader.Read(msg) -} - -func (frame *hixieLengthFrameReader) PayloadType() byte { - if frame.FrameType == '\xff' && frame.Length == 0 { - return CloseFrame - } - return UnknownFrame -} - -func (frame *hixieLengthFrameReader) HeaderReader() io.Reader { - if frame.header == nil { - return nil - } - if frame.header.Len() == 0 { - frame.header = nil - return nil - } - return frame.header -} - -func (frame *hixieLengthFrameReader) TrailerReader() io.Reader { return nil } - -func (frame *hixieLengthFrameReader) Len() (n int) { return frame.length } - -// A HixieSentinelFrameReader is a reader for frame type 0x00-0x7F -// as defined in hixie draft. -type hixieSentinelFrameReader struct { - reader *bufio.Reader - FrameType byte - header *bytes.Buffer - data []byte - seenTrailer bool - trailer *bytes.Buffer -} - -func (frame *hixieSentinelFrameReader) Read(msg []byte) (n int, err error) { - if len(frame.data) == 0 { - if frame.seenTrailer { - return 0, io.EOF - } - frame.data, err = frame.reader.ReadSlice('\xff') - if err == nil { - frame.seenTrailer = true - frame.data = frame.data[:len(frame.data)-1] // trim \xff - frame.trailer = bytes.NewBuffer([]byte{0xff}) - } - } - n = copy(msg, frame.data) - frame.data = frame.data[n:] - return n, err -} - -func (frame *hixieSentinelFrameReader) PayloadType() byte { - if frame.FrameType == 0 { - return TextFrame - } - return UnknownFrame -} - -func (frame *hixieSentinelFrameReader) HeaderReader() io.Reader { - if frame.header == nil { - return nil - } - if frame.header.Len() == 0 { - frame.header = nil - return nil - } - return frame.header -} - -func (frame *hixieSentinelFrameReader) TrailerReader() io.Reader { - if frame.trailer == nil { - return nil - } - if frame.trailer.Len() == 0 { - frame.trailer = nil - return nil - } - return frame.trailer -} - -func (frame *hixieSentinelFrameReader) Len() int { return -1 } - -// A HixieFrameReaderFactory creates new frame reader based on its frame type. -type hixieFrameReaderFactory struct { - *bufio.Reader -} - -func (buf hixieFrameReaderFactory) NewFrameReader() (r frameReader, err error) { - var header []byte - var b byte - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - if b&0x80 == 0x80 { - length, lengthFields, err := readHixieLength(buf.Reader) - if err != nil { - return nil, err - } - if length == 0 { - return nil, io.EOF - } - header = append(header, lengthFields...) - return &hixieLengthFrameReader{ - reader: io.LimitReader(buf.Reader, length), - FrameType: b, - Length: length, - header: bytes.NewBuffer(header)}, err - } - return &hixieSentinelFrameReader{ - reader: buf.Reader, - FrameType: b, - header: bytes.NewBuffer(header)}, err -} - -type hixiFrameWriter struct { - writer *bufio.Writer -} - -func (frame *hixiFrameWriter) Write(msg []byte) (n int, err error) { - frame.writer.WriteByte(0) - frame.writer.Write(msg) - frame.writer.WriteByte(0xff) - err = frame.writer.Flush() - return len(msg), err -} - -func (frame *hixiFrameWriter) Close() error { return nil } - -type hixiFrameWriterFactory struct { - *bufio.Writer -} - -func (buf hixiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { - if payloadType != TextFrame { - return nil, ErrNotSupported - } - return &hixiFrameWriter{writer: buf.Writer}, nil -} - -type hixiFrameHandler struct { - conn *Conn -} - -func (handler *hixiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { - if header := frame.HeaderReader(); header != nil { - io.Copy(ioutil.Discard, header) - } - if frame.PayloadType() != TextFrame { - io.Copy(ioutil.Discard, frame) - return nil, nil - } - return frame, nil -} - -func (handler *hixiFrameHandler) WriteClose(_ int) (err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - closingFrame := []byte{'\xff', '\x00'} - handler.conn.buf.Write(closingFrame) - return handler.conn.buf.Flush() -} - -// newHixiConn creates a new WebSocket connection speaking hixie draft protocol. -func newHixieConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - if buf == nil { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - buf = bufio.NewReadWriter(br, bw) - } - ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, - frameReaderFactory: hixieFrameReaderFactory{buf.Reader}, - frameWriterFactory: hixiFrameWriterFactory{buf.Writer}, - PayloadType: TextFrame} - ws.frameHandler = &hixiFrameHandler{ws} - return ws -} - -// getChallengeResponse computes the expected response from the -// challenge as described in section 5.1 Opening Handshake steps 42 to -// 43 of http://www.whatwg.org/specs/web-socket-protocol/ -func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte, err error) { - // 41. Let /challenge/ be the concatenation of /number_1/, expressed - // a big-endian 32 bit integer, /number_2/, expressed in a big- - // endian 32 bit integer, and the eight bytes of /key_3/ in the - // order they were sent to the wire. - challenge := make([]byte, 16) - binary.BigEndian.PutUint32(challenge[0:], number1) - binary.BigEndian.PutUint32(challenge[4:], number2) - copy(challenge[8:], key3) - - // 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big- - // endian 128 bit string. - h := md5.New() - if _, err = h.Write(challenge); err != nil { - return - } - expected = h.Sum(nil) - return -} - -// Generates handshake key as described in 4.1 Opening handshake step 16 to 22. -// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func generateKeyNumber() (key string, number uint32) { - // 16. Let /spaces_n/ be a random integer from 1 to 12 inclusive. - spaces := rand.Intn(12) + 1 - - // 17. Let /max_n/ be the largest integer not greater than - // 4,294,967,295 divided by /spaces_n/ - max := int(4294967295 / uint32(spaces)) - - // 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive. - number = uint32(rand.Intn(max + 1)) - - // 19. Let /product_n/ be the result of multiplying /number_n/ and - // /spaces_n/ together. - product := number * uint32(spaces) - - // 20. Let /key_n/ be a string consisting of /product_n/, expressed - // in base ten using the numerals in the range U+0030 DIGIT ZERO (0) - // to U+0039 DIGIT NINE (9). - key = fmt.Sprintf("%d", product) - - // 21. Insert between one and twelve random characters from the ranges - // U+0021 to U+002F and U+003A to U+007E into /key_n/ at random - // positions. - n := rand.Intn(12) + 1 - for i := 0; i < n; i++ { - pos := rand.Intn(len(key)) + 1 - ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))] - key = key[0:pos] + string(ch) + key[pos:] - } - - // 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random - // positions other than the start or end of the string. - for i := 0; i < spaces; i++ { - pos := rand.Intn(len(key)-1) + 1 - key = key[0:pos] + " " + key[pos:] - } - - return -} - -// Generates handshake key_3 as described in 4.1 Opening handshake step 26. -// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func generateKey3() (key []byte) { - // 26. Let /key3/ be a string consisting of eight random bytes (or - // equivalently, a random 64 bit integer encoded in big-endian order). - key = make([]byte, 8) - for i := 0; i < 8; i++ { - key[i] = byte(rand.Intn(256)) - } - return -} - -// Cilent handhake described in (soon obsolete) -// draft-ietf-hybi-thewebsocket-protocol-00 -// (draft-hixie-thewebsocket-protocol-76) -func hixie76ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - switch config.Version { - case ProtocolVersionHixie76, ProtocolVersionHybi00: - default: - panic("wrong protocol version.") - } - // 4.1. Opening handshake. - // Step 5. send a request line. - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - - // Step 6-14. push request headers in fields. - fields := []string{ - "Upgrade: WebSocket\r\n", - "Connection: Upgrade\r\n", - "Host: " + config.Location.Host + "\r\n", - "Origin: " + config.Origin.String() + "\r\n", - } - if len(config.Protocol) > 0 { - if len(config.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - fields = append(fields, "Sec-WebSocket-Protocol: "+config.Protocol[0]+"\r\n") - } - // TODO(ukai): Step 15. send cookie if any. - - // Step 16-23. generate keys and push Sec-WebSocket-Key in fields. - key1, number1 := generateKeyNumber() - key2, number2 := generateKeyNumber() - if config.handshakeData != nil { - key1 = config.handshakeData["key1"] - n, err := strconv.ParseUint(config.handshakeData["number1"], 10, 32) - if err != nil { - panic(err) - } - number1 = uint32(n) - key2 = config.handshakeData["key2"] - n, err = strconv.ParseUint(config.handshakeData["number2"], 10, 32) - if err != nil { - panic(err) - } - number2 = uint32(n) - } - fields = append(fields, "Sec-WebSocket-Key1: "+key1+"\r\n") - fields = append(fields, "Sec-WebSocket-Key2: "+key2+"\r\n") - - // Step 24. shuffle fields and send them out. - for i := 1; i < len(fields); i++ { - j := rand.Intn(i) - fields[i], fields[j] = fields[j], fields[i] - } - for i := 0; i < len(fields); i++ { - bw.WriteString(fields[i]) - } - // Step 25. send CRLF. - bw.WriteString("\r\n") - - // Step 26. generate 8 bytes random key. - key3 := generateKey3() - if config.handshakeData != nil { - key3 = []byte(config.handshakeData["key3"]) - } - // Step 27. send it out. - bw.Write(key3) - if err = bw.Flush(); err != nil { - return - } - - // Step 28-29, 32-40. read response from server. - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return err - } - // Step 30. check response code is 101. - if resp.StatusCode != 101 { - return ErrBadStatus - } - - // Step 41. check websocket headers. - if resp.Header.Get("Upgrade") != "WebSocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return ErrBadUpgrade - } - - if resp.Header.Get("Sec-Websocket-Origin") != config.Origin.String() { - return ErrBadWebSocketOrigin - } - - if resp.Header.Get("Sec-Websocket-Location") != config.Location.String() { - return ErrBadWebSocketLocation - } - - if len(config.Protocol) > 0 && resp.Header.Get("Sec-Websocket-Protocol") != config.Protocol[0] { - return ErrBadWebSocketProtocol - } - - // Step 42-43. get expected data from challenge data. - expected, err := getChallengeResponse(number1, number2, key3) - if err != nil { - return err - } - - // Step 44. read 16 bytes from server. - reply := make([]byte, 16) - if _, err = io.ReadFull(br, reply); err != nil { - return err - } - - // Step 45. check the reply equals to expected data. - if !bytes.Equal(expected, reply) { - return ErrChallengeResponse - } - // WebSocket connection is established. - return -} - -// Client Handshake described in (soon obsolete) -// draft-hixie-thewebsocket-protocol-75. -func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - if config.Version != ProtocolVersionHixie75 { - panic("wrong protocol version.") - } - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - bw.WriteString("Upgrade: WebSocket\r\n") - bw.WriteString("Connection: Upgrade\r\n") - bw.WriteString("Host: " + config.Location.Host + "\r\n") - bw.WriteString("Origin: " + config.Origin.String() + "\r\n") - if len(config.Protocol) > 0 { - if len(config.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - bw.WriteString("WebSocket-Protocol: " + config.Protocol[0] + "\r\n") - } - bw.WriteString("\r\n") - bw.Flush() - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return - } - if resp.Status != "101 Web Socket Protocol Handshake" { - return ErrBadStatus - } - if resp.Header.Get("Upgrade") != "WebSocket" || - resp.Header.Get("Connection") != "Upgrade" { - return ErrBadUpgrade - } - if resp.Header.Get("Websocket-Origin") != config.Origin.String() { - return ErrBadWebSocketOrigin - } - if resp.Header.Get("Websocket-Location") != config.Location.String() { - return ErrBadWebSocketLocation - } - if len(config.Protocol) > 0 && resp.Header.Get("Websocket-Protocol") != config.Protocol[0] { - return ErrBadWebSocketProtocol - } - return -} - -// newHixieClientConn returns new WebSocket connection speaking hixie draft protocol. -func newHixieClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { - return newHixieConn(config, buf, rwc, nil) -} - -// Gets key number from Sec-WebSocket-Key: field as described -// in 5.2 Sending the server's opening handshake, 4. -func getKeyNumber(s string) (r uint32) { - // 4. Let /key-number_n/ be the digits (characters in the range - // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/, - // interpreted as a base ten integer, ignoring all other characters - // in /key_n/. - r = 0 - for i := 0; i < len(s); i++ { - if s[i] >= '0' && s[i] <= '9' { - r = r*10 + uint32(s[i]) - '0' - } - } - return -} - -// A Hixie76ServerHandshaker performs a server handshake using -// hixie draft 76 protocol. -type hixie76ServerHandshaker struct { - *Config - challengeResponse []byte -} - -func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHybi00 - if req.Method != "GET" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - // HTTP version can be safely ignored. - - if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(req.Header.Get("Connection")) != "upgrade" { - return http.StatusBadRequest, ErrNotWebSocket - } - - // TODO(ukai): check Host - c.Origin, err = url.ParseRequestURI(req.Header.Get("Origin")) - if err != nil { - return http.StatusBadRequest, err - } - - key1 := req.Header.Get("Sec-Websocket-Key1") - if key1 == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - key2 := req.Header.Get("Sec-Websocket-Key2") - if key2 == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - key3 := make([]byte, 8) - if _, err := io.ReadFull(buf, key3); err != nil { - return http.StatusBadRequest, ErrChallengeResponse - } - - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - - // Step 4. get key number in Sec-WebSocket-Key fields. - keyNumber1 := getKeyNumber(key1) - keyNumber2 := getKeyNumber(key2) - - // Step 5. get number of spaces in Sec-WebSocket-Key fields. - space1 := uint32(strings.Count(key1, " ")) - space2 := uint32(strings.Count(key2, " ")) - if space1 == 0 || space2 == 0 { - return http.StatusBadRequest, ErrChallengeResponse - } - - // Step 6. key number must be an integral multiple of spaces. - if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 { - return http.StatusBadRequest, ErrChallengeResponse - } - - // Step 7. let part be key number divided by spaces. - part1 := keyNumber1 / space1 - part2 := keyNumber2 / space2 - - // Step 8. let challenge be concatenation of part1, part2 and key3. - // Step 9. get MD5 fingerprint of challenge. - c.challengeResponse, err = getChallengeResponse(part1, part2, key3) - if err != nil { - return http.StatusInternalServerError, err - } - protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - - return http.StatusSwitchingProtocols, nil -} - -func (c *hixie76ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - - // Step 10. send response status line. - buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n") - // Step 11. send response headers. - buf.WriteString("Upgrade: WebSocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("Sec-WebSocket-Origin: " + c.Origin.String() + "\r\n") - buf.WriteString("Sec-WebSocket-Location: " + c.Location.String() + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - // Step 12. send CRLF. - buf.WriteString("\r\n") - // Step 13. send response data. - buf.Write(c.challengeResponse) - return buf.Flush() -} - -func (c *hixie76ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) { - return newHixieServerConn(c.Config, buf, rwc, request) -} - -// A hixie75ServerHandshaker performs a server handshake using -// hixie draft 75 protocol. -type hixie75ServerHandshaker struct { - *Config -} - -func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHixie75 - if req.Method != "GET" || req.Proto != "HTTP/1.1" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - if req.Header.Get("Upgrade") != "WebSocket" { - return http.StatusBadRequest, ErrNotWebSocket - } - if req.Header.Get("Connection") != "Upgrade" { - return http.StatusBadRequest, ErrNotWebSocket - } - c.Origin, err = url.ParseRequestURI(strings.TrimSpace(req.Header.Get("Origin"))) - if err != nil { - return http.StatusBadRequest, err - } - - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - - return http.StatusSwitchingProtocols, nil -} - -func (c *hixie75ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - - buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n") - buf.WriteString("Upgrade: WebSocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("WebSocket-Origin: " + c.Origin.String() + "\r\n") - buf.WriteString("WebSocket-Location: " + c.Location.String() + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - buf.WriteString("\r\n") - return buf.Flush() -} - -func (c *hixie75ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) { - return newHixieServerConn(c.Config, buf, rwc, request) -} - -// newHixieServerConn returns a new WebSocket connection speaking hixie draft protocol. -func newHixieServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHixieConn(config, buf, rwc, request) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go deleted file mode 100644 index 8f387dd..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" -) - -// Test the getChallengeResponse function with values from section -// 5.1 of the specification steps 18, 26, and 43 from -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -func TestHixie76Challenge(t *testing.T) { - var part1 uint32 = 777007543 - var part2 uint32 = 114997259 - key3 := []byte{0x47, 0x30, 0x22, 0x2D, 0x5A, 0x3F, 0x47, 0x58} - expected := []byte("0st3Rl&q-2ZU^weu") - - response, err := getChallengeResponse(part1, part2, key3) - if err != nil { - t.Errorf("getChallengeResponse: returned error %v", err) - return - } - if !bytes.Equal(expected, response) { - t.Errorf("getChallengeResponse: expected %q got %q", expected, response) - } -} - -func TestHixie76ClientHandshake(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 WebSocket Protocol Handshake -Upgrade: WebSocket -Connection: Upgrade -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Location: ws://example.com/demo -Sec-WebSocket-Protocol: sample - -8jKS'y:G*Co,Wxa-`)) - - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://example.com/demo") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "sample") - config.Version = ProtocolVersionHixie76 - - config.handshakeData = map[string]string{ - "key1": "4 @1 46546xW%0l 1 5", - "number1": "829309203", - "key2": "12998 5 Y3 1 .P00", - "number2": "259970620", - "key3": "^n:ds[4U", - } - err = hixie76ClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/demo" { - t.Errorf("request path expected /demo, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "example.com" { - t.Errorf("request Host expected example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "WebSocket", - "Origin": "http://example.com", - "Sec-Websocket-Key1": config.handshakeData["key1"], - "Sec-Websocket-Key2": config.handshakeData["key2"], - "Sec-WebSocket-Protocol": config.Protocol[0], - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHixie76ServerHandshake(t *testing.T) { - config := new(Config) - handshaker := &hixie76ServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /demo HTTP/1.1 -Host: example.com -Connection: Upgrade -Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 -Sec-WebSocket-Protocol: sample -Upgrade: WebSocket -Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 -Origin: http://example.com - -^n:ds[4U`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 WebSocket Protocol Handshake", - "Upgrade: WebSocket", - "Connection: Upgrade", - "Sec-WebSocket-Origin: http://example.com", - "Sec-WebSocket-Location: ws://example.com/demo", - "Sec-WebSocket-Protocol: sample", - "", ""}, "\r\n") + "8jKS'y:G*Co,Wxa-" - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHixie76SkipLengthFrame(t *testing.T) { - b := []byte{'\x80', '\x01', 'x', 0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(b[4:9], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n]) - } -} - -func TestHixie76SkipNoUTF8Frame(t *testing.T) { - b := []byte{'\x01', 'n', '\xff', 0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(b[4:9], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[4:9], msg[0:n]) - } -} - -func TestHixie76ClosingFrame(t *testing.T) { - b := []byte{0, 'h', 'e', 'l', 'l', 'o', '\xff'} - buf := bytes.NewBuffer(b) - br := bufio.NewReader(buf) - bw := bufio.NewWriter(buf) - config := newConfig(t, "/") - ws := newHixieConn(config, bufio.NewReadWriter(br, bw), nil, nil) - msg := make([]byte, 5) - n, err := ws.Read(msg) - if err != nil { - t.Errorf("read: %v", err) - } - if !bytes.Equal(b[1:6], msg[0:n]) { - t.Errorf("Read: expected %q got %q", b[1:6], msg[0:n]) - } - n, err = ws.Read(msg) - if err != io.EOF { - t.Errorf("read: %v", err) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go deleted file mode 100644 index ab18ffc..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -// This file implements a protocol of hybi draft. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 - -import ( - "bufio" - "bytes" - "crypto/rand" - "crypto/sha1" - "encoding/base64" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "strings" -) - -const ( - websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - closeStatusNormal = 1000 - closeStatusGoingAway = 1001 - closeStatusProtocolError = 1002 - closeStatusUnsupportedData = 1003 - closeStatusFrameTooLarge = 1004 - closeStatusNoStatusRcvd = 1005 - closeStatusAbnormalClosure = 1006 - closeStatusBadMessageData = 1007 - closeStatusPolicyViolation = 1008 - closeStatusTooBigData = 1009 - closeStatusExtensionMismatch = 1010 - - maxControlFramePayloadLength = 125 -) - -var ( - ErrBadMaskingKey = &ProtocolError{"bad masking key"} - ErrBadPongMessage = &ProtocolError{"bad pong message"} - ErrBadClosingStatus = &ProtocolError{"bad closing status"} - ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"} - ErrNotImplemented = &ProtocolError{"not implemented"} -) - -// A hybiFrameHeader is a frame header as defined in hybi draft. -type hybiFrameHeader struct { - Fin bool - Rsv [3]bool - OpCode byte - Length int64 - MaskingKey []byte - - data *bytes.Buffer -} - -// A hybiFrameReader is a reader for hybi frame. -type hybiFrameReader struct { - reader io.Reader - - header hybiFrameHeader - pos int64 - length int -} - -func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) { - n, err = frame.reader.Read(msg) - if err != nil { - return 0, err - } - if frame.header.MaskingKey != nil { - for i := 0; i < n; i++ { - msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] - frame.pos++ - } - } - return n, err -} - -func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode } - -func (frame *hybiFrameReader) HeaderReader() io.Reader { - if frame.header.data == nil { - return nil - } - if frame.header.data.Len() == 0 { - return nil - } - return frame.header.data -} - -func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil } - -func (frame *hybiFrameReader) Len() (n int) { return frame.length } - -// A hybiFrameReaderFactory creates new frame reader based on its frame type. -type hybiFrameReaderFactory struct { - *bufio.Reader -} - -// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. -// See Section 5.2 Base Frameing protocol for detail. -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 -func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) { - hybiFrame := new(hybiFrameReader) - frame = hybiFrame - var header []byte - var b byte - // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 - for i := 0; i < 3; i++ { - j := uint(6 - i) - hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 - } - hybiFrame.header.OpCode = header[0] & 0x0f - - // Second byte. Mask/Payload len(7bits) - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - mask := (b & 0x80) != 0 - b &= 0x7f - lengthFields := 0 - switch { - case b <= 125: // Payload length 7bits. - hybiFrame.header.Length = int64(b) - case b == 126: // Payload length 7+16bits - lengthFields = 2 - case b == 127: // Payload length 7+64bits - lengthFields = 8 - } - for i := 0; i < lengthFields; i++ { - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) - } - if mask { - // Masking key. 4 bytes. - for i := 0; i < 4; i++ { - b, err = buf.ReadByte() - if err != nil { - return - } - header = append(header, b) - hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) - } - } - hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) - hybiFrame.header.data = bytes.NewBuffer(header) - hybiFrame.length = len(header) + int(hybiFrame.header.Length) - return -} - -// A HybiFrameWriter is a writer for hybi frame. -type hybiFrameWriter struct { - writer *bufio.Writer - - header *hybiFrameHeader -} - -func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) { - var header []byte - var b byte - if frame.header.Fin { - b |= 0x80 - } - for i := 0; i < 3; i++ { - if frame.header.Rsv[i] { - j := uint(6 - i) - b |= 1 << j - } - } - b |= frame.header.OpCode - header = append(header, b) - if frame.header.MaskingKey != nil { - b = 0x80 - } else { - b = 0 - } - lengthFields := 0 - length := len(msg) - switch { - case length <= 125: - b |= byte(length) - case length < 65536: - b |= 126 - lengthFields = 2 - default: - b |= 127 - lengthFields = 8 - } - header = append(header, b) - for i := 0; i < lengthFields; i++ { - j := uint((lengthFields - i - 1) * 8) - b = byte((length >> j) & 0xff) - header = append(header, b) - } - if frame.header.MaskingKey != nil { - if len(frame.header.MaskingKey) != 4 { - return 0, ErrBadMaskingKey - } - header = append(header, frame.header.MaskingKey...) - frame.writer.Write(header) - var data []byte - - for i := 0; i < length; i++ { - data = append(data, msg[i]^frame.header.MaskingKey[i%4]) - } - frame.writer.Write(data) - err = frame.writer.Flush() - return length, err - } - frame.writer.Write(header) - frame.writer.Write(msg) - err = frame.writer.Flush() - return length, err -} - -func (frame *hybiFrameWriter) Close() error { return nil } - -type hybiFrameWriterFactory struct { - *bufio.Writer - needMaskingKey bool -} - -func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType} - if buf.needMaskingKey { - frameHeader.MaskingKey, err = generateMaskingKey() - if err != nil { - return nil, err - } - } - return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil -} - -type hybiFrameHandler struct { - conn *Conn - payloadType byte -} - -func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) { - if handler.conn.IsServerConn() { - // The client MUST mask all frames sent to the server. - if frame.(*hybiFrameReader).header.MaskingKey == nil { - handler.WriteClose(closeStatusProtocolError) - return nil, io.EOF - } - } else { - // The server MUST NOT mask all frames. - if frame.(*hybiFrameReader).header.MaskingKey != nil { - handler.WriteClose(closeStatusProtocolError) - return nil, io.EOF - } - } - if header := frame.HeaderReader(); header != nil { - io.Copy(ioutil.Discard, header) - } - switch frame.PayloadType() { - case ContinuationFrame: - frame.(*hybiFrameReader).header.OpCode = handler.payloadType - case TextFrame, BinaryFrame: - handler.payloadType = frame.PayloadType() - case CloseFrame: - return nil, io.EOF - case PingFrame: - pingMsg := make([]byte, maxControlFramePayloadLength) - n, err := io.ReadFull(frame, pingMsg) - if err != nil && err != io.ErrUnexpectedEOF { - return nil, err - } - io.Copy(ioutil.Discard, frame) - n, err = handler.WritePong(pingMsg[:n]) - if err != nil { - return nil, err - } - return nil, nil - case PongFrame: - return nil, ErrNotImplemented - } - return frame, nil -} - -func (handler *hybiFrameHandler) WriteClose(status int) (err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) - if err != nil { - return err - } - msg := make([]byte, 2) - binary.BigEndian.PutUint16(msg, uint16(status)) - _, err = w.Write(msg) - w.Close() - return err -} - -func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) { - handler.conn.wio.Lock() - defer handler.conn.wio.Unlock() - w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) - if err != nil { - return 0, err - } - n, err = w.Write(msg) - w.Close() - return n, err -} - -// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. -func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - if buf == nil { - br := bufio.NewReader(rwc) - bw := bufio.NewWriter(rwc) - buf = bufio.NewReadWriter(br, bw) - } - ws := &Conn{config: config, request: request, buf: buf, rwc: rwc, - frameReaderFactory: hybiFrameReaderFactory{buf.Reader}, - frameWriterFactory: hybiFrameWriterFactory{ - buf.Writer, request == nil}, - PayloadType: TextFrame, - defaultCloseStatus: closeStatusNormal} - ws.frameHandler = &hybiFrameHandler{conn: ws} - return ws -} - -// generateMaskingKey generates a masking key for a frame. -func generateMaskingKey() (maskingKey []byte, err error) { - maskingKey = make([]byte, 4) - if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil { - return - } - return -} - -// genetateNonce geneates a nonce consisting of a randomly selected 16-byte -// value that has been base64-encoded. -func generateNonce() (nonce []byte) { - key := make([]byte, 16) - if _, err := io.ReadFull(rand.Reader, key); err != nil { - panic(err) - } - nonce = make([]byte, 24) - base64.StdEncoding.Encode(nonce, key) - return -} - -// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of -// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string. -func getNonceAccept(nonce []byte) (expected []byte, err error) { - h := sha1.New() - if _, err = h.Write(nonce); err != nil { - return - } - if _, err = h.Write([]byte(websocketGUID)); err != nil { - return - } - expected = make([]byte, 28) - base64.StdEncoding.Encode(expected, h.Sum(nil)) - return -} - -func isHybiVersion(version int) bool { - switch version { - case ProtocolVersionHybi08, ProtocolVersionHybi13: - return true - default: - } - return false -} - -// Client handhake described in draft-ietf-hybi-thewebsocket-protocol-17 -func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) { - if !isHybiVersion(config.Version) { - panic("wrong protocol version.") - } - - bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n") - - bw.WriteString("Host: " + config.Location.Host + "\r\n") - bw.WriteString("Upgrade: websocket\r\n") - bw.WriteString("Connection: Upgrade\r\n") - nonce := generateNonce() - if config.handshakeData != nil { - nonce = []byte(config.handshakeData["key"]) - } - bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n") - if config.Version == ProtocolVersionHybi13 { - bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") - } else if config.Version == ProtocolVersionHybi08 { - bw.WriteString("Sec-WebSocket-Origin: " + strings.ToLower(config.Origin.String()) + "\r\n") - } - bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n") - if len(config.Protocol) > 0 { - bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n") - } - // TODO(ukai): send extensions. - // TODO(ukai): send cookie if any. - - bw.WriteString("\r\n") - if err = bw.Flush(); err != nil { - return err - } - - resp, err := http.ReadResponse(br, &http.Request{Method: "GET"}) - if err != nil { - return err - } - if resp.StatusCode != 101 { - return ErrBadStatus - } - if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" || - strings.ToLower(resp.Header.Get("Connection")) != "upgrade" { - return ErrBadUpgrade - } - expectedAccept, err := getNonceAccept(nonce) - if err != nil { - return err - } - if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) { - return ErrChallengeResponse - } - if resp.Header.Get("Sec-WebSocket-Extensions") != "" { - return ErrUnsupportedExtensions - } - offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol") - if offeredProtocol != "" { - protocolMatched := false - for i := 0; i < len(config.Protocol); i++ { - if config.Protocol[i] == offeredProtocol { - protocolMatched = true - break - } - } - if !protocolMatched { - return ErrBadWebSocketProtocol - } - config.Protocol = []string{offeredProtocol} - } - - return nil -} - -// newHybiClientConn creates a client WebSocket connection after handshake. -func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn { - return newHybiConn(config, buf, rwc, nil) -} - -// A HybiServerHandshaker performs a server handshake using hybi draft protocol. -type hybiServerHandshaker struct { - *Config - accept []byte -} - -func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) { - c.Version = ProtocolVersionHybi13 - if req.Method != "GET" { - return http.StatusMethodNotAllowed, ErrBadRequestMethod - } - // HTTP version can be safely ignored. - - if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" || - !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") { - return http.StatusBadRequest, ErrNotWebSocket - } - - key := req.Header.Get("Sec-Websocket-Key") - if key == "" { - return http.StatusBadRequest, ErrChallengeResponse - } - version := req.Header.Get("Sec-Websocket-Version") - var origin string - switch version { - case "13": - c.Version = ProtocolVersionHybi13 - origin = req.Header.Get("Origin") - case "8": - c.Version = ProtocolVersionHybi08 - origin = req.Header.Get("Sec-Websocket-Origin") - default: - return http.StatusBadRequest, ErrBadWebSocketVersion - } - c.Origin, err = url.ParseRequestURI(origin) - if err != nil { - return http.StatusForbidden, err - } - var scheme string - if req.TLS != nil { - scheme = "wss" - } else { - scheme = "ws" - } - c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) - if err != nil { - return http.StatusBadRequest, err - } - protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol")) - protocols := strings.Split(protocol, ",") - for i := 0; i < len(protocols); i++ { - c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) - } - c.accept, err = getNonceAccept([]byte(key)) - if err != nil { - return http.StatusInternalServerError, err - } - return http.StatusSwitchingProtocols, nil -} - -func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) { - if len(c.Protocol) > 0 { - if len(c.Protocol) != 1 { - return ErrBadWebSocketProtocol - } - } - buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n") - buf.WriteString("Upgrade: websocket\r\n") - buf.WriteString("Connection: Upgrade\r\n") - buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n") - if len(c.Protocol) > 0 { - buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n") - } - // TODO(ukai): support extensions - buf.WriteString("\r\n") - return buf.Flush() -} - -func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHybiServerConn(c.Config, buf, rwc, request) -} - -// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. -func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn { - return newHybiConn(config, buf, rwc, request) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go deleted file mode 100644 index 7976054..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go +++ /dev/null @@ -1,584 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "bytes" - "fmt" - "io" - "net/http" - "net/url" - "strings" - "testing" -) - -// Test the getNonceAccept function with values in -// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 -func TestSecWebSocketAccept(t *testing.T) { - nonce := []byte("dGhlIHNhbXBsZSBub25jZQ==") - expected := []byte("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=") - accept, err := getNonceAccept(nonce) - if err != nil { - t.Errorf("getNonceAccept: returned error %v", err) - return - } - if !bytes.Equal(expected, accept) { - t.Errorf("getNonceAccept: expected %q got %q", expected, accept) - } -} - -func TestHybiClientHandshake(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= -Sec-WebSocket-Protocol: chat - -`)) - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "chat") - config.Protocol = append(config.Protocol, "superchat") - config.Version = ProtocolVersionHybi13 - - config.handshakeData = map[string]string{ - "key": "dGhlIHNhbXBsZSBub25jZQ==", - } - err = hybiClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/chat" { - t.Errorf("request path expected /chat, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "server.example.com" { - t.Errorf("request Host expected server.example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "websocket", - "Sec-Websocket-Key": config.handshakeData["key"], - "Origin": config.Origin.String(), - "Sec-Websocket-Protocol": "chat, superchat", - "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi13), - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHybiClientHandshakeHybi08(t *testing.T) { - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - br := bufio.NewReader(strings.NewReader(`HTTP/1.1 101 Switching Protocols -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= -Sec-WebSocket-Protocol: chat - -`)) - var err error - config := new(Config) - config.Location, err = url.ParseRequestURI("ws://server.example.com/chat") - if err != nil { - t.Fatal("location url", err) - } - config.Origin, err = url.ParseRequestURI("http://example.com") - if err != nil { - t.Fatal("origin url", err) - } - config.Protocol = append(config.Protocol, "chat") - config.Protocol = append(config.Protocol, "superchat") - config.Version = ProtocolVersionHybi08 - - config.handshakeData = map[string]string{ - "key": "dGhlIHNhbXBsZSBub25jZQ==", - } - err = hybiClientHandshake(config, br, bw) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - req, err := http.ReadRequest(bufio.NewReader(b)) - if err != nil { - t.Fatalf("read request: %v", err) - } - if req.Method != "GET" { - t.Errorf("request method expected GET, but got %q", req.Method) - } - if req.URL.Path != "/chat" { - t.Errorf("request path expected /demo, but got %q", req.URL.Path) - } - if req.Proto != "HTTP/1.1" { - t.Errorf("request proto expected HTTP/1.1, but got %q", req.Proto) - } - if req.Host != "server.example.com" { - t.Errorf("request Host expected example.com, but got %v", req.Host) - } - var expectedHeader = map[string]string{ - "Connection": "Upgrade", - "Upgrade": "websocket", - "Sec-Websocket-Key": config.handshakeData["key"], - "Sec-Websocket-Origin": config.Origin.String(), - "Sec-Websocket-Protocol": "chat, superchat", - "Sec-Websocket-Version": fmt.Sprintf("%d", ProtocolVersionHybi08), - } - for k, v := range expectedHeader { - if req.Header.Get(k) != v { - t.Errorf(fmt.Sprintf("%s expected %q but got %q", k, v, req.Header.Get(k))) - } - } -} - -func TestHybiServerHandshake(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 13 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHybiServerHandshakeHybi08(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 8 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} - -func TestHybiServerHandshakeHybiBadVersion(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: Upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Sec-WebSocket-Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 9 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != ErrBadWebSocketVersion { - t.Errorf("handshake expected err %q but got %q", ErrBadWebSocketVersion, err) - } - if code != http.StatusBadRequest { - t.Errorf("status expected %q but got %q", http.StatusBadRequest, code) - } -} - -func testHybiFrame(t *testing.T, testHeader, testPayload, testMaskedPayload []byte, frameHeader *hybiFrameHeader) { - b := bytes.NewBuffer([]byte{}) - frameWriterFactory := &hybiFrameWriterFactory{bufio.NewWriter(b), false} - w, _ := frameWriterFactory.NewFrameWriter(TextFrame) - w.(*hybiFrameWriter).header = frameHeader - _, err := w.Write(testPayload) - w.Close() - if err != nil { - t.Errorf("Write error %q", err) - } - var expectedFrame []byte - expectedFrame = append(expectedFrame, testHeader...) - expectedFrame = append(expectedFrame, testMaskedPayload...) - if !bytes.Equal(expectedFrame, b.Bytes()) { - t.Errorf("frame expected %q got %q", expectedFrame, b.Bytes()) - } - frameReaderFactory := &hybiFrameReaderFactory{bufio.NewReader(b)} - r, err := frameReaderFactory.NewFrameReader() - if err != nil { - t.Errorf("Read error %q", err) - } - if header := r.HeaderReader(); header == nil { - t.Errorf("no header") - } else { - actualHeader := make([]byte, r.Len()) - n, err := header.Read(actualHeader) - if err != nil { - t.Errorf("Read header error %q", err) - } else { - if n < len(testHeader) { - t.Errorf("header too short %q got %q", testHeader, actualHeader[:n]) - } - if !bytes.Equal(testHeader, actualHeader[:n]) { - t.Errorf("header expected %q got %q", testHeader, actualHeader[:n]) - } - } - } - if trailer := r.TrailerReader(); trailer != nil { - t.Errorf("unexpected trailer %q", trailer) - } - frame := r.(*hybiFrameReader) - if frameHeader.Fin != frame.header.Fin || - frameHeader.OpCode != frame.header.OpCode || - len(testPayload) != int(frame.header.Length) { - t.Errorf("mismatch %v (%d) vs %v", frameHeader, len(testPayload), frame) - } - payload := make([]byte, len(testPayload)) - _, err = r.Read(payload) - if err != nil { - t.Errorf("read %v", err) - } - if !bytes.Equal(testPayload, payload) { - t.Errorf("payload %q vs %q", testPayload, payload) - } -} - -func TestHybiShortTextFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} - payload := []byte("hello") - testHybiFrame(t, []byte{0x81, 0x05}, payload, payload, frameHeader) - - payload = make([]byte, 125) - testHybiFrame(t, []byte{0x81, 125}, payload, payload, frameHeader) -} - -func TestHybiShortMaskedTextFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame, - MaskingKey: []byte{0xcc, 0x55, 0x80, 0x20}} - payload := []byte("hello") - maskedPayload := []byte{0xa4, 0x30, 0xec, 0x4c, 0xa3} - header := []byte{0x81, 0x85} - header = append(header, frameHeader.MaskingKey...) - testHybiFrame(t, header, payload, maskedPayload, frameHeader) -} - -func TestHybiShortBinaryFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: BinaryFrame} - payload := []byte("hello") - testHybiFrame(t, []byte{0x82, 0x05}, payload, payload, frameHeader) - - payload = make([]byte, 125) - testHybiFrame(t, []byte{0x82, 125}, payload, payload, frameHeader) -} - -func TestHybiControlFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: PingFrame} - payload := []byte("hello") - testHybiFrame(t, []byte{0x89, 0x05}, payload, payload, frameHeader) - - frameHeader = &hybiFrameHeader{Fin: true, OpCode: PongFrame} - testHybiFrame(t, []byte{0x8A, 0x05}, payload, payload, frameHeader) - - frameHeader = &hybiFrameHeader{Fin: true, OpCode: CloseFrame} - payload = []byte{0x03, 0xe8} // 1000 - testHybiFrame(t, []byte{0x88, 0x02}, payload, payload, frameHeader) -} - -func TestHybiLongFrame(t *testing.T) { - frameHeader := &hybiFrameHeader{Fin: true, OpCode: TextFrame} - payload := make([]byte, 126) - testHybiFrame(t, []byte{0x81, 126, 0x00, 126}, payload, payload, frameHeader) - - payload = make([]byte, 65535) - testHybiFrame(t, []byte{0x81, 126, 0xff, 0xff}, payload, payload, frameHeader) - - payload = make([]byte, 65536) - testHybiFrame(t, []byte{0x81, 127, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}, payload, payload, frameHeader) -} - -func TestHybiClientRead(t *testing.T) { - wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', - 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping - 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) - - msg := make([]byte, 512) - n, err := conn.Read(msg) - if err != nil { - t.Errorf("read 1st frame, error %q", err) - } - if n != 5 { - t.Errorf("read 1st frame, expect 5, got %d", n) - } - if !bytes.Equal(wireData[2:7], msg[:n]) { - t.Errorf("read 1st frame %v, got %v", wireData[2:7], msg[:n]) - } - n, err = conn.Read(msg) - if err != nil { - t.Errorf("read 2nd frame, error %q", err) - } - if n != 5 { - t.Errorf("read 2nd frame, expect 5, got %d", n) - } - if !bytes.Equal(wireData[16:21], msg[:n]) { - t.Errorf("read 2nd frame %v, got %v", wireData[16:21], msg[:n]) - } - n, err = conn.Read(msg) - if err == nil { - t.Errorf("read not EOF") - } - if n != 0 { - t.Errorf("expect read 0, got %d", n) - } -} - -func TestHybiShortRead(t *testing.T) { - wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o', - 0x89, 0x05, 'h', 'e', 'l', 'l', 'o', // ping - 0x81, 0x05, 'w', 'o', 'r', 'l', 'd'} - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) - - step := 0 - pos := 0 - expectedPos := []int{2, 5, 16, 19} - expectedLen := []int{3, 2, 3, 2} - for { - msg := make([]byte, 3) - n, err := conn.Read(msg) - if step >= len(expectedPos) { - if err == nil { - t.Errorf("read not EOF") - } - if n != 0 { - t.Errorf("expect read 0, got %d", n) - } - return - } - pos = expectedPos[step] - endPos := pos + expectedLen[step] - if err != nil { - t.Errorf("read from %d, got error %q", pos, err) - return - } - if n != endPos-pos { - t.Errorf("read from %d, expect %d, got %d", pos, endPos-pos, n) - } - if !bytes.Equal(wireData[pos:endPos], msg[:n]) { - t.Errorf("read from %d, frame %v, got %v", pos, wireData[pos:endPos], msg[:n]) - } - step++ - } -} - -func TestHybiServerRead(t *testing.T) { - wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, - 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello - 0x89, 0x85, 0xcc, 0x55, 0x80, 0x20, - 0xa4, 0x30, 0xec, 0x4c, 0xa3, // ping: hello - 0x81, 0x85, 0xed, 0x83, 0xb4, 0x24, - 0x9a, 0xec, 0xc6, 0x48, 0x89, // world - } - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) - - expected := [][]byte{[]byte("hello"), []byte("world")} - - msg := make([]byte, 512) - n, err := conn.Read(msg) - if err != nil { - t.Errorf("read 1st frame, error %q", err) - } - if n != 5 { - t.Errorf("read 1st frame, expect 5, got %d", n) - } - if !bytes.Equal(expected[0], msg[:n]) { - t.Errorf("read 1st frame %q, got %q", expected[0], msg[:n]) - } - - n, err = conn.Read(msg) - if err != nil { - t.Errorf("read 2nd frame, error %q", err) - } - if n != 5 { - t.Errorf("read 2nd frame, expect 5, got %d", n) - } - if !bytes.Equal(expected[1], msg[:n]) { - t.Errorf("read 2nd frame %q, got %q", expected[1], msg[:n]) - } - - n, err = conn.Read(msg) - if err == nil { - t.Errorf("read not EOF") - } - if n != 0 { - t.Errorf("expect read 0, got %d", n) - } -} - -func TestHybiServerReadWithoutMasking(t *testing.T) { - wireData := []byte{0x81, 0x05, 'h', 'e', 'l', 'l', 'o'} - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, new(http.Request)) - // server MUST close the connection upon receiving a non-masked frame. - msg := make([]byte, 512) - _, err := conn.Read(msg) - if err != io.EOF { - t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) - } -} - -func TestHybiClientReadWithMasking(t *testing.T) { - wireData := []byte{0x81, 0x85, 0xcc, 0x55, 0x80, 0x20, - 0xa4, 0x30, 0xec, 0x4c, 0xa3, // hello - } - br := bufio.NewReader(bytes.NewBuffer(wireData)) - bw := bufio.NewWriter(bytes.NewBuffer([]byte{})) - conn := newHybiConn(newConfig(t, "/"), bufio.NewReadWriter(br, bw), nil, nil) - - // client MUST close the connection upon receiving a masked frame. - msg := make([]byte, 512) - _, err := conn.Read(msg) - if err != io.EOF { - t.Errorf("read 1st frame, expect %q, but got %q", io.EOF, err) - } -} - -// Test the hybiServerHandshaker supports firefox implementation and -// checks Connection request header include (but it's not necessary -// equal to) "upgrade" -func TestHybiServerFirefoxHandshake(t *testing.T) { - config := new(Config) - handshaker := &hybiServerHandshaker{Config: config} - br := bufio.NewReader(strings.NewReader(`GET /chat HTTP/1.1 -Host: server.example.com -Upgrade: websocket -Connection: keep-alive, upgrade -Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== -Origin: http://example.com -Sec-WebSocket-Protocol: chat, superchat -Sec-WebSocket-Version: 13 - -`)) - req, err := http.ReadRequest(br) - if err != nil { - t.Fatal("request", err) - } - code, err := handshaker.ReadHandshake(br, req) - if err != nil { - t.Errorf("handshake failed: %v", err) - } - if code != http.StatusSwitchingProtocols { - t.Errorf("status expected %q but got %q", http.StatusSwitchingProtocols, code) - } - b := bytes.NewBuffer([]byte{}) - bw := bufio.NewWriter(b) - - config.Protocol = []string{"chat"} - - err = handshaker.AcceptHandshake(bw) - if err != nil { - t.Errorf("handshake response failed: %v", err) - } - expectedResponse := strings.Join([]string{ - "HTTP/1.1 101 Switching Protocols", - "Upgrade: websocket", - "Connection: Upgrade", - "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", - "Sec-WebSocket-Protocol: chat", - "", ""}, "\r\n") - - if b.String() != expectedResponse { - t.Errorf("handshake expected %q but got %q", expectedResponse, b.String()) - } -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go deleted file mode 100644 index 63f48e2..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bufio" - "fmt" - "io" - "net/http" -) - -func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request) (conn *Conn, err error) { - config := new(Config) - var hs serverHandshaker = &hybiServerHandshaker{Config: config} - code, err := hs.ReadHandshake(buf.Reader, req) - if err == ErrBadWebSocketVersion { - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) - buf.WriteString("\r\n") - buf.WriteString(err.Error()) - buf.Flush() - return - } - if err != nil { - hs = &hixie76ServerHandshaker{Config: config} - code, err = hs.ReadHandshake(buf.Reader, req) - } - if err != nil { - hs = &hixie75ServerHandshaker{Config: config} - code, err = hs.ReadHandshake(buf.Reader, req) - } - if err != nil { - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.WriteString(err.Error()) - buf.Flush() - return - } - config.Protocol = nil - - err = hs.AcceptHandshake(buf.Writer) - if err != nil { - code = http.StatusBadRequest - fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) - buf.WriteString("\r\n") - buf.Flush() - return - } - conn = hs.NewServerConn(buf, rwc, req) - return -} - -/* -Handler is an interface to a WebSocket. - -A trivial example server: - - package main - - import ( - "io" - "net/http" - "websocket" - ) - - // Echo the data received on the WebSocket. - func EchoServer(ws *websocket.Conn) { - io.Copy(ws, ws); - } - - func main() { - http.Handle("/echo", websocket.Handler(EchoServer)); - err := http.ListenAndServe(":12345", nil); - if err != nil { - panic("ListenAndServe: " + err.Error()) - } - } -*/ -type Handler func(*Conn) - -// ServeHTTP implements the http.Handler interface for a Web Socket -func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - rwc, buf, err := w.(http.Hijacker).Hijack() - if err != nil { - panic("Hijack failed: " + err.Error()) - return - } - // The server should abort the WebSocket connection if it finds - // the client did not send a handshake that matches with protocol - // specification. - defer rwc.Close() - conn, err := newServerConn(rwc, buf, req) - if err != nil { - return - } - if conn == nil { - panic("unepxected nil conn") - } - h(conn) -} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go deleted file mode 100644 index f7aabc9..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package websocket implements a client and server for the WebSocket protocol. -// The protocol is defined at http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol -package websocket - -import ( - "bufio" - "crypto/tls" - "encoding/json" - "errors" - "io" - "io/ioutil" - "net" - "net/http" - "net/url" - "sync" - "time" -) - -const ( - ProtocolVersionHixie75 = -75 - ProtocolVersionHixie76 = -76 - ProtocolVersionHybi00 = 0 - ProtocolVersionHybi08 = 8 - ProtocolVersionHybi13 = 13 - ProtocolVersionHybi = ProtocolVersionHybi13 - SupportedProtocolVersion = "13, 8" - - ContinuationFrame = 0 - TextFrame = 1 - BinaryFrame = 2 - CloseFrame = 8 - PingFrame = 9 - PongFrame = 10 - UnknownFrame = 255 -) - -// WebSocket protocol errors. -type ProtocolError struct { - ErrorString string -} - -func (err *ProtocolError) Error() string { return err.ErrorString } - -var ( - ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} - ErrBadScheme = &ProtocolError{"bad scheme"} - ErrBadStatus = &ProtocolError{"bad status"} - ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} - ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} - ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} - ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} - ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} - ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} - ErrBadFrame = &ProtocolError{"bad frame"} - ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} - ErrNotWebSocket = &ProtocolError{"not websocket protocol"} - ErrBadRequestMethod = &ProtocolError{"bad method"} - ErrNotSupported = &ProtocolError{"not supported"} -) - -// Addr is an implementation of net.Addr for WebSocket. -type Addr struct { - *url.URL -} - -// Network returns the network type for a WebSocket, "websocket". -func (addr *Addr) Network() string { return "websocket" } - -// Config is a WebSocket configuration -type Config struct { - // A WebSocket server address. - Location *url.URL - - // A Websocket client origin. - Origin *url.URL - - // WebSocket subprotocols. - Protocol []string - - // WebSocket protocol version. - Version int - - // TLS config for secure WebSocket (wss). - TlsConfig *tls.Config - - handshakeData map[string]string -} - -// serverHandshaker is an interface to handle WebSocket server side handshake. -type serverHandshaker interface { - // ReadHandshake reads handshake request message from client. - // Returns http response code and error if any. - ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) - - // AcceptHandshake accepts the client handshake request and sends - // handshake response back to client. - AcceptHandshake(buf *bufio.Writer) (err error) - - // NewServerConn creates a new WebSocket connection. - NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) -} - -// frameReader is an interface to read a WebSocket frame. -type frameReader interface { - // Reader is to read payload of the frame. - io.Reader - - // PayloadType returns payload type. - PayloadType() byte - - // HeaderReader returns a reader to read header of the frame. - HeaderReader() io.Reader - - // TrailerReader returns a reader to read trailer of the frame. - // If it returns nil, there is no trailer in the frame. - TrailerReader() io.Reader - - // Len returns total length of the frame, including header and trailer. - Len() int -} - -// frameReaderFactory is an interface to creates new frame reader. -type frameReaderFactory interface { - NewFrameReader() (r frameReader, err error) -} - -// frameWriter is an interface to write a WebSocket frame. -type frameWriter interface { - // Writer is to write playload of the frame. - io.WriteCloser -} - -// frameWriterFactory is an interface to create new frame writer. -type frameWriterFactory interface { - NewFrameWriter(payloadType byte) (w frameWriter, err error) -} - -type frameHandler interface { - HandleFrame(frame frameReader) (r frameReader, err error) - WriteClose(status int) (err error) -} - -// Conn represents a WebSocket connection. -type Conn struct { - config *Config - request *http.Request - - buf *bufio.ReadWriter - rwc io.ReadWriteCloser - - rio sync.Mutex - frameReaderFactory - frameReader - - wio sync.Mutex - frameWriterFactory - - frameHandler - PayloadType byte - defaultCloseStatus int -} - -// Read implements the io.Reader interface: -// it reads data of a frame from the WebSocket connection. -// if msg is not large enough for the frame data, it fills the msg and next Read -// will read the rest of the frame data. -// it reads Text frame or Binary frame. -func (ws *Conn) Read(msg []byte) (n int, err error) { - ws.rio.Lock() - defer ws.rio.Unlock() -again: - if ws.frameReader == nil { - frame, err := ws.frameReaderFactory.NewFrameReader() - if err != nil { - return 0, err - } - ws.frameReader, err = ws.frameHandler.HandleFrame(frame) - if err != nil { - return 0, err - } - if ws.frameReader == nil { - goto again - } - } - n, err = ws.frameReader.Read(msg) - if err == io.EOF { - if trailer := ws.frameReader.TrailerReader(); trailer != nil { - io.Copy(ioutil.Discard, trailer) - } - ws.frameReader = nil - goto again - } - return n, err -} - -// Write implements the io.Writer interface: -// it writes data as a frame to the WebSocket connection. -func (ws *Conn) Write(msg []byte) (n int, err error) { - ws.wio.Lock() - defer ws.wio.Unlock() - w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) - if err != nil { - return 0, err - } - n, err = w.Write(msg) - w.Close() - if err != nil { - return n, err - } - return n, err -} - -// Close implements the io.Closer interface. -func (ws *Conn) Close() error { - err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) - if err != nil { - return err - } - return ws.rwc.Close() -} - -func (ws *Conn) IsClientConn() bool { return ws.request == nil } -func (ws *Conn) IsServerConn() bool { return ws.request != nil } - -// LocalAddr returns the WebSocket Origin for the connection for client, or -// the WebSocket location for server. -func (ws *Conn) LocalAddr() net.Addr { - if ws.IsClientConn() { - return &Addr{ws.config.Origin} - } - return &Addr{ws.config.Location} -} - -// RemoteAddr returns the WebSocket location for the connection for client, or -// the Websocket Origin for server. -func (ws *Conn) RemoteAddr() net.Addr { - if ws.IsClientConn() { - return &Addr{ws.config.Location} - } - return &Addr{ws.config.Origin} -} - -var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") - -// SetDeadline sets the connection's network read & write deadlines. -func (ws *Conn) SetDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetDeadline(t) - } - return errSetDeadline -} - -// SetReadDeadline sets the connection's network read deadline. -func (ws *Conn) SetReadDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetReadDeadline(t) - } - return errSetDeadline -} - -// SetWriteDeadline sets the connection's network write deadline. -func (ws *Conn) SetWriteDeadline(t time.Time) error { - if conn, ok := ws.rwc.(net.Conn); ok { - return conn.SetWriteDeadline(t) - } - return errSetDeadline -} - -// Config returns the WebSocket config. -func (ws *Conn) Config() *Config { return ws.config } - -// Request returns the http request upgraded to the WebSocket. -// It is nil for client side. -func (ws *Conn) Request() *http.Request { return ws.request } - -// Codec represents a symmetric pair of functions that implement a codec. -type Codec struct { - Marshal func(v interface{}) (data []byte, payloadType byte, err error) - Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) -} - -// Send sends v marshaled by cd.Marshal as single frame to ws. -func (cd Codec) Send(ws *Conn, v interface{}) (err error) { - if err != nil { - return err - } - data, payloadType, err := cd.Marshal(v) - if err != nil { - return err - } - ws.wio.Lock() - defer ws.wio.Unlock() - w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) - _, err = w.Write(data) - w.Close() - return err -} - -// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. -func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { - ws.rio.Lock() - defer ws.rio.Unlock() - if ws.frameReader != nil { - _, err = io.Copy(ioutil.Discard, ws.frameReader) - if err != nil { - return err - } - ws.frameReader = nil - } -again: - frame, err := ws.frameReaderFactory.NewFrameReader() - if err != nil { - return err - } - frame, err = ws.frameHandler.HandleFrame(frame) - if err != nil { - return err - } - if frame == nil { - goto again - } - payloadType := frame.PayloadType() - data, err := ioutil.ReadAll(frame) - if err != nil { - return err - } - return cd.Unmarshal(data, payloadType, v) -} - -func marshal(v interface{}) (msg []byte, payloadType byte, err error) { - switch data := v.(type) { - case string: - return []byte(data), TextFrame, nil - case []byte: - return data, BinaryFrame, nil - } - return nil, UnknownFrame, ErrNotSupported -} - -func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { - switch data := v.(type) { - case *string: - *data = string(msg) - return nil - case *[]byte: - *data = msg - return nil - } - return ErrNotSupported -} - -/* -Message is a codec to send/receive text/binary data in a frame on WebSocket connection. -To send/receive text frame, use string type. -To send/receive binary frame, use []byte type. - -Trivial usage: - - import "websocket" - - // receive text frame - var message string - websocket.Message.Receive(ws, &message) - - // send text frame - message = "hello" - websocket.Message.Send(ws, message) - - // receive binary frame - var data []byte - websocket.Message.Receive(ws, &data) - - // send binary frame - data = []byte{0, 1, 2} - websocket.Message.Send(ws, data) - -*/ -var Message = Codec{marshal, unmarshal} - -func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { - msg, err = json.Marshal(v) - return msg, TextFrame, err -} - -func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { - return json.Unmarshal(msg, v) -} - -/* -JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. - -Trival usage: - - import "websocket" - - type T struct { - Msg string - Count int - } - - // receive JSON type T - var data T - websocket.JSON.Receive(ws, &data) - - // send JSON type T - websocket.JSON.Send(ws, data) -*/ -var JSON = Codec{jsonMarshal, jsonUnmarshal} diff --git a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go b/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go deleted file mode 100644 index 27c58a0..0000000 --- a/eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package websocket - -import ( - "bytes" - "fmt" - "io" - "log" - "net" - "net/http" - "net/http/httptest" - "net/url" - "strings" - "sync" - "testing" -) - -var serverAddr string -var once sync.Once - -func echoServer(ws *Conn) { io.Copy(ws, ws) } - -type Count struct { - S string - N int -} - -func countServer(ws *Conn) { - for { - var count Count - err := JSON.Receive(ws, &count) - if err != nil { - return - } - count.N++ - count.S = strings.Repeat(count.S, count.N) - err = JSON.Send(ws, count) - if err != nil { - return - } - } -} - -func startServer() { - http.Handle("/echo", Handler(echoServer)) - http.Handle("/count", Handler(countServer)) - server := httptest.NewServer(nil) - serverAddr = server.Listener.Addr().String() - log.Print("Test WebSocket server listening on ", serverAddr) -} - -func newConfig(t *testing.T, path string) *Config { - config, _ := NewConfig(fmt.Sprintf("ws://%s%s", serverAddr, path), "http://localhost") - return config -} - -func TestEcho(t *testing.T) { - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/echo"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - msg := []byte("hello, world\n") - if _, err := conn.Write(msg); err != nil { - t.Errorf("Write: %v", err) - } - var actual_msg = make([]byte, 512) - n, err := conn.Read(actual_msg) - if err != nil { - t.Errorf("Read: %v", err) - } - actual_msg = actual_msg[0:n] - if !bytes.Equal(msg, actual_msg) { - t.Errorf("Echo: expected %q got %q", msg, actual_msg) - } - conn.Close() -} - -func TestAddr(t *testing.T) { - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/echo"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - ra := conn.RemoteAddr().String() - if !strings.HasPrefix(ra, "ws://") || !strings.HasSuffix(ra, "/echo") { - t.Errorf("Bad remote addr: %v", ra) - } - la := conn.LocalAddr().String() - if !strings.HasPrefix(la, "http://") { - t.Errorf("Bad local addr: %v", la) - } - conn.Close() -} - -func TestCount(t *testing.T) { - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/count"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - var count Count - count.S = "hello" - if err := JSON.Send(conn, count); err != nil { - t.Errorf("Write: %v", err) - } - if err := JSON.Receive(conn, &count); err != nil { - t.Errorf("Read: %v", err) - } - if count.N != 1 { - t.Errorf("count: expected %d got %d", 1, count.N) - } - if count.S != "hello" { - t.Errorf("count: expected %q got %q", "hello", count.S) - } - if err := JSON.Send(conn, count); err != nil { - t.Errorf("Write: %v", err) - } - if err := JSON.Receive(conn, &count); err != nil { - t.Errorf("Read: %v", err) - } - if count.N != 2 { - t.Errorf("count: expected %d got %d", 2, count.N) - } - if count.S != "hellohello" { - t.Errorf("count: expected %q got %q", "hellohello", count.S) - } - conn.Close() -} - -func TestWithQuery(t *testing.T) { - once.Do(startServer) - - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - - config := newConfig(t, "/echo") - config.Location, err = url.ParseRequestURI(fmt.Sprintf("ws://%s/echo?q=v", serverAddr)) - if err != nil { - t.Fatal("location url", err) - } - - ws, err := NewClient(config, client) - if err != nil { - t.Errorf("WebSocket handshake: %v", err) - return - } - ws.Close() -} - -func TestWithProtocol(t *testing.T) { - once.Do(startServer) - - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - - config := newConfig(t, "/echo") - config.Protocol = append(config.Protocol, "test") - - ws, err := NewClient(config, client) - if err != nil { - t.Errorf("WebSocket handshake: %v", err) - return - } - ws.Close() -} - -func TestHTTP(t *testing.T) { - once.Do(startServer) - - // If the client did not send a handshake that matches the protocol - // specification, the server MUST return an HTTP respose with an - // appropriate error code (such as 400 Bad Request) - resp, err := http.Get(fmt.Sprintf("http://%s/echo", serverAddr)) - if err != nil { - t.Errorf("Get: error %#v", err) - return - } - if resp == nil { - t.Error("Get: resp is null") - return - } - if resp.StatusCode != http.StatusBadRequest { - t.Errorf("Get: expected %q got %q", http.StatusBadRequest, resp.StatusCode) - } -} - -func TestTrailingSpaces(t *testing.T) { - // http://code.google.com/p/go/issues/detail?id=955 - // The last runs of this create keys with trailing spaces that should not be - // generated by the client. - once.Do(startServer) - config := newConfig(t, "/echo") - for i := 0; i < 30; i++ { - // body - ws, err := DialConfig(config) - if err != nil { - t.Errorf("Dial #%d failed: %v", i, err) - break - } - ws.Close() - } -} - -func TestSmallBuffer(t *testing.T) { - // http://code.google.com/p/go/issues/detail?id=1145 - // Read should be able to handle reading a fragment of a frame. - once.Do(startServer) - - // websocket.Dial() - client, err := net.Dial("tcp", serverAddr) - if err != nil { - t.Fatal("dialing", err) - } - conn, err := NewClient(newConfig(t, "/echo"), client) - if err != nil { - t.Errorf("WebSocket handshake error: %v", err) - return - } - - msg := []byte("hello, world\n") - if _, err := conn.Write(msg); err != nil { - t.Errorf("Write: %v", err) - } - var small_msg = make([]byte, 8) - n, err := conn.Read(small_msg) - if err != nil { - t.Errorf("Read: %v", err) - } - if !bytes.Equal(msg[:len(small_msg)], small_msg) { - t.Errorf("Echo: expected %q got %q", msg[:len(small_msg)], small_msg) - } - var second_msg = make([]byte, len(msg)) - n, err = conn.Read(second_msg) - if err != nil { - t.Errorf("Read: %v", err) - } - second_msg = second_msg[0:n] - if !bytes.Equal(msg[len(small_msg):], second_msg) { - t.Errorf("Echo: expected %q got %q", msg[len(small_msg):], second_msg) - } - conn.Close() -} diff --git a/eBook/examples/chapter_15/dial.go b/eBook/examples/chapter_15/dial.go deleted file mode 100644 index 29754ab..0000000 --- a/eBook/examples/chapter_15/dial.go +++ /dev/null @@ -1,29 +0,0 @@ -// dial.go.go -package main - -import ( - "fmt" - "net" - "os" -) - -func main() { - conn, err:= net.Dial("tcp", "192.0.32.10:80") - checkConnection(conn, err) - - conn, err =net.Dial("udp", "192.0.32.10:80") - checkConnection(conn, err) - - conn, err =net.Dial("tcp", "[2620:0:2d0:200::10]:80") - checkConnection(conn, err) -} - -func checkConnection(conn net.Conn, err error) { - if err!= nil { - fmt.Printf("error %v connecting!") - os.Exit(1) - } - - fmt.Println("Connection is made with %v", conn) - -} diff --git a/eBook/examples/chapter_15/elaborated_webserver.go b/eBook/examples/chapter_15/elaborated_webserver.go deleted file mode 100644 index 60c17f2..0000000 --- a/eBook/examples/chapter_15/elaborated_webserver.go +++ /dev/null @@ -1,146 +0,0 @@ -//Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -package main - -import ( - "bytes" - "expvar" - "flag" - "fmt" - "net/http" - "io" - "log" - "os" - "strconv" -) - -// hello world, the web server -var helloRequests = expvar.NewInt("hello-requests") -// flags: -var webroot = flag.String("root", "/home/user", "web root directory") -// simple flag server -var booleanflag = flag.Bool("boolean", true, "another flag for testing") - -// Simple counter server. POSTing to it will set the value. -type Counter struct { - n int -} - -// a channel -type Chan chan int - -func main() { - flag.Parse() - http.Handle("/", http.HandlerFunc(Logger)) - http.Handle("/go/hello", http.HandlerFunc(HelloServer)) - // The counter is published as a variable directly. - ctr := new(Counter) - expvar.Publish("counter", ctr) - http.Handle("/counter", ctr) - // http.Handle("/go/", http.FileServer(http.Dir("/tmp"))) // uses the OS filesystem - http.Handle("/go/", http.StripPrefix("/go/", http.FileServer(http.Dir(*webroot)))) - http.Handle("/flags", http.HandlerFunc(FlagServer)) - http.Handle("/args", http.HandlerFunc(ArgServer)) - http.Handle("/chan", ChanCreate()) - http.Handle("/date", http.HandlerFunc(DateServer)) - err := http.ListenAndServe(":12345", nil) - if err != nil { - log.Panicln("ListenAndServe:", err) - } -} - -func Logger(w http.ResponseWriter, req *http.Request) { - log.Print(req.URL.String()) - w.WriteHeader(404) - w.Write([]byte("oops")) -} - -func HelloServer(w http.ResponseWriter, req *http.Request) { - helloRequests.Add(1) - io.WriteString(w, "hello, world!\n") -} - -// This makes Counter satisfy the expvar.Var interface, so we can export -// it directly. -func (ctr *Counter) String() string { return fmt.Sprintf("%d", ctr.n) } - -func (ctr *Counter) ServeHTTP(w http.ResponseWriter, req *http.Request) { - switch req.Method { - case "GET": // increment n - ctr.n++ - case "POST": // set n to posted value - buf := new(bytes.Buffer) - io.Copy(buf, req.Body) - body := buf.String() - if n, err := strconv.Atoi(body); err != nil { - fmt.Fprintf(w, "bad POST: %v\nbody: [%v]\n", err, body) - } else { - ctr.n = n - fmt.Fprint(w, "counter reset\n") - } - } - fmt.Fprintf(w, "counter = %d\n", ctr.n) -} - -func FlagServer(w http.ResponseWriter, req *http.Request) { - w.Header().Set("Content-Type", "text/plain; charset=utf-8") - fmt.Fprint(w, "Flags:\n") - flag.VisitAll(func(f *flag.Flag) { - if f.Value.String() != f.DefValue { - fmt.Fprintf(w, "%s = %s [default = %s]\n", f.Name, f.Value.String(), f.DefValue) - } else { - fmt.Fprintf(w, "%s = %s\n", f.Name, f.Value.String()) - } - }) -} - -// simple argument server -func ArgServer(w http.ResponseWriter, req *http.Request) { - for _, s := range os.Args { - fmt.Fprint(w, s, " ") - } -} - -func ChanCreate() Chan { - c := make(Chan) - go func(c Chan) { - for x := 0; ; x++ { - c <- x - } - }(c) - return c -} - -func (ch Chan) ServeHTTP(w http.ResponseWriter, req *http.Request) { - io.WriteString(w, fmt.Sprintf("channel send #%d\n", <-ch)) -} - -// exec a program, redirecting output -func DateServer(rw http.ResponseWriter, req *http.Request) { - rw.Header().Set("Content-Type", "text/plain; charset=utf-8") - r, w, err := os.Pipe() - if err != nil { - fmt.Fprintf(rw, "pipe: %s\n", err) - return - } - - p, err := os.StartProcess("/bin/date", []string{"date"}, &os.ProcAttr{Files: []*os.File{nil, w, w}}) - defer r.Close() - w.Close() - if err != nil { - fmt.Fprintf(rw, "fork/exec: %s\n", err) - return - } - defer p.Release() - io.Copy(rw, r) - wait, err := p.Wait() - if err != nil { - fmt.Fprintf(rw, "wait: %s\n", err) - return - } - if !wait.Exited() { - fmt.Fprintf(rw, "date: %v\n", wait) - return - } -} \ No newline at end of file diff --git a/eBook/examples/chapter_15/hello_world_webserver.go b/eBook/examples/chapter_15/hello_world_webserver.go deleted file mode 100644 index d2414e8..0000000 --- a/eBook/examples/chapter_15/hello_world_webserver.go +++ /dev/null @@ -1,24 +0,0 @@ -// hello_world_webserver.go -package main - -import ( - "fmt" - "net/http" - "log" -) - -func HelloServer(w http.ResponseWriter, req *http.Request) { - fmt.Println("Inside HelloServer handler") - //fmt.Fprint(w, "Hello, " + req.URL.Path[1:]) - fmt.Fprintf(w, "Hello, %s ", req.URL.Path[1:]) - // io.WriteString(w, "hello, world!\n") -} - -func main() { - http.HandleFunc("/", HelloServer) - err := http.ListenAndServe("localhost:8080", nil) - if err != nil { - log.Fatal("ListenAndServe: ", err.Error()) - } - // http.ListenAndServe(":8080", http.HandlerFunc(HelloServer)) -} diff --git a/eBook/examples/chapter_15/http_fetch.go b/eBook/examples/chapter_15/http_fetch.go deleted file mode 100644 index 8b66d62..0000000 --- a/eBook/examples/chapter_15/http_fetch.go +++ /dev/null @@ -1,23 +0,0 @@ -// httpfetch.go -package main - -import ( - "fmt" - "net/http" - "io/ioutil" - "log" -) - -func main() { - res, err := http.Get("http://www.google.com") - CheckError(err) - data, err := ioutil.ReadAll(res.Body) - CheckError(err) - fmt.Printf("Got: %q", string(data)) -} - -func CheckError(err error) { - if err != nil { - log.Fatalf("Get: %v", err) - } -} diff --git a/eBook/examples/chapter_15/pipeline1.go b/eBook/examples/chapter_15/pipeline1.go deleted file mode 100644 index 32d74ee..0000000 --- a/eBook/examples/chapter_15/pipeline1.go +++ /dev/null @@ -1,17 +0,0 @@ -// pipeline1.go -package main - -import ( - "text/template" - "os" -) - -func main() { - t := template.New("template test") - t = template.Must(t.Parse("This is just static text. \n{{\"This is pipeline data - because it is evaluated within the double braces.\"}} {{`So is this, but within reverse quotes.`}}\n")) - t.Execute(os.Stdout, nil) -} -/* -This is just static text. -This is pipeline data - because it is evaluated within the double braces. So is this, but within reverse quotes. -*/ \ No newline at end of file diff --git a/eBook/examples/chapter_15/poll_url.go b/eBook/examples/chapter_15/poll_url.go deleted file mode 100644 index 2efe5f2..0000000 --- a/eBook/examples/chapter_15/poll_url.go +++ /dev/null @@ -1,30 +0,0 @@ -// poll_url.go -package main - -import ( - "fmt" - "net/http" -) - -var urls = []string{ - "http://www.google.com/", - "http://golang.org/", - "http://blog.golang.org/", -} - -func main() { - // Execute an HTTP HEAD request for all url's - // and returns the HTTP status string or an error string. - for _, url := range urls { - resp, err := http.Head(url) - if err != nil { - fmt.Println("Error", url, err) - } - fmt.Print(url, ": ", resp.Status) - } -} -/* Output: -http://www.google.com/ : 302 Found -http://golang.org/ : 200 OK -http://blog.golang.org/ : 200 OK -*/ diff --git a/eBook/examples/chapter_15/predefined_functions.go b/eBook/examples/chapter_15/predefined_functions.go deleted file mode 100644 index ec8962e..0000000 --- a/eBook/examples/chapter_15/predefined_functions.go +++ /dev/null @@ -1,14 +0,0 @@ -// predefined_functions.go -package main - -import ( - "os" - "text/template" -) - -func main() { - t := template.New("test") - t = template.Must(t.Parse("{{with $x := `hello`}}{{printf `%s %s` $x `Mary`}}{{end}}!\n")) - t.Execute(os.Stdout, nil) -} -// hello Mary! \ No newline at end of file diff --git a/eBook/examples/chapter_15/robust_webserver.go b/eBook/examples/chapter_15/robust_webserver.go deleted file mode 100644 index 07501fe..0000000 --- a/eBook/examples/chapter_15/robust_webserver.go +++ /dev/null @@ -1,57 +0,0 @@ -// robust_webserver.go -package main - -import ( - "net/http" - "io" - "log" -) - -const form = `
- - -
` - -type HandleFnc func(http.ResponseWriter,*http.Request) - -/* handle a simple get request */ -func SimpleServer(w http.ResponseWriter, request *http.Request) { - io.WriteString(w, "

hello, world

") -} - -/* handle a form, both the GET which displays the form - and the POST which processes it.*/ -func FormServer(w http.ResponseWriter, request *http.Request) { - w.Header().Set("Content-Type", "text/html") - switch request.Method { - case "GET": - /* display the form to the user */ - io.WriteString(w, form ); - case "POST": - /* handle the form data, note that ParseForm must - be called before we can extract form data*/ - //request.ParseForm(); - //io.WriteString(w, request.Form["in"][0]) - io.WriteString(w, request.FormValue("in")) - } -} - -func main() { - http.HandleFunc("/test1", logPanics(SimpleServer)) - http.HandleFunc("/test2", logPanics(FormServer)) - if err := http.ListenAndServe(":8088", nil); err != nil { - panic(err) - } -} - -func logPanics(function HandleFnc) HandleFnc { - return func(writer http.ResponseWriter, request *http.Request) { - defer func() { - if x := recover(); x != nil { - log.Printf("[%v] caught panic: %v", request.RemoteAddr, x) - } - }() - function(writer, request) - } -} - diff --git a/eBook/examples/chapter_15/rpc_client.go b/eBook/examples/chapter_15/rpc_client.go deleted file mode 100644 index 3d0ea60..0000000 --- a/eBook/examples/chapter_15/rpc_client.go +++ /dev/null @@ -1,39 +0,0 @@ -// rpc_client.go -// if the server is not started: -// can't get the server to start, so client stops immediately with error: -// 2011/08/01 16:08:05 Error dialing:dial tcp :1234: -// The requested address is not valid in its context. -// with serverAddress = localhost: -// 2011/08/01 16:09:23 Error dialing:dial tcp 127.0.0.1:1234: -// No connection could be made because the target machine actively refused it. -package main - -import ( - "fmt" - "log" - "net/rpc" - "./rpc_objects" -) - -const serverAddress = "localhost" - -func main() { - client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") - if err != nil { - log.Fatal("Error dialing:", err) - } - // Synchronous call - args := &rpc_objects.Args{7, 8} - var reply int - err = client.Call("Args.Multiply", args, &reply) - if err != nil { - log.Fatal("Args error:", err) - } - fmt.Printf("Args: %d * %d = %d", args.N, args.M, reply) -} -/* Output: -Starting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_client.exe ... - -Args: 7 * 8 = 56 -End Process exit status 0 -*/ diff --git a/eBook/examples/chapter_15/rpc_objects.go b/eBook/examples/chapter_15/rpc_objects.go deleted file mode 100644 index 76def53..0000000 --- a/eBook/examples/chapter_15/rpc_objects.go +++ /dev/null @@ -1,13 +0,0 @@ -// rpc_objects.go -package rpc_objects - -import "net" - -type Args struct { - N, M int -} - -func (t *Args) Multiply(args *Args, reply *int) net.Error { - *reply = args.N * args.M - return nil -} \ No newline at end of file diff --git a/eBook/examples/chapter_15/rpc_server.go b/eBook/examples/chapter_15/rpc_server.go deleted file mode 100644 index 7184b48..0000000 --- a/eBook/examples/chapter_15/rpc_server.go +++ /dev/null @@ -1,32 +0,0 @@ -// rpc_server.go -// after client-exits the server shows the message: -// 1:1234: The specified network name is no longer available. -// 2011/08/01 16:19:04 rpc: rpc: server cannot decode request: WSARecv tcp 127.0.0. -package main - -import ( - "net/http" - "log" - "net" - "net/rpc" - "time" - "./rpc_objects" -) - -func main() { - calc := new(rpc_objects.Args) - rpc.Register(calc) - rpc.HandleHTTP() - listener, e := net.Listen("tcp", "localhost:1234") - if e != nil { - log.Fatal("Starting RPC-server -listen error:", e) - } - go http.Serve(listener, nil) - time.Sleep(1000e9) -} -/* Output: -Starting Process E:/Go/GoBoek/code_examples/chapter_14/rpc_server.exe ... - -** after 5 s: ** -End Process exit status 0 -*/ diff --git a/eBook/examples/chapter_15/server.go b/eBook/examples/chapter_15/server.go deleted file mode 100644 index f7938bb..0000000 --- a/eBook/examples/chapter_15/server.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "fmt" - "net" -) - -func main() { - fmt.Println("Starting the server ...") - // create listener: - listener, err := net.Listen("tcp", "localhost:50000") - if err != nil { - fmt.Println("Error listening", err.Error()) - return // terminate program - } - // listen and accept connections from clients: - for { - conn, err := listener.Accept() - if err != nil { - fmt.Println("Error accepting", err.Error()) - return // terminate program - } - go doServerStuff(conn) - } -} - -func doServerStuff(conn net.Conn) { - for { - buf := make([]byte, 512) - _, err := conn.Read(buf) - if err != nil { - fmt.Println("Error reading", err.Error()) - return // terminate program - } - fmt.Printf("Received data: %v", string(buf)) - } -} diff --git a/eBook/examples/chapter_15/simple_tcp_server.go b/eBook/examples/chapter_15/simple_tcp_server.go deleted file mode 100644 index 79b6c22..0000000 --- a/eBook/examples/chapter_15/simple_tcp_server.go +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Simple multi-thread/multi-core TCP server. - * -*/ -package main - -import ( - "flag" - "net" - "fmt" - "syscall" -) - -const maxRead = 25 - -func main() { - flag.Parse() - if flag.NArg() != 2 { - panic("usage: host port") - } - hostAndPort := fmt.Sprintf("%s:%s", flag.Arg(0), flag.Arg(1)) - listener := initServer(hostAndPort) - for { - conn, err := listener.Accept() - checkError(err, "Accept: ") - go connectionHandler(conn) - } -} - -func initServer(hostAndPort string) *net.TCPListener { - serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort) - checkError(err, "Resolving address:port failed: `" + hostAndPort + "'") - listener, err := net.ListenTCP("tcp", serverAddr) - checkError(err, "ListenTCP: ") - println("Listening to: ", listener.Addr().String()) - return listener -} - -func connectionHandler(conn net.Conn) { - connFrom := conn.RemoteAddr().String() - println("Connection from: ", connFrom) - sayHello(conn) - for { - var ibuf []byte = make([]byte, maxRead + 1) - length, err := conn.Read(ibuf[0:maxRead]) - ibuf[maxRead] = 0 // to prevent overflow - switch err { - case nil: - handleMsg(length, err, ibuf) - case syscall.EAGAIN: // try again - continue - default: - goto DISCONNECT - } - } - -DISCONNECT: - err := conn.Close() - println("Closed connection: ", connFrom) - checkError(err, "Close: ") -} - -func sayHello(to net.Conn) { - obuf := []byte{'L', 'e', 't', '\'', 's', ' ', 'G', 'O', '!', '\n'} - wrote, err := to.Write(obuf) - checkError(err, "Write: wrote " + string(wrote) + " bytes.") -} - -func handleMsg(length int, err error, msg []byte) { - if length > 0 { - print("<", length, ":") - for i := 0; ; i++ { - if msg[i] == 0 { - break - } - fmt.Printf("%c", msg[i]) - } - print(">") - } -} - -func checkError(error error, info string) { - if error != nil { - panic("ERROR: " + info + " " + error.Error()) // terminate program - } -} diff --git a/eBook/examples/chapter_15/simple_webserver.go b/eBook/examples/chapter_15/simple_webserver.go deleted file mode 100644 index bf17349..0000000 --- a/eBook/examples/chapter_15/simple_webserver.go +++ /dev/null @@ -1,42 +0,0 @@ -// simple_webserver.go -package main - -import ( - "net/http" - "io" -) - -const form = `
- - -
` - -/* handle a simple get request */ -func SimpleServer(w http.ResponseWriter, request *http.Request) { - io.WriteString(w, "

hello, world

") -} - -/* handle a form, both the GET which displays the form - and the POST which processes it.*/ -func FormServer(w http.ResponseWriter, request *http.Request) { - w.Header().Set("Content-Type", "text/html") - switch request.Method { - case "GET": - /* display the form to the user */ - io.WriteString(w, form ); - case "POST": - /* handle the form data, note that ParseForm must - be called before we can extract form data*/ - //request.ParseForm(); - //io.WriteString(w, request.Form["in"][0]) - io.WriteString(w, request.FormValue("in")) - } -} - -func main() { - http.HandleFunc("/test1", SimpleServer) - http.HandleFunc("/test2", FormServer) - if err := http.ListenAndServe(":8088", nil); err != nil { - panic(err) - } -} diff --git a/eBook/examples/chapter_15/smtp.go b/eBook/examples/chapter_15/smtp.go deleted file mode 100644 index 58338c1..0000000 --- a/eBook/examples/chapter_15/smtp.go +++ /dev/null @@ -1,30 +0,0 @@ -// smtp.go -package main - -import ( - "bytes" - "log" - "net/smtp" -) - -func main() { - // Connect to the remote SMTP server. - client, err := smtp.Dial("mail.example.com:25") - if err != nil { - log.Fatal(err) - } - // Set the sender and recipient. - client.Mail("sender@example.org") - client.Rcpt("recipient@example.net") - // Send the email body. - wc, err := client.Data() - if err != nil { - log.Fatal(err) - } - defer wc.Close() - buf := bytes.NewBufferString("This is the email body.") - if _, err = buf.WriteTo(wc); err != nil { - log.Fatal(err) - } -} - diff --git a/eBook/examples/chapter_15/smtp_auth.go b/eBook/examples/chapter_15/smtp_auth.go deleted file mode 100644 index 91a298b..0000000 --- a/eBook/examples/chapter_15/smtp_auth.go +++ /dev/null @@ -1,29 +0,0 @@ -// smtp_auth.go -package main - -import ( - "log" - "net/smtp" -) - -func main() { - // Set up authentication information. - auth := smtp.PlainAuth( - "", - "user@example.com", - "password", - "mail.example.com", - ) - // Connect to the server, authenticate, set the sender and recipient, - // and send the email all in one step. - err := smtp.SendMail( - "mail.example.com:25", - auth, - "sender@example.org", - []string{"recipient@example.net"}, - []byte("This is the email body."), - ) - if err != nil { - log.Fatal(err) - } -} diff --git a/eBook/examples/chapter_15/socket.go b/eBook/examples/chapter_15/socket.go deleted file mode 100644 index f5a4d3d..0000000 --- a/eBook/examples/chapter_15/socket.go +++ /dev/null @@ -1,32 +0,0 @@ -// socket.go -package main - -import ( - "fmt" - "net" - "io" -) - -func main() { - var ( - host = "www.apache.org" - port = "80" - remote = host + ":" + port - msg string = "GET / \n" - data = make([]uint8, 4096) - read = true - count = 0 - ) - // create the socket - con, err := net.Dial("tcp", remote) - // send our message. an HTTP GET request in this case - io.WriteString(con, msg) - // read the response from the webserver - for read { - count, err = con.Read(data) - read = (err == nil) - fmt.Printf(string(data[0:count])) - } - - con.Close() -} diff --git a/eBook/examples/chapter_15/template_field.go b/eBook/examples/chapter_15/template_field.go deleted file mode 100644 index cac2ada..0000000 --- a/eBook/examples/chapter_15/template_field.go +++ /dev/null @@ -1,23 +0,0 @@ -// template_field.go -package main - -import ( - "fmt" - "os" - "text/template" -) - -type Person struct { - Name string - nonExportedAgeField string -} - -func main() { - t := template.New("hello") - t, _ = t.Parse("hello {{.Name}}!") - p := Person{Name:"Mary", nonExportedAgeField: "31"} - if err := t.Execute(os.Stdout, p); err != nil { - fmt.Println("There was an error:", err.Error()) - } -} -// Output: hello Mary! \ No newline at end of file diff --git a/eBook/examples/chapter_15/template_ifelse.go b/eBook/examples/chapter_15/template_ifelse.go deleted file mode 100644 index cd47316..0000000 --- a/eBook/examples/chapter_15/template_ifelse.go +++ /dev/null @@ -1,26 +0,0 @@ -// template_ifelse.go -package main - -import ( - "os" - "text/template" -) - -func main() { - tEmpty := template.New("template test") - tEmpty = template.Must(tEmpty.Parse("Empty pipeline if demo: {{if ``}} Will not print. {{end}}\n")) //empty pipeline following if - tEmpty.Execute(os.Stdout, nil) - - tWithValue := template.New("template test") - tWithValue = template.Must(tWithValue.Parse("Non empty pipeline if demo: {{if `anything`}} Will print. {{end}}\n")) //non empty pipeline following if condition - tWithValue.Execute(os.Stdout, nil) - - tIfElse := template.New("template test") - tIfElse = template.Must(tIfElse.Parse("if-else demo: {{if `anything`}} Print IF part. {{else}} Print ELSE part.{{end}}\n")) //non empty pipeline following if condition - tIfElse.Execute(os.Stdout, nil) -} -/* Output: -Empty pipeline if demo: -Non empty pipeline if demo: Will print. -if-else demo: Print IF part. -*/ diff --git a/eBook/examples/chapter_15/template_validation.go b/eBook/examples/chapter_15/template_validation.go deleted file mode 100644 index 0969dfb..0000000 --- a/eBook/examples/chapter_15/template_validation.go +++ /dev/null @@ -1,22 +0,0 @@ -// template_validation.go -package main - -import ( - "text/template" - "fmt" -) - -func main() { - tOk := template.New("ok") - //a valid template, so no panic with Must: - template.Must(tOk.Parse("/* and a comment */ some static text: {{ .Name }}")) - fmt.Println("The first one parsed OK.") - fmt.Println("The next one ought to fail.") - tErr := template.New("error_template") - template.Must(tErr.Parse(" some static text {{ .Name }")) -} -/* Output: -The first one parsed OK. -The next one ought to fail. -panic: template: error_template:1: unexpected "}" in command -*/ diff --git a/eBook/examples/chapter_15/template_variables.go b/eBook/examples/chapter_15/template_variables.go deleted file mode 100644 index 0e4f5ef..0000000 --- a/eBook/examples/chapter_15/template_variables.go +++ /dev/null @@ -1,24 +0,0 @@ -// template_variables.go -package main - -import ( - "os" - "text/template" -) - -func main() { - t := template.New("test") - t = template.Must(t.Parse("{{with $3 := `hello`}}{{$3}}{{end}}!\n")) - t.Execute(os.Stdout, nil) - - t = template.Must(t.Parse("{{with $x3 := `hola`}}{{$x3}}{{end}}!\n")) - t.Execute(os.Stdout, nil) - - t = template.Must(t.Parse("{{with $x_1 := `hey`}}{{$x_1}} {{.}} {{$x_1}}{{end}}!\n")) - t.Execute(os.Stdout, nil) -} -/* Output: -hello! -hola! -hey hey hey! -*/ diff --git a/eBook/examples/chapter_15/template_with_end.go b/eBook/examples/chapter_15/template_with_end.go deleted file mode 100644 index 21fc9f4..0000000 --- a/eBook/examples/chapter_15/template_with_end.go +++ /dev/null @@ -1,20 +0,0 @@ -// template_with_end.go -package main - -import ( - "os" - "text/template" -) - -func main() { - t := template.New("test") - t, _ = t.Parse("{{with `hello`}}{{.}}{{end}}!\n") - t.Execute(os.Stdout, nil) - - t, _ = t.Parse("{{with `hello`}}{{.}} {{with `Mary`}}{{.}}{{end}}{{end}}!\n") - t.Execute(os.Stdout, nil) -} -/* Output: -hello! -hello Mary! -*/ \ No newline at end of file diff --git a/eBook/examples/chapter_15/twitter_status.go b/eBook/examples/chapter_15/twitter_status.go deleted file mode 100644 index 3157ea6..0000000 --- a/eBook/examples/chapter_15/twitter_status.go +++ /dev/null @@ -1,42 +0,0 @@ -// twitter_status.go -package main - -import ( - "net/http" - "fmt" - "encoding/xml" - "io/ioutil" -) -/* these structs will house the unmarshalled response. - they should be hierarchically shaped like the XML - but can omit irrelevant data. */ -type Status struct { - Text string -} - -type User struct { - XMLName xml.Name - Status Status -} -// var user User - -func main() { - // perform an HTTP request for the twitter status of user: Googland - resp, _ := http.Get("http://twitter.com/users/Googland.xml") - // initialize the structure of the XML response - user := User{xml.Name{"", "user"}, Status{""}} - // unmarshal the XML into our structures - defer resp.Body.Close() - if body, err := ioutil.ReadAll(resp.Body); err != nil { - fmt.Printf("error: %s", err.Error()) - } else { - fmt.Printf("%s ---", body) - xml.Unmarshal(body, &user) - } - fmt.Printf("name: %s ", user.XMLName) - fmt.Printf("status: %s", user.Status.Text) -} -/* Output: -status: Robot cars invade California, on orders from Google: Google has been testing self-driving cars ... http://bit.ly/cbtpUN http://retwt.me/97p -After Go1: no output: name: { user} status: -*/ \ No newline at end of file diff --git a/eBook/examples/chapter_15/websocket_client.go b/eBook/examples/chapter_15/websocket_client.go deleted file mode 100644 index 6f2aad3..0000000 --- a/eBook/examples/chapter_15/websocket_client.go +++ /dev/null @@ -1,29 +0,0 @@ -// websocket_client.go -package main - -import ( - "fmt" - "time" - "code.google.com/p/go.net/websocket" -) - -func main() { - ws, err := websocket.Dial("ws://localhost:12345/websocket", "", - "http://localhost/") - if err != nil { - panic("Dial: " + err.Error()) - } - go readFromServer(ws) - time.Sleep(5e9) - ws.Close() -} - -func readFromServer(ws *websocket.Conn) { - buf := make([]byte, 1000) - for { - if _, err := ws.Read(buf); err != nil { - fmt.Printf("%s\n", err.Error()) - break - } - } -} diff --git a/eBook/examples/chapter_15/websocket_server.go b/eBook/examples/chapter_15/websocket_server.go deleted file mode 100644 index a191420..0000000 --- a/eBook/examples/chapter_15/websocket_server.go +++ /dev/null @@ -1,29 +0,0 @@ -// websocket_server.go -package main - -import ( - "fmt" - "net/http" - "code.google.com/p/go.net/websocket" -) - -func server(ws *websocket.Conn) { - fmt.Printf("new connection\n") - buf := make([]byte, 100) - for { - if _, err := ws.Read(buf); err != nil { - fmt.Printf("%s", err.Error()) - break - } - } - fmt.Printf(" => closing connection\n") - ws.Close() -} - -func main() { - http.Handle("/websocket", websocket.Handler(server)) - err := http.ListenAndServe(":12345", nil) - if err != nil { - panic("ListenAndServe: " + err.Error()) - } -} diff --git a/eBook/examples/chapter_15/wiki/ANewPage.txt b/eBook/examples/chapter_15/wiki/ANewPage.txt deleted file mode 100644 index e994fea..0000000 --- a/eBook/examples/chapter_15/wiki/ANewPage.txt +++ /dev/null @@ -1,2 +0,0 @@ -Testing input of new page! -Go Go Go ! \ No newline at end of file diff --git a/eBook/examples/chapter_15/wiki/TestPage.txt b/eBook/examples/chapter_15/wiki/TestPage.txt deleted file mode 100644 index 0963b99..0000000 --- a/eBook/examples/chapter_15/wiki/TestPage.txt +++ /dev/null @@ -1 +0,0 @@ -This is a sample Page. \ No newline at end of file diff --git a/eBook/examples/chapter_15/wiki/edit.html b/eBook/examples/chapter_15/wiki/edit.html deleted file mode 100644 index 34e314a..0000000 --- a/eBook/examples/chapter_15/wiki/edit.html +++ /dev/null @@ -1,6 +0,0 @@ -

Editing {{.Title |html}}

- -
-
-
-
\ No newline at end of file diff --git a/eBook/examples/chapter_15/wiki/page.txt b/eBook/examples/chapter_15/wiki/page.txt deleted file mode 100644 index 810b9b7..0000000 --- a/eBook/examples/chapter_15/wiki/page.txt +++ /dev/null @@ -1,2 +0,0 @@ -Hello Go - World !!! -This works great. diff --git a/eBook/examples/chapter_15/wiki/page1.txt b/eBook/examples/chapter_15/wiki/page1.txt deleted file mode 100644 index eedd24f..0000000 --- a/eBook/examples/chapter_15/wiki/page1.txt +++ /dev/null @@ -1 +0,0 @@ -This is a test!! \ No newline at end of file diff --git a/eBook/examples/chapter_15/wiki/page5.txt b/eBook/examples/chapter_15/wiki/page5.txt deleted file mode 100644 index 22298c7..0000000 --- a/eBook/examples/chapter_15/wiki/page5.txt +++ /dev/null @@ -1,3 +0,0 @@ -Page5 is hereby started. -This is a first addition. -2nd addition. \ No newline at end of file diff --git a/eBook/examples/chapter_15/wiki/view.html b/eBook/examples/chapter_15/wiki/view.html deleted file mode 100644 index 0233915..0000000 --- a/eBook/examples/chapter_15/wiki/view.html +++ /dev/null @@ -1,5 +0,0 @@ -

{{.Title |html}}

- -

[edit]

- -
{{printf "%s" .Body |html}}
diff --git a/eBook/examples/chapter_15/wiki/wiki.go b/eBook/examples/chapter_15/wiki/wiki.go deleted file mode 100644 index ee7a76c..0000000 --- a/eBook/examples/chapter_15/wiki/wiki.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "io/ioutil" - "log" - "net/http" - "regexp" - "text/template" -) - -const lenPath = len("/view/") - -var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$") -var templates = make(map[string]*template.Template) -var err error - -type Page struct { - Title string - Body []byte -} - -func init() { - for _, tmpl := range []string{"edit", "view"} { - templates[tmpl] = template.Must(template.ParseFiles(tmpl + ".html")) - } -} - -func main() { - http.HandleFunc("/view/", makeHandler(viewHandler)) - http.HandleFunc("/edit/", makeHandler(editHandler)) - http.HandleFunc("/save/", makeHandler(saveHandler)) - err := http.ListenAndServe("localhost:8080", nil) - if err != nil { - log.Fatal("ListenAndServe: ", err.Error()) - } -} - -func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - title := r.URL.Path[lenPath:] - if !titleValidator.MatchString(title) { - http.NotFound(w, r) - return - } - fn(w, r, title) - } -} - -func viewHandler(w http.ResponseWriter, r *http.Request, title string) { - p, err := load(title) - if err != nil { // page not found - http.Redirect(w, r, "/edit/"+title, http.StatusFound) - return - } - renderTemplate(w, "view", p) -} - -func editHandler(w http.ResponseWriter, r *http.Request, title string) { - p, err := load(title) - if err != nil { - p = &Page{Title: title} - } - renderTemplate(w, "edit", p) -} - -func saveHandler(w http.ResponseWriter, r *http.Request, title string) { - body := r.FormValue("body") - p := &Page{Title: title, Body: []byte(body)} - err := p.save() - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - http.Redirect(w, r, "/view/"+title, http.StatusFound) -} - -func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) { - err := templates[tmpl].Execute(w, p) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} - -func (p *Page) save() error { - filename := p.Title + ".txt" - // file created with read-write permissions for the current user only - return ioutil.WriteFile(filename, p.Body, 0600) -} - -func load(title string) (*Page, error) { - filename := title + ".txt" - body, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return &Page{Title: title, Body: body}, nil -} diff --git a/eBook/examples/chapter_15/wiki/wiki_part1.go b/eBook/examples/chapter_15/wiki/wiki_part1.go deleted file mode 100644 index aec8c38..0000000 --- a/eBook/examples/chapter_15/wiki/wiki_part1.go +++ /dev/null @@ -1,32 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" -) - -type Page struct { - Title string - Body []byte -} - -func (p *Page) save() error { - filename := p.Title + ".txt" - return ioutil.WriteFile(filename, p.Body, 0600) -} - -func load(title string) (*Page, error) { - filename := title + ".txt" - body, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return &Page{Title: title, Body: body}, nil -} - -func main() { - p1 := &Page{Title: "TestPage", Body: []byte("This is a sample Page.")} - p1.save() - p2, _ := load("TestPage") - fmt.Println(string(p2.Body)) -} diff --git a/eBook/examples/chapter_15/wiki/wiki_part2.go b/eBook/examples/chapter_15/wiki/wiki_part2.go deleted file mode 100644 index a0445ba..0000000 --- a/eBook/examples/chapter_15/wiki/wiki_part2.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "io/ioutil" -) - -type Page struct { - Title string - Body []byte -} - -func (p *Page) save() error { - filename := p.Title + ".txt" - return ioutil.WriteFile(filename, p.Body, 0600) -} - -func load(title string) (*Page, error) { - filename := title + ".txt" - body, err := ioutil.ReadFile(filename) - if err != nil { - return nil, err - } - return &Page{Title: title, Body: body}, nil -} - -const lenPath = len("/view/") - -func viewHandler(w http.ResponseWriter, r *http.Request) { - title := r.URL.Path[lenPath:] - p, _ := load(title) - fmt.Fprintf(w, "

%s

%s
", p.Title, p.Body) -} - -func main() { - http.HandleFunc("/view/", viewHandler) - http.ListenAndServe(":8080", nil) -} diff --git a/eBook/examples/chapter_5/booleans.go b/eBook/examples/chapter_5/booleans.go new file mode 100644 index 0000000..bcf145f --- /dev/null +++ b/eBook/examples/chapter_5/booleans.go @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + bool1 := true + if bool1 { + fmt.Printf("The value is true\n") + } else { + fmt.Printf("The value is false\n") + } +} diff --git a/eBook/examples/chapter_5/for1.go b/eBook/examples/chapter_5/for1.go new file mode 100644 index 0000000..79f958a --- /dev/null +++ b/eBook/examples/chapter_5/for1.go @@ -0,0 +1,9 @@ +package main + +import "fmt" + +func main() { + for i := 0; i < 5; i++ { + fmt.Printf("This is the %d iteration\n", i) + } +} diff --git a/eBook/examples/chapter_5/for2.go b/eBook/examples/chapter_5/for2.go new file mode 100644 index 0000000..bf9b294 --- /dev/null +++ b/eBook/examples/chapter_5/for2.go @@ -0,0 +1,12 @@ +package main + +import "fmt" + +func main() { + var i int = 5 + + for i >= 0 { + i = i - 1 + fmt.Printf("The variable i is now: %d\n", i) + } +} diff --git a/eBook/examples/chapter_5/for3.go b/eBook/examples/chapter_5/for3.go new file mode 100644 index 0000000..62bd74b --- /dev/null +++ b/eBook/examples/chapter_5/for3.go @@ -0,0 +1,15 @@ +package main + +import "fmt" + +func main() { + var i int = 5 + + for { + i = i - 1 + fmt.Printf("The variable i is now: %d\n", i) + if i < 0 { + break + } + } +} diff --git a/eBook/examples/chapter_5/for4.go b/eBook/examples/chapter_5/for4.go new file mode 100644 index 0000000..cb16d54 --- /dev/null +++ b/eBook/examples/chapter_5/for4.go @@ -0,0 +1,13 @@ +package main + +func main() { + for i:=0; i<3; i++ { + for j:=0; j<10; j++ { + if j>5 { + break + } + print(j) + } + print(" ") + } +} \ No newline at end of file diff --git a/eBook/examples/chapter_5/for5.go b/eBook/examples/chapter_5/for5.go new file mode 100644 index 0000000..6e1135e --- /dev/null +++ b/eBook/examples/chapter_5/for5.go @@ -0,0 +1,11 @@ +package main + +func main() { + for i := 0; i < 10; i++ { + if i == 5 { + continue + } + print(i) + print(" ") + } +} diff --git a/eBook/examples/chapter_5/for6.go b/eBook/examples/chapter_5/for6.go new file mode 100644 index 0000000..81a96d8 --- /dev/null +++ b/eBook/examples/chapter_5/for6.go @@ -0,0 +1,17 @@ +package main + +import "fmt" + +func main() { + +LABEL1: + for i := 0; i <= 5; i++ { + for j := 0; j <= 5; j++ { + if j == 4 { + continue LABEL1 + } + fmt.Printf("i is: %d, and j is: %d\n", i, j) + } + } + +} diff --git a/eBook/examples/chapter_5/for_string.go b/eBook/examples/chapter_5/for_string.go new file mode 100644 index 0000000..7b18921 --- /dev/null +++ b/eBook/examples/chapter_5/for_string.go @@ -0,0 +1,57 @@ +// for_string.go +package main + +import "fmt" + +func main() { + str := "Go is a beautiful language!" + fmt.Printf("The length of str is: %d\n", len(str)) + for ix :=0; ix < len(str); ix++ { + fmt.Printf("Character on position %d is: %c \n", ix, str[ix]) + } + str2 := "日本語" + fmt.Printf("The length of str2 is: %d\n", len(str2)) + for ix :=0; ix < len(str2); ix++ { + fmt.Printf("Character on position %d is: %c \n", ix, str2[ix]) + } +} +/* Output: +The length of str is: 27 +Character on position 0 is: G +Character on position 1 is: o +Character on position 2 is: +Character on position 3 is: i +Character on position 4 is: s +Character on position 5 is: +Character on position 6 is: a +Character on position 7 is: +Character on position 8 is: b +Character on position 9 is: e +Character on position 10 is: a +Character on position 11 is: u +Character on position 12 is: t +Character on position 13 is: i +Character on position 14 is: f +Character on position 15 is: u +Character on position 16 is: l +Character on position 17 is: +Character on position 18 is: l +Character on position 19 is: a +Character on position 20 is: n +Character on position 21 is: g +Character on position 22 is: u +Character on position 23 is: a +Character on position 24 is: g +Character on position 25 is: e +Character on position 26 is: ! +The length of str2 is: 9 +Character on position 0 is: æ +Character on position 1 is: — +Character on position 2 is: ¥ +Character on position 3 is: æ +Character on position 4 is: œ +Character on position 5 is: ¬ +Character on position 6 is: è +Character on position 7 is: ª +Character on position 8 is: ž +*/ \ No newline at end of file diff --git a/eBook/examples/chapter_5/goto.go b/eBook/examples/chapter_5/goto.go new file mode 100644 index 0000000..e61b47c --- /dev/null +++ b/eBook/examples/chapter_5/goto.go @@ -0,0 +1,12 @@ +package main + +func main() { + i:=0 + HERE: + print(i) + i++ + if i==5 { + return + } + goto HERE +} \ No newline at end of file diff --git a/eBook/examples/chapter_5/goto2.go b/eBook/examples/chapter_5/goto2.go new file mode 100644 index 0000000..705b9a9 --- /dev/null +++ b/eBook/examples/chapter_5/goto2.go @@ -0,0 +1,13 @@ +// compile error goto2.go:8: goto TARGET jumps over declaration of b at goto2.go:8 +package main + +import "fmt" + +func main() { + a := 1 + goto TARGET // compile error + b := 9 + TARGET: + b += a + fmt.Printf("a is %v *** b is %v", a, b) +} \ No newline at end of file diff --git a/eBook/examples/chapter_5/ifelse.go b/eBook/examples/chapter_5/ifelse.go new file mode 100644 index 0000000..0b8dc61 --- /dev/null +++ b/eBook/examples/chapter_5/ifelse.go @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + var first int = 10 + var cond int + + if first <= 0 { + fmt.Printf("first is less than or equal to 0\n") + } else if first > 0 && first < 5 { + fmt.Printf("first is between 0 and 5\n") + } else { + fmt.Printf("first is 5 or greater\n") + } + + if cond = 5; cond > 10 { + fmt.Printf("cond is greater than 10\n") + } else { + fmt.Printf("cond is not greater than 10\n") + } +} diff --git a/eBook/examples/chapter_5/range_string.go b/eBook/examples/chapter_5/range_string.go new file mode 100644 index 0000000..f5bdc2e --- /dev/null +++ b/eBook/examples/chapter_5/range_string.go @@ -0,0 +1,80 @@ +package main + +import "fmt" + +func main() { + str := "Go is a beautiful language!" + fmt.Printf("The length of str is: %d\n", len(str)) + for pos, char := range str { + fmt.Printf("Character on position %d is: %c \n", pos, char) + } + fmt.Println() + str2 := "Chinese: 日本語" + fmt.Printf("The length of str2 is: %d\n", len(str2)) + for pos, char := range str2 { + fmt.Printf("character %c starts at byte position %d\n", char, pos) + } + fmt.Println() + fmt.Println("index int(rune) rune char bytes") + for index, rune := range str2 { + fmt.Printf("%-2d %d %U '%c' % X\n", index, rune, rune, rune, []byte(string(rune))) + } +} +/* Output: +The length of str is: 27 +Character on position 0 is: G +Character on position 1 is: o +Character on position 2 is: +Character on position 3 is: i +Character on position 4 is: s +Character on position 5 is: +Character on position 6 is: a +Character on position 7 is: +Character on position 8 is: b +Character on position 9 is: e +Character on position 10 is: a +Character on position 11 is: u +Character on position 12 is: t +Character on position 13 is: i +Character on position 14 is: f +Character on position 15 is: u +Character on position 16 is: l +Character on position 17 is: +Character on position 18 is: l +Character on position 19 is: a +Character on position 20 is: n +Character on position 21 is: g +Character on position 22 is: u +Character on position 23 is: a +Character on position 24 is: g +Character on position 25 is: e +Character on position 26 is: ! + +The length of str2 is: 18 +character C starts at byte position 0 +character h starts at byte position 1 +character i starts at byte position 2 +character n starts at byte position 3 +character e starts at byte position 4 +character s starts at byte position 5 +character e starts at byte position 6 +character : starts at byte position 7 +character starts at byte position 8 +character 日 starts at byte position 9 +character 本 starts at byte position 12 +character 語 starts at byte position 15 + +index int(rune) rune char bytes +0 67 U+0043 'C' 43 +1 104 U+0068 'h' 68 +2 105 U+0069 'i' 69 +3 110 U+006E 'n' 6E +4 101 U+0065 'e' 65 +5 115 U+0073 's' 73 +6 101 U+0065 'e' 65 +7 58 U+003A ':' 3A +8 32 U+0020 ' ' 20 +9 26085 U+65E5 '日' E6 97 A5 +12 26412 U+672C '本' E6 9C AC +15 35486 U+8A9E '語' E8 AA 9E +*/ diff --git a/eBook/examples/chapter_5/string_conversion2.go b/eBook/examples/chapter_5/string_conversion2.go new file mode 100644 index 0000000..4add5eb --- /dev/null +++ b/eBook/examples/chapter_5/string_conversion2.go @@ -0,0 +1,25 @@ +package main + +import ( + "fmt" + "strconv" +) + +func main() { + var orig string = "ABC" + // var an int + var newS string + // var err error + + fmt.Printf("The size of ints is: %d\n", strconv.IntSize) + // anInt, err = strconv.Atoi(origStr) + an, err := strconv.Atoi(orig) + if err != nil { + fmt.Printf("orig %s is not an integer - exiting with error\n", orig) + return + } + fmt.Printf("The integer is %d\n", an) + an = an + 5 + newS = strconv.Itoa(an) + fmt.Printf("The new string is: %s\n", newS) +} diff --git a/eBook/examples/chapter_5/switch1.go b/eBook/examples/chapter_5/switch1.go new file mode 100644 index 0000000..a108596 --- /dev/null +++ b/eBook/examples/chapter_5/switch1.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func main() { + var num1 int = 100 + + switch num1 { + case 98, 99: + fmt.Println("It's equal to 98") + case 100: + fmt.Println("It's equal to 100") + default: + fmt.Println("It's not equal to 98 or 100") + } +} diff --git a/eBook/examples/chapter_5/switch2.go b/eBook/examples/chapter_5/switch2.go new file mode 100644 index 0000000..4f0afe8 --- /dev/null +++ b/eBook/examples/chapter_5/switch2.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func main() { + var num1 int = 7 + + switch { + case num1 < 0: + fmt.Println("Number is negative") + case num1 > 0 && num1 < 10: + fmt.Println("Number is between 0 and 10") + default: + fmt.Println("Number is 10 or greater") + } +} diff --git a/eBook/exercises/chapter_5/bitwise_complement.go b/eBook/exercises/chapter_5/bitwise_complement.go new file mode 100644 index 0000000..c2c2c56 --- /dev/null +++ b/eBook/exercises/chapter_5/bitwise_complement.go @@ -0,0 +1,22 @@ +package main + +import "fmt" + +func main() { + for i:=0; i <= 10; i ++ { + fmt.Printf("the complement of %b is: %b\n", i, ^i) + } +} +/* Output: +the complement of 0 is: -1 +the complement of 1 is: -10 +the complement of 10 is: -11 +the complement of 11 is: -100 +the complement of 100 is: -101 +the complement of 101 is: -110 +the complement of 110 is: -111 +the complement of 111 is: -1000 +the complement of 1000 is: -1001 +the complement of 1001 is: -1010 +the complement of 1010 is: -1011 +*/ \ No newline at end of file diff --git a/eBook/exercises/chapter_5/fizzbuzz.go b/eBook/exercises/chapter_5/fizzbuzz.go new file mode 100644 index 0000000..8b30374 --- /dev/null +++ b/eBook/exercises/chapter_5/fizzbuzz.go @@ -0,0 +1,24 @@ +package main + +import "fmt" + +const ( + FIZZ=3 + BUZZ=5 + FIZZBUZZ=15 +) + +func main() { + for i:=0; i <= 100; i++ { + switch { + case i%FIZZBUZZ==0: + fmt.Println("FizzBuzz") + case i%FIZZ==0: + fmt.Println("Fizz") + case i%BUZZ==0: + fmt.Println("Buzz") + default: + fmt.Println(i) + } + } +} \ No newline at end of file diff --git a/eBook/exercises/chapter_5/for_character.go b/eBook/exercises/chapter_5/for_character.go new file mode 100644 index 0000000..98979a4 --- /dev/null +++ b/eBook/exercises/chapter_5/for_character.go @@ -0,0 +1,17 @@ +package main + +func main() { + // 1 - use 2 nested for loops + for i:=1; i <= 25; i++ { + for j:=1; j <=i; j++ { + print("G") + } + println() + } + // 2 - use only one for loop and string concatenation + str := "G" + for i:=1; i <= 25; i++ { + println(str) + str += "G" + } +} \ No newline at end of file diff --git a/eBook/exercises/chapter_5/for_loop.go b/eBook/exercises/chapter_5/for_loop.go new file mode 100644 index 0000000..b7eeb95 --- /dev/null +++ b/eBook/exercises/chapter_5/for_loop.go @@ -0,0 +1,16 @@ +package main + +import "fmt" + +func main() { + // 1: + for i:=0; i < 15; i++ { + fmt.Printf("The counter is at %d\n", i) + } + // 2: + i := 0 +START: + fmt.Printf("The counter is at %d\n", i) + i++ + if i < 15 { goto START } +} \ No newline at end of file diff --git a/eBook/exercises/chapter_5/i_undefined.go b/eBook/exercises/chapter_5/i_undefined.go new file mode 100644 index 0000000..d4b47e9 --- /dev/null +++ b/eBook/exercises/chapter_5/i_undefined.go @@ -0,0 +1,14 @@ +// i_undefined.go +package main + +import ( + "fmt" +) + +func main() { + var i int + for i=0; i<10; i++ { + fmt.Printf("%v\n", i) + } + fmt.Printf("%v\n", i) //<-- compile error: undefined i +} diff --git a/eBook/exercises/chapter_5/multiple_for.go b/eBook/exercises/chapter_5/multiple_for.go new file mode 100644 index 0000000..2e7f3d9 --- /dev/null +++ b/eBook/exercises/chapter_5/multiple_for.go @@ -0,0 +1,17 @@ +// multiple_for.go +package main + +import "fmt" + +func main() { + //multiple initialization; a consolidated bool expression with && and ||; multiple ‘incrementation’ + for i, j, s := 0, 5, "a"; i < 3 && j < 100 && s != "aaaaa"; i, j, s = i+1, + j+1, s + "a" { + fmt.Println("Value of i, j, s:", i, j, s) + } +} +/* Output: +Value of i, j, s: 0 5 a +Value of i, j, s: 1 6 aa +Value of i, j, s: 2 7 aaa +*/ \ No newline at end of file diff --git a/eBook/exercises/chapter_5/rectangle_stars.go b/eBook/exercises/chapter_5/rectangle_stars.go new file mode 100644 index 0000000..bb5def0 --- /dev/null +++ b/eBook/exercises/chapter_5/rectangle_stars.go @@ -0,0 +1,26 @@ +// rectangle_stars.go +package main + +import "fmt" + +func main() { + w, h := 20, 10 + for y := 0; y < h; y++ { + for x := 0; x < w; x++ { + fmt.Print("*") + } + fmt.Println() + } +} +/* Output: +******************** +******************** +******************** +******************** +******************** +******************** +******************** +******************** +******************** +******************** +*/ diff --git a/eBook/exercises/chapter_5/season.go b/eBook/exercises/chapter_5/season.go new file mode 100644 index 0000000..1a21bed --- /dev/null +++ b/eBook/exercises/chapter_5/season.go @@ -0,0 +1,17 @@ +package main + +import "fmt" + +func main() { + fmt.Printf(Season(3)) +} + +func Season(month int) string { + switch month { + case 12,1,2: return "Winter" + case 3,4,5: return "Spring" + case 6,7,8: return "Summer" + case 9,10,11: return "Autumn" + } + return "Season unknown" +} \ No newline at end of file