Unknown 12 vuotta sitten
vanhempi
commit
67be80ca8c
100 muutettua tiedostoa jossa 56 lisäystä ja 7307 poistoa
  1. 1 1
      README.md
  2. 1 16
      eBook/05.0.md
  3. 16 1
      eBook/05.1.md
  4. 5 0
      eBook/directory.md
  5. 0 1027
      eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm
  6. 0 205
      eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/all.css
  7. 0 47
      eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/ga.js
  8. 0 190
      eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/godocs.js
  9. 0 36
      eBook/examples/chapter_15/client.go
  10. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i
  11. 0 1
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/branch
  12. 0 2
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/branchheads
  13. 0 2
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/tags
  14. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/dirstate
  15. 0 2
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc
  16. 0 4
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires
  17. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i
  18. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i
  19. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i
  20. BIN
      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
  21. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i
  22. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i
  23. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/codereview.cfg.i
  24. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/dict/dict.go.i
  25. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i
  26. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i
  27. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/types.go.i
  28. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/write.go.i
  29. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/client.go.i
  30. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie.go.i
  31. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie__test.go.i
  32. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i
  33. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi__test.go.i
  34. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i
  35. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i
  36. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket__test.go.i
  37. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i
  38. 0 19
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache
  39. BIN
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo
  40. 0 0
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks
  41. 0 1
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch
  42. 0 3
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc
  43. 0 0
      eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate
  44. 0 2
      eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore
  45. 0 3
      eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS
  46. 0 3
      eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS
  47. 0 27
      eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE
  48. 0 3
      eBook/examples/chapter_15/code.google.com/p/go.net/README
  49. 0 2
      eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg
  50. 0 210
      eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go
  51. 0 312
      eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go
  52. 0 497
      eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go
  53. 0 369
      eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go
  54. 0 285
      eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go
  55. 0 137
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go
  56. 0 695
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go
  57. 0 201
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go
  58. 0 549
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go
  59. 0 584
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go
  60. 0 102
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go
  61. 0 412
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go
  62. 0 274
      eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go
  63. 0 29
      eBook/examples/chapter_15/dial.go
  64. 0 146
      eBook/examples/chapter_15/elaborated_webserver.go
  65. 0 24
      eBook/examples/chapter_15/hello_world_webserver.go
  66. 0 23
      eBook/examples/chapter_15/http_fetch.go
  67. 0 17
      eBook/examples/chapter_15/pipeline1.go
  68. 0 30
      eBook/examples/chapter_15/poll_url.go
  69. 0 14
      eBook/examples/chapter_15/predefined_functions.go
  70. 0 57
      eBook/examples/chapter_15/robust_webserver.go
  71. 0 39
      eBook/examples/chapter_15/rpc_client.go
  72. 0 13
      eBook/examples/chapter_15/rpc_objects.go
  73. 0 32
      eBook/examples/chapter_15/rpc_server.go
  74. 0 37
      eBook/examples/chapter_15/server.go
  75. 0 86
      eBook/examples/chapter_15/simple_tcp_server.go
  76. 0 42
      eBook/examples/chapter_15/simple_webserver.go
  77. 0 30
      eBook/examples/chapter_15/smtp.go
  78. 0 29
      eBook/examples/chapter_15/smtp_auth.go
  79. 0 32
      eBook/examples/chapter_15/socket.go
  80. 0 23
      eBook/examples/chapter_15/template_field.go
  81. 0 26
      eBook/examples/chapter_15/template_ifelse.go
  82. 0 22
      eBook/examples/chapter_15/template_validation.go
  83. 0 24
      eBook/examples/chapter_15/template_variables.go
  84. 0 20
      eBook/examples/chapter_15/template_with_end.go
  85. 0 42
      eBook/examples/chapter_15/twitter_status.go
  86. 0 29
      eBook/examples/chapter_15/websocket_client.go
  87. 0 29
      eBook/examples/chapter_15/websocket_server.go
  88. 0 2
      eBook/examples/chapter_15/wiki/ANewPage.txt
  89. 0 1
      eBook/examples/chapter_15/wiki/TestPage.txt
  90. 0 6
      eBook/examples/chapter_15/wiki/edit.html
  91. 0 2
      eBook/examples/chapter_15/wiki/page.txt
  92. 0 1
      eBook/examples/chapter_15/wiki/page1.txt
  93. 0 3
      eBook/examples/chapter_15/wiki/page5.txt
  94. 0 5
      eBook/examples/chapter_15/wiki/view.html
  95. 0 97
      eBook/examples/chapter_15/wiki/wiki.go
  96. 0 32
      eBook/examples/chapter_15/wiki/wiki_part1.go
  97. 0 39
      eBook/examples/chapter_15/wiki/wiki_part2.go
  98. 12 0
      eBook/examples/chapter_5/booleans.go
  99. 9 0
      eBook/examples/chapter_5/for1.go
  100. 12 0
      eBook/examples/chapter_5/for2.go

+ 1 - 1
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 语言在国内的学习群体,给大家提供更好的学习资源。

+ 1 - 16
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 节)
 
 可以使用迭代或循环结构来重复执行一次或多次某段代码(任务):

+ 16 - 1
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)

+ 5 - 0
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

+ 0 - 1027
eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language.htm

@@ -1,1027 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0035)http://golang.org/doc/codelab/wiki/ -->
-<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-
-  <title>Codelab: Writing Web Applications - The Go Programming Language</title>
-<link rel="stylesheet" href="./Codelab  Writing Web Applications - The Go Programming Language_files/all.css" type="text/css" media="all" charset="utf-8">
-<!--[if lt IE 8]>
-<link rel="stylesheet" href="/doc/ie.css" type="text/css">
-<![endif]-->
-<script type="text/javascript" async="" src="./Codelab  Writing Web Applications - The Go Programming Language_files/ga.js"></script><script type="text/javascript" src="./Codelab  Writing Web Applications - The Go Programming Language_files/godocs.js"></script>
-<script type="text/javascript">
-var _gaq = _gaq || [];
-_gaq.push(["_setAccount", "UA-11222381-2"]);
-_gaq.push(["_trackPageview"]);
-</script>
-</head>
-<body id="top">
-<div id="container">
-  <div id="topnav">
-    <h1 id="title">The Go Programming Language</h1>
-    <div id="nav-main">
-      <ul>
-      <li><a href="http://golang.org/">Home</a></li><li><a href="http://golang.org/doc/install.html">Getting Started</a></li><li><a href="http://golang.org/doc/docs.html">Documentation</a></li><li><a href="http://golang.org/doc/contrib.html">Contributing</a></li><li><a href="http://golang.org/doc/community.html">Community</a></li>
-      </ul>
-      <div class="quickref">
-        <form method="GET" action="http://golang.org/search">
-        References:
-        <a href="http://golang.org/pkg/">Packages</a> <span class="sep">|</span>
-        <a href="http://golang.org/cmd/">Commands</a> <span class="sep">|</span>
-        <a href="http://golang.org/doc/go_spec.html">Specification</a>
-        <input id="search" type="search" name="q" value="" class="inactive" placeholder="code search" results="0">
-        </form>
-      </div>
-    </div>
-    <a id="logo-box" href="http://golang.org/"></a>
-  </div>
-  <div id="content">
-  <!-- Menu is HTML-escaped elsewhere -->
-
-    <h1 id="generatedHeader">Codelab: Writing Web Applications</h1>
-
-  <!-- The Table of Contents is automatically inserted in this <div>.
-       Do not delete this <div>. -->
-  <div id="nav"><table class="unruled"><tbody><tr><td class="first"><dl><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_13">Introduction</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_23">Getting Started</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_39">Data Structures</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_87">Introducing the http package (an interlude)</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_111">Using http to serve wiki pages</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_139">Editing Pages</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_153">The template package</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_191">Handling non-existent pages</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_199">Saving Pages</a></dt></dl></td><td><dl><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_209">Error handling</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_225">Template caching</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_242">Validation</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_262">Introducing Function Literals and Closures</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_290">Try it out!</a></dt><dt><a href="http://golang.org/doc/codelab/wiki/#tmp_300">Other tasks</a></dt></dl></td></tr></tbody></table></div>
-
-  <!-- Content is HTML-escaped elsewhere -->
-  <!-- Codelab: Writing Web Applications -->
-<h2 id="tmp_13">Introduction<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-Covered in this codelab:
-</p>
-<ul>
-<li>Creating a data structure with load and save methods</li>
-<li>Using the <code>http</code> package to build web applications
-</li><li>Using the <code>template</code> package to process HTML templates</li>
-<li>Using the <code>regexp</code> package to validate user input</li>
-<li>Using closures</li>
-</ul>
-
-<p>
-Assumed knowledge:
-</p>
-<ul>
-<li>Programming experience</li>
-<li>Understanding of basic web technologies (HTTP, HTML)</li>
-<li>Some UNIX command-line knowledge</li>
-</ul>
-
-<h2 id="tmp_23">Getting Started<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-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 
-<a href="http://www.virtualbox.org/">VirtualBox</a> or similar) or a
-<a href="http://www.google.com/search?q=virtual+private+server">Virtual 
-Private Server</a>.
-</p>
-
-<p>
-Install Go (see the <a href="http://golang.org/doc/install.html">Installation Instructions</a>).
-</p>
-
-<p>
-Make a new directory for this codelab and cd to it:
-</p>
-
-<pre>$ mkdir ~/gowiki
-$ cd ~/gowiki
-</pre>
-
-<p>
-Create a file named <code>wiki.go</code>, open it in your favorite editor, and 
-add the following lines:
-</p>
-
-<pre>package main
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-)
-</pre>
-
-<p>
-We import the <code>fmt</code>, <code>ioutil</code> and <code>os</code>
-packages from the Go standard library. Later, as we implement additional
-functionality, we will add more packages to this <code>import</code>
-declaration.
-</p>
-
-<h2 id="tmp_39">Data Structures<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-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 <code>Page</code> as a struct with two fields representing
-the title and body.
-</p>
-
-<pre>type Page struct {
-	Title	string
-	Body	[]byte
-}
-</pre>
-
-<p>
-The type <code>[]byte</code> means "a <code>byte</code> slice". 
-(See <a href="http://golang.org/doc/effective_go.html#slices">Effective Go</a> 
-for more on slices.)  
-The <code>Body</code> element is a <code>[]byte</code> rather than
-<code>string</code> because that is the type expected by the <code>io</code>
-libraries we will use, as you'll see below.
-</p>
-
-<p>
-The <code>Page</code> struct describes how page data will be stored in memory. 
-But what about persistent storage? We can address that by creating a 
-<code>save</code> method on <code>Page</code>:
-</p>
-
-<pre>func (p *Page) save() os.Error {
-	filename := p.Title + ".txt"
-	return ioutil.WriteFile(filename, p.Body, 0600)
-}
-</pre>
-
-<p>
-This method's signature reads: "This is a method named <code>save</code> that
-takes as its receiver <code>p</code>, a pointer to <code>Page</code> . It takes
-no parameters, and returns a value of type <code>os.Error</code>." 
-</p>
-
-<p>
-This method will save the <code>Page</code>'s <code>Body</code> to a text 
-file. For simplicity, we will use the <code>Title</code> as the file name.
-</p>
-
-<p>
-The <code>save</code> method returns an <code>os.Error</code> value because
-that is the return type of <code>WriteFile</code> (a standard library function
-that writes a byte slice to a file).  The <code>save</code> method returns the
-error value, to let the application handle it should anything go wrong while
-writing the file.  If all goes well, <code>Page.save()</code> will return
-<code>nil</code> (the zero-value for pointers, interfaces, and some other 
-types).
-</p>
-
-<p>
-The octal integer constant <code>0600</code>, passed as the third parameter to
-<code>WriteFile</code>, indicates that the file should be created with
-read-write permissions for the current user only. (See the Unix man page
-<code>open(2)</code> for details.)
-</p>
-
-<p>
-We will want to load pages, too:
-</p>
-
-<pre>func loadPage(title string) *Page {
-	filename := title + ".txt"
-	body, _ := ioutil.ReadFile(filename)
-	return &amp;Page{Title: title, Body: body}
-}
-</pre>
-
-<p>
-The function <code>loadPage</code> constructs the file name from
-<code>Title</code>, reads the file's contents into a new
-<code>Page</code>, and returns a pointer to that new <code>page</code>.
-</p>
-
-<p>
-Functions can return multiple values. The standard library function 
-<code>io.ReadFile</code> returns <code>[]byte</code> and <code>os.Error</code>. 
-In <code>loadPage</code>, error isn't being handled yet; the "blank identifier"
-represented by the underscore (<code>_</code>) symbol is used to throw away the
-error return value (in essence, assigning the value to nothing). 
-</p>
-
-<p>
-But what happens if <code>ReadFile</code> encounters an error?  For example,
-the file might not exist. We should not ignore such errors.  Let's modify the
-function to return <code>*Page</code> and <code>os.Error</code>.
-</p>
-
-<pre>func loadPage(title string) (*Page, os.Error) {
-	filename := title + ".txt"
-	body, err := ioutil.ReadFile(filename)
-	if err != nil {
-		return nil, err
-	}
-	return &amp;Page{Title: title, Body: body}, nil
-}
-</pre>
-
-<p>
-Callers of this function can now check the second parameter; if it is
-<code>nil</code> then it has successfully loaded a Page. If not, it will be an
-<code>os.Error</code> that can be handled by the caller (see the <a href="http://golang.org/pkg/os/#Error">os package documentation</a> for 
-details).
-</p>
-
-<p>
-At this point we have a simple data structure and the ability to save to and
-load from a file. Let's write a <code>main</code> function to test what we've
-written:
-</p>
-
-<pre>func main() {
-	p1 := &amp;Page{Title: "TestPage", Body: []byte("This is a sample Page.")}
-	p1.save()
-	p2, _ := loadPage("TestPage")
-	fmt.Println(string(p2.Body))
-}
-</pre>
-
-<p>
-After compiling and executing this code, a file named <code>TestPage.txt</code>
-would be created, containing the contents of <code>p1</code>. The file would
-then be read into the struct <code>p2</code>, and its <code>Body</code> element
-printed to the screen.
-</p>
-
-<p>
-You can compile and run the program like this: 
-</p>
-
-<pre>$ 8g wiki.go
-$ 8l wiki.8
-$ ./8.out
-This is a sample page.
-</pre>
-
-<p>
-(The <code>8g</code> and <code>8l</code> commands are applicable to
-<code>GOARCH=386</code>. If you're on an <code>amd64</code> system,
-substitute 6's for the 8's.)
-</p>
-
-<p>
-<a href="http://golang.org/doc/codelab/wiki/part1.go">Click here to view the code we've written so far.</a>
-</p>
-
-<h2 id="tmp_87">Introducing the <code>http</code> package (an interlude)<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-Here's a full working example of a simple web server:
-</p>
-
-<pre>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)
-}
-</pre>
-
-<p>
-The <code>main</code> function begins with a call to 
-<code>http.HandleFunc</code>, which tells the <code>http</code> package to 
-handle all requests to the web root (<code>"/"</code>) with 
-<code>handler</code>. 
-</p>
-
-<p>
-It then calls <code>http.ListenAndServe</code>, specifying that it should
-listen on port 8080 on any interface (<code>":8080"</code>). (Don't
-worry about its second parameter, <code>nil</code>, for now.)
-This function will block until the program is terminated.
-</p>
-
-<p>
-The function <code>handler</code> is of the type <code>http.HandlerFunc</code>.
-It takes an <code>http.ResponseWriter</code> and an <code>http.Request</code> as
-its arguments.
-</p>
-
-<p>
-An <code>http.ResponseWriter</code> value assembles the HTTP server's response; by writing 
-to it, we send data to the HTTP client.
-</p>
-
-<p>
-An <code>http.Request</code> is a data structure that represents the client
-HTTP request.  The string <code>r.URL.Path</code> is the path component
-of the request URL.  The trailing <code>[1:]</code> means
-"create a sub-slice of <code>Path</code> from the 1st character to the end." 
-This drops the leading "/" from the path name.
-</p>
-
-<p>
-If you run this program and access the URL: 
-</p>
-<pre>http://localhost:8080/monkeys</pre>
-<p>
-the program would present a page containing:
-</p>
-<pre>Hi there, I love monkeys!</pre>
-
-<h2 id="tmp_111">Using <code>http</code> to serve wiki pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-To use the <code>http</code> package, it must be imported:
-</p>
-
-<pre>import (
-	"fmt"
-	<b>"http"</b>
-	"io/ioutil"
-	"os"
-)
-</pre>
-
-<p>
-Let's create a handler to view a wiki page: 
-</p>
-
-<pre>const lenPath = len("/view/")
-
-func viewHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, _ := loadPage(title)
-	fmt.Fprintf(w, "&lt;h1&gt;%s&lt;/h1&gt;&lt;div&gt;%s&lt;/div&gt;", p.Title, p.Body)
-}
-</pre>
-
-<p>
-First, this function extracts the page title from <code>r.URL.Path</code>,
-the path component of the request URL. The global constant 
-<code>lenPath</code> is the length of the leading <code>"/view/"</code>
-component of the request path.
-The <code>Path</code> is re-sliced with <code>[lenPath:]</code> to drop the 
-first 6 characters of the string. This is because the path will invariably 
-begin with <code>"/view/"</code>, which is not part of the page title.
-</p>
-
-<p>
-The function then loads the page data, formats the page with a string of simple 
-HTML, and writes it to <code>w</code>, the <code>http.ResponseWriter</code>. 
-</p>
-
-<p>
-Again, note the use of <code>_</code> to ignore the <code>os.Error</code> 
-return value from <code>loadPage</code>. This is done here for simplicity
-and generally considered bad practice. We will attend to this later.
-</p>
-
-<p>
-To use this handler, we create a <code>main</code> function that
-initializes <code>http</code> using the <code>viewHandler</code> to handle
-any requests under the path <code>/view/</code>.
-</p>
-
-<pre>func main() {
-	http.HandleFunc("/view/", viewHandler)
-	http.ListenAndServe(":8080", nil)
-}
-</pre>
-
-<p>
-<a href="http://golang.org/doc/codelab/wiki/part2.go">Click here to view the code we've written so far.</a>
-</p>
-
-<p>
-Let's create some page data (as <code>test.txt</code>), compile our code, and
-try serving a wiki page:
-</p>
-
-<pre>$ echo "Hello world" &gt; test.txt
-$ 8g wiki.go
-$ 8l wiki.8
-$ ./8.out
-</pre>
-
-<p>
-With this web server running, a visit to <code><a href="http://localhost:8080/view/test">http://localhost:8080/view/test</a></code>
-should show a page titled "test" containing the words "Hello world".
-</p>
-
-<h2 id="tmp_139">Editing Pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-A wiki is not a wiki without the ability to edit pages. Let's create two new
-handlers: one named <code>editHandler</code> to display an 'edit page' form,
-and the other named <code>saveHandler</code> to save the data entered via the
-form.
-</p>
-
-<p>
-First, we add them to <code>main()</code>: 
-</p>
-
-<pre>func main() {
-	http.HandleFunc("/view/", viewHandler)
-	http.HandleFunc("/edit/", editHandler)
-	http.HandleFunc("/save/", saveHandler)
-	http.ListenAndServe(":8080", nil)
-}
-</pre>
-
-<p>
-The function <code>editHandler</code> loads the page 
-(or, if it doesn't exist, create an empty <code>Page</code> struct), 
-and displays an HTML form.
-</p>
-
-<pre>func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	fmt.Fprintf(w, "&lt;h1&gt;Editing %s&lt;/h1&gt;"+
-		"&lt;form action=\"/save/%s\" method=\"POST\"&gt;"+
-		"&lt;textarea name=\"body\"&gt;%s&lt;/textarea&gt;&lt;br&gt;"+
-		"&lt;input type=\"submit\" value=\"Save\"&gt;"+
-		"&lt;/form&gt;",
-		p.Title, p.Title, p.Body)
-}
-</pre>
-
-<p>
-This function will work fine, but all that hard-coded HTML is ugly.
-Of course, there is a better way.
-</p>
- 
-<h2 id="tmp_153">The <code>template</code> package<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-The <code>template</code> package is part of the Go standard library.  We can
-use <code>template</code> to keep the HTML in a separate file, allowing
-us to change the layout of our edit page without modifying the underlying Go
-code.
-</p>
-
-<p>
-First, we must add <code>template</code> to the list of imports:
-</p>
-
-<pre>import (
-	"http"
-	"io/ioutil"
-	"os"
-	<b>"template"</b>
-)
-</pre>
-
-<p>
-Let's create a template file containing the HTML form. 
-Open a new file named <code>edit.html</code>, and add the following lines:
-</p>
-
-<pre>&lt;h1&gt;Editing {Title}&lt;/h1&gt;
-
-&lt;form action="/save/{Title}" method="POST"&gt;
-&lt;div&gt;&lt;textarea name="body" rows="20" cols="80"&gt;{Body|html}&lt;/textarea&gt;&lt;/div&gt;
-&lt;div&gt;&lt;input type="submit" value="Save"&gt;&lt;/div&gt;
-&lt;/form&gt;
-</pre>
-
-<p>
-Modify <code>editHandler</code> to use the template, instead of the hard-coded
-HTML:
-</p>
-
-<pre>func editHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	p, err := loadPage(title)
-	if err != nil {
-		p = &amp;Page{Title: title}
-	}
-	t, _ := template.ParseFile("edit.html", nil)
-	t.Execute(w, p)
-}
-</pre>
-
-<p>
-The function <code>template.ParseFile</code> will read the contents of 
-<code>edit.html</code> and return a <code>*template.Template</code>. 
-</p>
-
-<p>
-The method <code>t.Execute</code> replaces all occurrences of 
-<code>{Title}</code> and <code>{Body}</code> with the values of 
-<code>p.Title</code> and <code>p.Body</code>, and writes the resultant
-HTML to the <code>http.ResponseWriter</code>.
-</p>
-
-<p>
-Note that we've used <code>{Body|html}</code> in the above template.  
-The <code>|html</code> part asks the template engine to pass the value
-<code>Body</code> through the <code>html</code> formatter before outputting it,
-which escapes HTML characters (such as replacing <code>&gt;</code> with 
-<code>&amp;gt;</code>). 
-This will prevent user data from corrupting the form HTML. 
-</p>
-
-<p>
-Now that we've removed the <code>fmt.Fprintf</code> statement, we can remove
-<code>"fmt"</code> from the <code>import</code> list.
-</p>
-
-<p>
-While we're working with templates, let's create a template for our
-<code>viewHandler</code> called <code>view.html</code>:
-</p>
-
-<pre>&lt;h1&gt;{Title}&lt;/h1&gt;
-
-&lt;p&gt;[&lt;a href="/edit/{Title}"&gt;edit&lt;/a&gt;]&lt;/p&gt;
-
-&lt;div&gt;{Body}&lt;/div&gt;
-</pre>
-
-<p>
-Modify <code>viewHandler</code> accordingly:
-</p>
-
-<pre>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)
-}
-</pre>
-
-<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:
-</p>
-
-<pre>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 = &amp;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)
-}
-</pre>
-
-<p>
-The handlers are now shorter and simpler. 
-</p>
-
-<h2 id="tmp_191">Handling non-existent pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-What if you visit <code>/view/APageThatDoesntExist</code>? The program will 
-crash. This is because it ignores the error return value from
-<code>loadPage</code>. Instead, if the requested Page doesn't exist, it should 
-redirect the client to the edit Page so the content may be created:
-</p>
-
-<pre>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)
-}
-</pre>
-
-<p>
-The <code>http.Redirect</code> function adds an HTTP status code of 
-<code>http.StatusFound</code> (302) and a <code>Location</code>
-header to the HTTP response.
-</p>
-
-<h2 id="tmp_199">Saving Pages<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-The function <code>saveHandler</code> will handle the form submission. 
-</p>
-
-<pre>func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title := r.URL.Path[lenPath:]
-	body := r.FormValue("body")
-	p := &amp;Page{Title: title, Body: []byte(body)}
-	p.save()
-	http.Redirect(w, r, "/view/"+title, http.StatusFound)
-}
-</pre>
-
-<p>
-The page title (provided in the URL) and the form's only field, 
-<code>Body</code>, are stored in a new <code>Page</code>. 
-The <code>save()</code> method is then called to write the data to a file,
-and the client is redirected to the <code>/view/</code> page.
-</p>
-
-<p>
-The value returned by <code>FormValue</code> is of type <code>string</code>.
-We must convert that value to <code>[]byte</code> before it will fit into 
-the <code>Page</code> struct.  We use <code>[]byte(body)</code> to perform
-the conversion.
-</p>
-
-<h2 id="tmp_209">Error handling<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-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.
-</p>
-
-<p>
-First, let's handle the errors in <code>renderTemplate</code>:
-</p>
-
-<pre>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)
-	}
-}
-</pre>
-
-<p>
-The <code>http.Error</code> 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.
-</p>
-
-<p>
-Now let's fix up <code>saveHandler</code>:
-</p>
-
-<pre>func saveHandler(w http.ResponseWriter, r *http.Request) {
-	title, err := getTitle(w, r)
-	if err != nil {
-		return
-	}
-	body := r.FormValue("body")
-	p := &amp;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)
-}
-</pre>
-
-<p>
-Any errors that occur during <code>p.save()</code> will be reported 
-to the user.
-</p>
-
-<h2 id="tmp_225">Template caching<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-There is an inefficiency in this code: <code>renderTemplate</code> calls 
-<code>ParseFile</code> every time a page is rendered. 
-A better approach would be to call <code>ParseFile</code> once for each 
-template at program initialization, and store the resultant 
-<code>*Template</code> values in a data structure for later use.
-</p>
-
-<p>
-First we create a global map named <code>templates</code> in which to store 
-our <code>*Template</code> values, keyed by <code>string</code> 
-(the template name):
-</p>
-
-<pre>var templates = make(map[string]*template.Template)
-</pre>
-
-<p>
-Then we create an <code>init</code> function, which will be called before
-<code>main</code> at program initialization. The function
-<code>template.MustParseFile</code> is a convenience wrapper around
-<code>ParseFile</code> 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.
-</p>
-
-<pre>func init() {
-	for _, tmpl := range []string{"edit", "view"} {
-		templates[tmpl] = template.MustParseFile(tmpl+".html", nil)
-	}
-}
-</pre>
-
-<p>
-A <code>for</code> loop is used with a <code>range</code> 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.
-</p>
-
-<p>
-We then modify our <code>renderTemplate</code> function to call 
-the <code>Execute</code> method on the appropriate <code>Template</code> from 
-<code>templates</code>:
-
-</p><pre>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)
-	}
-}
-</pre>
-
-<h2 id="tmp_242">Validation<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-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.
-</p>
-
-<p>
-First, add <code>"regexp"</code> to the <code>import</code> list.
-Then we can create a global variable to store our validation regexp:
-</p>
-
-<pre>var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
-</pre>
-
-<p>
-The function <code>regexp.MustCompile</code> will parse and compile the 
-regular expression, and return a <code>regexp.Regexp</code>. 
-<code>MustCompile</code>, like <code>template.MustParseFile</code>,
-is distinct from <code>Compile</code> in that it will panic if 
-the expression compilation fails, while <code>Compile</code> returns an 
-<code>os.Error</code> as a second parameter. 
-</p>
-
-<p>
-Now, let's write a function that extracts the title string from the request 
-URL, and tests it against our <code>TitleValidator</code> expression:
-</p>
-
-<pre>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
-}
-</pre>
-
-<p>
-If the title is valid, it will be returned along with a <code>nil</code>
-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. 
-</p>
-
-<p>
-Let's put a call to <code>getTitle</code> in each of the handlers:
-</p>
-
-<pre>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 = &amp;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 := &amp;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)
-}
-</pre>
-
-<h2 id="tmp_262">Introducing Function Literals and Closures<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-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 
-<a href="http://golang.org/doc/go_spec.html#Function_declarations">function 
-literals</a> provide a powerful means of abstracting functionality 
-that can help us here.
-</p>
-
-<p>
-First, we re-write the function definition of each of the handlers to accept
-a title string:
-</p>
-
-<pre>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)
-</pre>
-
-<p>
-Now let's define a wrapper function that <i>takes a function of the above
-type</i>, and returns a function of type <code>http.HandlerFunc</code>
-(suitable to be passed to the function <code>http.HandleFunc</code>):
-</p>
-
-<pre>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'
-	}
-}
-</pre>
-
-<p>
-The returned function is called a closure because it encloses values defined
-outside of it. In this case, the variable <code>fn</code> (the single argument
-to <code>makeHandler</code>) is enclosed by the closure. The variable
-<code>fn</code> will be one of our save, edit, or view handlers.
-</p>
-
-<p>
-Now we can take the code from <code>getTitle</code> and use it here
-(with some minor modifications):
-</p>
-
-<pre>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)
-	}
-}
-</pre>
-
-<p>
-The closure returned by <code>makeHandler</code> is a function that takes
-an <code>http.ResponseWriter</code> and <code>http.Request</code> (in other
-words, an <code>http.HandlerFunc</code>). 
-The closure extracts the <code>title</code> from the request path, and
-validates it with the <code>TitleValidator</code> regexp. If the
-<code>title</code> is invalid, an error will be written to the
-<code>ResponseWriter</code> using the <code>http.NotFound</code> function. 
-If the <code>title</code> is valid, the enclosed handler function
-<code>fn</code> will be called with the <code>ResponseWriter</code>,
-<code>Request</code>, and <code>title</code> as arguments.
-</p>
-
-<p>
-Now we can wrap the handler functions with <code>makeHandler</code> in 
-<code>main</code>, before they are registered with the <code>http</code> 
-package:
-</p>
-
-<pre>func main() {
-	http.HandleFunc("/view/", makeHandler(viewHandler))
-	http.HandleFunc("/edit/", makeHandler(editHandler))
-	http.HandleFunc("/save/", makeHandler(saveHandler))
-	http.ListenAndServe(":8080", nil)
-}
-</pre>
-
-<p>
-Finally we remove the calls to <code>getTitle</code> from the handler functions,
-making them much simpler:
-</p>
-
-<pre>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 = &amp;Page{Title: title}
-	}
-	renderTemplate(w, "edit", p)
-}
-
-func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
-	body := r.FormValue("body")
-	p := &amp;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)
-}
-</pre>
-
-<h2 id="tmp_290">Try it out!<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-<a href="http://golang.org/doc/codelab/wiki/final.go">Click here to view the final code listing.</a>
-</p>
-
-<p>
-Recompile the code, and run the app:
-</p>
-
-<pre>$ 8g wiki.go
-$ 8l wiki.8
-$ ./8.out
-</pre>
-
-<p>
-Visiting <a href="http://localhost:8080/view/ANewPage">http://localhost:8080/view/ANewPage</a>
-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.
-</p>
-
-<h2 id="tmp_300">Other tasks<span class="navtop"><a href="http://golang.org/doc/codelab/wiki/#top">[Top]</a></span></h2>
-
-<p>
-Here are some simple tasks you might want to tackle on your own:
-</p>
-
-<ul>
-<li>Store templates in <code>tmpl/</code> and page data in <code>data/</code>.
-</li><li>Add a handler to make the web root redirect to 
-	<code>/view/FrontPage</code>.</li>
-<li>Spruce up the page templates by making them valid HTML and adding some
-	CSS rules.</li>
-<li>Implement inter-page linking by converting instances of 
-	<code>[PageName]</code> to <br>
-	<code>&lt;a href="/view/PageName"&gt;PageName&lt;/a&gt;</code>.
-	(hint: you could use <code>regexp.ReplaceAllFunc</code> to do this)
-	</li>
-</ul>
-
-  </div>
-  <div id="site-info">
-    <p>release.r58.1 8699. Except as noted, this content is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 License</a>.</p>
-  </div>
-</div>
-<script type="text/javascript">
-(function() {
-  var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;
-  ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";
-  var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);
-})();
-</script>
-
-
-<!-- generated at Wed Jul 13 14:07:27 EST 2011 -->
-</body></html>

+ 0 - 205
eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/all.css

@@ -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;
-}

+ 0 - 47
eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/ga.js

@@ -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<d[r];e++)if(d[e]){var f=d[e][m]("=");f<0?c(d[e],"1"):c(d[e][x](0,f),d[e][x](f+1))}}function xa(a,b){if(B(a))return"-";if("["==a[l](0)&&"]"==a[l](a[r]-1))return"-";var c=F.domain;c+=b&&b!="/"?b:"";return a[m](c)==(a[m]("http://")==0?7:a[m]("https://")==0?8:0)?"0":a};var ya=0;function G(a){return(a?"_":"")+ya++}var za=G(),Aa=G(),Ba=G(),H=G(),J=G(),K=G(),L=G(),Ca=G(),Da=G(),Ea=G(),Fa=G(),Ga=G(),Ha=G(),Ia=G(),Ja=G(),Ka=G(),La=G(),Ma=G(),Na=G(),Oa=G(),Pa=G(),Qa=G(),Ra=G(),Sa=G(),Ta=G(),Ua=G(),Va=G(),Wa=G(),Xa=G(),Ya=G(),Za=G(),$a=G(),ab=G(),bb=G(),cb=G();G();
-var M=G(!0),db=G(),eb=G(),fb=G(),gb=G(),hb=G(),ib=G(),jb=G(),kb=G(),lb=G(),mb=G(),N=G(!0),nb=G(!0),ob=G(!0),rb=G(!0),sb=G(!0),tb=G(!0),ub=G(!0),vb=G(!0),wb=G(!0),xb=G(!0),yb=G(!0),O=G(!0),zb=G(!0),Ab=G(!0),Bb=G(!0),Cb=G(!0),Db=G(!0),Eb=G(!0),Fb=G(!0),Gb=G(!0),Hb=G(!0),Ib=G(!0),Jb=G(!0),Kb=G(!0),Lb=G(!0),Mb=G(),Nb=G();G();var Ob=G(),Pb=G(),Qb=G(),Tb=G(),Ub=G(),Vb=G(),Wb=G(),Xb=G();G();var Yb=G(),Zb=G();var $b=function(){function a(a,c,d){P(Q[s],a,c,d)}R("_getName",Ba,58);R("_getAccount",za,64);R("_visitCode",N,54);R("_getClientInfo",Ia,53,1);R("_getDetectTitle",La,56,1);R("_getDetectFlash",Ja,65,1);R("_getLocalGifPath",Va,57);R("_getServiceMode",Wa,59);S("_setClientInfo",Ia,66,2);S("_setAccount",za,3);S("_setNamespace",Aa,48);S("_setAllowLinker",Fa,11,2);S("_setDetectFlash",Ja,61,2);S("_setDetectTitle",La,62,2);S("_setLocalGifPath",Va,46,0);S("_setLocalServerMode",Wa,92,g,0);S("_setRemoteServerMode",
-Wa,63,g,1);S("_setLocalRemoteServerMode",Wa,47,g,2);S("_setSampleRate",Ua,45,1);S("_setCampaignTrack",Ka,36,2);S("_setAllowAnchor",Ga,7,2);S("_setCampNameKey",Na,41);S("_setCampContentKey",Sa,38);S("_setCampIdKey",Ma,39);S("_setCampMediumKey",Qa,40);S("_setCampNOKey",Ta,42);S("_setCampSourceKey",Pa,43);S("_setCampTermKey",Ra,44);S("_setCampCIdKey",Oa,37);S("_setCookiePath",L,9,0);S("_setMaxCustomVariables",Xa,0,1);S("_setVisitorCookieTimeout",Ca,28,1);S("_setSessionCookieTimeout",Da,26,1);S("_setCampaignCookieTimeout",
-Ea,29,1);S("_setReferrerOverride",fb,49);a("_trackPageview",Q[s].ka,1);a("_trackEvent",Q[s].t,4);a("_trackSocial",Q[s].la,104);a("_trackPageLoadTime",Q[s].ja,100);a("_trackTrans",Q[s].ma,18);a("_sendXEvent",Q[s].s,78);a("_createEventTracker",Q[s].S,74);a("_getVersion",Q[s].X,60);a("_setDomainName",Q[s].r,6);a("_setAllowHash",Q[s].ba,8);a("_getLinkerUrl",Q[s].W,52);a("_link",Q[s].link,101);a("_linkByPost",Q[s].aa,102);a("_setTrans",Q[s].ea,20);a("_addTrans",Q[s].L,21);a("_addItem",Q[s].J,19);a("_setTransactionDelim",
-Q[s].fa,82);a("_setCustomVar",Q[s].ca,10);a("_deleteCustomVar",Q[s].U,35);a("_getVisitorCustomVar",Q[s].Y,50);a("_setXKey",Q[s].ha,83);a("_setXValue",Q[s].ia,84);a("_getXKey",Q[s].Z,76);a("_getXValue",Q[s].$,77);a("_clearXKey",Q[s].P,72);a("_clearXValue",Q[s].Q,73);a("_createXObj",Q[s].T,75);a("_addIgnoredOrganic",Q[s].H,15);a("_clearIgnoredOrganic",Q[s].M,97);a("_addIgnoredRef",Q[s].I,31);a("_clearIgnoredRef",Q[s].N,32);a("_addOrganic",Q[s].K,14);a("_clearOrganic",Q[s].O,70);a("_cookiePathCopy",
-Q[s].R,30);a("_get",Q[s].V,106);a("_set",Q[s].da,107);a("_addEventListener",Q[s].addEventListener,108);a("_removeEventListener",Q[s].removeEventListener,109);a("_initData",Q[s].l,2);a("_setVar",Q[s].ga,22);S("_setSessionTimeout",Da,27,3);S("_setCookieTimeout",Ea,25,3);S("_setCookiePersistence",Ca,24,1);a("_setAutoTrackOutbound",oa,79);a("_setTrackOutboundSubdomains",oa,81);a("_setHrefExamineLimit",oa,80)},P=function(a,b,c,d){a[b]=function(){D(d);return c.apply(this,arguments)}},R=function(a,b,c,d){Q[s][a]=
-function(){D(c);return ka(this.a.get(b),d)}},S=function(a,b,c,d,e){Q[s][a]=function(a){D(c);e==g?this.a.set(b,ka(a,d)):this.a.set(b,e)}},ac=function(a,b){return{type:b,target:a,stopPropagation:function(){throw"aborted";}}};var bc=function(a,b){return b!=="/"?!1:(a[m]("www.google.")==0||a[m](".google.")==0||a[m]("google.")==0)&&!(a[m]("google.org")>-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<b[r];c++){var d=b[c];if(""+a==d||d[m](a+".")==0)return d}return"-"}
-var hc=function(a,b){var c=a.b(K,1),d=b[t](".");if(d[r]!==6||d[0]!=c)return!1;var c=d[1]*1,e=d[2]*1,f=d[3]*1,j=d[4]*1,d=d[5]*1;if(!(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;f<e[r];f++){var j=e[f];j&&j[ia]==1&&d[k](f+"="+C(j[ga])+"="+C(j[ea])+"=1")}d[r]>0&&(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<d[r];e++){var f=d[e][t]("=");if(f[r]==4){var j={};ca(j,E(f[1]));j.value=E(f[2]);j.scope=1;a.get(M)[f[0]]=j}}c[1][m]("^")>=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<this.q[r];b++)this.q[b].ua.call(U,a)}catch(c){}};function rc(a){a.get(Ua)!=100&&a.get(N)%1E4>=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<a[r];c++)a[c]&&(b[i.floor(c/6)]^=1<<c%6);for(c=0;c<b[r];c++)b[c]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"[l](b[c]||0);return b[y]("")+"~"}};function D(a){wc.set(a)};var U=window,F=document,tc=function(){var a=U._gaUserPrefs;return a&&a.ioo&&a.ioo()},xc=function(a,b){setTimeout(a,b)},V=function(a){for(var b=[],c=F.cookie[t](";"),a=RegExp("^\\s*"+a+"=\\s*(.*?)\\s*$"),d=0;d<c[r];d++){var e=c[d][fa](a);e&&b[k](e[1])}return b},X=function(a,b,c,d,e){var f;f=tc()?!1:bc(d,c)?!1:!0;if(f){if(b&&U[ja].userAgent[m]("Firefox")>=0){b=b.replace(/\n|\r/g," ");f=0;for(var j=b[r];f<j;++f){var p=b.charCodeAt(f)&255;if(p==10||p==13)b=b[x](0,f)+"?"+b[x](f+1)}}b&&b[r]>2E3&&(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<b[r]&&!e;c++)d=b[c],d[ga][m]("Shockwave Flash")>-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;d<b[r];d++)try{if(typeof b[d]==="function")b[d]();else{var e="",f=b[d][0],j=f.lastIndexOf(".");j>0&&(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;d<j[r];d++)if(g!=f[a][j[d]]){c=!1;break}c&&(f[a]=g)}}function d(a){var b="",c=!1,d,e;for(d=0;d<j[r];d++)if(e=a[j[d]],g!=e){c&&(b+=j[d]);for(var c=[],f=g,W=g,W=0;W<e[r];W++)if(g!=e[W]){f="";W!=kd&&g==e[W-1]&&(f+=W[o]()+pb);for(var Ic=e[W],Jc="",qb=g,Rb=g,Sb=g,qb=0;qb<Ic[r];qb++)Rb=
-Ic[l](qb),Sb=I[Rb],Jc+=g!=Sb?Sb:Rb;f+=Jc;c[k](f)}b+=p+c[y](q)+n;c=!1}else c=!0;return b}var e=this,f=[],j=["k","v"],p="(",n=")",q="*",pb="!",I={"'":"'0"};I[n]="'1";I[q]="'2";I[pb]="'3";var kd=1;e.qa=function(a){return g!=f[a]};e.n=function(){for(var a="",b=0;b<f[r];b++)g!=f[b]&&(a+=b[o]()+d(f[b]));return a};e.pa=function(a){if(a==g)return e.n();for(var b=a.n(),c=0;c<f[r];c++)g!=f[c]&&!a.qa(c)&&(b+=c[o]()+d(f[c]));return b};e.e=function(b,c,d){if(!Ec(d))return!1;a(b,"k",c,d);return!0};e.j=function(b,
-c,d){if(!Fc(d))return!1;a(b,"v",c,d[o]());return!0};e.w=function(a,c){return b(a,"k",c)};e.z=function(a,c){return b(a,"v",c)};e.u=function(a){c(a,"k")};e.v=function(a){c(a,"v")};P(e,"_setKey",e.e,89);P(e,"_setValue",e.j,90);P(e,"_getKey",e.w,87);P(e,"_getValue",e.z,88);P(e,"_clearKey",e.u,85);P(e,"_clearValue",e.v,86)};function Ec(a){return typeof a=="string"}function Fc(a){return typeof a!="number"&&(g==Number||!(a instanceof Number))||i.round(a)!=a||a==NaN||a==Infinity?!1:!0};var Hc=function(a){var b=U.gaGlobal;a&&!b&&(U.gaGlobal=b={});return b},Kc=function(){var a=Hc(!0).hid;if(a==h)a=na(),Hc(!0).hid=a;return a},Lc=function(a){a.set(gb,Kc());var b=Hc();if(b&&b.dh==a.get(K)){var c=b.sid;c&&(c=="0"&&D(112),a.set(ub,c),a.get(nb)&&a.set(tb,c));b=b.vid;a.get(nb)&&b&&(b=b[t]("."),b[1]*1||D(112),a.set(N,b[0]*1),a.set(sb,b[1]*1))}};var Mc,Nc=function(a,b,c){var d=a[w](J,""),e=a[w](L,"/"),a=a.b(Ca,0);X(b,c,e,d,a)},fc=function(a){var b=a[w](J,"");a.b(K,1);var c=a[w](L,"/");X("__utma",ic(a),c,b,a.get(Ca));X("__utmb",jc(a),c,b,a.get(Da));X("__utmc",""+a.b(K,1),c,b);var d=oc(a,!0);d?X("__utmz",d,c,b,a.get(Ea)):X("__utmz","",c,b,-1);(d=lc(a,!1))?X("__utmv",d,c,b,a.get(Ca)):X("__utmv","",c,b,-1)},ec=function(a){var b=a.b(K,1);if(!hc(a,T(b,V("__utma"))))return a.set(rb,!0),!1;var c=!kc(a,T(b,V("__utmb"))),d=T(b,V("__utmc"))!=a.b(K,
-1);d&&!c&&D(116);a.set(xb,c||d);qc(a,T(b,V("__utmz")));mc(a,T(b,V("__utmv")));Mc=!c;return!0},Oc=function(a){!Mc&&!(V("__utmb")[r]>0)&&(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<Sc[r];d++){var e=Sc[d];c[e]=a.get(e)}d=va(F[v].href,a.get(Ga));if(!(sa(d.c.get(a.get(Ta)))=="1"&&b)&&(!Tc(a,d)&&!Uc(a)&&!b&&a.get(wb)&&a.get(wb)&&Vc(a,g,"(direct)",g,g,"(direct)","(none)",g,g),a.set(Db,Wc(a,c)),b=a.get(Ib)=="(direct)"&&a.get(Fb)=="(direct)"&&a.get(Jb)=="(none)",a.get(Db)||a.get(wb)&&!b))a.set(Ab,a.get(H)),a.set(Bb,a.get(vb)),a.m(Cb)}},Tc=function(a,b){function c(c,d){var d=d||"-",e=sa(b.c.get(a.get(c)));return e&&e!="-"?E(e):d}var d=sa(b.c.get(a.get(Ma)))||
-"-",e=sa(b.c.get(a.get(Pa)))||"-",f=sa(b.c.get(a.get(Oa)))||"-",j=sa(b.c.get("dclid"))||"-",p=c(Na,"(not set)"),n=c(Qa,"(not set)"),q=c(Ra),pb=c(Sa);if(B(d)&&B(f)&&B(j)&&B(e))return!1;if(B(q)){var I=xa(a.get(fb),a.get(L)),I=va(I,!0);(I=Yc(a,I))&&!B(I[1]&&!I[2])&&(q=I[1])}Vc(a,d,e,f,j,p,n,q,pb);return!0},Uc=function(a){var b=xa(a.get(fb),a.get(L)),c=va(b,!0);if(!(b!=g&&b!=h&&b!=""&&b!="0"&&b!="-"&&b[m]("://")>=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<b[r];++e)if(d[m](b[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<c[r];++d){var e=c[d][t](":");if(b[ha][m](e[0][z]())>-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;j<d[r];++j)if(c==d[j]){c=!0;break a}c=!1}return[e[0],f,c]}}}return h},Vc=function(a,
-b,c,d,e,f,j,p,n){a.set(Eb,b);a.set(Ib,c);a.set(Gb,d);a.set(Hb,e);a.set(Fb,f);a.set(Jb,j);a.set(Kb,p);a.set(Lb,n)},Sc=[Fb,Eb,Gb,Hb,Ib,Jb,Kb,Lb],Wc=function(a,b){for(var c=0;c<Sc[r];c++){var d=Sc[c],e=b[d]||"-",d=a.get(d)||"-";if(e!=d)return!0}return!1};var $c=function(a){Zc(a,F[v].href)?(a.set(Yb,!0),D(12)):a.set(Yb,!1)},Zc=function(a,b){if(!a.get(Fa))return!1;var c=a.b(K,1),d=va(b,a.get(Ga)),e=T(c,d.c.get("__utma")),f=T(c,d.c.get("__utmb")),j=T(c,d.c.get("__utmc")),p=T(c,d.c.get("__utmx")),n=T(c,d.c.get("__utmz")),q=T(c,d.c.get("__utmv")),d=ra(d.c.get("__utmk"));if(ma(""+e+f+j+p+n+q)!=d)return!1;if(!hc(a,e))return e&&e[m](c+".")!=0&&D(126),!1;kc(a,f);a.b(K,1);qc(a,n);mc(a,q);c=E(p);e=a.b(K,1);f=c[t](".");f[r]<2||f[0]!=e||Nc(a,"__utmx",c);return!0},
-ad=function(a,b,c){var d;a.b(K,1);d=ic(a)||"-";var e=jc(a)||"-",f=""+a.b(K,1)||"-",j=ra(V("__utmx"))||"-",p=oc(a,!1)||"-",a=lc(a,!1)||"-",n=ma(""+d+e+f+j+p+a),q=[];q[k]("__utma="+d);q[k]("__utmb="+e);q[k]("__utmc="+f);q[k]("__utmx="+j);q[k]("__utmz="+p);q[k]("__utmv="+a);q[k]("__utmk="+n);d=q[y]("&");if(!d)return b;e=b[m]("#");return c?e<0?b+"#"+d:b+"&"+d:(c="",f=b[m]("?"),e>0&&(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<p[r];n++)if(p[n].sku_==c){p=p[n];break a}}p=h}n=p||{};n.transId_=b;n.sku_=c;n.name_=d;n.category_=e;n.price_=f;n.quantity_=j;p||a.items_[k](n);return n},cd=function(a,b){for(var c=a.get(ab),
-d=0;d<c[r];d++)if(c[d].id_==b)return c[d];return h};var fd,gd=function(a){var f;var e;if(!fd){var b;b=F[v].hash;var c=U[ga],d=/^#?gaso=([^&]*)/;if(f=(e=(b=b&&b[fa](d)||c&&c[fa](d))?b[1]:ra(V("GASO")),b=e)&&b[fa](/^(?:\|([-0-9a-z.]{1,30})\|)?([-.\w]{10,1200})$/i),c=f)if(Nc(a,"GASO",""+b),Z._gasoDomain=a.get(J),Z._gasoCPath=a.get(L),b="https://"+((c[1]||"www")+".google.com")+"/analytics/reporting/overlay_js?gaso="+c[2]+"&"+na())a=F.createElement("script"),a.type="text/javascript",a.async=!0,a.src=b,a.id="_gasojs",a.onload=g,b=F.getElementsByTagName("script")[0],
-b.parentNode.insertBefore(a,b);fd=!0}};var ld=function(a,b){if(a.b(N,0)%100>=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<b[r];d++)b[d].call(e,c)}}var e=this;this.a=new gc;this.get=function(a){return this.a.get(a)};this.set=function(a,b,c){this.a.set(a,b,c)};this.set(za,b||"UA-XXXXX-X");this.set(Ba,a||"");this.set(Aa,c||"");this.set(H,i.round((new Date).getTime()/1E3));this.set(L,"/");this.set(Ca,63072E6);this.set(Ea,15768E6);this.set(Da,18E5);this.set(Fa,!1);this.set(Xa,50);this.set(Ga,!1);this.set(Ha,!0);this.set(Ia,
-!0);this.set(Ja,!0);this.set(Ka,!0);this.set(La,!0);this.set(Na,"utm_campaign");this.set(Ma,"utm_id");this.set(Oa,"gclid");this.set(Pa,"utm_source");this.set(Qa,"utm_medium");this.set(Ra,"utm_term");this.set(Sa,"utm_content");this.set(Ta,"utm_nooverride");this.set(Ua,100);this.set(Xb,10);this.set(Va,"/__utm.gif");this.set(Wa,1);this.set(ab,[]);this.set(M,[]);this.set(Ya,Rc);this.set(Za,[]);this.set($a,[]);this.r("auto");this.set(fb,F.referrer);this.set(Zb,{hit:[],load:[]});this.a.h("0",$c);this.a.h("1",
-Qc);this.a.h("2",Xc);this.a.h("4",d("load"));this.a.h("5",gd);this.a.d("A",sc);this.a.d("B",uc);this.a.d("C",Qc);this.a.d("D",rc);this.a.d("E",cc);this.a.d("F",md);this.a.d("G",Oc);this.a.d("H",vc);this.a.d("I",Cc);this.a.d("J",Lc);this.a.d("K",d("hit"));this.a.d("L",nd);this.a.d("M",od);this.get(H)===0&&D(111);this.a.G()};A=Q[s];A.g=function(){var a=this.get(bb);a||(a=new Gc,this.set(bb,a));return a};
-A.oa=function(a){for(var b in a){var c=a[b];a.hasOwnProperty(b)&&typeof c!="function"&&this.set(b,c,!0)}};A.ka=function(a){a&&a!=g&&(a.constructor+"")[m]("String")>-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&&d<c[r];d++)if(c[d]==b){c.splice(d,1);break}};A.X=function(){return"5.1.2"};A.r=function(a){this.get(Ha);a=a=="auto"?ua(F.domain):!a||a=="-"||a=="none"?"":a[z]();this.set(J,a)};
-A.ba=function(a){this.set(Ha,!!a)};A.W=function(a,b){return ad(this.a,a,b)};A.link=function(a,b){if(this.a.get(Fa)&&a){var c=ad(this.a,a,b);F[v].href=c}};A.aa=function(a,b){this.a.get(Fa)&&a&&a.action&&(a.action=ad(this.a,a.action,b))};
-A.ea=function(){this.l();var a=this.a,b=F.getElementById?F.getElementById("utmtrans"):F.utmform&&F.utmform.utmtrans?F.utmform.utmtrans:h;if(b&&b[ea]){a.set(ab,[]);for(var b=b[ea][t]("UTM:"),c=0;c<b[r];c++){b[c]=la(b[c]);for(var d=b[c][t](bd),e=0;e<d[r];e++)d[e]=la(d[e]);"T"==d[0]?dd(a,d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8]):"I"==d[0]&&ed(a,d[1],d[2],d[3],d[4],d[5],d[6])}}};A.L=function(a,b,c,d,e,f,j,p){return dd(this.a,a,b,c,d,e,f,j,p)};A.J=function(a,b,c,d,e,f){return ed(this.a,a,b,c,d,e,f)};
-A.fa=function(a){bd=a||"|"};A.ca=function(a,b,c,d){var e=this.a;if(a<=0||a>e.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<e[r];f++){var j=e[f];j&&(c||(c=new Gc),c.e(8,f,j[ga]),c.e(9,f,j[ea]),j[ia]!=3&&c.e(11,f,""+j[ia]))}!B(a.get(Ob))&&!B(a.get(Pb))&&(c||(c=new Gc),c.e(5,1,a.get(Ob)),c.e(5,2,a.get(Pb)),e=a.get(Qb),e!=g&&c.e(5,3,e),e=a.get(Tb),e!=g&&c.j(5,1,e));c?b.add("utme",c.pa(d),!0):d&&b.add("utme",d.n(),!0)},xd=function(a,b,c){var d=new pd;qd(a,c);rd(a,d);d.add("utmt","tran");d.add("utmtid",b.id_,!0);d.add("utmtst",b.affiliation_,!0);d.add("utmtto",b.total_,!0);d.add("utmttx",b.tax_,!0);d.add("utmtsp",
-b.shipping_,!0);d.add("utmtci",b.city_,!0);d.add("utmtrg",b.state_,!0);d.add("utmtco",b.country_,!0);!c&&td(a,d);return d[o]()},yd=function(a,b,c){var d=new pd;qd(a,c);rd(a,d);d.add("utmt","item");d.add("utmtid",b.transId_,!0);d.add("utmipc",b.sku_,!0);d.add("utmipn",b.name_,!0);d.add("utmiva",b.category_,!0);d.add("utmipr",b.price_,!0);d.add("utmiqt",b.quantity_,!0);!c&&td(a,d);return d[o]()},zd=function(a,b){var c=a.get(Mb);if(c=="page")c=new pd,qd(a,b),rd(a,c),wd(a,c),ud(a,c),vd(a,c),!b&&td(a,
-c),c=[c[o]()];else if(c=="event")c=new pd,qd(a,b),rd(a,c),c.add("utmt","event"),wd(a,c),ud(a,c),vd(a,c),!b&&td(a,c),c=[c[o]()];else if(c=="var")c=new pd,qd(a,b),rd(a,c),c.add("utmt","var"),!b&&td(a,c),c=[c[o]()];else if(c=="trans")for(var c=[],d=a.get(ab),e=0;e<d[r];++e){c[k](xd(a,d[e],b));for(var f=d[e].items_,j=0;j<f[r];++j)c[k](yd(a,f[j],b))}else c=="social"?b?c=[]:(c=new pd,qd(a,b),rd(a,c),c.add("utmt","social"),c.add("utmsn",a.get(Ub),!0),c.add("utmsa",a.get(Vb),!0),c.add("utmsid",a.get(Wb),
-!0),wd(a,c),ud(a,c),vd(a,c),td(a,c),c=[c[o]()]):c=[];return c},nd=function(a){var b,c=a.get(Nb),d=a.get(Wa);if(d==0||d==2){var e=a.get(Va)+"?";b=zd(a,!0);for(var f=0,j=b[r];f<j;f++)Ad(b[f],d!=2&&f==j-1&&c,e,!0)}if(d==1||d==2){b=zd(a);f=0;for(j=b[r];f<j;f++)try{Ad(b[f],f==j-1&&c)}catch(p){var d=a,e=p,n=new pd;n.add("err",e[ga]);n.add("max",e.message);n.add("len",e.D);n.add("utmwv","5.1.2e");n.add("utmac",d.get(za));n.add("utmn",na());Z.o&&n.add("aip",1);Ad(n[o]())}}};var Bd="https:"==F[v].protocol?"https://ssl.google-analytics.com":"http://www.google-analytics.com",Cd=function(a){ca(this,"len");this.message=8192;this.D=a},Dd=function(a){ca(this,"ff2post");this.message=2036;this.D=a},Ad=function(a,b,c,d){b=b||oa;if(d||a[r]<=2036)Ed(a,b,c);else if(a[r]<=8192){if(U[ja].userAgent[m]("Firefox")>=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('<iframe name="'+a+'"></iframe>')}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)});})();

+ 0 - 190
eBook/examples/chapter_15/Codelab Writing Web Applications - The Go Programming Language_files/godocs.js

@@ -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 <h2>This <i>sweet</i> header</h2>.
- * 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);
-  }
-}

+ 0 - 36
eBook/examples/chapter_15/client.go

@@ -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))
-	}
-}

BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/00changelog.i


+ 0 - 1
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/branch

@@ -1 +0,0 @@
-default

+ 0 - 2
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/branchheads

@@ -1,2 +0,0 @@
-844fb91a777b63798d4657e1c40669e8968f79ad 4
-844fb91a777b63798d4657e1c40669e8968f79ad default

+ 0 - 2
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/cache/tags

@@ -1,2 +0,0 @@
-4 844fb91a777b63798d4657e1c40669e8968f79ad
-

BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/dirstate


+ 0 - 2
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/hgrc

@@ -1,2 +0,0 @@
-[paths]
-default = https://code.google.com/p/go.net

+ 0 - 4
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/requires

@@ -1,4 +0,0 @@
-revlogv1
-store
-fncache
-dotencode

BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00changelog.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/00manifest.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_a_u_t_h_o_r_s.i


BIN
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


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_l_i_c_e_n_s_e.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/_r_e_a_d_m_e.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/codereview.cfg.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/dict/dict.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/read.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/spdy__test.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/types.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/spdy/write.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/client.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hixie__test.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/hybi__test.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/server.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/websocket/websocket__test.go.i


BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/data/~2ehgignore.i


+ 0 - 19
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/fncache

@@ -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

BIN
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/store/undo


+ 0 - 0
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.bookmarks


+ 0 - 1
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.branch

@@ -1 +0,0 @@
-default

+ 0 - 3
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.desc

@@ -1,3 +0,0 @@
-0
-pull
-https://code.google.com/p/go.net

+ 0 - 0
eBook/examples/chapter_15/code.google.com/p/go.net/.hg/undo.dirstate


+ 0 - 2
eBook/examples/chapter_15/code.google.com/p/go.net/.hgignore

@@ -1,2 +0,0 @@
-syntax:glob
-last-change

+ 0 - 3
eBook/examples/chapter_15/code.google.com/p/go.net/AUTHORS

@@ -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.

+ 0 - 3
eBook/examples/chapter_15/code.google.com/p/go.net/CONTRIBUTORS

@@ -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.

+ 0 - 27
eBook/examples/chapter_15/code.google.com/p/go.net/LICENSE

@@ -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.

+ 0 - 3
eBook/examples/chapter_15/code.google.com/p/go.net/README

@@ -1,3 +0,0 @@
-This repository holds supplementary Go networking libraries.
-
-To submit changes to this repository, see http://golang.org/doc/contribute.html.

+ 0 - 2
eBook/examples/chapter_15/code.google.com/p/go.net/codereview.cfg

@@ -1,2 +0,0 @@
-defaultcc: [email protected]
-contributors: http://go.googlecode.com/hg/CONTRIBUTORS

+ 0 - 210
eBook/examples/chapter_15/code.google.com/p/go.net/dict/dict.go

@@ -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])
-}

+ 0 - 312
eBook/examples/chapter_15/code.google.com/p/go.net/spdy/read.go

@@ -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
-}

+ 0 - 497
eBook/examples/chapter_15/code.google.com/p/go.net/spdy/spdy_test.go

@@ -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)
-	}
-}

+ 0 - 369
eBook/examples/chapter_15/code.google.com/p/go.net/spdy/types.go

@@ -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
-}

+ 0 - 285
eBook/examples/chapter_15/code.google.com/p/go.net/spdy/write.go

@@ -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
-}

+ 0 - 137
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/client.go

@@ -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}
-}

+ 0 - 695
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie.go

@@ -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<n> 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<n>: 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<n> fields.
-	keyNumber1 := getKeyNumber(key1)
-	keyNumber2 := getKeyNumber(key2)
-
-	// Step 5. get number of spaces in Sec-WebSocket-Key<n> 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)
-}

+ 0 - 201
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hixie_test.go

@@ -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)
-	}
-}

+ 0 - 549
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi.go

@@ -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)
-}

+ 0 - 584
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/hybi_test.go

@@ -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())
-	}
-}

+ 0 - 102
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/server.go

@@ -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)
-}

+ 0 - 412
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket.go

@@ -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}

+ 0 - 274
eBook/examples/chapter_15/code.google.com/p/go.net/websocket/websocket_test.go

@@ -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()
-}

+ 0 - 29
eBook/examples/chapter_15/dial.go

@@ -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)
-
-}

+ 0 - 146
eBook/examples/chapter_15/elaborated_webserver.go

@@ -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
-	}
-}

+ 0 - 24
eBook/examples/chapter_15/hello_world_webserver.go

@@ -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))
-}

+ 0 - 23
eBook/examples/chapter_15/http_fetch.go

@@ -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)
-	}
-}

+ 0 - 17
eBook/examples/chapter_15/pipeline1.go

@@ -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.
-*/

+ 0 - 30
eBook/examples/chapter_15/poll_url.go

@@ -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
-*/

+ 0 - 14
eBook/examples/chapter_15/predefined_functions.go

@@ -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!

+ 0 - 57
eBook/examples/chapter_15/robust_webserver.go

@@ -1,57 +0,0 @@
-// robust_webserver.go
-package main  
-  
-import (  
-     "net/http"  
-     "io"  
-	 "log"
-)  
-
-const form = `<html><body><form action="#" method="post" name="bar">
-		      <input type="text" name="in"/>
-			  <input type="submit" value="Submit"/>
-			  </form></html></body>`
-
-type HandleFnc func(http.ResponseWriter,*http.Request)
-
-/* handle a simple get request */  
-func SimpleServer(w http.ResponseWriter, request *http.Request) {  
-     io.WriteString(w, "<h1>hello, world</h1>")  
-}  
-  
-/* 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)
-    }
-}
-

+ 0 - 39
eBook/examples/chapter_15/rpc_client.go

@@ -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
-*/

+ 0 - 13
eBook/examples/chapter_15/rpc_objects.go

@@ -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
-}

+ 0 - 32
eBook/examples/chapter_15/rpc_server.go

@@ -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
-*/

+ 0 - 37
eBook/examples/chapter_15/server.go

@@ -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))
-	}
-}

+ 0 - 86
eBook/examples/chapter_15/simple_tcp_server.go

@@ -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
-	}
-}

+ 0 - 42
eBook/examples/chapter_15/simple_webserver.go

@@ -1,42 +0,0 @@
-// simple_webserver.go
-package main  
-  
-import (  
-     "net/http"  
-     "io"  
-)  
-
-const form = `<html><body><form action="#" method="post" name="bar">
-		      <input type="text" name="in"/>
-			  <input type="submit" value="Submit"/>
-			  </form></html></body>`
-
-/* handle a simple get request */  
-func SimpleServer(w http.ResponseWriter, request *http.Request) {  
-     io.WriteString(w, "<h1>hello, world</h1>")  
-}  
-  
-/* 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)
-     }
-}  

+ 0 - 30
eBook/examples/chapter_15/smtp.go

@@ -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("[email protected]")
-        client.Rcpt("[email protected]")
-        // 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)
-        }
-}
-

+ 0 - 29
eBook/examples/chapter_15/smtp_auth.go

@@ -1,29 +0,0 @@
-// smtp_auth.go
-package main
-
-import (
-        "log"
-        "net/smtp"
-)
-
-func main() {
-        // Set up authentication information.
-        auth := smtp.PlainAuth(
-                "",
-                "[email protected]",
-                "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,
-                "[email protected]",
-                []string{"[email protected]"},
-                []byte("This is the email body."),
-        )
-        if err != nil {
-                log.Fatal(err)
-        }
-}

+ 0 - 32
eBook/examples/chapter_15/socket.go

@@ -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()  
-}  

+ 0 - 23
eBook/examples/chapter_15/template_field.go

@@ -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!

+ 0 - 26
eBook/examples/chapter_15/template_ifelse.go

@@ -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. 
-*/

+ 0 - 22
eBook/examples/chapter_15/template_validation.go

@@ -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
-*/

+ 0 - 24
eBook/examples/chapter_15/template_variables.go

@@ -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!
-*/

+ 0 - 20
eBook/examples/chapter_15/template_with_end.go

@@ -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!
-*/

+ 0 - 42
eBook/examples/chapter_15/twitter_status.go

@@ -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<exit code="0" msg="process exited normally"/>
-After Go1: no output: name: { user} status:
-*/

+ 0 - 29
eBook/examples/chapter_15/websocket_client.go

@@ -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
-		}
-	}
-}

+ 0 - 29
eBook/examples/chapter_15/websocket_server.go

@@ -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())
-	}
-}

+ 0 - 2
eBook/examples/chapter_15/wiki/ANewPage.txt

@@ -1,2 +0,0 @@
-Testing input of new page!
-Go Go Go !

+ 0 - 1
eBook/examples/chapter_15/wiki/TestPage.txt

@@ -1 +0,0 @@
-This is a sample Page.

+ 0 - 6
eBook/examples/chapter_15/wiki/edit.html

@@ -1,6 +0,0 @@
-<h1>Editing {{.Title |html}}</h1>
-
-<form action="/save/{{.Title |html}}" method="POST">
-<div><textarea name="body" rows="20" cols="80">{{printf "%s" .Body|html}}</textarea></div>
-<div><input type="submit" value="Save"></div>
-</form>

+ 0 - 2
eBook/examples/chapter_15/wiki/page.txt

@@ -1,2 +0,0 @@
-Hello Go - World !!!
-This works great.

+ 0 - 1
eBook/examples/chapter_15/wiki/page1.txt

@@ -1 +0,0 @@
-This is a test!!

+ 0 - 3
eBook/examples/chapter_15/wiki/page5.txt

@@ -1,3 +0,0 @@
-Page5 is hereby started.
-This is a first addition.
-2nd addition.

+ 0 - 5
eBook/examples/chapter_15/wiki/view.html

@@ -1,5 +0,0 @@
-<h1>{{.Title |html}}</h1>
-
-<p>[<a href="/edit/{{.Title |html}}">edit</a>]</p>
-
-<div>{{printf "%s" .Body |html}}</div>

+ 0 - 97
eBook/examples/chapter_15/wiki/wiki.go

@@ -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
-}

+ 0 - 32
eBook/examples/chapter_15/wiki/wiki_part1.go

@@ -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))
-}

+ 0 - 39
eBook/examples/chapter_15/wiki/wiki_part2.go

@@ -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, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)
-}
-
-func main() {
-	http.HandleFunc("/view/", viewHandler)
-	http.ListenAndServe(":8080", nil)
-}

+ 12 - 0
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")
+	}
+}

+ 9 - 0
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)
+	}
+}

+ 12 - 0
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)
+	}
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä